Direct port manipulation, using the digital ports [tutorial part 3]

Hello fellow readers, today I will write a bit about the digital pins and how to read and write digital values to then, its a simple subject but of a great importance, because almost everything that our micro-controller does is using its inputs and outputs to talk with leds, motor drivers, lcd’s, shift-register, to read data from digital sensors and a lot other things, so lets start reading about how to do it.
Our micro-controller, the Atmega328p as registers, those registers are associated with the input/output ports, each port as a specific name and the associated registers, in fact our atmega as port B,C and D, and each port as a different number of pins(this is a restriction of the 28 pins PDIP package and not from the micro-controller, because an 40 pins PDIP for example as 4 ports with the full 8 bits each one), the only port that as the full 8 input/output pins is PORTD.

As you may already know, each pin can have multiple functions, like pwm generation, or ADC capabilities, the pins 7 and 7 from PORTB are also the input pins for the crystal oscillator, and pin 6 from PORTC is the reset button.
In this image you can see all the alternative functions that each pin can have, the chip in question is the Atmega328p.

And here is the mapping between arduino port names and their real name:

So, how can we interact with our digital pins?
Well, to begin there is a dedicated register for each PORT that defines if each pin is a input or an output, that register is the DDRx, where x is the letter from the PORT that we want to configure, in the case of the Arduino there is DDRB, DDRC and DDRD. As every logic value, each bit in the DDRx registers can be either 1 or 0, being that putting a specific bit of DDRx at 1 configures the pin as output and putting it at 0 will configure the pin as an input, lets see a small example that configures pins 0,1,2,3 as digital inputs and pins 4,5,6,7 as digital outputs:

DDRD = 0b11110000;

And all pins as outputs:

DDRD = 0b11111111;

And if you need all outputs? Try it yourself, or wait a few days and I will release my bit-manipulation tutorial.
There should be some care when using PORTD and Serial/USART because pins 0 and 1 from the PORTD are the ones used by the USART and if you put both of them as inputs or outputs the USART may be unable to read or write data in those pins.

We can already say to the Atmega how the pins should be handled, but we want to know how to read an write data to those said pins, so to write data to a given port, we use the PORTx register, this one is easy to remember, where x is the port name, after configuration a pin as an output its just a matter of putting 0 or 1 in the PORTx register to drive that pin low or high respectively, lets see some code for it:

DDRD = 0b11111111;    //All pins in PORTD are outputs
PORTD = 0b11111111;    //All pins in PORTD are high

 

DDRD = 0b11111111;    //All pins in PORTD are outputs
PORTD = 0b00000000;    //All pins in PORTD are low

And how about a pattern of on,off,on,..?

DDRD = 0b11111111;    //All pins in PORTD are outputs
PORTD = 0b10101010;    //The small on,off,on,.. pattern

Now, the only thing left is how to read on pin so we can read data from sensors or even the mighty push-button, to read the state of a digital pin configured as input, we will used a third register called PINx, where again x is the port name of where the pin is located, so first using DDRx we say to the micro-controller that we want some pins as digital inputs, then using PINx we read their values, seems easy right, so lets dig into the code:

DDRD = 0b00000000;    //All pins in PORTD are inputs
char my_var = 0;    //Create a variable to store the data read from PORTD
my_var = PIND;        //Read the PORTD and put the values in the variable

Its as easy as it can be, and when compared with the Arduino digitalWrite and  Read functions, using direct port access you save space in flash and also win a lot of speed, because the Arduino functions can take more than 40 clock cycles to read or write a single bit in a port and the same time to read another single bit, and the code is pretty complex with a load of lines that occupy at least some 40 bytes, it might be a small save in flash but its a huge steep to speed up any program, but they are easy to use by the people that don’t understand a lot about programming/micro-controllers, so every implementation as its advantages and drawbacks, but lets continue.

Its a bit uncommon that you need to read or write a full port at each time, for example if you want to light a led, or to read a button we only need to use one pin, and writing all those bits one by one every time that we want to change a value in a PORT is a boring task, but the AVR lib-c as some little nice defined Px0..7 “words” where x is again the port that we want to use and 0..7 is the value of the individual pin of the said port, so to light a led we would do something like this:

