This post wants to show a little more about how we can modify AVR registers.

In the first post with the name Direct Acces to AVR Register I introduce the concept of modify a entire register, programming at once all the single bits inside. That could be helpfull sometimes, but we can use a more powerfull tool to setup some peripheral devices inside AVR (USART, timmers,..). The most of registers in AVR get different meaning bit by bit an there’s only few registers that can be considered as one block, from the point of view of the meaning (EEDR or TCNT0 for exemple).

For instance, accesing to Arduino digital out 13 is accesing to single bit PB5 in Port B. If we want to turn the LED on pin 13 ON and OFF we need to access only to that bit without changing the other bits values in PORT B.

I want introduce some ways to accesing single bits in registers.

Using Masks

This method is an universal procedure to set and clear bits or check their values. This works using binay operations:

  • OR ( | ) : To set bits to one.
  • AND ( & ) : To clear bits to zero.

If you know how the mask works, you can skip the following lines.

The following code is a version of the most famous Arduino Code: The Blink

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
  Uses the direct acces to the PORT B
  This example code is in the public domain.
 */

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(13, OUTPUT);     
}

void loop() { 
  PORTB = PORTB | 0b100000;   // set the LED on
  delay(1000);                // wait for a second
  PORTB = PORTB & 0b011111;   // set the LED off
  delay(1000);                // wait for a second
}

As we can see, this code only changed the digitalWrite for a modification of the PORTB, giving a new value to that port using itsef as one operator with the help of the MASK 0b100000 as second operator.  The operation “|” together with te mask set the bit in the position where the “1” is found and leave the rest of bits in register unchanged: “1” if there was a “1”, and “0” otherwise.

The operation “&” acts placing “0” in the register position bits, acording where the “0” are found in the mask, leaving the rest of bits unchanged.

This could be done in a diferent way

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
  This example code is in the public domain.
 */

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(13, OUTPUT);     
}

void loop() {

  PORTB |= 0b100000;   // set the LED on
  delay(1000);         // wait for a second
  PORTB &= 0b011111;   // set the LED off
  delay(1000);         // wait for a second
}

This code is exactly the same code than before but shorter. I’m using PORTB as source operator and destination of the operation at the same time, so is possible to write as “|=” to do the OR operation in that conditions.

Aditionally we can use another notation:

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
  This example code is in the public domain.
 */

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(13, OUTPUT);     
}

void loop() {

  PORTB |= (1<<PB5);   // set the LED on
  delay(1000);         // wait for a second
  PORTB &= ~(1<<PB5);   // set the LED off
  delay(1000);         // wait for a second
}

The ‘<<‘ operator is called shift left operator and moves a “binary” value to the left a number of positions. For instance:

(4<<2)

moves de number 4, two positions left. The binary value of 4 is:

4 = 0b100

So, the result of (4<<2) is:

(0b100<<2) = 0b10000 = 16

In our code, we use PB5, defined in the library as value “5”. So in the same way that in the former example:

(1<<PB5) = (1 << 5) = 0b100000 = 32

In that way, we can realize that all the three versions of blink program are equivalent.

And… THAT’S ALL!!  But….  not yet.  🙂

The use of left-shift operator is very usefull that the avr library includes a tool to build such operation. The name is Bit Vector and using it we can re-write the blink code as follows:

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
  This example code is in the public domain.
 */

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(13, OUTPUT);     
}

void loop() {

  PORTB |= _BV(PB5);   // set the LED on
  delay(1000);         // wait for a second
  PORTB &= ~_BV(PB5);   // set the LED off
  delay(1000);         // wait for a second
}

Use the available tools as you wish.

Multiple bits set/clear at once

To configure the registers, normally some bits must be set to one and some bits must be clear to zero. How to do that using our tools?

Let’s consider I need to setup the SPI interface in the AVR to use it in a program to control a SPI device. Take a look to the chapter “18. SPI – Serial Peripheral Interface” from AVR reference manual.

There’s two configuration register for SPI setup: SPCR – SPI Control Register and SPSR – SPI Status Register.

Lets consider the following setup:

  • f_clk =125 kHz
  • MSB first
  • Mode 2 (CPOL=1, CPHA=0): Clock iddle: high, Data sent to MOSI line: Rising edge

The following code is just a partial example to show how to set and clear bits. Don’t worry too much about the meaning.

void init_SPI(void){
  /* Setup SPI 
   ------------
   Ennable SPI
   Master
   f_clk: 16MHz/128 =125 kHz
   MSB first
   Mode 2 (CPOL=1, CPHA=0)
   */
  SPSR &= ~(1<<SPI2X);
  SPCR |= ((1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0) | (1<<CPOL) ); 
  SPCR &= ~((1<<SPIE) | (1<<DORD) | (1<<CPHA));
 }

First we clear the bit SPI2X in SPSR register. This is done in the same way you already see before.

Then we use the SPCR register twice: The first time we set all the bits that must be set to one. And then we place zeros to the positions in SPCR that must be configured with this value. Is important to realize that the second time we setup SPCR we are modifying just three bits: The other five holds their own value, whatever it was.

One more exemple how to use this bit manipulation of registers is showing bellow, using the blink program once more.
At this time we will setup the Arduino pins 8 and 9 as inputs and 11 and 13 as outputs, without taking care about the function of 10 and 12.

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
  This example code is in the public domain.
 */

void setup() { 
   /*  This lines are quite "uggly" because 
       I'm using diferent notation to do the same.
       My intetion is to show that both are doing 
       the same job*/               
  // initialize the digital pins 13 and 11 as outputs.
  DDRB |= _BV(PB5) | (1<<PB3) ;
  // initialize the digital pins 8 and 9 as inputs.
  DDRB &= ~( (1<<PB0) | _BV(PB1));
} 

void loop() {   
   PORTB |= _BV(PB5);   // set the LED on 
   delay(1000);   // wait for a second
   PORTB &= ~_BV(PB5);   // set the LED off
   delay(1000);         // wait for a second 
}
Anuncios