The following three lines of code are optimized ways to modify bits with 1 MOV instruction instead of using a less interrupt safe read-modify-write idiom. They are identical to each other, and set the LED_RED bit in the GPIO Port's Data Register:
*((volatile unsigned long*)(0x40025000 + (LED_RED << 2))) = LED_RED;
*(GPIO_PORTF_DATA_BITS_R + LED_RED) = LED_RED;
GPIO_PORTF_DATA_BITS_R[LED_RED] = LED_RED;
LED_RED
is simply (volatile unsigned long) 0x02
. In the memory map of this microcontroller, the first 2 LSBs of this register (and others) are unused, so the left shift in the first example makes sense.
The definition for GPIO_PORTF_DATA_BITS_R is:
#define GPIO_PORTF_DATA_BITS_R ((volatile unsigned long *)0x40025000)
My question is: How come I do not need to left shift twice when using pointer arithmetic or array indexing (2nd method and 3rd method, respectively)? I'm having a hard time understanding. Thank you in advance.
Remember how C pointer arithmetic works: adding an offset to a pointer operates in units of the type pointed to. Since GPIO_PORTF_DATA_BITS_R
has type unisgned long *
, and sizeof(unsigned long) == 4
, then GPIO_PORTF_DATA_BITS_R + LED_RED
effectively adds 2 * 4
= 8 bytes.
Note that (1) does arithmetic on 0x40025000
, which is an integer, not a pointer, so we need to add 8 to get the same result. Left shift by 2 is the same as multiplication by 4, so LED_RED << 2
again equals 8.
(3) is exactly equivalent to (2) by definition of the []
operator.