DDRD = (1<<PD2);    //Pin 2 of portd is an output
PORTD = (1<<PD2);    //Pin 2 of portd as now the logic value 1

Or reading a button value:

DDRD = 0b11111101;        //Pin 1 of PORTD is an input, all others are outputs
char my_var = 0;        //Create a variable to store the data read from PORTD
my_var = (PIND & (1<<PD1));    //Read the PORTD pin 1 value and put it in the variable

We can also use this Px0..7 thing multiple times in a statement, for example in this piece of code, there will be some code executed only if two buttons are pressed at the same time:

DDRD = 0b11111100;        //Portd pins 0 and 1 are inputs, all the others are outputs
if(PIND & ((1<<PD0) | (1<<PD1))){
 //Code inside the if() statement will be executed when both buttons are high
}

I think that you are getting the point, but the bit manipulation tutorial will help a bit about this subjects
There is yet some more things that we can do with our input and output pins/ports that is very useful for i2c interfaces, and for example to use buttons, I’m talking about the pull-ups that our micro-controller and inside it and I will show you how you can enable them and why should you use them when using push-buttons.

When you have a push-button it can have two states, one is disconnected, and when you push it it will make the connection between the micro-controller pin and lets say, ground, but when it is disconnected there is nothing forcing a stable value in the input pin, to the untrained eye we could assume that the pin will be reading 1, because when we press the button it read 0, but the fact is that the pin can read either 1’s or 0’s because the pin is very sensitive to electro-magnetic noise, much like a little antenna, so we can solve this problem in two similar ways, one is to attach a resistor of 10Kohms or more between the Vcc(+5v) and the input pin, or just save some pennies and use the integrated pull-ups that our micro-controller as to offer, it also makes our circuits a bit simpler and that’s also a good thing.

To enable the pull-ups we need to do something that may seem a bit strange at the first look,because there is no dedicated register to enable or disable the pull-ups, their are enabled or disabled writing respectively 1 or 0 to the PORTx register when the DDRx register is configured as inputs, lets see some code to clarify this:

DDRD = 0b00000000;    //All pins in PORTD are inputs
PORTD = 0b00001111;    //Pull-ups enabled in the pins 0,1,2 and 3 and pull-ups disabled in pins 4,5,6 and 7
char my_var = 0;        //Create a variable to store the data read from PORTD
my_var = PIND;        //Read the PORTD and put the values in the variable

If you execute this code with nothing attached to PORTD, the four high bits of the my_var variable may have 1’s or 0’s or any possible combination of them because they are floating(acting like little antennas), but the four lower bits will read all 1’s because the pull-ups impose a weak 5v signal that is read as a 1 logic value.

In a basic sense this is all you need to know to master the direct port manipulation but the bit manipulation tutorial will teach you more things about nifty things like bitmask’s, AND,OR, NOT and XOR operations and how to set and clearer bits in a register and some nice tricks about the shift left and shift right operations, all good things to know as they can speed up your program and are handy when using the digital ports.

Lets now make a small test program that will take use of the digital inputs and outputs and also the pull-ups, because this as been a very theoretical tutorial its nice to come to the end and blink some leds!
This is the pseudo-code for our program, its intent is to read a switch and every-time that the switch is read it will toggle a led, so you press ounce and the led lights up, press again and the led goes off, and again from the start, this could be done using some if’s conditions but can be done in a single line using the powerful XOR(^) operator.

Main{
 Configure the pins, in this example code, I will use PORTD
 Infinite loop{
 Read button value
 If led is on and button==1 turn led off
 If led is off and button==1 turn led on
 }
 }

Just one more thing(I know I’m always going a bit off-topic, but this one is important) when we use a button wired to a digital input we must be aware that a push-button doesn’t give a nice and clean transition from 0 to 1 of from 1 to 0, but instead the signal as some ringing and bouncing, this is due to the mechanical proprieties of the button and not a design flaw, the button as small metallic reed inside, and when we press it the reed closes and close the circuit between its input and output but this reed will oscillate a bit until it rests firmly in its stop, so we must take care of this, and as usual there are two ways, using a small capacitor near the button to debounce the value, or make this very same debouncing in code, which is easier to do when we have a lot of buttons and again is cheaper than adding a lot of caps to our circuit.

