Optimized initialization and configuration APIs

Initialization and configuration principles

HAL1

In HAL1, the HAL_PPP_Init function is provided to:

  • Link the logical handle object to a physical peripheral instance and storing the instance in the handle object.

  • Initialize the internal fields of the logical driver handle object.

  • Configure the peripheral depending on the user parameters provided by a PPP_InitTypeDef structure, storing the peripheral configuration in the handle object.

User actions required before calling HAL_PPP_Init

Before calling HAL_PPP_Init :

  • configure the physical peripheral instance by accessing the handle.

  • set the peripheral parameters.

Important

Do not modify any other handle fields, as these are managed internally by the HAL driver.

HAL1 Msp callbacks: sequence and resource configuration

The sequence requires enabling the peripheral clock, selecting the clock source, and configuring necessary resources such as GPIOs, NVIC lines, and DMA. This setup is handled by the HAL_PPP_MspInit callback. Likewise, the HAL_PPP_MspDeInit callback allows releasing these resources during the HAL_PPP_DeInit sequence.

HAL1 limitation

  • Ambiguity: There is ambiguity between the PPP handle public fields that need to be set by the user.

  • Extra RAM Footprint: The HAL PPP handle holds the full peripheral configuration/init parameters, leading to extra RAM usage.

  • Complexity: The usage of Msp callbacks is not straightforward, as the user must know and implement the Msp callbacks. There is ambiguity in using the HAL_PPP_MspInit callback during initialization, and users may forget to implement or update the Msp callback function.

HAL2

In HAL2, direct access to handle parameters is not required because all HAL handle fields are private.

HAL_PPP_Init

The HAL_PPP_Init function links the logical object (handle) to the physical peripheral instance and initializes the internal fields of the logical driver handle object. Since the peripheral instance is provided as a parameter to HAL_PPP_Init ,users do not need to access the HAL driver handle object directly.

HAL_PPP_SetConfig

HAL2 introduces a new HAL_PPP_SetConfig function that configures peripherals based on the user parameters. The configuration is supplied through a dedicated structure passed by reference to HAL_PPP_SetConfig . User configuration parameters are applied directly to the peripheral registers and are not stored in the handle, which helps reduce RAM usage.

Configuration levels

The HAL configuration APIs provide two levels of settings:

  • Global: To start any process, a set of peripheral parameters must be applied. For peripherals with different subblocks or subinstances, such as TIM input capture (IC) or output compare (OC), dedicated configuration APIs are used for each subblock or subinstance, in addition to the global configuration. For example, HAL_TIM_SetConfig is used for global configuration, while HAL_TIM_OC_SetConfigChannel is used for configuring a specific output compare channel.

  • Unitary: Apply an individual parameter.

HAL2 Msp callbacks removed: simplified sequence

The sequence has been split into two functions with distinct responsibilities: handle object initialization and peripheral configuration. As a result:

  • The HAL_PPP_MspInit and HAL_PPP_MspDeInit callbacks are no longer provided.

  • Resource settings for PPP (clock, GPIO, NVIC, DMA) are now inserted directly by the application code between calls to HAL_PPP_Init and HAL_PPP_SetConfig.

Note

Note : The full application sequence required for initialization and configuration is generated by STM32CubeMX2.

Separation of initialization and configuration functions

The legacy HAL_PPP_Init function has been divided into two distinct functions: HAL_PPP_Init , responsible for building the handle object, and HAL_PPP_Config , which applies the configuration settings. This separation improves modularity and clarity in the initialization process.

HAL_PPP_Init for HAL1 and HAL2

Function

HAL1

HAL2

HAL_PPP_Init

Scope : Initialize the specified HAL PPP handle object and the PPP peripheral based on the parameters specified in PPP_InitTypeDef.

Prototype : HAL_StatusTypeDef HAL_PPP_Init(PPP_HandleTypeDef* hppp)

Scope : Initialize the specified HAL PPP handle object and link it to the specified peripheral instance.

Prototype : hal_status_t HAL_PPP_Init(hal_ppp_handle_t *hppp, hal_ppp_t instance)

HAL_PPP_SetConfig

N/A

Scope : Configure the PPP peripheral based on the parameters specified in hal_ppp_config_t, given as a parameter by reference.

Prototype : hal_status_t HAL_PPP_SetConfig(hal_ppp_handle_t *hppp, const hal_ppp_config_t *p_config)

HAL_PPP_DeInit

Scope : De-initialize the specified HAL PPP handle object and peripheral based on the configuration parameters defined in the handle structure of the peripheral.

Prototype : HAL_StatusTypeDef HAL_PPP_DeInit(PPP_HandleTypeDef* hppp)

Scope : Force stopping any ongoing operation(s) and restore the handle object to default/reset state.

