I/O ports (Part 1)
Arduino’s IDE offers a set of functions in order to drive the I/O ports. As usual, their handyness has a price: speed! And an other drawback: lack of synchronisation.
If you want to drive, say, digital pins 8 and 9, you will probably write the following code if you want to alternatively blink leds attached to these ports:
void setup(){ pinMode(8, OUTPUT); // sets the digital pin 8 as output pinMode(9, OUTPUT); // sets the digital pin 9 as output } void loop(){ digitalWrite(8, HIGH); // sets the LED on digitalWrite(9, LOW); // sets the LED of delay(1000); // waits for a second digitalWrite(8, LOW); // sets the LED off digitalWrite(9, HIGH); // sets the LED on delay(1000); // waits for a second }
Let’s see what we can do to improve this code. First, we can set output ports in one line, both or more ports together:
void setup(){ DDRB |= ((1 << PINB1) | (1 << PINB0)); // sets the digital pins 8 and 9 as output }
We can also bring some refinements to the loop in order to speed up PORTB settings:
void loop(){ PORTB |= (1 << PINB0); // sets the LED on PORTB &= ~(1 << PINB1); // sets the LED off delay(1000); // waits for a second PORTB &= ~(1 << PINB0); // sets the LED off PORTB |= (1 << PINB1); // sets the LED on delay(1000); // waits for a second }
Is that all we can do? No, no, no! The following code illustrates the use of the XOR logical operator:
void setup(){ DDRB |= ((1 << PINB1) | (1 << PINB0)); // sets the digital pins 8 and 9 as output PORTB |= (1 << PINB0); // presets the LED on PORTB &= ~(1 << PINB1); // presets the LED off } void loop(){ PORTB ^= ((1 << PINB1) | (1 << PINB0)); // toggle LEDs delay(1000); // waits for a second }
That’s neat!
Starting from here, let us try to solve the synchronisation problem. Let’s say that these two bits are driving a R2R ladder in a Digital to Analog Converter (DAC, exemple here). We need to set all (both) bits together in order to set the proper voltage without spikes.
This is a real raw solution which sets the decimal value 2 to PORTB:
byte value = B000010; PORTB = value;
Problem: all upper bits are cleared, which may not be expected. This is a turnaround to this problem:
PORTB &= ~B000011; // Clear the two lower bits byte value = B000010; PORTB |= value; // Set the two lower bits
This solution is still not satisfactory because of the nulling stage prior to the PORT setting. The next one is much more elegant…
byte value = B000011; PORTB ^= (PORTB ^ value); // Set the two lower bits
Or with even mode protections
byte value = B000011; PORTB ^= ((PORTB ^ value) & B000011); // Set the two lower bits
Whatever the status of PORTB pins, solely the pins which need to be changed are toggled using the XOR logical operator.
Some more reading on ports manipulation.