Saturday, May 9, 2009

Basic AVR IO

I found this very confusing when I started to program my ATtiny13. I read a bunch of different sites/blogs/datasheets to get a feel for it (and am still slightly confused tbh). So, I thought I'd give a brief overview of the basics in a way that makes sense to me. I'm not going to cover input here, just output; but it's a good start.

Here's the key concepts:
  1. AVR uCs are 8-bit devices.
  2. Every AVR has at least some pins which can act as either Input or Output (I/O) devices.
  3. The voltage on the pins can be sensed or controlled via software.
  4. Because of 1. physical pins are logically grouped in sets of 8 as I/O ports. For my ATtiny13 there is only one port (port B) as there are only 8 pins.
  5. Each I/O port has 3 registers associated with it: DDRx, PINx and PORTx (more on this later), where x represents the port (in my case there's only BBRB, PINB and PORTB available).
  6. I/O pins exists as registers inside the processor. It's the software controlled contents of these registers that controls the state and operation of the I/O ports and pins.
  7. The registers are 8-bits in size. Each bit in the register determines the operation of the corresponding number pin (0-7).
DDRx
This register the direction (input/output) of the pins on port x. Again, for the ATtiny13 there is only a port B so we only have DDRB available but on other AVRs there can be many ports (e.g. the atmega168 has 3 ports B, C and D controlling 23 programmable I/O pins) .

A '0' bit makes that port pin act as input.
A '1' bit makes that port pin act as output.

PORTx
This register contains the output state of the pins on port x.

A '0' bit is considered LOW (~0V)
A '1' bit is considered HIGH (~Vcc)

PINx
This register contains the input state of the pins on port x.

A '0' bit indicates that the port pin is LOW (~0V).
A '1' bit indicates that the port pin is HIGH (~Vcc).

So let's take a quick look at the simple code I posted in "Starting with the AVR uC":

1:  #include 
2: #include
3:
4: //LED is wired into pin 7 (PB2)
5: #define LED PB2
6:
7: int main(void){
8: //set data direction register for pin 7 to output
9: DDRB |= _BV(DDB2);
10:
11: //infinite loop
12: while (1) {
13: //turn on the LED
14: PORTB |= _BV(LED);
15: //wait for 1/4 of a second
16: _delay_ms(250);
17: //turn off the LED
18: PORTB &= ~_BV(LED);
19: //wait for 1/4 second
20: _delay_ms(250);
21: }
22: }
5: define LED as PB2. This is just a convenience to allow us to refer to pin 7 as the variable LED. I have the LED wired into pin 7 and this pin is called PB2 according to my datasheet (this can be different for different uCs). PB2 is actually another variable which has been defined thanks to the avr/io.h import statement. I could have used the number 7 instead but I decided to use the name of the pin from the datasheet.

9: This sets bit 7 of DDRB to '1' without affecting any of the other bits in the register. This is the data direction register (DDR), setting a pin in this register to a '1' makes that pin act as an output. I think I should have used the LED variable (defined earlier) rather than DDR2, just for clarity. Ok, next time.

14: Here we manipulate the bits in the PORTB register. This statement sets bit 7 to '1' without affecting any of the other bits in the register. Assigning a '1' to bit 7 of PORTB sets pin 7 to HIGH (~Vcc). This allows current to flow through our LED, lighting it up (or burning it out if you've not added a resistor in serial with it).

18: This statement sets bit 7 of the PORTB register to '0' without affecting any of the other bits. Pin 7 is now LOW, no current flows through the LED (so we've turned it off).

Now, here's a little nugget of info that didn't click until my trip to Noisebridge and a chat with Mitch Altman: I/O pins, by their very nature, are ambivalent towards the direction of current flow. Let me say that again as it's something I didn't realise to begin with and it has an impact on programming LEDs with the AVR. I/O pins don't care which way current flows through them. So, if you have an LED attached to an I/O port (via a resistor) then it can be orientated in either direction (i.e. connected to ground or Vcc, it doesn't matter), the only difference will be whether or not a '1' or a '0' on the corresponding PORTB pin switches it on or off. If the LED is connected to ground, then a '1' in the PORTB register will turn it on; on the other hand, if it's connected to Vcc, then a '0' will switch the LED on.

I know I laboured that last point, but it was non-obvious to me (as a beginner) so I wanted to share. It also meant that those common cathode RGB LEDs I bought aren't as useless as I thought - I was under the wrong impression that I'd only be able to program them using 3 transistors attached to the AVR as switches, because I thought you could only turn on LEDs [with an AVR] by placing a '1' in the PORTB register... doh!

1 comment:

Patrick Linehan said...

you are so advanced. you skipped port 'A' and went right to port 'B'.