Prototype : void HAL_PPP_DeInit(hal_ppp_handle_t *hppp)

Usage

/* Declare a HAL PPP handle */
PPP_HandleTypeDef hppp;

/* Instance selection */
hppp.Instance = PPPi;

/* Fill PPP init struct */
hppp.Init.param1 = VALUE;
hppp.Init.paramN = VALUE;

/* PPP Init and config */
HAL_PPP_Init(&hppp);

/* De-initialize the PPP peripheral */
HAL_PPP_DeInit(&hppp);
/* Declare a HAL PPP handle */
hal_ppp_handle_t hppp;

/* Fill PPP Config struct */
ppp_config_t ppp_config;

ppp_config.param1 = VALUE;
ppp_config.paramN = VALUE;

/* Init */
HAL_PPP_Init(&hppp, PPPi);

/* Config */
HAL_PPP_SetConfig(&hppp, &ppp_config);

/* De-initialize the PPP peripheral */
HAL_PPP_DeInit(&hppp);

Removal and conversion of init structure to configuration structure

In HAL2, the initialization structure is no longer embedded within the HAL PPP handle. The former PPP_InitTypeDef has been renamed to hal_ppp_config_t and is passed by reference to the HAL_PPP_SetConfig and HAL_PPP_GetConfig functions. This configuration structure retains only the essential fields needed to start the peripheral, while optional parameters from the original PPP_InitTypeDef are now managed through dedicated set/get configuration functions.

Comparison of HAL1 and HAL2 API structures

HAL1

HAL2

typedef struct
{
  uint32_t Timing;
  uint32_t OwnAddress1;
  uint32_t AddressingMode;
  uint32_t DualAddressMode;
  uint32_t OwnAddress2;
  uint32_t OwnAddress2Masks;
  uint32_t GeneralCallMode;
  uint32_t NoStretchMode;
} I2C_InitTypeDef;

/* I2C handle structure type */
typedef struct __I2C_HandleTypeDef
{
  I2C_TypeDef *Instance;
  I2C_InitTypeDef Init;
  ...
} I2C_HandleTypeDef;


/* Initialization and configuration API */
HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c);
typedef struct
{
  uint32_t timing;
  uint32_t own_address1;
  hal_i2c_addressing_mode_t addressing_mode;
} hal_i2c_config_t;






/* I2C handle structure type */
typedef struct hal_i2c_handle_s hal_i2c_handle_t;

struct hal_i2c_handle_s
{
  hal_i2c_t                   instance;
  ...
};

/* Initialization API */
hal_status_t HAL_I2C_Init(hal_i2c_handle_t *hi2c,
                          hal_i2c_t instance);

/* Configuration API */
hal_status_t HAL_I2C_SetConfig(hal_i2c_handle_t *hi2c,
                               const hal_i2c_config_t *p_config);

/* Optional APIs: OwnAddress 2 */
hal_status_t HAL_I2C_SetConfigOwnAddress2(hal_i2c_handle_t *hi2c,
                                          uint32_t addr,
                                          hal_i2c_own_addr2_mask_t mask);

hal_status_t HAL_I2C_EnableOwnAddress2(hal_i2c_handle_t *hi2c);
hal_status_t HAL_I2C_DisableOwnAddress2(hal_i2c_handle_t *hi2c);

/* Optional APIs: Acknowledge General Call */
hal_status_t HAL_I2C_SLAVE_EnableAckGeneralCall(hal_i2c_handle_t *hi2c);
hal_status_t HAL_I2C_SLAVE_DisableAckGeneralCall(hal_i2c_handle_t *hi2c);

/* Optional APIs: Clock Stretching */
hal_status_t HAL_I2C_SLAVE_EnableClockStretching(hal_i2c_handle_t *hi2c);
hal_status_t HAL_I2C_SLAVE_DisableClockStretching(hal_i2c_handle_t *hi2c);

Renaming of the handle structure

In HAL2, the HAL PPP handle structure has been renamed from PPP_HandleTypeDef to hal_ppp_handle_t.

Comparison of HAL1 and HAL2 handle structure naming

HAL1

HAL2

/* I2C handle structure type */
typedef struct __I2C_HandleTypeDef
{
  I2C_TypeDef *Instance;
  I2C_InitTypeDef Init;
  ...
} I2C_HandleTypeDef;
/* I2C handle structure type */
typedef struct hal_i2c_handle_s hal_i2c_handle_t;

struct hal_i2c_handle_s
{
  hal_i2c_t instance;
  ...
};

Renaming unitary init structures

Any initialization structure previously used to configure a specific feature, subblock, or subinstance is now replaced in HAL2. The term InitTypeDef has been discontinued and replaced by configuration structures named xxx_config_t .

Remove MspInit and MspDeInit callbacks

In HAL1 drivers, the MspInit and MspDeInit callbacks intercept the initialization and de-initialization sequences (HAL_PPP_Init/HAL_PPP_DeInit) to configure PPP resources such as clocks, GPIO, NVIC, DMA, and RCC resets. However, using these callbacks requires prior knowledge and manual implementation by the user.

In HAL2, the HAL_PPP_Init function is split into HAL_PPP_Init and HAL_PPP_SetConfig, eliminating the need for MspInit callbacks to intercept initialization. This change also reduces the HAL PPP handle size by removing callback pointers, as they are only relevant during initialization.

Example: HAL I2C full initialization and configuration

Full initialization and configuration for HAL1 and HAL2

File

HAL1

HAL2

stm32u5xx_hal_msp.c

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /* I2C1 GPIOs Init */
  __HAL_RCC_GPIOB_CLK_ENABLE();

  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* Set the I2C1 clock source */
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
  PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
  HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);

  /* I2C1 clock enable */
  __HAL_RCC_I2C1_CLK_ENABLE();

  /* I2C1 interrupts Init */
  HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
  HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
}

void HAL_I2C_MspDeInit(I2C_HandleTypeDef* hi2c)
{
  /* I2C1 clock disable */
  __HAL_RCC_I2C1_CLK_DISABLE();

  /* I2C1 GPIOs DeInit */
  HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8);
  HAL_GPIO_DeInit(GPIOB, GPIO_PIN_9);

  /* I2C1 interrupt DeInit */
  HAL_NVIC_DisableIRQ(I2C1_EV_IRQn);
  HAL_NVIC_DisableIRQ(I2C1_ER_IRQn);
}
/* No Msp Callback in HAL2 more */

main.c (or applicative file)

/* HAL I2C handle */
I2C_HandleTypeDef hi2c;

/* I2C instance selection */
hi2c.Instance = I2C1;

/* Fill I2C init struct */
hi2c.Init.Timing = 0x00C01F67;
hi2c.Init.OwnAddress1 = I2C_ADDRESS;
hi2c.Init.AddressingMode = I2C_ADDRESSINGMODE_10BIT;
hi2c.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c.Init.OwnAddress2 = 0;
hi2c.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

/* Init */
HAL_I2C_Init(&hi2c);



































/* De-initialize the I2C peripheral */
HAL_I2C_DeInit(&hi2c);
/* HAL I2C handle */
hal_i2c_handle_t hi2c;




/* Fill I2C Config struct */
hal_i2c_config_t i2c_config;

i2c_config.timing = 0x00C01F67;
i2c_config.own_address1 = I2C_ADDRESS;
i2c_config.addressing_mode = HAL_I2C_ADDRESSING_10BIT;




/* Init */
HAL_I2C_Init(&hi2c, HAL_I2C1);

/* MspInit equivalent sequence */

/* Set the I2C1 clock source */
HAL_RCC_I2C1_SetPeriphClockSource(HAL_RCC_I2C1_CLK_SOURCE_PCLK1);

/* I2C1 GPIOs Init */
HAL_RCC_GPIOB_EnableClock();

hal_gpio_config_t gpio_config;
gpio_config.mode = HAL_GPIO_MODE_ALTERNATE;
gpio_config.output_type = HAL_GPIO_OUTPUT_OPENDRAIN;
gpio_config.pull = HAL_GPIO_PULL_UP;
gpio_config.speed = HAL_GPIO_SPEED_FREQ_LOW;
gpio_config.alternate = HAL_GPIO_AF4_I2C1;

HAL_GPIO_Init(HAL_GPIOB, HAL_GPIO_PIN_8 | HAL_GPIO_PIN_9, &gpio_config);

/* I2C1 clock enable */
HAL_RCC_I2C1_EnableClock();

/* I2C1 interrupts Init */
HAL_CORTEX_NVIC_SetPriority(I2C1_EV_IRQn,
                            HAL_CORTEX_NVIC_PREEMP_PRIORITY_1,
                            HAL_CORTEX_NVIC_SUB_PRIORITY_0);
HAL_CORTEX_NVIC_EnableIRQ(I2C1_EV_IRQn);

HAL_CORTEX_NVIC_SetPriority(I2C1_ER_IRQn,
                           HAL_CORTEX_NVIC_PREEMP_PRIORITY_1,
                           HAL_CORTEX_NVIC_SUB_PRIORITY_0);
HAL_CORTEX_NVIC_EnableIRQ(I2C1_ER_IRQn);

/* Config */
HAL_I2C_SetConfig(&hi2c, &i2c_config);

/* De-initialize the I2C1 */
HAL_I2C_DeInit(&hi2c);