Pages Menu
TwitterRssFacebook
Categories Menu

Posted by on Jun 10, 2011 in Atmel AVR, Microcontrollers | 178 comments

I/O Port Operations in AVR

I/O Port Operations in AVR

AVR SeriesHello friends! In this post, we will discuss about the port operations in AVR. Before going further, I suggest that you read my previous post regarding AVR Basics. The examples discussed here are in accordance with ATMEGA16/32 MCU. However, the concepts are equally good for any AVR MCU.

Register

Okay, now I hope you are familiar with the term register. If not, then you must have heard of it. Basically, a processor register is a memory space within the CPU itself so that they can be accessed very frequently and fast. These registers are linked with the operation of the MCU. Let’s consider the following memory space.

Register Memory Space

Register Memory Space

Here, you can see that I have represented 8 bits together to form a memory of 1 byte. Note the sequence in which the bits are numbered. They are numbered as 7, 6, 5, … , 1, 0. This is because the bits are numbered from the Least Significant Bit (LSB) to the Most Significant Bit (MSB). From the knowledge of digital logic, we know that the last bit is the LSB whereas the first bit is the MSB. Hence, Bit 0 = LSB and Bit 7 = MSB.

Register Concept Explained

Let me explain you why LSB is the last bit. Let’s take an example. Please note that 0b stands for binary and 0x stands for hexadecimal. If nothing is prefixed, it means that it is in decimal number system.

A = 0b 0110 0111 = 103

Now here, let’s change the value of the last bit (orange colour bit) from 1 to 0. This makes

B = 0b 0110 0110 = 102

Now, once again in A, let’s change the first bit (magenta colour bit) from 0 to 1. This makes

C = 0b 1110 0111 = 231

We see that by changing the last bit, the result (B) is very close to the original data (A), whereas by changing the first bit, the result (C) varies quite significantly from the original data (A). Hence, the last bit is the LSB (as the data doesn’t change significantly) whereas the first bit is the MSB (as the data changes significantly).

Now, we also know that 1 nibble = 4 bits. Hence, bits 0,1,2,3 are called lower nibble whereas bits 4,5,6,7 are called upper nibble. So basically, a register is a memory allocated in the CPU, usually having a size of 1 byte (8 bits).

Next, every register has a name, and every bit of it also has a name. Take the following example.

ADMUX

ADMUX Register

Here, the name of the register is ADMUX (don’t worry about the name, we will discuss it later). Also, note that each bit also has a name and an initial value. Each bit of the register can have a value of either 0 or 1 (initially it is 0). Now suppose, I write

ADMUX = 0b01000111;

This means that the ADMUX register has been updated as follows:

ADMUX Register after setting values

ADMUX Register after setting values

This can also be achieved by the following codes:

ADMUX = (1<<REFS0)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0);
ADMUX = 0x47;    //Hex form

So, got an idea of what registers are and how are they defined/initialized? We will discuss about various registers one by one as required. Right now, we are concerned with only three registers, DDR, PIN and PORT.

Port Operation Registers

The following registers are related to the various port operations that we can perform with the GPIO pins.

  • DDRx – Data Direction Register
  • PORTx – Pin Output Register
  • PINx – Pin Input Register

where x = GPIO port name (A, B, C or D)

Different Port Operations

Different Port Operations

DDRx Register

As I mentioned earlier, the GPIO pins are the digital I/O pins i.e. they can act as both input and output. Now, how do we know that the pin is an output pin or input? The DDRx (Data Direction Register) helps in it.

DDRx initializes the port. Have a look at it’s bit structure.

DDRx Register

DDRx Register

The ‘x’ in DDRx is just for representation. It is replaced by the corresponding port i.e. x = A, B, C, D. Say for the example shown in the diagram above

DDRC = (1<<DDC0)|(1<<DDC4)|(1<<DDC5)|(1<<DDC7);

This is equivalent to

DDRC = 0b10110001;

and as well as

DDRC = 0xB1;

and even

DDRC = (1<<0)|(1<<4)|(1<<5)|(1<<7);

So, did you get how to declare it? Suppose I would like to initialize my port B, then I would have written

DDRB = (1<<DDB0)|(1<<DDB4)|(1<<DDB5)|(1<<DDB7);
DDRC Example

DDRC Example

