Knowledge Base‎ > ‎General Gyaan‎ > ‎Tutorials‎ > ‎

Anatomy of C Program for AVR

posted Sep 17, 2016, 6:37 AM by Rohit Bhaskar

  • #includes. They tell the compiler where to look for other bits of code you are using that do not reside in this file. They are normally ".h" or header files that contain macros and function prototypes. Before a the .c file is compiled, the contents of the #includes are literally copied into the file.

      // compiler includes

      #include <avr/io.h>


      // avrlib includes

      #include "global.h"

Some of the basic ones that you’ll end up using:

       #include <avr/io.h>  :basic io functions(discussed below)

       #include <compat/deprecated.h> : functions like sbi,cbi(discussed below)

       #include <util/delay.h> : functions to introduce delays in the program

       #include <avr/interrupt.h>: interrupt definitions



  • #defines are "macros". Before the program is compiled, the argument of the #define is simply substituted everywhere the name is used. They are handy for giving meaningful names to numbers and for changing a single value that may be used numerous times in a program without having to change every instance.

      #define DELAY 1000

  • Function prototypes give the name, arguments and return type of functions that will be used later in the program. They can be included in header files or in the .c file before the function is defined.

      int checkButton(int whichButton);

      void setLED(int whichLED, int on);

  • The main function is literally the main part of the code. There can only be one main function in the final compiled program. The code inside the main function is executed sequentially, one line at a time.

      int main(void)

        {..your code here..}



  • Comments are not evaluated by the compiler. They are there to help the programmer. There are two styles of comments that can be used:
      // This is a C++-style 'slash-slash comment' 
      /* This is a C-style comment*/
      /* These comments must be terminated with a star-slash */


As you have noticed above , the avr compiler uses c/c++ syntax and incase of any problems in the syntax just open your first year programming textbook    ( balagurusamy).

Now moving to functions which you will frequently require when programming the atmel atmega for i/p and o/p purposes.

Setting and Clearing Bits

  • Remember that setting a bit is setting it high or to 1, and clearing a bit is making it low or 0.
  • sbi - set a bit.
           void sbi(int register, int bit)

Sets a bit in a register. For example, to set the 0th bit of Port D, you can use:





  • cbi - clear a bit.
          void cbi(int register,int bit)

Clears a bit in a register. For example, to clear the 2nd bit of Port B, you can use:


Writing to and Reading from registers

  • The easiest way to write a byte to a register is with an assignment. To write the byte 0x0F (0000 1111 in binary) to the PORTD register:
           PORTD = 0x0F;
  • Direct assignment to registers is a recent addition to AVR C syntax. Previously, the outb function was used:
           void outb(int port, int val)

writes the byte val to a port. To write the byte 0x0F to the PORTD register, you would do the following:

  • To read a byte from a register, you can also use an assignment.
          int status;
          status = PIND;

would read the value of the PIND register and store it in the variable status.

  • Similar to outb, the function u08 inb(u08 port) returns the value of the register port.
          status = inb(PIND);

is equivalent to the previous statement.

AVR Registers

  • All information in the microcontroller, from the program memory, the timer information, to the state on any of input or output pins, is stored in registers. Registers are like shelves in the bookshelf of processor memory. In an 8-bit processor, like the AVR ATMega 16 we are using, the shelf can hold 8 books, where each book is a one bit binary number, a 0 or 1. Each shelf has an address in memory, so that the controller knows where to find it.
  • The 32 IO pins of the ATMega32 are divided into 4 ports, A, B, C, and D. Each port has 3 associated registers. For example, for port D, these registers are referred to in C-language by PORTD, PIND, and DDRD. For port B, these would be PORTB, PINB, and DDRB, etc. In C-language, PORTD is really a macro, which refers to a number that is the address of the register in the AVR, but it is much easier to remember PORTD than some arbitray hexadecimal number.

The DDRx Register

  • The DDRD register sets the direction of Port D. Each bit of the DDRD register sets the corresponding Port D pin to be either an input or an output. A 1 makes the corresponding pin an output, and a 0 makes the corresponding pin an input. To set the first pin of Port D to be an output pin, you could use the sbi(reg,bit) function, which sets a bit (makes it high or binary 1) in a register:
     sbi(DDRD, 0);   //these two statements are equivalent
     sbi(DDRD, PD0); //both set the first pin of port D to be an input
                     //by setting the 0 bit of the DDRD register.
  • To set the second pin to be an input, you could use the cbi(reg,bit) function which clears a bit (makes it low or binary 0) in a register:
     cbi(DDRD, 1);   //these two statements are equivalent
     cbi(DDRD, PD1); //both set the second pin of port D to be an output
                     //by setting the 1 bit of the DDRD register.
  • Note that in C, the bit indexing begins with 0. The bits in a register go from 0 to 7. This may be a source of confusion, because when we talk about the "first pin" or "pin 1" of a port, we are referring to the 0 bit or P0 in C. You can also set the value of all the bits in the DDRx register (or any register) using the outb(reg,byte) command. It writes a byte (8 bits) to a register. For example, if you wanted to set pins 1-4 of port B to output and pins 5-8 to input, you could use:
     outb(DDRB, 0x0F); //Set the low 4 pins of Port B to output
                       //and the high 4 pins to input
  • An alternate way to write a value to a register is using the same syntax as a C assignment:
     DDRB = 0x0F; //Set the low 4 pins of Port B to output
                   //and the high 4 pins to input

The PORTx Register

  • The PORTx register functions differently depending on whether a pin is set to input or output. The simpler case is if a pin is set to output. Then, the PORTC register, for example, controls the value at the physical IO pins on Port C. For example, we can set all the port C pins to output and then make 4 of them high (binary 1) and 4 of them low (binary 0):
     DDRC = 0xFF;  //Set all Port C pins to output
     PORTC = 0xF0; //Set first 4 pins of Port C low and next 4 pins high
  • When a pin is set to be an input, PORTx register DOES NOT contain the logic values applied to the pins. We use the PINx register for that. If a pin is an input, a 1 in the PORTx register sets a pull-up resistor. This is helpful for a variety of circuits.
     DDRC = 0x00; //Set all Port C pins to input
     PORTC = 0xFF; //Set pull-up resistors on Port C

The PINx Register

  • When a pin is set to input, the PINx register contains the value applied to the pin. The pins have an electrical threshold of around 2.5 volts. If a voltage above this level is applied to an input pin, the corresponding bit of the PINx register will be a 1. Below this voltage, the bit will be a zero. See the ATMega16 schematic for the specific threshold voltages. To read the value of an input port, you can use the inb(reg) function or a direct assignment. It returns an 8-bit number that is the value of the 8 bits in the specified register.
     int var;  // declare an 8-bit variable
     DDRD = 0x00;   // set port D pins to input
     PORTD = 0xFF;  // set pull-ups on port D
     var = PIND;    // read the value of the port D pins
                    // and store in the variable var
  • There is also a function available for checking the state of an individual bit, without reading the entire PINx register. The function bit_is_set(reg,bit) returns a 1 if the given bit in the given register is set, and a 0 if the bit is cleared.
     int bar;  // declare an 8-bit variable
     DDRD = 0x00;    // set port D pins to input
     PORTD = 0xFF;   // set pull-ups on port D
     bar = bit_is_set(PIND,1);
                     // bar now contains the logic value at Port D pin 2