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?