Sie sind auf Seite 1von 12

Hardware Interaction Through 'C'

‘C’ as we know is a middle level programming language. It offers extreme flexibility in


interacting with the hardware. Therefore it is not surprising to find that maximum number of
people involved in programming the hardware, use it as their preferred language. We will
also use ‘C’ for examples demonstrated in this article. ‘C’ offers us the ability to interact freely
with the hardware with little overheads and very compact code. Before actually jumping into
this realm of hardware interaction we will first see what is it that we call hardware, and how
do we interact with it.

Hardware is nothing but some external circuitry/device that receives commands from a
computer that is running a program. And how exactly do we communicate with this so-called
device? The answer is through something called ports. But for some of us it is still a mystery
as to what exactly is a port? If we hear the word port the first thing that comes to our mind is
a large place by the ocean, with deep waters and numerous ships loading and unloading
goods (a harbor sometimes), generally not something that could fit into the backside of our
computer. In computers a port does pretty much the same job as the seaside one. The
computer places data on them to be loaded and carried by some cable to its destination,
most of the times some device (sometimes other computers). But there is a small difference
here, not all ports are designed to receive as well as send data, some support only one-way
traffic, while others support bi-directional traffic. A port thus is a place in the I/O space of the
computer that is directly connected to the connectors that we see at the back of our
computers. These are I/O's mapped devices, i.e. whatever we write on these I/O's locations it
will be reflected on the pins of the sockets. There are also some ports that are not connected
to any external pins. There are numerous types of sockets that can be attached to our
computer. Some of the more commonly used ones are the COMM port, the LPT, the PS/2
etc. A computer is capable of writing as well as reading data from a port. Most popular
libraries contain functions like outportb( ), inportb( ), outport( ), inport( ) to read/write
byte/word from/to a specified port.

In this article we will read the actual time stored in CMOS memory. CMOS uses two ports—a
control port (70h) and a data port (71h). On sending a pre-defined offset value to the control
port a value representing the offset is made available at the data port. The following ‘C’
program shows how this can be achieved:

#include <stdio.h>
#include <dos.h>
main( )
{
unsigned char sec, min, hrs;

clrscr( ) ;
while ( !kbhit( ) )
{
outportb ( 0x70, 0x95 ) ;
outportb ( 0x70, 0x00 ) ;
sec = inportb ( 0x71 ) ;
outportb ( 0x70, 0x02 ) ;
min = inportb ( 0x71 ) ;
outportb ( 0x70, 0x04 ) ;
hrs = inportb ( 0x71 ) ;
printf ( "Time: %x:%x:%x", hrs, min, sec ) ;
delay ( 500 ) ;
}
}
In the above example we have placed the value 0x000h at port no 0x70h using the
outportb( ) function. The CMOS in return places the seconds part of the system time at port
number 71h, which we have read using the call to inportb(). On similar lines we have
obtained the values of hours and minutes of system time by sending the offset values 02h
and 04h to the control port and reading the values from the data port. Finally we have printed
the time using printf( ) function. Similarly we can use all other items of information that
CMOS memory contains.

Serial Port Communication


Hardware interaction will always take place through ports. Port I/O is not limited to functions
such as inportb( ), outportb( ) etc. In this article we will see how two computers can
communicate via the Serial/COM port.

The Serial/COM port is a 9/25-pin port connector at the back of a computer. 9-pin mail
connectors are more common. All COM ports are mail connectors. If the connector is 25-pin
and a female, it is an LPT port and not a COM port. The type of connector will not affect the
performance of the port.

A serial port is so named because it transmits data serially i.e. one bit at a time. So even if
there are 9/25-pins on the connector, only 3 are of importance to us. The other are either left
open. Some are loped back. Looping back is something like fooling the computer to believe
that it is actually connected to some serial device like a modem.

