Synchronized multiple pin setting

Rick Kimball
Wed Dec 28, 2016 9:48 pm
I came across an interesting feature of the GPIO BSRR registers. The BSRR register allows you to modify both the reset (BSRR_H) and set (BSRR_L) registers for the same pin in a single C statement. Probably obvious to some, but I’ve not seen it mentioned or used much. What this allows you to do is to set the final state of multiple pins with one c statement, even if you are clearing one and setting another. You don’t have to load and modify, you just modify. This saves a few cycles and makes the pin setting atomic.

Here is an example, say you wanted to toggle two pins 180 degrees out of phase with one call. You clear both pins using the reset register, I’m using PA0 and PA1 for this example, so you use 0b11 to reset both pins, then you use 0b01 to set PA0 and then next time use 0b10 to set PA1 the next.
void setup() {
pinMode(PA0,OUTPUT);
pinMode(PA1,OUTPUT);
}

void loop() {
static gpio_reg_map * const _GPIOA = GPIOA->regs;

while(1) {
_GPIOA->BSRR = 0b11 << 16 | 0b01;
delay(1);
_GPIOA->BSRR = 0b11 << 16 | 0b10;
delay(1);
}
}


RogerClark
Wed Dec 28, 2016 10:00 pm
Rick

That’s how the libmaple core handles digitalWrite

https://github.com/rogerclarkmelbourne/ … gpio.h#L92


Rick Kimball
Wed Dec 28, 2016 10:06 pm
Heh .. ok well we have a very efficient core :)

However, using it to synchronize several pins on the same port doesn’t seem to be talked about much. This feature might come in handy with a DMA call and values.


RogerClark
Wed Dec 28, 2016 10:09 pm
I did look at it some time ago, as I wast sure about the efficiency of having to invert the bit and then do the shifting, so I tried a few other ways to do it, but everything else was slow ;-)

You can do synchronised bit setting and resetting using the ODR register, but the overhead of reading the reg first and masking it etc, is slower.


Rick Kimball
Wed Dec 28, 2016 11:00 pm
RogerClark wrote:You can do synchronised bit setting and resetting using the ODR register, but the overhead of reading the reg first and masking it etc, is slower.

stevestrong
Wed Dec 28, 2016 11:10 pm
This feature is used in the 8/16 bit parallel-driven display modules.

victor_pv
Fri Dec 30, 2016 4:34 am
Rick Kimball wrote:
while(1) {
_GPIOA->BSRR = 0b11 << 16 | 0b01;
delay(1);
_GPIOA->BSRR = 0b11 << 16 | 0b10;
delay(1);
}
}

Rick Kimball
Fri Dec 30, 2016 5:17 am
Yes in this case you could do it like that.

A better use case might be using it with a dynamic data variable and you don’t know ahead of time which pins will be on an which will be off. I could have made a 2 bit random variable and used that instead of the hard coded 0b01 and it would still set all the bits properly. It would still be atomic and not require a load and modify.

Maybe you want to use a counter on the pins:
void setup() {
pinMode(PA0,OUTPUT);
pinMode(PA1,OUTPUT);
pinMode(PA2,OUTPUT);
}

void loop() {
static gpio_reg_map * const _GPIO = GPIOA->regs;

unsigned counter=0;
while(1) {
_GPIO->BSRR = 0b111 << 16 | counter++ & 0b111;
}
}


racemaniac
Fri Dec 30, 2016 7:07 am
Rick Kimball wrote:Heh .. ok well we have a very efficient core :)

However, using it to synchronize several pins on the same port doesn’t seem to be talked about much. This feature might come in handy with a DMA call and values.


victor_pv
Sat Dec 31, 2016 7:11 am
Rick, I think I understand, you reset them all “by default”, and then set whatever needs to be set. Rather than have to specifically reset some pins, you reset all, and set what’s needed to be set.
Then the set, 16 lower bits, take precedence, correct?

Rick Kimball
Sat Dec 31, 2016 4:27 pm
yes exactly

Leave a Reply

Your email address will not be published. Required fields are marked *