A micro-controller such as the MicroStamp11 has a number of pins as shown in the pinout of figure 1. The MicroStamp11 communicates with the outside world by changing the logical state of these pins or by reading the logical state of the pins. The logical state of a pin is said to be high if the voltage of the pin relative to ground is 5 volts. The logical state is low if the voltage on the pin is zero (relative to ground).
The majority of the pins on the MicroStamp11 are arranged into
two ports that have the logical names PORTA
and PORTD
. PORTA has 8 pins associated with it
(pins 1
through 8 on figure 1).
PORTD has 6 pins associated with (pins 15-20). The other 6
pins on the MicroStamp11 are either used for power (pins
10-12) or they are special input pins that can interrupt
the execution of a program (pins 9, 13, and 14). Our
current interest is with the I/O pins associated with
PORTA/PORTD (pins 1-8 and 15-20).
The pins on PORTA/PORTD have two distinct types of states. The logical state, as mentioned above, refers to the voltage level on the pin (5 volts or zero volts). In addition to this, however, each pin has a direction state. In other words, the MicroStamp11 either reads from or writes to a pin; it cannot do both at the same time. This means that each pin on PORTA/PORTD has a directional state that is either IN or OUT. When a pin has the OUT directional state it behaves like an independent voltage source of 0/5 volts. When a pin has the IN directional state it behaves as a high resistance load on the circuit it is connected to.
The MicroStamp11 can control the direction state and
logical
state of the I/O pins by writing to specific memory
locations in its RAM's address space. Remember that
Micro-controllers like the MicroStamp11 directly map
their I/O pins to hardware registers that are in turn
mapped to specific locations in the device's address
space. The file kernel.c
defines the logical
names for these hardware registers controlling the port's
logical state. These logical names are PORTA
and
PORTD
. They are 8-bit variables in which each bit
is associated with one of the pins on the I/O port.
As mentioned above, these ports can serve as either
inputs or outputs. At a given time a pin can only
act as input or output, not both. The directional state
of an I/O pin is determined by setting appropriate bits in
a direction register. Direction registers are
hardware registers that can be written to or read from by
a program because they are mapped directly into the
device's
address space. The logical name for PORTD's direction
register is DDRD
. If the th bit in
DDRD
is set high, then the th pin on PORTD has its
directional state set to output. If the
th bit in
DDRD
is low (0), then the th pin on PORTD is
treated as an input pin.
The pins on PORTA are somewhat special in that not all of
the pins' are bi-directional. In fact, only two of the
pins (PA3-pin5 and PA7-pin1) can have their direction
states changed. This is accomplished by setting the
appropriate bits in a hardware control register with the
logical name PACTL
(PortA's control register). The
logical names DDRA7
and DDRA3
refer to bytes
in which only the 7th and 3rd bit are set to one. Through
the use of bitwise operators, we can use these
logical names to set, clear, or toggle the specified bits
in PACTL
, thereby controlling the directional
state of these pins. The other pins in PORTA have their
directional states fixed because they are associated with
specific input or output interrupt functions. In
particular, the directional state of
pins 2-4 (PA4-PA6) is always OUTPUT, whereas the
directional state of pins 6-8 (PA0-PA2) is always IN.
If an I/O pin's directional state is OUTPUT, then we can change its logical state by simply writing a 1 or 0 to the appropriate bit in the port's register. So let's assume that we wish to set pin PD5 (To "set a pin" means to make its logical state HIGH. To "clear a pin" means to set its logical state LOW). The following C-language code segment uses bitwise OR operators to accomplish this
DDRD |= bit(5); PORTD |= bit(5);The macro
bit(i)
produces a byte that only has its
kernel.c
).
The first statement sets the 5th
bit in the DDRD register to one, thereby setting the pin's
directional state to output. The second statement sets
the logical state of the 5th bit to high.
If we wish to clear this pin, then we must use a bitwise
logical operator on the NOT (complement) of bit(5)
. This is done in the following code segment
DDRD |= bit(5); PORTD &= ~bit(5);As before the first statement sets PD5's directional state to OUT. The second statement is equivalent to the statement
PORTD = PORTD & (~bit(5))
which simply
switches the 5th bit in PORTD to zero.
Setting the logical state for pins in PORTA is similar. Recall that only pins PA3-PA7 can take an OUT directional state. The following code segment sets pin PA7, clears PA5, and toggles PA3.
PACTL |= DDRA7; PORTA |= bit(7); PORTA &= ~bit(5); PACTL |= DDRA3; PORTA ^= bit(3);We used
DDRA7
and DDRA3
to set
the appropriate bits in PORTA's control register so that
pins PA7 and PA3 are output pins. Note that we did not
need to do this for PA5 since it is always an output pin.
To read the logical state of an I/O pin, the pin's directional state must be set to IN. This is accomplished by setting the appropriate bit in the PORT's control/direction register to zero. Once this is done, we can simply read the bit by testing it to see if that bit is one or zero. The following code segment does this for pin PD5 on PORTD.
DDRD &= ~bit(5); if((PORTD & bit(5))==0){ OutString("PD5 = LOW"); }else{ OutString("PD5 = HIGH"); }As before the first statement clears the 5th bit in PORTD's direction register thereby making sure PD5 is an input pin. The logical test computes the logical AND of PORTD and
bit(5)
. This logical AND returns a 0 only if the
fifth bit in PORTD is zero. If this occurs, our program
writes out that PD5 is LOW. If the logical AND does not
return 0, then we know the 5th bit must have been set and
so the program writes out that PD5 is HIGH. A similar type
of test can be used to test the status of pin PA2.
if((PORTA & bit(2))==0){ OutString("PA2 = LOW"); }else{ OutString("PA2 = HIGH"); }Notice that we didn't need to set any bits in PORTA's control register because PA2 is always an input pin. If we had attempted to read pin PA3 or PA7, of course, then we would need to set the appropriate pins (
DDRA3
or DDRA7
) in
control register PACTL
.