AVR GCC Tutorial (1) – Basic I/O Operations

My no frills Parallel Programmer

My no frills programmer

Atmel has come up with a whole range of superb AVR microcontrollers which are the dream of every hobbyist. However, the real icing on the cake is that these µControllers can be programmed with just a parallel port connector and nothing else!!!. However, Atmel µC’s have their downsides too. They have to be programmed in either Assembly or in C. There is one program – Bascom AVR – which reportedly allows programming in BASIC. But, unfortunately it doesn’t seem to work (?) with the el-cheapo parallel port programmers :( . So, the only option left is C, unless you are willing to dabble with Assembly.

I’ll attempt to give a brief introduction to basic I/O operations in C for AVR microcontrollers.

First of all, you need a programmer to program. As i told you, its all very simple, you just need a parallel port cable. Follow this excellent tutorial by The Real Elliot at Instructables.com

So, first make a simple programmer, test out the blinking LED program and then come back.

PS: Your programmer doesn’t have to include all the header pins, a board etc, that Real Elliot has shown in his tutorial. A basic parallel port connector with a few wires and resistors soldered directly on it is what i have been using from the past couple of months.  It works just as well.

Before starting, there are a few things i would wish to tell. First of all, at first sight , (especially to those unacquainted to C), all this “_BV(PD4)” stuff may seem a little too cryptic. At one point, you probably will be tempted to ditch GCC and learn Assembly.  However, i assure you that Assembly is much more frightening than AVR C.  AVR C is not at all hard to learn, it just looks a bit alarming because of those weird symbols like << >> ~ which keep floating around. Once, you understand what all that is about, it’ll all be a piece of cake.

What you should know before reading this tutorial:

1) I expect that you are somewhat well acquainted with atleast any 1 high level programming language. (BASIC, Java, C/C++ etc)

2) I expect that you are reasonably comfortable with the C syntax. You do not need to know all the stuff that comes in the 10th and 11th chapters of C++ books. Just the basics – Program syntax, header files, functions, if then else, loops - should be enough.

3) I expect you to be comfortable with binary numbers, logic gates and Boolean algebra.

If the answer to any of the above is no , then brush up and then come back.

I also recommend that everyone goes through this excellent pdf to brush up you C skills in case  they are a bit rusty.

The Port Control Registers

ATMEL AVR Port control Registers overview

For easier access , the I/O pins on an AVR uC are grouped into a number of ports. Each port contains a number of pins ranging from 3 to a maximum of 8. To control these Ports , 3 registers have been provided. Each register controls a specific feature of the Port. Let us examine each Register one by one.

And , one more thing. Unfortunately , both the register PORTx and the Portx physical pins are referred to by the same name. To avoid confusion , i will be referring to the register as PORTx and the physical pins as Portx.

Defining a pin as either Input or Output – The DDRx Registers

If you take a look at the tiny2313 datasheet, it says something on the 1st page – “18 I/O lines”. This means that out of the 20 pins of tiny2313, 18 can be used as either input or output pins.

But then, immediately the doubt arises “How do I tell the micro controller whether I want to use a pin as input or as output?”

For this purpose, there exists a register known as the DDRx register for every port. The “x” stands for the port alphabet. Every bit in the register controls a single pin of the port.

ATMEL AVR C DDRx Register

In case of the DDRx register, each bit controls the I/O of the respective port pin. I.e., the 5th bit controls the 5th pin of the PORT et cetera.

To make a pin work as Input, just write a LOW to its respective bit in the appropriate DDRx register.

To make a pin work as Output, just write a HIGH to its respective bit in the appropriate DDRx register.

Now suppose, i wanted to configure the 3rd and 7th pin of portb as output and the rest as input :

DDRB = 0b10001000;

Notice that the 7th and the 3rd bits are made high. And the rest are all LOWs.  The above command, when executed, will configure PORTB as we selected.

Similarly, you do the same thing for other ports as well.

Suppose you wanted to make all the pins of portd as output:

PORTD = 0b11111111;

But, if you are a keen observer , you might have noticed one thing. PORTD has only 7 pins. However, we have written an 8 bit value to the DDRD register. So, what happens to the last bit? The answer is: nothing. Nothing happens. The eight bit is simply ignored by the microcontroller.

Making a pin go HIGH or LOW – The PORTx register

There is one more register that controls the HIGH/LOW status of a pin. That register is called the PORTx register. Every bit in the register controls the state of the respective bit in that port. i.e. the 5th bit controls the 5th pin of the port etc.

It has 2 functions:

Case 1 : To make a pin go high or low ( if it is an output pin).

ATMEL AVR C PORTx  Register - For output pins