If you google the terms button debouncing you will find a great variety of ways to debounce buttons in code, the way that I will use here is the simplest of all, its just a small delay inserted between consecutive reads of the button, this is of course a blocking method because our micro-controller will just stop and sit there delaying for some miliseconds, there are smarter ways that use timers and other smart tricks but for 2-3 buttons and projects that don’t need super precise timing this is a commonly used method.

To do this delay I will use the built-in delay routines provided by the AVR lib-c.
So lets start coding, all the code is straight forward if you understood everything that was written above.

#include <avr/io.h>        //This is our usual include
#define F_CPU 16000000UL    //This says to the compiler at what frequency our Atmega is running, in this case its 16Mhz
#include <util/delay.h>        //The delay functions/routines

uint8_t readButton(void);    //Declaration of the readButton function

int main(void){
DDRD &= ~(1<<PD2);    //Configure PORTD pin 2 as an input
PORTD |= (1<<PD2);    //Activate pull-ups in PORTD pin 2
DDRB |= (1<<PB5);    //Configure PORTB pin 5 an output, this is the digital 13 in the Arduino that as the built-in led

 while(1){                //Infinite loop
 if(readButton()==1){        //Verify the button state
 PORTB ^=(1<<PB5);    //This is the above mentioned XOR that toggles the led
 }
 _delay_ms(250);            //Delay between consecutive button presses
 }
}

uint8_t readButton(void){
 if((PIND & (1<<PD2)) == 0){        //If the button was pressed
 _delay_ms(25); }        //Debounce the read value
 if((PIND & (1<<PD2)) == 0){        //Verify that the value is the same that what was read
 return 1; }            //If it is still 0 its because we had a button press
 else{                    //If the value is different the press is invalid
 return 0; }

}

I think that there is no need to show how to create the project in AvrStudio at this point, but if you have any doubts consult the other tutorials or leave a comment, and don’t forget to press the reset button when you want to upload your code.
I also don’t have a circuit schematic but I can get one if someone requests it, or when I have the time to make one, the led used is the built-in led that Arduino as in Digital pin 13, and the button is also easy, you connect one of the legs of the button to the PORTD2 pin, that is digital pin 2 and the other leg of the button you connect to the ground.

There is here a small video showing you this code in action, the led doesn’t always toggle, but this is “normal” because my button as already suffered a bit in its life and I can also press the button when the long delay of 250ms is being executed, but it works and here is the proof:

I have done some corrections to the text, and here is the code in an Avr Studio project ready to compile and upload:

http://code.google.com/p/avr-tutorials/downloads/detail?name=port.zip

Thanks for reading and don’t forget to comment anything that you want, and good programming!

About these ads

10 responses to “Direct port manipulation, using the digital ports [tutorial part 3]

  1. To enable the pullup in one pin, does the port to that pin must have all pins as an input? As the example shows?

    Or do you just need to write a 1 to the input pin?

    Thanks!
    – DaviD

  2. Hi,
    Great tutorial, very clear, but I still have a question.
    You configured the button pin as input and then set it HIGH. Why didn’t you just set the button pin as an output? How would that be different from what you have done?

  3. Hey, I learned alot here. But you may want to update your info or put in a note somewhere. It took me forever to figure it out. on the UNO and 1.0.2 IDE you can’t code (1<<PDx) it's changed to (1<<PINDx) or (1<<PORTDx) . "x" bring the pin you're trying to work with. Thanks for putting this together.

  4. no need to write void setupo()
    and void loop?

  5. Very nice tutorial! I’m still working in the Arduino IDE, but this tutorial was very helpful. Direct port manipulation has been useful for speeding up some code I’m running in an ISR; and by speeding up I mean I can now measure the time spent inside the ISR in terms of clock cycles. :)

  6. Royal ce moment passez avec vous, un enorme compliment et felicitation. Merci beaucoup pour cette bonne lecture.

  7. Great tutorial!!! thank you very much

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