Created on: 6 February 2013
Part 3 of the ATtiny2313 Tutorial
Pins on the ATtiny2313 AVR microcontroller are configured as outputs in this part of the tutorial. LEDs interfaced to the microcontroller are then switched on and off by writing to registers in the ATtiny2313 from a C program.
Before building a microcontroller circuit, interfacing LEDs to it and writing a C program, we need to know how much current the pins of the ATtiny2313 can source or sink. We therefore first look at the electrical characteristics of the microcontroller pins.
We can find out how much current the pins of the ATtiny2313 can sink or source by looking at the datasheet for the ATtiny2313.
From the datasheet, the absolute maximum DC current that I/O pins can handle (source or sink) is 40mA each.
We do not want to use the absolute maximum value when designing a circuit as any current drawn beyond this value can cause permanent damage to the microcontroller. Any design must ensure that current drawn from any pin will be much less than the 40mA absolute maximum.
I/O pin current capabilities from the datasheet:
The datasheet also states that a port can source or sink 20mA at 5V (these were test conditions used):
So any one port must not source or sink more than 20mA.
To keep within the specified current limits of the ATtiny2313, this means that:
Two example circuits are used to show how to set up pins on the ATtiny2313 as outputs.
In this circuit, two pins from port D of the ATtiny2313 are interfaced to LEDs (current sourcing).
This circuit is used to make a five LED knight rider display. The LEDs are also interfaced in a current sourcing configuration, but are connected to port B this time.
For a current sinking example, see the circuit diagram and C source code from the Starting AVR 8-bit Development tutorial.
A C program that sets up the LED pins of the microcontroller as outputs and writes to a register to switch the LEDs on and off can be found below for the two example circuits.
This C program sets up pins PD0 and PD5 as outputs by setting the corresponding bits (bits 0 and 5) in the DDR register for port D (DDRD register).
After setting up the output pins, the program execution drops into a continuous while loop. In the loop, the LEDs are switched on and off by writing either a 1 (on) or 0 (off) to the corresponding bit in the PORT register for the D port (PORTD register).
#include <avr/io.h> void Delay(void); int main(void) { // LEDs are on pins PD0 and PD5 DDRD |= ((1 << PD0) | (1 << PD5)); while(1) { PORTD &= ~(1 << PD0); // switch PD0 LED off PORTD |= (1 << PD5); // switch PD5 LED on Delay(); PORTD &= ~(1 << PD5); // switch PD5 LED off PORTD |= (1 << PD0); // switch PD0 LED on Delay(); } } void Delay(void) { volatile unsigned int del = 40000; while(del--); }
In this example, bits PB0 to PB4 in the DDRB register are set to logic 1. This enables pins PB0 to PB4 as outputs.
In the continuous loop in this program, the LEDs are first all switched off by clearing bits 0 to 4 in the PORTB register. Immediately after clearing the bits, the data in the data variable is written to the LEDs. Because there is no delay between clearing the LEDs and writing the data, the LEDs will never all appear to be off.
The data variable contains 1 bit that is set and is shifted to the left and then to the right making the corresponding LED switch on in turn.
#include <avr/io.h> void Delay(void); int main(void) { char forward = 1; // flags the direction of the knight rider display char data = 1; // data to write to knight rider display // set up pins PB0 to PB4 as output pins DDRB |= ((1 << PB4) | (1 << PB3) | (1 << PB2) | (1 << PB1) | (1 << PB0)); while(1) { PORTB &= 0xE0; // switch all LEDs off PORTB |= data; // write data to the LEDs Delay(); if (forward) { // moving LEDs from right to left if (data == 0x10) { forward = 0; // change direction data = 0x08; // prevent writing to same LED twice } else { data <<= 1; // move to next LED } } else { // moving LEDs left to right if (data == 0x01) { forward = 1; // change direction data = 0x02; // prevent writing to same LED twice } else { data >>= 1; // move to next LED } } } } void Delay(void) { volatile unsigned int del = 10000; while(del--); }
Both the above examples use the C OR operator | to switch bits on and the C AND operator & to switch bits off.
In this line of code, the |= operator performs a read-modify-write.
DDRD |= ((1 << PD0) | (1 << PD5));
The contents of the DDRD register are read (read), the value that was read is then ORed with the value to the right of the |= operator (modify). The new value is then written back to the DDRD register (write).
The read-modify-write shown below performs a C bitwise OR operation which sets bit 0 of the PORTD register.
PORTD |= (1 << PD0); // switch PD0 LED on
This code reads the PORTD register and then changes only the PD0 bit in the byte read from it by setting it to 1. The modified byte is then written back to the register. Because the OR operator is being used, all other bits in the register, whether 1's or 0's will remain unchanged as they are all being ORed with 0. Only the PD0 bit is being ORed with 1.
To switch a bit off, the C bitwise AND operator must be used. To prevent changing any other bit in the register when performing an AND operation, all the other bits must be ANDed with 1, except the bit that is to be cleared. The bit to be cleared is ANDed with 0.
PORTD &= ~(1 << PD0); // switch PD0 LED off
In the above line of code, the PD0 bit is cleared. The C bitwise invert operator ~ is used to invert all the bits in the value that is ANDed with PORTD. This makes the bit to be cleared 0 and all the rest of the bits 1 so that the corresponding bits will remain unchanged in the register (bits 7 to 1 will remain unchanged, bit 0 will be cleared).
In this example, the value ANDed with the PORTD register would be 00000001b, but after inverting the value it becomes 11111110b, so that only bit 0 is cleared when the AND operation is performed.
The above code listings can be copied and pasted into your own Atmel Studio project, or you can download the Atmel Studio projects that include the source and hex files for the two circuits.
Example 1: LED Flasher – LED_flasher.zip (14.5kB)
Example 2: LED Knight Rider – knight5.zip (15.3kB)