Reading some C files related to programming an STM32F4 series Arm controller, I found the following instruction:

/* Reset CFGR register */
RCC->CFGR = 0x00000000;

Since I never found "->" in C language I wonder if it's an instruction strictly related to configuration of STM32F4 family.
Someone could explain me the meaning of it?
Where can I find documentation about?
STM32F4 Reference Manual seems to not explain this.

  • 7
    \$\begingroup\$ I'm voting to close this question as off-topic because this is not an electronic engineering, but a (C language) programming question. \$\endgroup\$ – Marcus Müller Jun 25 '19 at 9:16
  • 1
    \$\begingroup\$ @MarcusMüller Writing firmware for microcontrollers is considered ontopic here. it's an instruction strictly related to configuration of STM32F4 family makes it a firmware related question even though the answer is no. Compare with this one \$\endgroup\$ – berendi - protesting Jun 25 '19 at 9:43
  • 2
    \$\begingroup\$ "Since I never found "->" in C language" Then you never found a beginner-level C programming book either. Learn fundamental C programming before attempting to read C code, maybe... \$\endgroup\$ – Lundin Jun 25 '19 at 10:48
  • 2
    \$\begingroup\$ @berendi none of the question revolves around microcontroller programming. OP simply doesn't know basic C Struct pointer handling. OP claiming this is specific to STM32F4 has nothing to do with it actually being specific to microcontroller programming. (it's simply not) \$\endgroup\$ – Marcus Müller Jun 25 '19 at 11:06
  • \$\begingroup\$ @MarcusMüller I assume you are famiar with the definition of RCC in the stm32 headers. Using the -> operator on a pointer acquired by converting a nonzero integer constant to a pointer is certainly implementation defined, maybe even undefined behaviour. Imagine asking this on stackoverflow, language lawyers would come down on the question with all their wrath. This question makes sense only in an embedded context. \$\endgroup\$ – berendi - protesting Jun 25 '19 at 13:47

The -> operator is the structure/union pointer operator, which is definitely part of the C language. (Thanks Jeroen, see comment below).

Assume you have a struct:

  int width;
  int length;
} Box;

Box aBox;

Than you can get to the properties as:

int width  = aBox.width;
int length = aBox.length;

Now assume you have a pointer to that box:

Box* pBox = &aBox;

Than you have to use the -> instead of the . symbol:

int width  = pBox->width;
int length = pBox->length;

In both cases, width and length will have the same value. aBox will contain the properties and e.g. if it is located at addres 0x123456, than the content of pBox will be 0x123456.

  • 1
    \$\begingroup\$ It's called the structure/union pointer operator. \$\endgroup\$ – Jeroen3 Jun 25 '19 at 9:33
  • \$\begingroup\$ @Jeroen3 Thanks, I updated my answer. \$\endgroup\$ – Michel Keijzers Jun 25 '19 at 10:07

The -> operator is a convenient way to address a memory mapped register of a certain peripheral.

Registers in the STM32 memory map are grouped in a way that makes it possible to write a struct definition for each peripheral, having the registers of the peripheral in one unit, for example:

typedef struct
  __IO uint32_t CR;            /*!< RCC clock control register,       Address offset: 0x00 */
  __IO uint32_t PLLCFGR;       /*!< RCC PLL configuration register,   Address offset: 0x04 */
  __IO uint32_t CFGR;          /*!< RCC clock configuration register, Address offset: 0x08 */
   /* ... */
}  RCC_TypeDef;

It's advantage over using a separate definition for each register becomes more apparent when there are more than one peripherals of the same kind, e.g. UARTs. There is a typedef struct { /* ... */ } USART_TypeDef; similar to the above one, describing the arrangement of the UART registers, then the base address of each USART/UART is defined as pointers to a USART_TypeDef.

#define USART1              ((USART_TypeDef *) USART1_BASE)
#define USART2              ((USART_TypeDef *) USART2_BASE)

Consider the following function:

void usart1_send_byte(uint8_t data) {
  while((USART1->SR & USART_SR_TXE) == 0)
  USART1->DR = data;

If there were separate definitions for each register in the system headers, it would become someting like

void usart1_send_byte(uint8_t data) {
  while((*USART1_SR & USART_SR_TXE) == 0)
  *USART1_DR = data;

Now we would like to generalize this function to work with any UART/USART in the system, not just USART1. Converting the first variant is easy,

void usart1_send_byte(USART_TypeDef *u, uint8_t data) {
  while((u->SR & USART_SR_TXE) == 0)
  u->DR = data;

but the other one would become quite awkward. Should we pass the address of each register it touches as a parameter? Or use more complicated constructs like *(usart_base + USART_DR_OFFSET) every time?

  • \$\begingroup\$ It's actually an inconvenient way to access a memory-mapped register. The machine code generated will work with offsets rather than absolute addresses, so it must first load the base register address, then calculate the offset, which gives slower register access time. This is why bloatware libs from ST, Atmel etc results in needlessly slow code. \$\endgroup\$ – Lundin Jun 25 '19 at 10:51
  • 3
    \$\begingroup\$ @Lundin well, I've yet to see a compiler emit that code in anything but -O0, sorry. \$\endgroup\$ – Marcus Müller Jun 25 '19 at 11:08
  • \$\begingroup\$ @MarcusMüller Since these are volatile qualified structs and registers, the compiler can't very well optimize the access or it is non-conforming. \$\endgroup\$ – Lundin Jun 25 '19 at 11:09
  • \$\begingroup\$ volatile doesn't mean the addressing needs to be as indirect as you describe. Even if you declared the pointer, and not the object, volatile, \$\endgroup\$ – Marcus Müller Jun 25 '19 at 11:10
  • 1
    \$\begingroup\$ @Lundin 1. Accesses without an offset are NOT faster on ARM 2. because of (1.), there is no difference when accessing a single register, however there are lots of common cases where at least two registers are accessed, i.e. reading a status register, then writing a control or data register (or both), and in such cases, the struct approach generates shorter and faster code, like in the example linked above. \$\endgroup\$ – berendi - protesting Jun 25 '19 at 12:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.