Please let us in the comment zone any suggestions that you think will improve the article!
If you like the article click the follow button from social media to stay in touch with us!
We all know what digitalWrite and digitalRead do but these are slow. If we have an application which need some very short times what can we do???
Well we will modify the pins states direct from port registers.
Arduino uno has three ports which contains all pins like in the picture below:
–B- this is used by digital pin 8 to digital pin 13
-C-this is used by analog pins
-D- this is used by digital pin 0 to digital pin 7
Ok, but how we control just one pin if B(for example) control pins from 8 to 13. For that each port has three register which define each pin apart.
These register are:
-DDR-makes pins input or output
-PORT-makes pin LOW or HIGH
-PIN-this is for reading the state of input pins
Now because arduino has three ports we will have
-DDRB, PORTB, PINB-each with 8 bits for pins 8 to 13(bit 6 and 7 must be high because they are for crystal)
-DDRC, PORTC, PINC-each with 8 bits for pins A0 to A5(we can control only 6, pins 6 and 7 are avaible on other boards).
-DDRD, PORTD, PIND-each with 8 bits for pin 0 to 7
Each bit from each register corresponds to a single pin like:
(for example) DDRB
XTAL | XTAL | D13 | D12 | D11 | D10 | D9 | D8 |
1 | 1 | 0(1) | 0(1) | 0(1) | 0(1) | 0(1) | 0(1) |
Above we can see for DDRB register the posibility to control the mode of pins 8 to 13 as INPUTS or OUTPUTS. For example if the bit for pin 8 has the 0 value that means the pin 8 is an input pin, otherwise if it has 1 value is an output pin. You can observe that we let 1 for bits corresponding to 6 and 7 because these are for crystal.
But how we write this in arduino software. If we want to see all the bits on software we can do this:
DDRB=B11111111; this means that the all the six pins are outputs
DDRB=B11111100; this means that the pins 8 and 9 are inputs and pins 10 to 13 outputs
Other method to declare this is:
DDRB=0b11111111;
DDRB=0b11111100;
Now after declare them as inputs or outputs we need to make them high or low.
For that we have PORTB which is like the DDRB register only difference is that “0” means LOW and “1” means HIGH.
So PORTB=B11111111; this means all pins are HIGH
PORTB=B11011111; this means that pin 13 is LOW
Now to understand how it works let’s make a small program with led from pin 13 blinking at 1 second.
void setup() {
DDRB=B11100000;//pin 13 is in output mode
}
void loop() {
PORTB=B11100000; //make pin 13 high and power on the led
delay(1000);
PORTB=B11000000; //make pin 13 low and power off the led
delay(1000);
}
For C and D ports, how to set the mode of the pins and state of them is the same thing like for B.
One problem appear at PORTD with pins 0 and 1 because these are for serial communication and we shouldn’t change them.
For that we will use the “OR” function “|”-this symbol is used in programs which operates each bit and if both are 0 the result is 0 otherwise is 1, like the thruth table for OR gate(x|0=x).
OR
1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | |
1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | |
Result | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
Now for programs we have
DDRD=DDRD| B11111100; this means that pins from 2 to 5 are outputs and pin 0 and 1 remain at the value at they have been modified.
Think like that, for example the pin 0 became 1 so, the DDRD make OR function between initial state B11111100 and B11111101, the result is DDRD= B11111101, so pin 0 is changed how it’s need for serial comunication otherwise the pin 0 couldn’t change because it was 0 value fixed.
We also can use the “AND” function ”&”-this symbol is used in programs which operate each bit and if both are 1 the result is 1, otherwise is 0, like the thruth table for AND gate (x&1=x).
AND
1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | |
1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | |
Result | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
For programs we have DDRD=DDRD & B11111111, pin 0 and 1 must be 1 otherwise if it is 0 the result it will be always 0.
Now if we want to change only a bit we can do this without writing all 8 bits, but only the bit we want to change, and this with SET and CLEAR.
In program DDRB for pin 13 is put in output mode with DDRB |= (1<<DDB5)(set-make 1), where DDB5 is the bit for PIN 13(picture below you can see this in atmega 328 datasheet), and for PORTB is PORTB |= (1<<PORTB5)(set), also PORTB5 is in datasheet. To clear a bit or “set it to zero” in program we use PORTB &= ~(1<<PORTB5)(clear-make 0).
In the image below you can see the association of bits for PORTB, DDRB and PINB(taken from atmega datasheet):
The program below make the same thing as the program above but we control only the pin 13:
void setup() {
DDRB |= (1<<DDB5);//pin 13 is in output mode
}
void loop() {
PORTB |=(1<< PORTB5);//make pin 13 high and power on the led
delay(1000);
PORTB &= ~(1<<PORTB5);//make pin 13 low and power off the led
delay(1000);
}
It’s enough for output pins, now let try to read one pin by controling the registers instead the digitalRead.
As we have mentioned above the register responsible for reading a digital pin is PIN.
Now to understand this we will make a program which implies a button on pin 8, that when it is pressed the LED from pin 13 will be powered on.
First we must make the pin 8 an input pin and pin 13 an output pin like
DDRB |= (1<<DDB5) ;//pin 13 is in output mode
DDRB &=~(1<<DDB0);//pin 8 is in input mode
If you want to write all bites we have DDRB = B11111110; pins 9 to 13 are outputs and pin 8 input(zero).
Now to read if pin 8 is in high or low state we know that a high pin means logic 1 and low state means logic 0, so when we push the button the bit from PINB register corresponding to pin 8(first from right) became 1.
If we do a “&” operation like PINB & B00000001 the result will be different from zero if button is pressed and PINB has a form like B00000001.
On serial monitor if you want to see the result Serial.println((PINB&B00000001)); will appear 1 because is 20.
If you move the button on pin 10 the operation will be PINB & B00000100; and the result will be 4 means 22.
Below you two small programs for pins 8 and 10 as inputs:
For pin 8:
void setup() {
//DDRB |= (1<<DDB5) ;//pin 13 is in output mode
//DDRB&=~(1<<DDB0); //pin 8 is in input mode
DDRB = B11111110; //pin 8 input pins 9 to 13 output
Serial.begin(9600);
}
void loop() {
Serial.println((PINB&B00000001));
if ((PINB & B00000001)==1){//
PORTB |=(1<< PORTB5);//make pin 13 high and power on the led
}
else{
PORTB &=~ (1<<PORTB5);//make pin 13 low and power off the led
}
}
For pin 10:
void setup() {
//DDRB |= (1<<DDB5) ;//pin 13 is in output mode
//DDRB&=~(1<<DDB0); //pin 8 is in input mode
DDRB = B11111011; //pin 10 input pins 8,9,11,12 and 13 output
Serial.begin(9600);
}
void loop() {
Serial.println((PINB&B00000100));
if ((PINB & B00000100)==4){//
PORTB |=(1<< PORTB5);//make pin 13 high and power on the led
}
else{
PORTB &=~ (1<<PORTB5);//make pin 13 low and power off the led
}
}
This is great, thank you so much for posting this.
Great knowledge, thank you from the bottom of my heart. Valued post!
Nice piece of clarification, thanks a lot!
Amazing post! Thank you!
Well explained and thank you very much.
you make hard staff very easy … you are a great teacher…
I really appreciate your write up. I have a code for inverter and needs help in adding a feedback to it