Electronic – Why does STM32 use a two-stage initialization process? What is the primary purpose of each stage

stm32stm32cubemx

I'm learning to use STM32 microcontrollers using CubeMX to generate a project. I've noticed that CubeMX generates two basic stages of initialization for nearly every peripheral:

(A) Within the stm32fxxxx_hap_msp.c file, it defines a number of callbacks of the form HAL_XXX_MspInit().

(B) Within main.c, it defines a number of functions of the form MX_XXX_Init().

Sometimes a given peripheral's HAL_XYZ_Init() function is called from within (A) as it is with the ADC and RTC. Other times a given peripheral's HAL_XYZ_Init() function is called from within (B) as it is done with GPIO.

Both sets of functions call __HAL_RCC_xxxx macros extensively.

I can't make heads or tails of the usage pattern and neither the HAL guide nor reference manual give much direction here.

So, questions:

  1. I suspect that the callback format provided by (A) has something to do with the uC waking from low-power modes, but I am not positive about this. Is this correct, or what is the purpose of the HAL_XXX_MspInit callback format?

  2. I noticed that (A) contains more user-definition block comments indicating room for my code to cohabit the functions generated by CubeMX. But why, for instance, would it be wrong to move code from (B) into the space of (A) in order to keep my initialization sequence clear and easy to read?

Best Answer

I was able to answer my own question.

The HAL_XXX_MspInit() functions are called by their respective HAL_XXX_Init() functions. As an example, when main() calls its MX_I2C_Init(), this prepares a global structure (defined in main.c) for initializing the I2C peripheral.

The call stack goes (roughly, with omissions):

main() [written by CubeMX]
MX_I2C_Init() [written by CubeMX]
HAL_I2C_Init() [provided by HAL Drivers and put in Drivers/ by CubeMX]
HAL_I2C_MspInit() [empty version provided by HAL Drivers, overrided by CubeMX in Core/Src/stm32fxxx_hal_msp.c ]

Note that this means that you can call the _MspInit() and _MspDeinit() functions during your code's runtime. You may choose to do this, for example, to change low-level resource state before or after a change in power modes.

This answer (and other details concerning HAL call flow) is only hinted at by the Description of HAL and LL Drivers document, and is best confirmed by checking the HAL source code. This code is quite a bit more friendly than you might expect, and is located under your project tree under HAL_Drivers for any CubeMX project.