cancel
Showing results for 
Search instead for 
Did you mean: 

Output word into two bytes on different ports. Is there a faster way?

pchala
Associate II
Posted on June 26, 2014 at 11:40

I would like to optimize next snippet:

void
write16Data(u16 data) {
GPIOA->BSRR = 0x00ff0000 | (u8)(data & 0xff);
GPIOB->BSRR = 0x00ff0000 | (u8)(data >> 8);
}

Any ideas?
5 REPLIES 5
Posted on June 26, 2014 at 13:48

If you call this repetitively in a loop, inline, of pull into the loop. Unroll to loop. Use constant pointers. Use assembler.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
pchala
Associate II
Posted on June 26, 2014 at 14:31

Indeed this function called in a loop so I will add ''inline''

But I would like to do something like:

union
{
u16 word;
u8 bytes[2];
} data;
(u8 * ) portA = data.bytes[0];
(u8 * ) portB = data.bytes[1];

So to avoid shift and other operations - just right usage of pointers
Danish1
Lead II
Posted on June 26, 2014 at 14:58

I didn't notice which stm32 microcontroller you are using.

This is possible in stm32f4xx, where the reference manual section 8.4 says GPIO registers are byte-addressable.

So how about trying

*(uint8_t*)GPIOA->ODR = data.bytes[0];

*(uint8_t*)GPIOB->ODR = data.bytes[1];

But not possible in stm32f10x where section 9.1 says the GPIO registers are only addressable as 32-bit words.

 - Danish

Edit: I forgot stm32f10x does not support byte-addressing

stm322399
Senior
Posted on June 26, 2014 at 15:27

GCC allows to do that very ugly stuff:

void gpiotest(uint data) {
uint32_t da = 0x00ff0000 | (u8)(data & 0xff);
uint32_t db = 0x00ff0000 | (u8)(data >> 8);
asm volatile (
''str %0, %1;''
''str %2, %3;''::
''r''(da),''m''(GPIOA->BSRRL),
''r''(db),''m''(GPIOB->BSRRL)
);
}

Which compiles down to:

c0000c20: b2c3 uxtb r3, r0
c0000c22: f3c0 2007 ubfx r0, r0, #8, #8
c0000c26: f443 037f orr.w r3, r3, #16711680 ; 0xff0000
c0000c2a: f440 007f orr.w r0, r0, #16711680 ; 0xff0000
c0000c2e: 4902 ldr r1, [pc, #8] ; (c0000c38 <
gpiotest
+0x18>)
c0000c30: 4a02 ldr r2, [pc, #8] ; (c0000c3c <
gpiotest
+0x1c>)
c0000c32: 6193 str r3, [r2, #24]
c0000c34: 6188 str r0, [r1, #24]
c0000c36: 4770 bx lr
c0000c38: 40020400 .word 0x40020400
c0000c3c: 40020000 .word 0x40020000

IMHO, it is the only way to make sure store instructions will be beside each other. Moreover, that way, it can be even more optimized when inlining and loop unrolling are automatically performed by the compiler. Solutions like (type*)<addr>=<value> are very good, as long as you use the volatile keyword to qualify the type, otherwise optimization may kill some writes.
pchala
Associate II
Posted on June 26, 2014 at 16:25

Got it,

So

((uint8_t __IO*)&GPIOA->ODR)[0] = data.bytes[0];

will not work on stm32f100 :(