I/O ports (Part 1)

Part 1, 2, 3, 4, 5

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.

Next post on same subject

Leave a Reply

You must be logged in to post a comment.