The serial port is commonly used by software developers to connect two computers for
testing new software. One computer runs the software while the other records the
information sent by the software. This method, often known as debugging helps the
developers find errors in their program. The cable used for connecting the two computers is
known as Null Modem cable, debug-cable, COM port to COM Port cable or simply serial
cable. We will us this cable for our program. Our program will display whatever is typed on
one computer on the other computer.

The software, is actually two different programs running on two different machines. The two
machines are connected through the serial cable. Select one machine as the server and
other as the client. The server will be used to type whereas the client will show whatever is
typed. Any machine can be made the server or client.

The client side program:

/* client.c */
#include <bios.h>
main( )
{
int out, status ;
bioscom ( 0, 0x80 | 0x02 | 0x00, 0 ) ;
while ( ! kbhit( ) )
{
status = bioscom ( 3, 0, 0 ) ;
if (status & 0x100 )
{
out = bioscom ( 2, 0, 0 ) & 0x007F ; /* to check the 7 bit data for error condition */
if ( out != 0 )
putch ( out ) ;
}
bioscom ( 1, 0, 0 ) ;
}
}

The server side program:

/* server.c*/
#include <bios.h>
main( )
{
int in, status ;
bioscom ( 0, 0x80 | 0x02 | 0x00, 0 ) ;
while ( ( in = getch( ) != 27 )
{
status = bioscom ( 3, 0, 0 ) ;
if ( status & 0x100 )
{
bioscom ( 2, 0, 0 ) ;
bioscom ( 1, in, 0 ) ;
}
}
}

In these programs we have not used the inportb( ), or outportb( ) functions instead we have
used the bioscom( ) function. This is a standard library function declared in the bios.h file.

Before compiling and running these programs, make sure the two computers are connected.
Now start the programs on separate machines. (Hopefully) Whatever is typed will be
displayed on both the server as well as the client. If nothing appears on the client side,
change the last parameter in the function bioscom( ) from 0 to1. This will change the
selected COM port from COM 1 to COM2 it is not necessary to connect COM1 to COM1 only
or COM2 to COM2 only. Set the appropriate COM port for both the computers.

There is one more problem. If even one of the two computer runs a WinNT family (WinNT,
Win2000, WinXp) operating system, this communication will not succeed. That is because
this WinNT family of Operating systems do not support direct port communication through
DOS programs. For this to work under windows, a windows program has to be written.

The bioscom( ) function accepts 3 parameters.

Parameter 1. It can have 4 ( 0 - 3 ) values depending upon what action to take.

0 - Sets the communication parameters like the speed of communication etc. for the
Second parameter
1 - Sends the second parameter over the communication cable.
2 - Receives a character from the communication line.
3 - Returns the current status of the port.

Parameter 2. It sets the communication parameters for the byte being sent through the
link.

Parameter 3. This can have 2 ( 0 - 1 ) values for the port through which to communicate.

0 - Communication through the COM1 port


1 - Communication through the COM2 port

In the initialization phase we have specified three speeds at which this communication can
take place. '0x80 | 0x02 | 0x00' signifies a speed of 1200 bits per sec.

Lets now see how the programs actually work. The client or listening program first initializes
the port through the call bioscom ( 0, 0x80 | 0x02 | 0x00, 0 ). It is now ready to listen to the
server. After this we start the while loop which will continue until the user presses any key.
During this time the program will listen on the COM port and display whatever it reads. We
have implemented the bioscom( ) function thrice in the while loop. The first instance (
bioscom ( 3, 0, 0 ) )will read the status of the port, the second instance ( bioscom ( 2, 0,
0 ) ) will read a character from the port and store it in the 'out' variable if the status is OK. The
third instance ( bioscom ( 1, 0, 0 ) ) will send back a notification to the server that it has
successfully received the character. The last instance will prompt the server to send another
byte.

In this case the program instead of reading from the port, will write on it and wait for
notification to send another byte. Here also the server will first initialize the port and proceed
into the while loop. This program will continue until the user presses the 'Esc' key. The server
program implements bioscom( ) three times inside the while loop. The first instance (
bioscom ( 3, 0, 0 ) ) will check for status of the port. The second instance ( bioscom ( 2, 0, 0
) )will see if the previously sent byte was received correctly or not. And the third instance (
bioscom ( 1, in, 0 ) ) will send the new byte. The byte being sent is the lower byte of the
integer (16-bit) value. The upper 8 bits represent the status of the data transfer.
The LPT is commonly known as the Parallel port or Printer port. It is a pink colored 25 pin
female connector at the back of a computer. Parallel port is so called because it transmits
data in bytes and not bits. We can transmit one complete byte at a time, along with control
signals for the distant device. This mechanism of transmitting bytes instead of bits greatly
increases the rate of data transfer through this port. Because of these features the parallel
port has become one of the most widely used for implementing projects dealing with control
of real world peripherals.
The LPT is actually a group of three ports, data port, control port and status port. These ports
enjoy three separate addresses. For a standard PC the port addresses are typically 378h,
379h, 37ah for data, status and control ports respectively, these addresses may differ
depending upon whether it is an LPT1 Port or a LPT2 port, but the order remains same.
The 25 pins are divided among the ports in the following manner.

Pin Number Port No / Port Name Pin Name(s)


18 – 25 Non Ground Pins
2–9 0x378 / Data Port Data Pins
10 – 13, 15 0x379 / Status Port Status Pins
1, 14, 16, 17 0x37a / Control Port Control Pins

The remaining pins are reserved for other use. The data pins are true logic, i.e. the output is
the same as we put. In other words if we send true logic ( logic 1 or 5V signal ) the same will
be reflected on the pins. There are some pins in the control port and the status pins that
show inverted logic. This means that if we output a true signal ( logic 1 or 5V signal ) on the
pin it will actually be reflected as a false (logic 0 or 0V signal). Pin number 1, 10, 14, 15, 17
are inverted logic pins. This makes the LPT a little bit more complicated than a serial port.
The LPT was primarily designed for printer interfacing, this required bi directional
communication of information. The computer needs to read the printer status of the printer
and the printer needs to get information to decide the mode of operation from the computer.
Of the 25 pins 17 are active pins whereas the rest are ground (0 voltage always). Of these
17, 2 to 9 are the data pins. The remaining ones are the ones that are shown in the table

In this article we will interface a simple 7-segment LED array with the LPT. The 7-segment
Pin Number Description (Port ) Type
1 Strobe ( Control ) Inverted Logic
10 ACK ( Status ) Inverted Logic
11 Busy (Status ) True logic
12 Paper Empty (Status ) True Logic
13 Select ( Status ) True Logic
14 Auto Feed (Control ) Inverted Logic
15 Error (Status ) Inverted Logic
16 Initialize Printer ( Control ) True Logic
17 Select Input (Control ) Inverted Logic
will display numbers 0- 9 in cyclic manner. The circuit for the same is as given below.
Rightmost connected pin in the top row of the Figure. 1 is the second pin whereas the
Leftmost connected pin is pin number 9 of the LPT.

This was our circuit, for someone else this might change a little bit. The resistors we used
were 560 ohm’s and the grounds were connected to pin 18 – 25.
The 7-Segments are separately controlled by 8 data pins of the LPT. To control each of
these pins we need to send specific numbers. The format of these numbers is given below.

32
-------
64| | The numbers are the Bit that needs to be sent
| 128 | 16 through the LPT in order to glow that specific
------- filament of the 7-Segment.
| |
1| | 4 These numbers activate only one pin of the LPT port at a
------- .8 time. The result is that when we OR these numbers in a
2 specific format, we get control over each filament of the
7-Segment.
The program to run this circuit is given below:

# include <dos.h>
main( )
{
int i = 0 ;
unsigned char seg[ ] = {
64 | 1 | 2 | 4 | 16 | 32, /* 0 */
16 | 4, /* 1 */
32 | 16 | 128 | 1 | 2, /* 2 */
32 | 16 | 128 | 4 | 2, /* 3 */
64 | 128 | 16 | 4, /* 4 */
32 | 64 | 128 | 4 | 2, /* 5 */
32 | 64 | 1 | 2 | 4 | 128, /* 6 */
32 | 16 | 4, /* 7 */
32 | 64 | 128 | 4 | 2 | 1 | 16, /* 8 */
32 | 64 | 128 | 16 | 4 | 2 /* 9 */
};
while ( ! kbhit( ) )
{
outportb ( 0x378, seg[ i ] ) ;
delay ( 1000 ) ;
i++ ;
if ( i == 10 )
i=0;
}
outportb ( 0x378, 0 ) ;
}

Again a word of caution these programs might not work properly under WinNT family of
operating systems.
2
E PROM Interfacing With A PC
E2PROM expands to Electronically Erasable Programmable Read-only Memory. This is
in the form of an IC ( Integrated Circuits ) or SMDs ( Surface Mount Devices ) whichever the
variant the main purpose of it is to store data without power backup. That is, once data is
written on it, it will remain written independent of any power supply. This is important in
today’s world where identities are becoming more and more digital. People, Machines,
Goods, need to be identified independent of one another and also independent of any human
intervention. These devices are small and cheep, and can be used in almost any kind of
environment. We are already living in a world full of these tiny gizmos.

An E2PROM is guaranteed to work for about 10000 read / write operations. It also promises
to store data for at least a decade and in some cases up to 4 decades. In this article we will
not be looking at how an E2PROM actually stores data or how it keeps it true for such a long
time without batteries. What we are interested in here is how best we can use a home PC to
take full advantage of this wonder chip.

Our test E2PROM is a small IC with 1024 bits (128 bytes) of EE memory. It works with a
normal 5V DC supply and is simple to interface with a PC. For Electronics buffs the IC we
are using is NM93C46 serial E2PROM. Our program will write an integer ( 16-bit value ) at a
predetermined location in the memory and retrieve it. This IC has 64 registers each of size
16-bits. The IC will be interfaced with the LPT port of a PC. The circuit for the same is shown
in Figure 1.

Figure 1.

For our example we have not provided any separate power supply, instead we are using the
Strobe pin (Pin 1) of the LPT as our power source. Ground for the source is the common
ground on the LPT (Pins 18 – 25).

A few things about the E2PROM IC now: The IC has 8 pins, we will be using 6 of them. Pin
number 6 and 7 will be left open. The functions of each pins are:

Pin 1: Clock Select.


Pin 2: Clock
Pin 3: Data In
Pin 4: Data Out.
Pin 5: Power Input
Pin 8: Ground.

In the circuit we have connected a diode in between strobe of the LPT and the power source
pin of the IC. This arrangement is made to protect the IC from accidental reverse voltage and
consequently reverse current. Reverse current will damage the IC in an unpredictable
manner. A diode allows current to flow in one direction only and acts as an insulator for the
reverse direction. This makes it ideal for this job. The other place where we have connected
the diode is between the power pin and the ground pin. This arrangement also works as a
safety mechanism to limit the amount of current flowing through the IC. Our first diode will not
allow reverse current but it is helpless when excess current flows in. To protect our IC
against this the power pin is connected to the ground pin via the 220-Ohm resistance. And
the ground is connected to the ground of the LPT. Any excess current flowing into the circuit
will be passed on through the resistance to ground, bypassing the IC.

Here is what each pin of the IC is intended to do:

Sending logic 1 ( +5 V signal ) to the Chip Select will tell the IC that it needs to be ready to
perform some action. Logic 1 on the Clock will cause the chip to read / write data from / to
the Data In / Data Out pins of the IC. Jobs of Data In and Data Out pins are self-explanatory.

On the LPT port side, we are interested in two of its ports the Data Port and the Status Port.
The Data Port is being used to send signals and the Status Port is receiving information. We
use pin number 13 of the LPT port connector. This is the pin on the status port, we use this
to get data from the IC. Pins 2, 3, 4 are connected to Chip-Select, Clock and Data-In pins of
the E2PROM IC respectively. Pin 1 on the LPT connector serves as the power source and is
connected to the power pin of the E2PROM IC. All grounds are shorted.

Getting down to the programming part of this article, here is the source code:

#include <stdio.h>
#include <conio.h>

#define PORTA 0x378 /* Address of LPT */


#define PORTB 0x379
#define CS 0x01 /* Chip Select */
#define STARTBIT 0x01

/* our address value can be changed */


#define DATA_ADD1 0x00

void write_bit ( unsigned int data )


{
if ( data == 0 )
{
data = ( CS ) ;
outportb ( PORTA, data ) ;
}
else
{
data = ( CS | 0x02 ) ; /*mask all bits except data */
outportb ( PORTA, data ) ;
}

data = ( data | 0x04 ) ;


outportb ( PORTA, data ) ; /* Give Clock Pulse */
data = ( data ^ 0x04 ) ;
outportb ( PORTA, data ) ; /* Remove Clock Pulse */
data = ( CS ) ;
outportb ( PORTA, data ) ;
return;
}

unsigned int read_bit( )


{
unsigned int data, rv ;
data = ( CS | 0x04 ) ;
outportb ( PORTA, data ) ; /* Apply Clock Pulse */

rv = inportb ( PORTB ) ; /* Read Data From Input port */

data = ( data ^ 0x04 ) ;


outportb ( PORTA, data ) ; /* Remove Clock Pulse */

rv = rv & 0x10 ; /* Mask Everything Except Data */


if ( rv == 0 )
data = 0x0 ;
else
data = 0x01 ;
return data ;
}

void enable_9346( )
{
outportb ( PORTA, CS ) ;
write_bit ( STARTBIT ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x01 ) ;
write_bit ( 0x01 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
outportb ( PORTA, 0x00 ) ;
}

void disable_9346( )
{
outportb ( PORTA, CS ) ;
write_bit ( STARTBIT ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
write_bit ( 0x00 ) ;
outportb ( PORTA, 0x00 ) ;
}

void write_9346 ( int my_add, unsigned int my_data )


{
unsigned int i, new_add, new_data ;
unsigned long int j ;
outportb ( PORTA, CS ); /* Give Chip Select High To Write A Bit */
write_bit ( STARTBIT ) ; /* Give Start Bit TO Write A Bit */
write_bit ( 0x00 ) ; /* Give Mode Bit 1 For Writing Operation */
write_bit ( 0x01 ) ; /* Give Mode Bit 2 For Writing Operation */

j = 0x20 ;
for ( i = 0 ; i < 6 ; i++ )
{
new_add = my_add & j ;
write_bit ( new_add ) ;
j = j >> 1 ;
}
j = 0x8000 ; /* j Is A 16 Bit Masking Number */
for ( i = 0 ; i < 16 ; i++ )
{
new_data = my_data & j ; /* Mask All Except Current Bit */
write_bit ( new_data ) ;
j = j >> 1 ;
}
outportb ( PORTA, 0x00 ) ; /* Remove Chip Select */
}

unsigned int read_9346 ( int my_add )


{
unsigned int i, j, new_add, my_bit ;
unsigned long int my_data ;

outportb ( PORTA, CS ) ; /* Set Chip Select ON */


write_bit ( STARTBIT ) ; /* Give A Start Bit to read Data */
write_bit ( 0x01 ) ; /* Give Mode Bit 1 For Read Operation */
write_bit ( 0x00 ) ; /* Give Mode Bit 2 For Read Operation */

j = 0x20 ;
for ( i = 0 ; i < 6 ; i++ )
{
new_add = my_add & j ;
write_bit ( new_add ) ;
j = j >> 1 ;
}
my_data = 0x0000 ;
for ( i = 0 ; i < 16 ; i++ )
{
my_data = my_data << 1 ;
my_bit = read_bit( ) ; /* GetOne Bit Of Data */
my_data = my_bit | my_data ; /* Setup Read Bit Into Data */
}
outportb ( PORTA, 0x00 ) ; /* Remove Port Select */
return my_data ;
}

void main( )
{
unsigned int data, info ;
clrscr( ) ;
enable_9346( ) ;
delay ( 10 ) ;
printf ( "Enter value to be stored in the chip: " ) ;
scanf ( "%d", &data ) ;

/* writing begins here */


enable_9346( ) ;
delay ( 10 ) ;
write_9346 ( DATA_ADD1, data ) ;
delay ( 10 ) ;
disable_9346( ) ;
printf ( "Press any key to read back the data...\n" ) ;
getch( ) ;

/* reading begins here */


enable_9346( ) ;
delay ( 10 ) ;
info = read_9346 ( DATA_ADD1 ) ;
printf ( "Value read from chip is: %d\n", info ) ;
disable_9346( ) ;
getch( ) ;
}

The program is basically divided into three parts, reading a number from the user, writing it
on the IC, and finally reading it and displaying it.
This procedure sounds simple but it is not. It must be clear from the program that lot many
things have to be done to achieve this simple task of reading and writing things from and to
the E2PROM.
We have declared 6 functions in this program, each with a specific job.

write_bit( ) It will writes one bit of data at a pre decided address.


read_bit( ) This reads one bit from the IC again from a pre set address.
Will use the above declared functions to first communicate the address
write_9346( )
and write data there.
Reads first it will communicate the address and then read data serially
read_9346( )
from the IC.
enable_9346( ) This instruction will enable the chip for further communication.
After the communication with the IC is over, always use this function to
disable_9346( )
disable it.

Enable and disable functions work in tandem with each other, there is a complex protocol
dictated by the chip architecture for this. We as users don’t have a choice to alter that
protocol.

Lets have a look at the things that we can alter.


The write_bit( ) function is fairly generic and can be used for almost any bit wise writing
operation. This function writes one bit of data on the Data-In Pin of the E2PROM IC. the
second bit on the 378h port of the LPT needs to be set accordingly.
read_bit( ) function will read data from the Data-Out pin of the E2PROM IC. There is though
a prerequisite to this, the user needs to initiate the mode of working of the IC to reading
before doing this. By default the IC is in no activity mode on this pin. Unlike in the case of
write_bit( ) where we can always write on the Chip-Select pin without worrying about the
mode in which the IC is.
write_9346( ) writes full 16 bits of information on the IC the procedure simple, first select the
bit by giving it a true logic ( +5 V ) signal in the Chip-Select pin of the IC, followed by giving
the start bit to ask the IC to listen to data sent next. The following two bits are the mode
select bits. Mode select tells the IC whether to read from or right to the 6-bit address
following it. Once this is done, send the 6-bit address and finally the 16-bit value to be
written.
read_9346( ) works similar to the write_9346( ) function. Except for the mode and call to
read_bit( ) almost nothing has changed. Each bit wise value received will be inserted into
the integer data variable at proper location.

The reason why we are passing a 6 bit address and a 16 bit data value is that we have only
64 memory locations each of size 16 bits. 64 registers can be separately addressed using 6
bits.
The delay( 10 ) calls in our program are essential as the chip takes approximately 3.5
milliseconds to process instructions that we pass on to it. The 10 millisecond margin that we
use here is just a precaution we take to be on the safer side.
And again as usual this program might not work on Windows NT family of operating systems.
I strongly believe that if we can read and write one bit of information from only one address,
extending the logic for multiple bits and multiple addresses id just a matter of sparing some
time.

Das könnte Ihnen auch gefallen