STM32 register map

1. Basic principles of registers

The register is a special memory inside the single-chip microcomputer, which can realize the control of various functions of the single-chip microcomputer. The final purpose of our program is to control the register
The following example platform is STM32F407ZG

1.1 STM32 register classification

Big category Small category Description
Kernel registers Kernel related registers Contains R0~R15, xPSR, special function registers, etc.
Interrupt control register Contains NVIC and SCB related registers, NVIC has: ISER, ICER, ISPR, IP, etc.; SCB has: VTOR, AIRCR, SCR, etc.
SysTick register Contains four registers of CTRL, LOAD, VAL and CALIB
Memory protection register Optional function, STM32F103 does not have
Debug system register ETM, ITM, DWT, IPIU and other related registers
peripheral registers Contains GPIO, UART, IIC, SPI, TIM, DMA, ADC, DAC, RTC, I/WWDG, PWR, Various peripheral registers such as CAN and USB

2. Register Mapping

The process of naming register addresses is called register mapping

In STM32F4, 0x4002 000C is the address of GPIOA_PUPDR, but we don’t know whose address it is by directly looking at 0x4002 000C. Although we can find it by searching the manual, this method is too cumbersome and not conducive to our subsequent development programs , so we need to name the address of the register so that we can know what its function is when we see the new name

2.1 Basic concept of address mapping

We first need to know the following concepts:

  • Bus base address (BUS_ BASE_ ADDR)
  • Peripheral offset based on bus base address (PERIPH_OFFSET)
  • The offset of the register relative to the base address of the peripheral (REG_OFFSET)
  • Register address = BUS_BASE_ADDR + PERIPH_OFFSET + REG_OFFSET

The above address can be easily found in the manual, the following is an example to find the address of GPIOA_PUPDR in STM32F4

2.2 How to find the register address through the manual

First of all, we need to clarify what the address we are looking for is (bus? peripheral? register?). GPIOA_PUPDR is a register of GPIOA, so what we are looking for here is a register, and the register is based on the peripheral offset, and the peripheral is based on the bus offset Yes, so we need to find out which peripheral on which bus the GPIOA is mounted to.
Open the “STM32F4xx Reference Manual”, this manual can be obtained for free from the punctual atomic information, or you can go to the ST official to download the English version or the Chinese community to find the Chinese version of the information.
In the Table 2.STM32F4xx register boundary address in 2.3 memory map, find the place where the peripheral is GPIOA, as shown in the figure below:

①: It is the peripheral we are looking for
②: You can see from the table that GPIOA is mounted on AHB1
③: The address of GPIOA is just the lowest address of AHB1, which means there is no offset relative to the start address of AHB1, that is, AHB1 mark>BUS_ BASE_ ADDR = 0x4002 0000, PERIPH_OFFSET=0
④: Click this hyperlink to see the details of GPIOA other registers


Click ④ to jump to 7.4.11 GPIO register map, in Table 32.GPIO register map and reset value, look for GPIOA_PUPDR as shown below Shown:

①: It is the register we are looking for
②: The offset relative to the base address of GPIOA, that is, REG_OFFSET=0x0C
③: You can see that this is a 32-bit register, that is, the size of each register is 4B (will be used later)
④: Note that GPIOB_PUPDR and GPIOA_PUPDR and even GPIOx_PUPDR (x is A…I) are the same offset, which is used for us A structure provides the possibility to initialize all GPIOx (continue to see later)

So far we have got all the addresses

BUS_ BASE_ ADDR PERIPH_OFFSET REG_OFFSET
0x4002 0000 0x00 0x0C

Concatenate them to get GPIOA_PUPDR = 0x4002 000C
So how do we verify the correctness of this address?
In theory, you can use the debug function of keil5 to debug and view the address of the register, but I tried it and it seems that there is a problem, so I will verify it in another way. Using the serial port routine of punctual atom, we use the serial port to send the address to the computer for viewing. I wrote the following program

int main(void)
{<!-- -->
    uint8_t len;
    uint16_t times = 0;
    
    HAL_Init(); /* Initialize the HAL library */
    sys_stm32_clock_init(336, 8, 2, 7); /* set clock, 168Mhz */
    delay_init(168); /* delay initialization */
    usart_init(115200); /* The serial port is initialized to 115200 */
    led_init(); /* Initialize the LED */
    while(1)
    {<!-- -->
        printf("%p\r\\
", &GPIOA->PUPDR);
        delay_ms(1000);
    }
}

The result received is

It is consistent with the result of our calculation, indicating that our calculation method is correct. Next, we will find out how he splices the address from the code level

2.3 How registers are named

2.3.1 Direct manipulation of registers

Since GPIOA_PUPDR is 32-bit, we need to use int pointer to get its address, use (unsigned int *) to get the address Force it to a pointer type, and then use * to access the value pointed to by the address to assign a value to the register (you need to pay attention to whether the register is readable, writable, etc.)

*(unsigned int *)(0x4002 000C)=0XFFFF;

2.3.2 Operate the register after naming

The address is named once using the macro definition, which is more readable than the first method

#define GPIOA_PUPDR *(unsigned int *)(0x4002 000C)
GPIOA_PUPDR = 0XFFFF;

2.3.3 Use structure to name register

The following is the content of stm32f407xx.h. It uses the structure to complete the register mapping. The following will explain why it can be mapped through the structure.

typedef struct
{<!-- -->
  __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
  __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
  __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
  __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
  __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
  __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
  __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */
  __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
  __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;

Through the basic knowledge of C language, we know that the name of the structure variable actually represents the first address of the structure, and the internal members of the structure are arranged in order. You can see GPIOA in the above manual The registers (except AFR) are all 32-bit registers, which means that we can define the members according to the names of the registers in the structure. For example GPIOA_PUPDR, we see in the manual that the offset based on GPIOA is 0x0C, according to the definition of the above code __IO uint32_t PUPDR ; is located on the 4th line, and there are 3 lines of definitions of other members in front of it. The total occupied space is 32bit x 3 =12B, which just corresponds to the offset in the manual. At the same time, we can also see it in the comments of the code The offset to the address is 0x0C. The previous section mentioned that GPIOx_PUPDR (x is A…I) is the same offset, so when the base address of GPIO_TypeDef is different, different groups can be defined This is the benefit of using the structure.

Next, find the address of GPIOx_PUPDR through the source code.
Open stm32f407xx.h, search for GPIOA, you can see the macro definition in the figure below, the macro definition here is the same as the previous section, but he still has dolls many layers,

We double-click GPIOA_BASE and operate as shown below

can get this

We continue to repeat the above steps

keep repeating

Until here we know that his address structure is

PERIPH_BASE AHB1PERIPH_BASE GPIOA_BASE
0x40000000UL PERIPH_BASE + 0x00020000UL AHB1PERIPH_BASE + 0x0000UL

The calculated GPIOA_BASE = 0x4002 0000, this address is transformed into the base address of the GPIOA structure through #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) Then we add the offset of GPIOx_PUPDR relative to GPIOA to get GPIOx_PUPDR = 0x4002 000C
At this point, the register mapping is over, and all register addresses can be found in a similar way, see more manuals and program source codes.