|
www.angelfire.com/dragon/letstry
cwave04 at yahoo dot com |
|
|
Using the PC parallel port
We want to send pulses to the microcontroller from the parallel
port. So
let us make sure we can indeed send pulses through the parallel
port. First we must familiarise ourselves with the pins of
the parallel port.
The basics
The parallel port connector is commonly called
DB-25. The connector that sticks out of the back side of your PC
is a female connector. You'll need a matching male connector. The
connector has 25 pins arranged as follows.
|
Parallel port |
Consider the left hand diagram first. This is drawn looking into
the female DB25 connector at the back of a PC. If you look
carefully at a male connector you'll see the numbers 1 to 25
engraved beside the pins for easy identification. Make sure you
know for sure which pin is which. The parallel port is actually
made of three ports put together. Each of these ports is
identified by a number. The numbers are (by default) 0x378, 0x379 and 0x37A.
The red pins corrspond to 0x378. Thus if you write a byte to this
port using a C instruction like
Out32(0x378,0x30);
the bit pattern for 0x30 will show up through the pins. Since
0x30 is 00110000 in binary this means pins 6 and 7 will show 5V,
while pins pins 2, 3, 4, 5, 8 and 9 will be at 0V. By the way
Out32 is not a standard C function. We shall soon
learn how to get it.
These pins are dedicated for output purposes. If you
externally connect one of these pins to 0V (or +5V) this will not
change the value of the port numbered 0x378 (though there is some
chance of your parallel port being damaged). However, I have done this
mistake a number of times without destroying the parallel port. Experiment
at your own risk!
The port numbered 0x379 is internally connected to the blue pins. Notice the
wierd numbering of these pins. If you externally connect thse pins to 0V
(or +5V)
and then try to read the port using a C statement like
val = Inp32(0x379);
you'll find that bits 3,4,5,6 and 7 reflect the logic values
present at the corresponding pins. But pins 0,1 and 2 will have
fixed values. The Inp32 function, just like the
Out32 function, is not a standard C function. We shall
discuss about it soon.
The ocre coloured pins are connected to port number 0x37A. These
are typically output pins (though on some machines they may also
be used as input pins). We shall not go into their details as we
shall not use them in our programmer.
The green pins are all grounds.
Communicating with the parallel port
Now that we know the basics of the parallel port we shall
manipulate them using software. This step is slightly tricky owing to the
diversity among operating systems.
- DOS or Windows 95/98: The
inport and
outport functions of Turbo C will be all that you
need to communicate with the parallel port. I have not tried this.
- Windows XP: You will need a dll file called
inpout32.dll (there are plenty of sites providing it for
free. Just do a web search). This dll has the functions Out32
and Inp32 that we have mentioned above. We shall discuss how
to access these functions from the dll file soon. You'll need root
privilege to communicate with the parallel port.
- Knoppix (an on-CD
version of Linux): The GNU C compiler in gcc has the functions
outb and inb in the header
asm/io.h .
- Fedora 8: I tried to use
asm/io.h just as
in Knoppix, but the gcc installed in my machine did not have that
header file. One possible option was to install a more
comprehensive version of gcc, which did not appear that easy!
Instead, I used a different technique: I open ed
/dev/port and accessed the ports directly. I shall
show how to do this presently.
I am fond of the C language, so I have talked about only C.
But
other programming languages (like Java, Ruby, VB) should be fine
too (these can load inpout32.dll). In Java you have to use native code.
Writing to the parallel port
Using Borland C in Windows XP
Here is a simple C code (to be compiled with the free Borland C
commandline compiler) that writes a byte through your parallel port.
writefixed.c
#define PPORT_BASE 0x378
#define PPORT_OUT PPORT_BASE
#define PPORT_INP (PPORT_BASE+1)
| 1 |
typedef short (_stdcall *inpfuncPtr)(short portaddr);
typedef void (_stdcall *oupfuncPtr)(short portaddr, short datum);
inpfuncPtr inp32fp;
oupfuncPtr oup32fp;
| 2 |
void init(void) {
HINSTANCE hLib;
hLib = LoadLibrary("inpout32.dll");
if(hLib == NULL) {
fprintf(stderr,"LoadLibrary Failed.\n");
exit(1);
}
inp32fp = (inpfuncPtr) GetProcAddress(hLib, "Inp32");
if(inp32fp == NULL) {
fprintf(stderr,"GetProcAddress for Inp32 Failed.\n");
exit(1);
}
oup32fp = (oupfuncPtr) GetProcAddress(hLib, "Out32");
if(oup32fp == NULL) {
fprintf(stderr,"GetProcAddress for Out32 Failed.\n");
exit(1);
}
}
| 3 |
short Inp32 (short portaddr) {
return (inp32fp)(portaddr);
}
void Out32 (short portaddr, short datum) {
(oup32fp)(portaddr,datum);
}
| 4 |
void main() {
init();
Out32(PPORT_OUT, 0x34);
}
| 5 |
|
Explanation of the code:
-
1:
- These are the addresses of the three memory locations
associated with the parallel port.
-
2:
- This is the most sophisticated piece of C programming
that we shall need in this tutorial. In fact, you can just
copy-n-paste this part without caring about what it means. But
here is a short explanation for the more inquisitive. We are
declaring two new types which we have called
inpfuncPtr
and oupfuncPtr . A C object of type
inpfuncPtr is supposed to be a function that takes a
short value as input, and returns a
short value as output. A C object of type
outfuncPtr is supposed to be a function that takes two
short values as input, and returns nothing.
-
3:
- This is where we extract the two functions
Inp32 and Out32 from the dll file.
-
4:
- Here we provide two wrapper functions around the newly
extracted functions. They make it easier to call the functions.
-
5:
- The
main functions sets up the port
communication and writes 0x34 (which is the bit pattern 0011
0100) to the parallel port.
Using gcc in Knoppix
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <asm/io.h>
#define PPORT_BASE 0x378
#define PPORT_OUT PPORT_BASE
#define PPORT_INP (PPORT_BASE+1)
|
void init() {
if(ioperm(PPORT_BASE, 3, 1)) {
perror("Cannot grab port!\n");
exit(1);
}
}
void done() {
if(ioperm(PPORT_BASE, 3, 0)) {
perror("Cannot release port!\n");
exit(1);
}
}
| 1 |
main() {
init();
outb(0x37,PPORT_OUT);
done();
}
|
|
Explanation of the code:
-
1:
- The
ioperm functions reserves permission
for accessing the ports.
Using gcc in Fedora 8
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
|
#define PPORT_BASE 0x378
#define PPORT_OUT PPORT_BASE
#define PPORT_INP (PPORT_BASE+1)
| 1 |
void init() {
if(!(fport = open("/dev/port",O_RDWR))) {
perror("Cannot grab port!\n");
exit(1);
}
}
void done() {
if(close(fport)) {
fprintf(stderr,"Cannot close port!\n");
exit(1);
}
}
| 2 |
int inb(int fromAddr) {
char buff;
if(lseek(fport,fromAddr,SEEK_SET)!=fromAddr) {
fprintf(stderr,
"Cannot arrive at port %x for reading.\n",fromAddr);
fflush(stderr);
exit(1);
}
if(read(fport,&buff,1)!=1) {
fprintf(stderr,"Cannot read from port %x.\n",fromAddr);
fflush(stderr);
exit(1);
}
return buff;
}
| 3 |
void outb(int what, int toAddr) {
if(lseek(fport,toAddr,SEEK_SET)!=toAddr) {
fprintf(stderr,
"Cannot arrive at port %x for writing.\n",toAddr);
fflush(stderr);
exit(1);
}
if(write(fport,&what, 1)!=1) {
fprintf(stderr,
"Cannot write to port %x.\n",toAddr);
fflush(stderr);
exit(1);
}
}
| 4 |
main() {
init();
outb(0x37,PPORT_OUT);
done();
}
|
|
Explanation of the code:
-
1:
- The parallel port addresses (just as in the windows
version).
-
2:
- Opening and closing the ports. Do not use the
fopen function here, as it uses buffering. Also, use
the /dev/port carefully. Once it is opened, it gives
you access to the entire hardware. So writing to a wrong location
may even corrupt your harddrive!
-
3:
- Notice how we are
lseek ing everytime we
want to read something. Each lseek advances the
reading head by 1 byte, so if we do not reset it back to desired
position before reading we shall be reading form the wrong
location. Remember that /dev/port is not like
an ordinary file. For an ordinary file the content remains fixed,
and the reading head moves. For /dev/port the
reading head should stay fixed (at the parallel port input
address, say) and the content at that location will change.
-
4:
- This is the function for writing to a port. Again note
the use of
lseek . Be very caregul that you do not
write to a wrong location!
Sending timed pulses
OK, now that we have been able to access the port, we are ready
to send
timed pulses. We shall first write a program that pulses a LED
connected to the parallel port. Connect a LED from any of the output pins (pins 2 through 9) to ground as
shown below. The 1K resistance is for safety purposes (it prevents the LED
from drawing too much current from the parallel port). The resistance
causes the LED to glow very feebly. I have also tried
the experiment without the resistance. Then the LED glows brightly (and
my parallel port is still alive). Experiment at your own risk!
|
Conecting a LED to the parallep port |
We want to make this LED pulse.
To send a pulse we need to write something to a port,
wait for sometime, and then restore the original value, like
write 1, wait, write 0. We already know how to achieve the
writing part in various platforms. The only new thing here is the
waiting. It turns out that there is a
little bit of platform-dependence even here.
Using Borland C in Windows XP
In Borland C for Windows XP we need to use the
Sleep(long) function, which takes as input the
duration of the wait in milliseconds. Thus
Sleep(1000) waits for 1 second.
Part of writepulse.c
#include <windows.h>
/*Load dll as before*/
void main() {
unsigned short i;
init();
while(1) {
for(i=0;i<256;i++) {
Out32(PPORT_OUT,i);
Sleep(1000);
}
}
}
|
Using gcc in Knoppix/Fedora 8
Here we can use the usleep(long) function that takes
the duration of wait in microseconds.
Part of writepulse.c
/*Define init, done etc as before*/
void main() {
unsigned short i;
init();
while(1) {
for(i=0;i<256;i++) {
outb(PPORT_OUT,i);
usleep(1000000);
}
}
}
|
The three R's of platform-dependence
In this page we have discussed all the platform-dependence that
is needed in this tutorial: reading, 'riting and resting
(i.e., waiting). Henceforth we shall use the Windows names
Inp32 ,
Out32 and Sleep generically for all
platforms. You should
remember to change them approriately for your platform as above.
|