Samstag, 8. November 2014

Input Output

This second post is about digital in and output (short: I/O), i.e. on/off-switching of ATMEGA8 pins, or checking if pins are at high or low potential.
"high", "on", "VCC", "1" are the same in this contex, as well as "low", "off", "GND", "0". They refer to the two different electrical potentials or logic levels in the digital ciruit. (VCC=Voltage Common Collector, GND=ground).

More details about I/O are all described in the ATMEGA8 datasheet, but I try to provide the minimum information to get things done. The notion of "minimum" is of course purely subjective.

Output:

Driving a pin high or low means that the controller internally connects it either to VCC or GND, respectively. The controller has a set of pins that can be driven high or low: The general purpose I/O lines. Groups of 8 pins are controlled by 3 registers (DDRx, PORTx, PINx) each. A register is a special 8bit value in the processor. There are 23 I/O pins controlled by 9 registers: DDR[B,C,D], PORT[B,C,D] and PIN[B,C,D].

In the last post, I've made an LED blink by making the pin PD0 of the controller.  For each I/O pin, the corresponding bit in the DDRD register (DDRD:0 = least significant bit, or 0-th bit, of register DDRD) decides if it is used as input (DDRD:0 = 0) or output (DDRD:0 = 1).
To power a LED, a driven output is needed. The DDRD:0 bit can be set by writing the value 1 in the register
DDRD = 1; // the 8 bits of DDRD are: 00000001
Names such as DDRD are defined in the <avr/io.h> header file.
The previous statement also sets all the other bits of DDRD to 0. If the other bits should be preserved in whatever state they are, one can use the following syntax (I assume familiarity with bitwise operators here):
DDRD |= 1;
Likewise one could make the pin PD3 and output by writing
DDRD |= 8; // the bits of DDRD are: 00001000 (binary representation of 8)
To make the code more readable, it is good practice to use the following notation:
DDRD |= (1 << DDD3); // (1<<DDD3) is same as (1<<3)
Setting two bits of a register can be done like this (set bits 0 and 3 of DDRD to 1):
DDRD |= (1<<DDD0)|(1<<DDD3);
To drive the B-pins or C-pins, one has to write to DDRB and DDRC, respectively.
If DDRD:0 is set to 1, the pin PD0 is high if PORTD:0 is 1 and pin PD0 is low if PORTD:0 is 0.
Changing a pin can be done by XOR operation (as shown in the previous post):
PORTD ^= 1;
Or to make it more readable:
PORTD ^= (1 << PD0);

Input:

To check if a pin is high or low, one has to check the bit-content of the registers PIN[B,C,D]. Reading from PORT[B,C,D] will not work.
To check if pin PD0 is high or low one has to check bit PIND:0 like this
if (PIND & 1)
{
    // PD0 is high.
}
else
{
    // PD0 is low.
}
Or like this to make it more readable:
if (PIND & (1<<PD0)) { //...

If a pin (PD0) is configured as input (DDRD:0 = 0), the value of PORTD:0 specifies if a pull-up resistor is connected (PORTD:0 = 1) or not connected (PORTD:0 = 0) to the pin.
The pull-up connector ensures that in case of no external signal on the pin, reading the pin-bit will give 1. The only way to read a 0 from that pin is, if it is externally driven low (for example by a connection to GND via a push button).


Keine Kommentare:

Kommentar veröffentlichen