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. Can you please explain what does 32K flash means in Atmega32, does it mean the compiled hexfile size should be maximum 32KB or something else. I gone through net but thing is not clear.
    Regards,
    Uttam Dutta

    • Every microcontroller needs a permanent memory for various reasons. Flash and EEPROM are the ones commonly used. The generated hex file is stored in the flash. So, in one way or other, it cannot exceed 32KB size for Atmega32, or else you’ll end up in trouble.

  2. HI , struck with the USART in Atmega16A i try with all possibilities and checked all hardware thrice but i cant able to send at least single character pls can you send a working code , requesting you.

    my code look like this.
    http://pastebin.com/aePhp2ZJ

    • Most of the USART errors are because of the devices not being clocked right, or due to baud rate mismatch. Check whether they are the same or not.

      • Thank you foe concentrate on my problem, i will explain you clearly regarding my problem , i can transmit the data , receive back and print it on LCD done successfully, but i can’t do same with PC Hyper terminal, what may be the problem , and i checked the baud rate and i cross checked with internet also that part perfect , pls Help me.

        • How are you connecting it to your PC? Which device are you using – USB to Serial converter, or USB-UART bridge like the one shown here.

          • Hi , i am using TTL to RS232 (MAX232 level converter), and i used same converter with other controller its working properly , when i use Atmega16a with this level converter my data is not transferring to Hyper terminal ,i am getting only dots like this ……., i checked 100 times connection and all , my DB9 connector wire also working properly , so i am really amazing for this problem please help me to solve this problem .

        • Did you try the loopback test? Does it work?

          • Ya i tested that and working properly , i print loop back character on LCD it also working but i can’t able to see in Hyper terminal , is it any problem in IDE? is it any option to check status of IC whether its working in internal crystal or external crystal ?, but loob back output i am getting very cool , please try to solve my problem this struck my other work also .

          • By loopback, I meant with your computer, not with your controller. Did you try the loopback test at your hyperterminal window? And the way that you are describing your problem, I doubt that you have set up your hyperterminal properly. Try using something else, like RealTerm or PuTTy.

          • yes, i tried with that also loop back i checked with pc side also thats look good, wire terminal window all i checked, i checked same setup with arduino is working .

          • Then there’s something wrong with your settings in your microcontroller. Make sure that the baud rates match (<2% error), with proper start/stop bits configuration, etc.

          • Okie thank you , and i will check one more time and let you know everything once again , thanks a lot .

    • try this :)
      [CODE REMOVED BY ADMIN]

  3. i’m trying the following block for taking input from 2 switches but its not working.
    plz help. http://pastebin.com/4xE5FGs0

    only method working is bit_is_clear function method but logically mine is also right so plz help me asap.

    • You are seem to be comparing the Register PIND directly to some values. Try reading the value of the register first and storing it in a variable and then try comparing the value of the variable with those values.

      Try something along the lines of this: http://pastebin.com/7f45NjRU
      Hope this helps!

      • but PINx is used to read status of PORTx so why cnt i directly compare PINx value to check its status..
        whats logic behind giving PINx value to variable and then compare it and not directly comparing PINx value to check input.

  4. i am making a line follower. Sensors r connected 2 c0 and c1 and c2,c3 and c4,c5 to motor driver. i used dis code: http://pastebin.com/5vyPKyMW
    but it always follows de else case.i had asigned pins to input and output correctly
    . Sensors r wrking fyn. wer did i go wrng??

    • Try replacing else statement with else if(PINC==0b00000000) and see if it works.
      Also, what’s the voltage output of your sensors?

      • Even after else if m also faceing same prob. And sensor output voltage is 3.4v

    • Yes, why not?!

  5. MAX,
    It is 2 millisecond if I write “_Delay_ms(200);” let say atmeg8 and with internal oscillator.
    Now I want to punt on a LED after 10 min. delay means number should be 600000

    So
    …..
    DDRA= 0xFF;

    PORTA= 0b00000000;

    _Delay_ms(600000);

    PORTA= 0b00000001;

    _Delay_ms(600000);
    …..

    like this,
    Will the LED will ON after 10 min.
    and maximum how big number I can put for delay
    Will this conception be same for PIC Microcontroller like 16F628?
    Please reply
    Regards,
    Akash Nil

    • I think I already replied to this question of yours somewhere.

    • Max,
      You give me a link where there is some explanation about delay and count, but not so lucid and straightforward like your explanation . I will be grateful if you answer my doubt straightforward.
      Regards,
      Akash

      • Akash, from what I recall, I have shared the link of the official man page of AVR’s documentation. As an embedded developer, you would be required to read man pages a lot and not every time you’ll find them easy to understand.

        What I wanted you to grab from the man page is that _delay_ms() accepts an argument of type double, which is an 8 bit quantity and ranges within 1.7E +/- 308 (15 digits). Is this what you wanted to know?

  6. sir you have an awesome collection of tutorials on avr. However,can you please create tutorials for msp430 on similar lines.I have been looking for tutorials on msp430 but, none of the tutorials online have given a vivid explanation of that micro controller.Please solve our problem.

    • We’re on it Arjun! It’ll come soon. :)
      Thank you!

  7. At the beginning , THANK YOU very much for this great web site.

    I am a little bit confused; as the two PIN example have contradiction.

    The 1st, the right most is 1 in the 1st & 2nd line and the 3rd is l, but in the 2nd example, the 5th is 1 in both 1st and 2nd lines and the 3rd is 0

    Could you please explain more?

    • Hi,
      Could you please try framing your question a bit better? We are having a hard time decoding what you are trying to say!
      Thanks!

  8. sir, I learned most of the things about MCU from your tutorials. thanks a lot.
    but I haven’t completed the whole session since it difficult me to find tutorials as step by step.it will really help me if you can give a tutorial list step by step.
    THANK YOU,

  9. Hi Sir,
    Your website is very useful to learn MCU. I’am very beginer of the controller. Am using atmega 2560 controller and atmel studio 6 for studying. In GPIO function I learned to use DDRX PINX PORTX register. Now I have one doubt on it. How can I use the single input pin for condition checking function.
    example:

    if(PINB==0b0000010) This function is working fine.

    but I’am trying to use like this

    if(PINB1==1)

    its not working. How can I use the separate PINB0 PINB1 PINB2? where it is used? This is cant used in Digital input reading. Kindly help me.

    Thanks and Regards
    T.Sriram

  10. Hi sir
    In my project I need to display the AC voltage in LCD.My circuit output is in PWM form.How should I read PWM pulse in Micro-controller.I am beginner for AVR. If anybody knows Kindly help me.
    thank you.

    • Hello Yuvaraj,
      You can read PWM signal using the input capture trigger of AVR timers. It is very well documented here. Also, check out my tutorials on AVR timers.

  11. is (((PINC & 0b00000001)>>0)==0) command correct to check pinc.0 in atmega 32 in winavr

    • Yes, but I don’t understand why would you want to right shift the result by zero. It does nothing!

      • sir..how can i make a pin high or low?for that do we want to use ddr register??

  12. will the foll. statement generate an error?

    DDRA=0x00;// set as input
    PORTA=0xFF;// written PORT Instead of PIN

  13. HAI MAX, IF I SET INPUT PORT FOR EXAMPLE HERE I WANT SELECT INPUT PORTB0 SO HERE I WANT TO SET DDRB=0b0000000 OR DDRB=0b00000001; which one code is correct for reading input value please tell me sir i am confused

  14. if(bit_is_set(PINA,dout))
    I am little bit confuse about above code
    using atmega16…. dout is the output of adc attached to PA.4
    please help me….

  15. i just want to know that if I have used a upper nibble for I/p and lower nibble for o/p for 3 different ports then can I compare the value of inputs form three different ports at every instant and change the value of output accordingly.

  16. in defining registors we used command such as 0b00100111, does that 0 before b signifies anything or is just a part of representation?

  17. can i use portA as input port of keypad and input for sensors to ADC channel 1 and 2 at the same time

  18. Hi,

    For interrupts you can detecting any edge, falling or rising. This is ok if your data is always changing every clock cycle 101010 etc. But my data has sequences such as 1001110 or 1011011 where it does not change every cycle, 1 to 1 does not count as a change, rising or falling. How can I detect this ?.

    Many Thanks

  19. hello
    i want to a save mobile number in atmega32 when i call from a number in permanent and its only save one time after it remains in mcu life time.
    and also want to save some status of some io pin (i.e they was high or low before power off in permanent manner)
    please give some idea

Leave a Reply

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

%d bloggers like this: