rosendo
Although the second one has "i" and "j" while the first one has only "state", I believe the second is better because "i" and "j" are 1 byte each, while "state" has 4 bytes.
I think it is difficult to deduce much without looking in detail at the code generated by the compiler, and doing some measurements. I would definitely advise everybody to avoid jumping to conclusions, and get some evidence instead.
if RAM is an issue, then:
for (uint16 state = 0x0001; state != 0; state <<= 1) {
uses the same amount as:
byte j=31;
for (byte i = 0; i < 16; i++) {
(One 4 byte variable would use less than two 1 byte variables if the compiler flag for 'word alignment of variables' is used.)
My reading of RM0008 9.2.5 "Port bit set/reset register (GPIOx_BSRR) (x=A..G)" is all 16 bits in a port are cleared by a
GPIOx_BASE->BSRR = 0xffff0000;
but an assignment to one of the bottom 16 'set' bits takes precedence.
So to set BIT(i) on, and clear every other bit, in a 16bit port:
GPIOx_BASE->BSRR = 0xffff0000 | BIT(i);
would clear all 16 bits in the port except BIT(i).
There is no need for "BIT(i) | BIT(j)
" or "state = 0x00010002
".
Redoing the bit-shifting version gives:
void loop() {
for (uint16 state = 0x0001; state != 0; state <<= 1) {
GPIOD_BASE->BSRR = 0xffff0000 | state; // clear all 16 bits, except the bit set by state
GPIOE_BASE->BSRR = 0xffff0000 | state;
GPIOF_BASE->BSRR = 0xffff0000 | state;
GPIOG_BASE->BSRR = 0xffff0000 | state;
delay(400);
}
GPIOD_BASE->BSRR = 0xffff0000; // switch all 16 port bits off
GPIOE_BASE->BSRR = 0xffff0000;
GPIOF_BASE->BSRR = 0xffff0000;
GPIOG_BASE->BSRR = 0xffff0000;
}
I haven't dumped the assembler, but I'd expect the bit-shift to be smaller than the code:
void loop() {
byte j=31;
for (byte i = 0; i < 16; i++) {
GPIOD_BASE->BSRR = BIT(i) | BIT(j);
GPIOE_BASE->BSRR = BIT(i) | BIT(j);
GPIOF_BASE->BSRR = BIT(i) | BIT(j);
GPIOG_BASE->BSRR = BIT(i) | BIT(j);
j = i+16;
delay(400);
}
GPIOD_BASE->BSRR = BIT(31);
GPIOE_BASE->BSRR = BIT(31);
GPIOF_BASE->BSRR = BIT(31);
GPIOG_BASE->BSRR = BIT(31);
}
which I'd expect to be bigger because BIT(i) | BIT(j)
expressions are calculated at run time, and it does j+=16
extra and 'i<16instead of
state != 0. It is possible the compiler is smart enough to convert this counting version into bit shifting (and the compiler can be very smart, so I would need to examine the assembler to be sure). But it won't know enough to convert to
0xffff0000 | BIT(i);`.
Of course, if this code makes more sense to you, use it, but I don't think it is 'better' than bit-shifting in any technical sense.
If we look at the assembler code generated, we might find that there are several other issues to consider before judging something 'better'.
One which is potentially more important than RAM, is the number of registers consumed.
If i and j are stored in two registers, whereas state consumes one, the code sequence for a function using i and j may be bigger because the code might need to move register values back to memory to free-up registers more often, or the code needs to use 32-bit instructions to manage the registers.
Further, slightly longer code inside a for
loop might force the compiler to use a different, larger instruction to branch back around a loop because a compact branch can't jump far enough.
Summary: IMHO it is more important to write clear code, and measure performance if it becomes an issue, than making assumptions.