by

Direct Control of Arduino Uno Digital Input/Output Pins Using Port Registers

The ATmega328 has a total of three input/output ports, port B, port C, and port D. Two of these ports, namely port B and port D, are associated with the 14 digital input/output pins on the Arduino Uno. Port C is associated with the analog input pins which can also be used as digital input/output pins if needed. The following table summarizes the mapping between the Arduino Uno digital pins and the ATmega328 port pins.

Arduino Digital Pin Port Pin
0 PD0
1 PD1
2 PD2
3 PD3
4 PD4
5 PD5
6 PD6
7 PD7
8 PB0
9 PB1
10 PB2
11 PB3
12 PB4
13 PB5


 

To turn on or off a Arduino digital pin, its mode is first set using pinMode() and then its state is set using digitalWrite(). For example, to turn on digital pin 5 for one second then turn if off for one second repeatedly, the following code could be used.


int myPin = 5;
void setup() {
   pinMode(myPin,OUTPUT);
}
void loop() {
   digitalWrite(myPin,HIGH);
   delay(1000);
   digitalWrite(myPin,LOW);
   delay(1000);
}

It is also possible to control the digital pins by accessing the port registers directly. For example, the following achieves the same result as the above code. It is explained below.


void setup() {
   DDRD = DDRD | B00100000; 
}
void loop() {
   PORTD = PORTD | B00100000;
   delay(1000);
   PORTD = PORTD & B11011111;
   delay(1000);
}

Each port has a one byte register defining the direction of the associated pins. This register is called DDRD for port D and DDRB for port B. From zero to seven, each bit in this register corresponds to the comparably numbered pin of that port. For example, the fifth bit of DDRD controls port pin D5 or Arduino digital pin 5. If this bit is set to zero, the pin is an input. If this bit is set to one, the pin is an output pin. Thus, the line of code in the setup function above sets Arduino digital pin 5 to an output pin using a bitwise or operator. Each port also has a one byte register defining the state of the associated pins. This register is called PORTD for port D and PORTB for port B. Just like the DDRx registers, each bit in these registers from zero to seven corresponds to the comparably numbered pin of that port. For example, the fifth bit of PORTD controls the state of pin D5 or Arduino digital pin 5. If this bit is set to zero, the pin is set to 0 V. If this bit is set to one, the pin is set to 5 V (on the Arduino Uno). Thus the lines of code in the loop function above first turns on Arduino digital pin 5 using a bitwise or and then turns this pin off using a bitwise and.

Controlling digital i/o pins at the register level is incredibly powerful. For example, you could turn all 8 pins of port D using a single line of code if you wanted to. However, with great power comes great responsibility. Manipulating registers can be tricky and may yield unexpected results. For example, buried in the ATmega328 data sheet is a warning that one can not change a pin from the high impedance input off state to the output on state in one step. You must first go to an intermediate state of output low or input high before going to output high.

8 Comments



  1. // Reply

    You might want to fix a mistake in your example of port output. You are discussing pin 5, yet you are manipulating bit 6. According to your pin/port map these use identical numbering.
    Regards.


    1. // Reply

      no he is not.
      pin 0 is the most right pin
      so he is manipulating pin 5


  2. // Reply

    Oops! My mistake – it is bit 5. Please feel free to delete my comments.
    Regards.


  3. // Reply

    If I use DDRB, PORTB not in the ino-file, the port Register do not work.

    Which Header file do I have to include for the DDRB and PORTB Definition if I use them in my own c file?

    #include


  4. // Reply

    I have another question.
    DDRB and PORTB register are known by the Compiler in the ino-file but not in any other of my c-files. Which h-file do I have to #include to make the Compiler work?
    Thanks for the answer.



  5. // Reply

    Have any idea what these PORTx constants are declared as? Looks like, perhaps uint8_t but it seems like they should be a pointer. But, since you can do this: PORTC = B00100000; that implies it’s NOT a pointer. How does that work?

Leave a Reply

Your email address will not be published.