07.25.2009

A 101 on avr-gcc

by Rahul Pisharody

The GNU/Linux offers a plethora of tools to program the ATmega8 efficiently and quickly. avr-gcc provides a port of the popular gcc compiler. This enables a newbie with basic knowledge in C to program the controller easily without the hassle of learning assembly instructions. Moreover, programming the controller in a middle-level language as opposed to assembly gives higher command over the code, but at a higher program size.

Installing avr-gcc is a breeze in Ubuntu/Debian with proper repositories enabled. It can be installed via Synaptic Package Manager or through command line by issuing the command apt-get install avr-gcc. In addition to the compiler, you also need the libraries which define the specialized functions. The library called avrlibc can be installed in a similar fashion.

Take a look at the man pages to find out the version of avrlibc you are using. Although the past couple of versions are not too different from each other; avrlibc has grown from what it was when it was first introduced in 2002. You will have to download the manual for avrlibc which is available here.

Let us now try to dissect a small program which blinks an LED; the “Hello, World” of microcontrollers.

#include <avr/io.h>
main()
{
    DDRC |= _BV(PC0);
    TCCR0 = _BV(CS00) | _BV(CS02); //Clock divided by 1024 mode
    while(1) {
        PORTC ^= _BV(PC0); // toggle PC5
        while(bit_is_clear(TIFR, TOV0));
        TIFR = _BV(TOV0);
    }
}
 

The #include directive includes the header file which defines the standard input output functions for the ATmega8. It is not specific to ATmega8, but can be used for a wide range of ATmega Products. It is followed by the main() function which is pretty much how standard C program follows. The variables in capital letters are standard registers of the device as specified in its datasheet.

The DDRC register (or the Data Direction Register, Port C) determines whether a particular pin of the Port should be an output register or an input register. The register is similar to the Tris register in PIC Devices. The difference is that writing a 1 to the pin makes the pin an output as opposed to what in PIC.

PC0 is the 0th bit of Port C and thus it is numerically equivalent to 0. The macro _BV(X) is equivalent to ( 1 << X ). Thus the code _BV(PC0) ultimately evaluates to ( 1 << 0) and is written to the DDRC register. To cut a long story short, we just set the bit 0 of the DDRC register, making the pin PC0 an output pin. The macro _BV() is helpful in setting more than one bits without sacrificing code clarity. For example, to make the pins PC5 and PC7 of Port C outputs, one just has to code DDRC |= ( _BV(PC5) | _BV(PC7) ); The code word generated is ORed with the DDRC register to save whatever configuration bits the other pins hold.

Next is the TCCR0 register. It controls the timer of the ATmega8. By looking at the timer section in the datasheet, one can know which bits to set for each configuration. The bits can then be set using the _BV(X) macro that we encountered earlier. It can also be set by equating the register with the values we generated manually. The preferred method is the usage of macro as it simplifies reading the code.

Since we want to blink the LED an indefinite number of times, we code the LED toggling in an infinite while loop. The operator ^ is the C XOR operator and it effectively toggles the PC0 bit. The macro bit_is_clear(X, Y) returns true if the Yth bit in the X register is cleared. The register and the bit coded in the program, ie TIFR and TOV0 pertains to the timer peripheral of the device. More operational details of the timer can be obtained from the datasheet. Essentially the operation is that on end of timer count, a bit TOV0 of the TIFR register is cleared which has to be set to re-enable the timer.

The next post will detail compilation and uploading the program to the device.

Related

Comments

Leave a Reply