All right, now that we are done with the declaration, let me explain you what it does. Always remember, in the case of DDRx, 1 stands for output and 0 stands for input. In the following statement (given below), port C is initialized such that the pins PC0, PC4, PC5 and PC7 are output pins whereas pins PC1, PC2, PC3 and PC6 are input pins.

This is represented in the adjoining figure. The pins marked as input now has the capability to read the voltage level at that pin and then treat it as HIGH if it is above the threshold level, or else it will treat it as LOW. Generally, the threshold level is half the VCC.

Similarily, the pins marked as output have the capability to either become HIGH (voltage = VCC) or LOW (voltage = zero) as directed/written in the code.

DDRC = (1<<DDC0)|(1<<DDC4)|(1<<DDC5)|(1<<DDC7);

PORTx Register

The PORTx register determines whether the output should be HIGH or LOW of the output pins. In simplified terms, once the DDRx register has defined the output pins, we need to set them to give an output of HIGH or LOW. The PORTx register goes as follows.

PORTx Register

PORTx Register

The register declaration is similar to the DDRx register, except that we change the names, that’s all! One such example is given above in the diagram. The following declarations are one and the same.

PORTD = (1 << PD0)|(1 << PD3)|(1 << PD6);
PORTD = (1 << 0)|(1 << 3)|(1 << 6);
PORTD = 0b01001001;
PORTD = 0x49;

Now, let’s consider the following statements:

DDRC   = 0b10110001;
PORTC  = 0b10010001;
OUTPUT = 0b10010001;  /*This is not a C executable line, this line is just for explanation*/

The port C is initialized using the DDRx register. The highlighted bits correspond to the output pins. Now, just concentrate on the highlighted bits only. Since they are output pins, wherever I state ‘1’ in PORTC, that pin goes HIGH (1), giving an output voltage of VCC at that pin.

Now consider the following set of statements:

DDRC   = 0b10110001;
PORTC  = 0b10010101;
OUTPUT = 0b10010001;  /*This is not a C executable line, this line is just for explanation*/

Once again, the bits highlighted in orange correspond to the output pins. So, whatever value (0 or 1) desired in the orange region is reflected in the output. Now, look at the magenta highlighted bit. Inspite of being set as HIGH in the PORTx register, the output is LOW. This is because that pin is initialized as an input pin by DDRx. Hence, PORTx cannot change the properties of that pin. Hence, in general, PORTx cannot modify the properties of a pin which is initialized as input by DDRx.

PINx Register

The PINx register gets the reading from the input pins of the MCU. The register goes as follows:

PINx Register

PINx Register

The register declaration procedure stands the same. Also note that the names of the bits of PORTx and PINx registers are same.

Now, let’s consider the following statements:

DDRC   = 0b10110001;
PINC   = 0b01001011;
INPUT  = 0b01001011;  /*This is not a C executable line, this line is just for explanation*/

Here, the highlighted bits correspond to the pins that are initialized as input by the DDRx. In the second line, the PINx register is defined. Well, this line is just to explain the concept, practically, we always use PINx as a condition (like in IF or in WHILE loop). As per the second statement, the PINx command reads the values only at the input pins.

Now, consider the following set of statements:

DDRC   = 0b10110001;
PINC   = 0b01011010;
INPUT  = 0b01001010;  /*This is not a C executable line, this line is just for explanation*/

Here, you can compare it with the example I gave for PORTx. Since the magenta-highlighted bit is an output pin, PINx cannot change it’s properties. Hence, in general, PINx cannot modify the properties of a pin which is initialized as output by DDRx and vice versa.

Example Code Snippet

Let’s demonstrate the use of the DDRx, PORTx and PINx registers from the following code snippet:

DDRC = 0x0F;
PORTC = 0x0C;

// lets assume a 4V supply comes to PORTC.6 and Vcc = 5V
if (PINC == 0b01000000)
    PORTC = 0x0B;
else
    PORTC = 0x00;