DDRB = 0b10001111;          /* Configuring I/O pins of  portb   */
PORTB = 0b10001010     /*   Write this byte to PORTB           */

Now, the 7th bit, 3rd bit and the 1st bit will be set.  The 0th and 2nd bit will be reset. The other bits will be in a High-Z state as they are configured as input pins.

Case 2 : To activate / Deactivate pull up resistors.

ATMEL AVR C PORTx register - For input Pins

The input pins of tiny2313 are generally in the Hi-Z state. This makes them prone to catching noise and picking up false signals. Hence, it is advisable to put a pull-up resistor to reduce noise. The resistor will normally hold the input pin at logic HIGH. Any external source can pull the voltage down to LOW when required.

Fortunately, Atmel has already included internal pull-up resistors. You can enable them by writing a HIGH to the appropriate bit of the PORTx register.

DDRB = 0b00000000;           /* Configuring I/O pins of portb  */
PORTB = 0b01110101;         /*enable internal pull-up resistors on output pins 1 , 3 , 5 , 6 , 7 */

Reading the status of an Input Pin - The PINx register

ATMEL AVR C PINx register

To put it bluntly, the PINx register contains the status of all the pins in that port. If the pin is an input pin, then its corresponding bit will mimic the logic level given to it. If it is an output pin, its bit will contain the data that has been previously output on that pin.

DDRD = 0b00000000;        /* Set all pins as input */
uint8_t status;                                /* Define a 8 bit integer variable */
status = PIND;                   /* Store the current value of PIND in status */

The above code will cause whatever input has been given to the 8 pins to be written to the variable status.

PS : Dont worry yourself if you cant understand the uint_8 stuff. All you need to understand is that we have just created a 8 bit variable with the name “status”.   “uint8_t” in AVR C is kinda like the equivalent  of “int” in ANSI C.

The Practical Stuff

Now that you know about the 3 registers , you are all set to actually start programming some real stuff. Why dont we program a running light sequence?

In our running light sequence , there will be 8 LEDs which will light up in sequence. It will be something very similar to a ring counter made using a CD4017.

Since ,we are using 8 LEDs , we must use a port that has 8 pins. That port in the Attiny2313 is the Portb.

AVR Tiny2313 Datasheet Portb Highlighted

So , first we must configure all the pins of Portb as output. For that we need to set all the bits in the DDRB register high.

DDRB = 0b11111111;

Now that we have configured the LEDs as output , we are now ready to start lighting the LEDs one by one. We will set each bit in PORTB register high one after the other , with a small delay in between

DDRB = ob11111111

PORTB = 0b10000000;
_delay_ms(50);
PORTB = 0b01000000;
_delay_ms(50);
PORTB = 0b00100000;
_delay_ms(50);
PORTB = 0b00010000;
_delay_ms(50);
PORTB = 0b00001000;
_delay_ms(50);
PORTB = 0b00000100;
_delay_ms(50);
PORTB = 0b00000010;
_delay_ms(50);
PORTB = 0b00000001;_
delay_ms (50);

With the above code , the LEDs will just run once and then stop. We need to it keep on running in a cycle like in a CD4017. To do that , let us add a infinite loop. We also have to declare the main function and include the all essential header files.

#define F_CPU 1000000UL                                    /* Clock Frequency = 1Mhz */

#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>

int main(){                         // The main function

DDRB = 0b11111111;                    // Set all the pins of PortB as output

while (1) {                        // Set up an infinite loop

PORTB = 0b10000000;                    // Turn on LED1
_delay_ms(50);                        // Wait
PORTB = 0b01000000;                    // Turn on LED2
_delay_ms(50);                        // Wait
PORTB = 0b00100000;                    // The same sequence repeats…
_delay_ms(50);
PORTB = 0b00010000;
_delay_ms(50);
PORTB = 0b00001000;
_delay_ms(50);
PORTB = 0b00000100;
_delay_ms(50);
PORTB = 0b00000010;
_delay_ms(50);
PORTB = 0b00000001;
_delay_ms(50);
}

}

The statment while  (1) is forever true , so the LEDs keep cycling forever.

There’s just one teeny weeny thing left : the Makefile. You can use the same one that Real Elliot provides in his Ghetto programming tutorial here. For your convenience i have uploaded both here :

Ready to use Makefile – The full credit goes to Real Elliot.

Running_LEDs.c

Dont forget to remove the .txt extension from the makefile after you download it.

If you compile it and load it into your Attiny2313 , you will see the LEDs on PortB running in a chain like this :

Animated GIF of AVR uC Attiny2313 Driving a LED Chain

Note : This code is a terrible example of how to make an LED chain. The same thing can be accomplished more efficiently by a loop in less than 5 lines of code.

Selective bit modification in Registers

However there is a huge disadvantage in the modifying registers in this way! Suppose you want to set the 7th bit of PORTB and reset the 3rd bit without affecting the rest, how would you do it?

It seems like its impossible, because bits in the AVR registers cannot be accessed individually. Despite this fact, with a little bit of Boolean algebra , it is possible to modify just the bits we need without touching the rest. I’m still writing the post on this. So, I’ll post the tut on selective bit modification later.

Thats all for now,

:)


80 Comments on “AVR GCC Tutorial (1) – Basic I/O Operations”

  1. Irvin Mangwiza says:

    Thank you man. Good tutorial

  2. BRDRSTR2 says:

    Great tut! Escpeccialy that scheme of the registers is good!

  3. jon soons says:

    Excellent illustrations! The links to external sites don’t work though. You are prepending your blog URL to every link which works only when they are local pages.

    jonsoons

  4. iamsuhasm says:

    ah…thanks for pointing that out. :)
    I’ve fixed it now.

  5. smm4k says:

    thanks, that is a great tutorial for first c learners.

  6. electronG says:

    Excellent tutorial!!! I’d be really interested in hearing more about “selective bit modification.”

  7. Arthur says:

    Cant wait for selective bit manipulation!

  8. Nagashree says:

    Hi,

    Thank you for this wonderful tutorial. I am really new to hardware programming and
    your tutorial helped to improve my confidence :). I can use the concept to connect
    a display device. If there are any examples you have for display devices please do
    let me know.

    Thanks.

  9. aalok bhatt says:

    u r awe some teacher……do u teach atmega32 avr-gcc tutorial????i am working on that sir

  10. Avr gcc says:

    [...] AVR GCC Tutorial (1) – Basic I/O Operations « Suhas’s Blog At one point, you probably will be tempted to ditch GCC and learn Assembly. However, i assure you that Assembly is much more frightening than AVR C. AVR C … iamsuhasm.wordpress.com/…/avr-gcc-tutorial/ – [...]

  11. MichaellaS says:

    tks for the effort you put in here I appreciate it!

  12. Aïssar says:

    I have just started reading your tutorial and I know that it is what I’ve been looking for the last days.
    Thank you very much of making me able to start with AVR µCs

  13. msk says:

    Nice tutorial…

  14. malvi says:

    I really help me on study about the AVR

  15. user024 says:

    Thank you. Really nice tutorial.

  16. ALB says:

    thanks:)!!! nice tutorial

  17. nixxe says:

    You can use bascom to write your program and it will generate a .hex file (and .eep if you need data in the eeprom area), then just upload it with any other program. I write smaller programs in bascom (Up to 4k output and you can use the FREE demo version :) and then program my chip with ponyprog using a simple parallell programmer.

  18. azul says:

    nice tutorial. very simple and helps new people like me understand stuffs a lot easier.

  19. JJ_Driller says:

    I am only a week into learning AVR microcontroller programming, and I found this tut exceptionally comprehendable. Great job man, and Thank you!!!

  20. fabarbuck@avrfreaks says:

    Quick remark about the DDRx section:

    [quote]Suppose you wanted to make all the pins of portd as output:

    PORTD = 0b11111111;[/quote]
    I’d write DDRD instead of PORTD…?!?

    • iamsuhasm says:

      Yeah…Thats what you would do.

      • Hesit8 says:

        if you declare PORD as 11 all you do is starting the internap pull-up on that port… you need to declare the DDRD as a data direction register to output…

        so the right statement would be

        DDRD=0xFF;
        PORTD=0b000001;

        and for the while
        int x=1;
        while(1)
        {
        for(x=0;x<=7;x++)
        PORTD=(1<<x);
        }

        this is so simple :|

  21. Paul says:

    Iamsuhasm,
    you’ve explained some complex topics simply and effectively.
    You are also appreciative of feedback, complementary or otherwise. Well done.
    Thank you for sharing.

  22. Daniel says:

    Thanks for the simple tutorial. Just bought an Atmel STK500 starter kit, couldn’t find appropriate information how to control the ports. The fact that register name and physical connection both are named PORT didn’t help. Glad you pointed that out.

  23. frank says:

    stay away from such simple LPT programmer…and invest in a good USB programmer…

  24. Vicu says:

    Nice tutorial to get started. I have actually worked with Bascom AVR and a cheap parallel port programmer. Never had any problems. But I must say, programming in Basic is not for me.

    Thanks

  25. rivalslayer says:

    Hi,

    Great post on AVR GCC. I was having some troubles with finding ATtinys and ATmega168s. Can you suggest some places where I can find some of these online?

  26. LBDL says:

    thankyou tor the clear explanation of the port registers and how to set the bits correctly, might I suggest an addendum of how to do the same but using hex ?
    Regardless it is very well put together and has clarified it for me (to the point that I can ask about hex !!)

  27. Matt says:

    When you say:

    “Now suppose, i wanted to configure the 3rd and 7th pin of portb as output and the rest as input :
    DDRB = 0b10001000;”

    is this right, or do you mean: “DDRB = 0b00100010″ – for pins 3 and 7 ?

  28. h3ll4dd1ct3d says:

    HOW TO change single bits of At8bit registers:

    -> DDRB is 0b00000000
    -> PORTB is 0b00000000

    Setting PB2, PB3, PB4 as OUT HIGH:
    DDRB |= ((1<<2) | (1<<3) | (1<<4));
    PORTB |= ((1<<2) | (1<<3) | (1< DDRB is 0b00011100 now
    -> PORTB is 0b00011100 now

    PB2 is set to IN:
    DDRB |= (1 < DDRB is 0b00011000 now

    PB4 is set to OUT-LOW:
    PORTB &= ~(1<<4)

    PORTB is 0b00001100 now

    • PE says:

      For people that don’t like the |= ((1<<2)|(1<<3)) syntax (although it clearly identifies the pins) you can also do:
      DDRB |= 0b00001100; //that sets the individual pins at 1 and leaves the other pins (the 0's) as they were before

  29. sameer says:

    Thanks,
    This is good for new ones
    Sameer Khanna

  30. leader says:

    Could you give us an example of context switch in AVR ?

  31. adentranter says:

    Great Tute, Best explanation of this stuff that i have found.

    Well done

  32. Testien says:

    This tutorial is just great, it helped me a lot in my beginnings. Thank you.

  33. Helped me greatly in my first LED to blink-Attiny461. Continue please

  34. Ahmed Abdien says:

    Thanks for sharing

  35. Giedrius says:

    Thanks very much! I’ve been struggling with the online documentation of avr-gcc and it’s got quite a learning curve… The description of port registers and what they do cleared things up! Thanks again ;)

  36. John Hobson says:

    Great tutorial. Make it easy to understand the registers with the graphics.

    QUOTE:

    Fortunately, Atmel has already included internal pull-up resistors. You can enable them by writing a HIGH to the appropriate bit of the PORTx register.

    DDRB = 0b00000000; /* Configuring I/O pins of portb */
    PORTB = 0b01110101; /*enable internal pull-up resistors on output pins 1 , 3 , 5 , 6 , 7 */

    QUOTE

    I think it should read PORTB = 0b11101010 ?

  37. [...] a single instruction that takes a single clock cycle to execute. You can learn something about that here. Now, let’s consider the Arduino [...]

  38. Gustav says:

    Nice tutorial :)

  39. Roozbeh says:

    Thank you.
    Sorry my eng is bad.

    I compiled code below :
    #define F_CPU 1000000UL /* Clock Frequency = 1Mhz */

    #include
    #include
    #include

    int main(){ // The main function

    DDRB = 0b11111111; // Set all the pins of PortB as output

    while (1) { // Set up an infinite loop

    PORTB = 0b10000000; // Turn on LED1
    _delay_ms(50); // Wait
    PORTB = 0b01000000; // Turn on LED2
    _delay_ms(50); // Wait
    PORTB = 0b00100000; // The same sequence repeats…
    _delay_ms(50);
    PORTB = 0b00010000;
    _delay_ms(50);
    PORTB = 0b00001000;
    _delay_ms(50);
    PORTB = 0b00000100;
    _delay_ms(50);
    PORTB = 0b00000010;
    _delay_ms(50);
    PORTB = 0b00000001;
    _delay_ms(50);
    }

    }

    But it don’t work , i use Ubuntu 10.10 and last version of eclipse IDE.

  40. Philb_au says:

    Fantastic graphics Sir, and that makes a great difference for people trying to understand.
    I appreciate your effort as I’m sure many others do.

    Now, if I could just understand ‘floating’ inputs..
    I’ve been playing around with (Attiny2313 USI –> I2C) and the board I’m using already has 10k pullups which may be a bit too much resistance.
    Just wondering if internal + external pullups actually accumulate resistance or not.

  41. Lucas says:

    Try this.

    #define F_CPU 1000000UL /* Clock Frequency = 1Mhz */

    #include
    #include
    #include

    int main(){ // The main function

    DDRB = 0b11111111; // Set all the pins of PortB as output
    PORTB = 0b10000000;
    while (1) { // Set up an infinite loop
    _delay_ms(50); // Wait
    PORTB = PORTB<<1;
    }

    }

  42. Luc says:
    #define F_CPU 1000000UL                                    /* Clock Frequency = 1Mhz */
    
    #include 
    #include 
    #include 
    
    int main(){                           // The main function
    
    DDRB = 0b11111111;                    // Set all the pins of PortB as output
    PORTB = 0b10000000;                   // Turn on LED1
    while (1) {                           // Set up an infinite loop
    PORTB = PORTB<<1;                     //Shift LED
    _delay_ms(50);                        // Wait;
    }
    
    }
    
  43. sudoku says:

    You made some first rate factors there. I seemed on the internet for the issue and found most people will go along with along with your website.

  44. Iqbal says:

    i still confuse using each of them… can it probably have different I/O for each of them like DDRx as input and port as ouput then pin as input…can you make it simple example for each of them??

  45. Samer says:

    I am not a programmer, i was in need for some basic info to do a small task related to my main master thesis project in laser, your tutorial was very useful and handy, many thanks

  46. teenytiny says:

    look for ‘ob11111111′ in the text. it should probably read as ’0b11111111′
    besides, i didn’t get this part
    DDRB = 0b00000000; /* Configuring I/O pins of portb */
    PORTB = 0b01110101; /*enable internal pull-up resistors on output pins 1 , 3 , 5 , 6 , 7 */
    the second line – is it correct?

  47. amber says:

    PORTB = 0b01110101; /*enable internal pull-up resistors on output pins
    1 , 3 , 5 , 6 , 7 */

    I dint get how it enables internal-pull resitors o pin 1,3,5,6,7 ?
    please help ?
    thanks.

  48. Ismail Saeed Kamal says:

    It is a wonderful tutorial
    am now working on fingerprint base attendance system and i need your help how to interface sm630 finger print module to atmega32 please give a hint
    thanks
    ismailkamal2099@gmail.com

  49. [...] ենք ինետից ցանկացած օրինակ, ասենք վազող լույսերը այստեղից քոմփայլ անում, ու լցնում կոնտրոլերի [...]

  50. [...] AVR GCC Tutorial (1) – Basic I/O Operations « Suhas’s Blog [...]

  51. Nick says:

    You show that you make the third and seventh pins outputs like this:

    DDRB = 0b10001000;

    And then that you can make all pins outputs like this:

    PORTD = 0b11111111;

    Does that mean that DDRx and PORTx are interchangeable and equivalent?

    Nick

  52. Serkan Ozkan says:

    Could you give and example for for every pulse input time distance assigned to an instance as an integer for a logic anaylises.
    Thanks.

  53. Fianto says:

    Thanks for your tutorial. it useful for me because i am learn about avr with programming C. I hope better more tutorial from you!!!!

  54. N&Me says:

    good animations for better understanding

  55. Unlocks.us says:

    Awesome, guys! Thanks! Use your uCs to unlock you phone!

  56. ABhinav says:

    Infinite thanks!!!

  57. Ali says:

    doesn’t the output current of port have a certain limit? isn’t it better to connect the led to vcc and give ground of the led through ucontroller? to protect it from damage? like PORTB = 0b11111110; to light up led1

  58. sixth89 says:

    can u help me..how to make only 1port as input and output..?for example switch on led blinking and motor run

  59. [...] to assist in generating the necessayr settings (and it even generates C-code!). By the way, here https://iamsuhasm.wordpress.com/tutsp…-gcc-tutorial/ is a tutorial on writing C-Code for the AVR controllers. [...]

  60. [...] Asli ArtikelNya Like this:LikeBe the first to like this [...]

  61. Fred says:

    pls some one should help me write c++ program to swap between two output pins at a time of 3hour per pin continuous. i mean one output pin must be on for 3hours , goes off the other pin comes on for 3hours continues and respective pin which switch on must display on an lcd screen, am using 8bit atmel avr mcontroller and a standard 16*2 lcd

  62. easy and understandable article on ports! nice explanation with diagrams especially the last gif image.

  63. amazing peice would you link to my article please?

  64. ma says:

    i need to test a single pin then what is instruction?

  65. [...] համար քաշում ենք ինետից ցանկացած օրինակ, ասենք վազող լույսերը այստեղից քոմփայլ անում, ու լցնում կոնտրոլերի [...]

  66. ajith says:

    informative and very usefull . thanks

  67. [...] Here is a nice writeup on basic port manipulation for AVR microcontrollers: Suhas's Blog: AVR GCC Tutorial (1) [...]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 38 other followers