Code Explained:

  • DDRC = 0x0F; is equivalent to DDRC = 0b00001111; This means that the pins PC0…PC3 are output pins (can be manipulated using PORTC) and pins PC4…PC7 are input pins (whose levels determine the value of PINC).
  • PORTC = 0x0C; is equivalent to PORTC = 0b00001100; This means that the pins PC2 and PC3 have a HIGH voltage (Vcc = 5V) and pins PC0 and PC1 have LOW voltage (0V). The other pins have low voltage by default.
  • if (PINC = 0b01000000) checks the input voltage at pin PC6. Since it is mentioned in the comment that a 4V is supplied to PORTC.6 (same as pin PC6), this condition is true (as 4 > 2.5, where 2.5V is the threshold, 5/2 = 2.5).
  • Since the if condition is true, PORTC = 0x0B; is executed.
  • If the if condition is not satisfied, PORTC = 0x00; will be executed.

We can also put it inside a while loop to run it continuously i.e. it always checks for the voltage at pin PC6, and the outputs at PC0, PC1 and PC3 go high only if the voltage at PC6 is greater than 4V.

DDRC = 0x0F;

while(1)
{
    // a condition of 4V supply to PORTC.6 and Vcc = 5V
    if (PINC == 0b01000000)
        PORTC = 0x0B;
    else 
        PORTC = 0x00;
}

To learn how to code and simulate using AVR Studio 5, visit this page.

So, here we end up with the port operations that can be performed with an AVR MCU. Though the concept has been explained using ATMEGA16/32, the concepts are equally good for any AVR MCU! Just go through the datasheet in order to get an idea of the registers.

Thank you for reading this! The comments’ box is down below! ;)

178 Comments

  1. after declaring the DDR, is it necesary to declare the port c in the example u mentioned(EXAMPLE CODE SNIPET) directly we could have used in IF ELSE staments itself

    • Hi Amruth
      DDR simply defines whether the pins are input or output. It doesn’t specify the output values of the pins (for which you need the PORT command). The example code snippet shown above is just to give you an idea how to use them in practical.

      • so u mean that we should put some values in the output side by default using port and then procede to the code??,, because u wrote port c=oxoc, and then u checked in if else statements if it was correct

        • No. Its not required to initialize it with some default value. It’s just an example code snippet, where I wanted to show that in case you wish to use all the three commands, they should be used in a way somewhat shown!

  2. Hello! I have a problem with avr studio 5 ports…i have a task to make a 7 segment LED display digits counter with two button swiches, where one count down, second up…how can i made B ports and C ports of atmega 8 in one program?

    • Hi Joe
      You can program all the ports of a microcontroller in a single program! For example:

      DDRB = 0xFF;
      DDRC = 0x0F;
      DDRD = 0x00;

      PORTB = 0x45;
      PORTC = 0x03;
      switch1 = PIND;

      This was just an example. If you want to read the values of each switch separately, then you can use the concept of bit masking, explained very beautifully here.

  3. great work mayank, you really cleared my doubts about avr I/O, thanks a lot pal, keep doin it

  4. In my code am checking whether the input pin PC1 is high or not and if it is high i want to make that input pin as low. Here is the code (In ATMEGA16)
    if(PINC & (1<<PC1))
    {
    PINC &=~(1<<PC1);
    }

    is it correct??
    Thanks in advance

    • Hi Minu,
      Yes, it seems fine. It should work. Try implementing it in hardware.

      • But how it can work? bcoz I am taking input from one micro controller and checking the input in second micro controller. how can i change input in second Micro controller…?

        • Hi Minu,
          In the first comment you mentioned above, you didn’t specify that you are using two microcontrollers.

          I am taking input from one micro controller and checking the input in second micro controller.

          Now may I know why are you trying this? Just think of it, if you are giving input to one microcontroller, why would you check it in other one? Or are you trying to say that you are sending data from one microcontroller to another?

          how can i change input in second Micro controller…?

          I am sorry, but I really didn’t get you. Please elaborate a bit more.

  5. I am using two micro controllers and sending data from one micro controller to another.

    • Alright, then you need to write separate codes for each microcontroller. One code can run only in one microcontroller. If you run the same code in the other one, then it will behave the same way as the first one behaves. Thus, write two codes, in the first one, use PORTx command to send the data, and in the second one, use the PINx (like the one you used above) to check the input.

  6. hello! i am trying to make a simple a wall detection bot,using atmega16.And other component i am using is L298n,ka7805.
    Can you give me a overview of program in c.thanks!

    • Hi Abhinandan,
      Well, for that you need to have a basic knowledge of C programming. I am sorry, but it may not be possible for me to demonstrate about C here. You should search the internet regarding this (remember, Google is your friend) or try reading some books on it.

  7. i have written the following code in avr studio6, there is no compilation error,i successfully burned the code on microcontroller, but its not working, there is problem with microcontroller as its working with other code

    Code can be found here.

    • sorry there is no problem with the microcontroller, sorry for mistake i have written

    • So, finally did it work?

      • no, i am not getting any output from the pins, but code is correct, in my development board red led is connected by manufacturer to pc0, and that’s blinking with 2 second delay as my program,but no output from pins

      • Dear Amruth,
        In the program, you have mentioned DDRC = 0b00000001; Which means that only PC0 pin is output whereas other pins of port C are input. And you say that LED is blinking perfectly, so where’s the problem? If you want output from other pins as well, you need to make the necessary changes in the DDRC line itself.

  8. hi can i access a specific pin from the all port? let say i want change pin 0 of PORTC with out affecting the other pins.
    i tried this one but is not working. Am using AVR Studio 6
    PORTC.0 = 0;
    PORTC.1 = 1;

    • Hi Dakeenya,
      Yes you can, but the way you are doing it is not correct. Try using bitwise operators.
      PORTC |= 0b00000010;
      This line sets PC1 bit.
      PORTC &= ~0b00000001;
      This line clears PC0 bit.
      I suggest you read this tutorial on bit manipulation. It should clear your concepts.

  9. can u explain me of code inside if statement, what is change if && is used
    DDRC = 0x01;
    while(1)
    if(PIND & 0x01)
    LIGHT AN LED

    • Hi Nitish,

      The condition inside if() will be TRUE only if the value of PD0 will be 1.
      Lets assume we have a switch connected to PD0 in active high configuration. When the switch is not pressed, then the value of PD0 = 0, and hence PIND = 0x00. Thus, PIND & 0x01 = 0x00 & 0x01 = FALSE. Thus, the if() condition will not be executed.

      But when you press the switch, then PD0 = 1, and hence PIND = 0x01. Thus, PIND & 0x01 = 0x01 & 0x01 = TRUE. Thus, the if() condition gets executed.

      If you use && instead of &, then the condition will always be TRUE. Remember, anything which is not zero will be treated as true.
      Read this tutorial on bit manipulation if you have any doubts regarding it.

  10. DDRC = 0x01;
    DDRC=0x01;
    int i=0;
    while(i<3)
    {
    PORTC = 0x01;
    _delay_ms(1000);
    PORTC = 0x00;
    _delay_ms(1000);
    }
    i++;

    this is code i have written for blinking led to certain time and stop after that, but my led is blinking with span of 1 second all the time

    • Hi Venkat,
      Please include i++ inside the while() loop. Then you should be good to go. :)

  11. Hi,
    I need yo help in coding.
    I am generating PWM and getting it on PB3 pin, which is set as output. I want to do the AND operation between PWM and PC0 pin, which is also set as output. how can I do that… plz help me

    • Hi Minu,
      Well, first of all, if you are generating signals from two pins, then why would you want to perform AND operation on them? You can perform AND operation within the software itself, and give the result as output through a third pin. Or else, you will have to use an external AND gate (like 7408) for this operation.

  12. if i m typing this above the infinite while loop..
    DDRB &= ~(1 << PINB1);
    PORTB |= 1 << PINB1;

    according to your tutorial it shouldn't modify that pin..since its an input pin.But on implementing it will,ask the microcontroller to read 5 volt.!!

    • Sorry I didn’t get you. Could you please post the complete code as to what you are trying to say?
      PS. Please use pastebin.com and paste your code there using proper syntax highlighting, and then post the link here. Thanks.

      • suppose,i ‘m trying to interface a button with the microcontroller,whose one end is at ground.If i give d above command,that pin will be high.As soon as the button is pressed(neglecting bouncing effect),the potential will be 0 volt at that pin .This is what the microcontrolller will implement.i meant to ask,the above command will initialise that pin to be an input pin and make it 5 volt,but according to your tutorial, the portb command cant modify the values of the pin which is classified as an input pin by data direction register.

  13. how to declare bit type variable?

    • Hi Saheta,

      There is no such one bit variable available in C. You declare variables of size 8, 16, 32, 64 bits, etc. However in C++, the boolean data type (bool) occupies one bit.

  14. Its really a very nice lecturer,bro.. I’m reading your postings one by one now only… I can clearly understand the basics of DDR, but I have one doubt..i.e.,why you have written statements for example. you have used a line to explain
    “DDRC = (1<<DDC0)|(1<<DDC4)|(1<<DDC5)|(1<<DDC7)" PORTC, Input/Output declaration, I can understand directly when you put them as "0b10110001" or "0XB1"… I can't understand why you have such syntax to explain the concept…please elaborate,why you are using that OR"|" symbol and double less than"<<" symbol..

    • Hi Seenivasan,
      This is just a syntax. We use “<<" operator here to assign values (not for left shift). For example, (1<<DDC4) means that PC4 will be made an output pin. We use the OR (|) operator here to conjugate all the assignments together.

  15. can we assign name for a pin in this way..?
    #define FRONT_IR PB1
    #define ULTRASONIC PB3

    • Yes, you can use macros. But I guess that naming the pins as PB1 and PB3 might not work. Anyways, try it out. But a better way would be to write them as:
      #define FRONT_IR 1
      #define ULTRASONIC 3

      And when you need to access them, you can access them as follows:
      DDRB |= (1<<FRONT_IR)|(1<<ULTRASONIC);
      PORTB |= (1<<FRONT_IR)|(0<<ULTRASONIC);

  16. Hi…. Really Useful article.
    Btw, m more interested in ADC o/ps.
    Can U explain in details the ADC operation of Atmega16?
    Well I have read other tuts too, they seem to have focused more on the ADCSR register. But every one fails to comment on the output of ADC, Different operations (arithmatic) and mainly interfacing this ADC output (well operated on) with 4bit 16×2 LCD. Any Headings???

    • Hi Parixit,
      Have you read my tutorial on ADC? If no, then I suggest you read it, should clear all your doubts.
      Thanks.

  17. hey mayank….
    i am using atmega16A for my project which is to display characters and also image (hopefully). It was all going right, i don’t know what has suddenly happened to the muc, i can’t access the GPIO pins individually of any port, say portB
    while(1)
    {
    PORTB=0xFF;
    PORTB=(0<<2);
    }

    logically, the above code should make all the pins of PORTB high, except pin2,i.e., PORTB2, but its not working. The above code is making B2, B3, B4, B5, B6, and B7 go low..!! I can't understand why is this happening.
    Earlier, the same code was working fine…
    please help me regarding this new unexpected problem which has left me frustrated at the time when i was already stressed up dealing with the ADC implementation of atmega16A.

    • Hi Abhishek,
      Sorry for the late reply. Did your problem sort out yet? Did you try to implement something like PORTB = 0b11111011; ?

  18. these r the two xmpl which u have used to xplain the function PINX
    DDRC = 0b10110001;
    PINC = 0b01001011;
    INPUT = 0b01001011;

    in this, LSB of both DDRC and PINC is one and the corresponding output is 1.. fine

    DDRC = 0b10110001;
    PINC = 0b01011010;
    INPUT = 0b01001010;

    and in this 4 bit from the right of both is again 1 bt the output is different..?? why so
    pls rply ASAP
    Thanks & Regards
    Ritesh Agarwal

    • Is it? It’s perfect. Please have a closer look.

      • sry bt i m nt still getting it could u pls help…

        • sry it is 4th bit in from the left..

  19. Hi..i’m new to this post…so excuse if i’m technically wrong..
    i’m doing 89v51 project..so i want to use 4 ports and it should work on 16bit hex numbers means if v1=0;v2=0;v3=0;v4=1 it should display an voltage value, again if we set v1=0;v2=0;v3=1;v4=0(it should display another voltage since i’m having up & dn keys)it shoud vary whenever up & dn key is pressed…[in hardware i have used r2r ladder ckt, but the thing is this should be generated by the 89v51 microcontroller]kindly give me some guidance regarding this..
    sbit V1 = P2^0;
    sbit V2 = P2^2;
    sbit V3 = P2^4;
    sbit V4 = P2^6;

    • Hi Angiey,
      First of all, this post is about AVR microcontroller, but it seems like you are using an 8051 microcontroller.
      Anyways, by the way you have described, I am totally confused as to what your problem is. Could you repost it by clarifying it more?

Trackbacks/Pingbacks

  1. Seven Segment Multiplexing | maxEmbedded - [...] It is assumed that you have prior idea about I/O port operations. If not, take a detour, go through…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: