Specialized clock and IRQ management in HAL2 ¶
The concept ¶
This item aims to decompose the large HAL1 RCC functions, such as
HAL_RCC_OscConfig
and
HAL_RCC_ClockConfig, into smaller, specialized, and more efficient unitary functions.
In HAL1, functions like
HAL_RCC_OscConfig,
HAL_RCC_ClockConfig, and
HAL_RCCEx_PeriphCLKConfig
are monolithic. They accept input structures covering all possible configurations; for example,
HAL_RCCEx_PeriphCLKConfig
uses the
RCC_PeriphCLKInitTypeDef
structure, which includes every peripheral and all possible clock sources. These functions rely on extensive switch-case and if/else logic to handle every scenario, leading to complex and bulky code.
HAL2 replaces this approach with modular HAL RCC APIs tailored to specific needs, eliminating these monolithic functions. This results in significant footprint reduction: over 60% savings in typical use cases. STM32CubeMX2 leverages these unitary APIs to generate optimized code based on the user’s clock tree configuration, simplifying usage by abstracting sequence details.
A similar approach applies to
HAL_PPP_IRQHandler
functions. While the original handlers remain, HAL2 introduces specialized IRQ handler functions for certain peripherals, such as HAL TIM, to optimize interrupt processing and reduce footprint by including only relevant interrupt code.
Replace HAL_RCC_OscConfig with atomic APIs for individual RCC oscillators ¶
When migrating from HAL1 to HAL2 for configuring STM32 oscillators and PLL, the approach shifts from using broad structures and single-function calls to more specific, granular function calls. In HAL1, configurations rely on structures like
RCC_OscInitTypeDef
and a single call to
HAL_RCC_OscConfig
to apply settings. This method consumes significant memory, as it covers all possible oscillators and PLL options, resulting in large if/else logic within the HAL library.
In contrast, HAL2 offers distinct function calls for each configuration step; for example,
HAL_RCC_MSIS_Enable
to activate the MSI oscillator and
HAL_RCC_MSI_SetTrimming
to adjust its trimming. PLL configuration uses a dedicated structure,
hal_rcc_pll_config_t, alongside separate functions such as
HAL_RCC_PLL1_SetConfig,
HAL_RCC_PLL1_Enable, and
HAL_RCC_PLL1_EnableOutput. This modular design provides clearer, more maintainable code and reduces memory footprint by handling each oscillator or PLL function individually, avoiding the extensive generalized logic of HAL1.
|
Scope |
HAL1 |
HAL2 |
|---|---|---|
|
Structures |
RCC_OscInitTypeDef RCC_OscInitStruct;
|
hal_rcc_pll_config_t config_pll1;
|
|
Oscillator initialization |
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4;
|
HAL_RCC_MSIS_Enable(HAL_RCC_MSIS_FREQ_4MHZ);
HAL_RCC_MSI_SetTrimming(HAL_RCC_MSICALIBRATION_DEFAULT, HAL_RCC_MSI_RANGE_CALIB_4_TO_7);
|
|
PLL configuration |
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLMBOOST = RCC_PLLMBOOST_DIV1;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 80;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLLVCIRANGE_0;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
|
config_pll1.pll_source = HAL_RCC_PLLSOURCE_MSIS;
config_pll1.pll_m_boost = HAL_RCC_PLLMBOOST_DIV1;
config_pll1.pll_m = 1;
config_pll1.pll_n = 80;
config_pll1.pll_p = 2;
config_pll1.pll_q = 2;
config_pll1.pll_r = 2;
config_pll1.pll_fracn = 0;
|
|
Function calls |
HAL_RCC_OscConfig(&RCC_OscInitStruct);
|
HAL_RCC_PLL1_SetConfig(&config_pll1);
HAL_RCC_PLL1_Enable();
HAL_RCC_PLL1_EnableOutput(HAL_RCC_PLL1_SYSCLK);
|
Replace HAL_RCC_ClockConfig with atomic APIs for clock source configuration ¶
When migrating from HAL1 to HAL2 for configuring CPU, AHB, and APB bus clocks, the approach shifts from using a single structure-based configuration to more granular function calls. In HAL1, clock settings rely on the
RCC_ClkInitTypeDef
structure and a single call to
HAL_RCC_ClockConfig. This method increases memory usage as it encompasses all possible clock types and dividers in one structure.
HAL2 replaces this with dedicated functions for each configuration step, such as
HAL_RCC_SetSysClkSource
to select the system clock source and
HAL_RCC_SetBusClockConfig
to configure bus clocks. Additionally, HAL2 offers unitary APIs to set and get individual bus prescalers, including
HAL_RCC_SetPCLK1Prescaler,
HAL_RCC_SetPCLK2Prescaler, and
HAL_RCC_SetPCLK3Prescaler
along with their corresponding getters. This modular design improves code clarity and maintainability, and reduces memory footprint.
|
Scope |
HAL1 |
HAL2 |
|---|---|---|
|
Structures |
RCC_ClkInitTypeDef RCC_ClkInitStruct;
|
hal_rcc_bus_clk_config_t config_bus;
|
|
Clock initialization |
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_PCLK3;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
|
HAL_RCC_SetSysClkSource(HAL_RCC_SYSCLKSOURCE_PLLCLK);
config_bus.ahb_clk_divider = HAL_RCC_HCLK_DIV1;
config_bus.apb1_clk_divider = HAL_RCC_PCLK_DIV1;
config_bus.apb2_clk_divider = HAL_RCC_PCLK_DIV1;
config_bus.apb3_clk_divider = HAL_RCC_PCLK_DIV1;
HAL_RCC_SetBusClockConfig(&config_bus);
|
|
Unitary APIs |
N/A |
void HAL_RCC_SetPCLK1Prescaler(hal_rcc_pclk_div_t prescaler);
void HAL_RCC_SetPCLK2Prescaler(hal_rcc_pclk_div_t prescaler);
void HAL_RCC_SetPCLK3Prescaler(hal_rcc_pclk_div_t prescaler);
hal_rcc_hclk_div_t HAL_RCC_GetHCLKPrescaler(void);
hal_rcc_pclk_div_t HAL_RCC_GetPCLK1Prescaler(void);
hal_rcc_pclk_div_t HAL_RCC_GetPCLK2Prescaler(void);
hal_rcc_pclk_div_t HAL_RCC_GetPCLK3Prescaler(void);
|
Provide unitary configuration APIs for PLL management ¶
When migrating from HAL1 to HAL2 for PLL configuration, the approach shifts from macro- and structure-based methods to more granular function calls. In HAL1, the
HAL_RCC_OscConfig
API configures all oscillators and main PLL parameters, automatically enabling the PLL and its primary output used for the system clock. However, enabling other PLL outputs, such as PLLP and PLLQ, requires additional macro calls, increasing code complexity and memory usage. Moreover, the forced configuration and enabling sequence inside
HAL_RCC_OscConfig
limits user customization.
HAL2 addresses this by providing dedicated functions for each step, such as
HAL_RCC_PLL1_SetConfig
for setting PLL1 parameters and
HAL_RCC_PLL1_EnableOutput
for enabling specific PLL outputs. This modular design improves code clarity and maintainability, and reduces memory footprint.
|
Scope |
HAL1 |
HAL2 |
|---|---|---|
|
PLL configuration |
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLMBOOST = RCC_PLLMBOOST_DIV1;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 80;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLLVCIRANGE_0;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
|
config_pll1.pll_source = HAL_RCC_PLLSOURCE_MSIS;
config_pll1.pll_m_boost = HAL_RCC_PLLMBOOST_DIV1;
config_pll1.pll_m = 1;
config_pll1.pll_n = 80;
config_pll1.pll_p = 2;
config_pll1.pll_q = 2;
config_pll1.pll_r = 2;
config_pll1.pll_fracn = 0;
HAL_RCC_PLL1_SetConfig(&config_pll1);
HAL_RCC_PLL1_Enable();
HAL_RCC_PLL1_EnableOutput(HAL_RCC_PLL1_SYSCLK);
|
|
PLL services |
#define __HAL_RCC_PLL_ENABLE()
#define __HAL_RCC_PLL_DISABLE()
#define __HAL_RCC_PLLCLKOUT_ENABLE(..)
#define __HAL_RCC_PLLCLKOUT_DISABLE(..)
#define __HAL_RCC_GET_PLLCLKOUT_CONFIG(..)
#define __HAL_RCC_PLLFRACN_ENABLE()
#define __HAL_RCC_PLLFRACN_DISABLE()
#define __HAL_RCC_PLL_CONFIG(..)
#define __HAL_RCC_PLL_PLLSOURCE_CONFIG(..)
#define __HAL_RCC_PLLFRACN_CONFIG(..)
#define __HAL_RCC_PLL_VCIRANGE(..)
#define __HAL_RCC_GET_PLL_OSCSOURCE()
|
hal_status_t HAL_RCC_PLL1_SetConfig(const hal_rcc_pll_config_t *p_config);
hal_status_t HAL_RCC_PLL1_Enable(void);
hal_status_t HAL_RCC_PLL1_Disable(void);
hal_status_t HAL_RCC_PLL1_EnableOutput(uint32_t output);
hal_status_t HAL_RCC_PLL1_DisableOutput(uint32_t output);
hal_status_t HAL_RCC_PLL1_Reset(void);
void HAL_RCC_PLL1_GetConfig(hal_rcc_pll_config_t *p_config);
uint32_t HAL_RCC_PLL1_GetOutput(void);
hal_rcc_pll_status_t HAL_RCC_PLL1_IsReady(void);
void HAL_RCC_PLL1_GetClockFreq(hal_rcc_pll_output_freq_t *p_clk);
hal_status_t HAL_RCC_PLL1_SetFRACN(uint16_t fracn);
uint16_t HAL_RCC_PLL1_GetFRACN(void);
|
For the auxiliary PLLs (PLLs that are used to clock peripherals) such as PLL2 and PLL3,
HAL1 provides a structure-based configuration and a function to configure and enable the PLL. In HAL1,
the
HAL_RCCEx_EnablePLL2
function configures all the PLL parameters, enables the PLL, and its outputs.
The same structure and enable/disable services are provided for other PLLs such as PLL3.
In HAL1, the function
HAL_RCCEx_DisablePLL2
fully disables the PLL and its outputs.
In addition to the global
HAL_RCCEx_EnablePLL2
and
HAL_RCCEx_DisablePLL2
functions,
several macros are provided to give more granularity to the user.
However, these macros’ scope and content are redundant with parts of these functions.
In HAL2, the same services as the main PLL1 are provided for auxiliary PLLs (example PLL2), allowing users to have the exact same sequence and approach for all the PLLs without distinguishing between the configuration and enabling sequence of the main PLL (PLL1) and the auxiliary PLLs. Additionally, there are no more macros in HAL2; instead, unitary functions are provided with a clear distinction between the configuration function scope and other unitary control services such as enable/disable and enable/disable outputs.
For auxiliary PLLs such as PLL2 and PLL3, HAL1 provides structure-based configuration along with functions like
HAL_RCCEx_EnablePLL2
to configure and enable the PLL and its outputs. Similar enable and disable functions exist for other PLLs, including
HAL_RCCEx_DisablePLL2. Additionally, several macros offer finer control, but their scope often overlaps with these functions, creating redundancy.
In HAL2, auxiliary PLLs follow the same modular approach as the main PLL1, allowing a unified configuration and enabling sequence across all PLLs. Macros have been removed and replaced with unitary functions that distinctly handle configuration, enable/disable operations, and output control, simplifying usage and improving maintainability.
|
Scope |
HAL1 |
HAL2 |
|---|---|---|
|
PLL2 Configuration Structure |
typedef struct
{
uint32_t PLL2Source;
uint32_t PLL2M;
uint32_t PLL2N;
uint32_t PLL2P;
uint32_t PLL2Q;
uint32_t PLL2R;
uint32_t PLL2RGE;
uint32_t PLL2FRACN;
uint32_t PLL2ClockOut;
} RCC_PLL2InitTypeDef;
|
typedef struct
{
uint16_t pll_n;
uint16_t pll_fracn;
uint8_t pll_m;
uint8_t pll_p;
uint8_t pll_q;
uint8_t pll_r;
hal_rcc_pll_mboost_div_t pll_m_boost;
hal_rcc_pll_src_t pll_source;
} hal_rcc_pll_config_t;
|
|
PLL2 configuration function |
HAL_StatusTypeDef HAL_RCCEx_EnablePLL2(RCC_PLL2InitTypeDef
*PLL2Init);
|
hal_status_t HAL_RCC_PLL2_SetConfig(const hal_rcc_pll_config_t
*p_config);
|
|
PLL2 enable/disable functions |
HAL_StatusTypeDef HAL_RCCEx_EnablePLL2(RCC_PLL2InitTypeDef
*PLL2Init);
HAL_StatusTypeDef HAL_RCCEx_DisablePLL2(void);
|
hal_status_t HAL_RCC_PLL2_Enable(void);
hal_status_t HAL_RCC_PLL2_Disable(void);
hal_status_t HAL_RCC_PLL2_EnableOutput(uint32_t output);
hal_status_t HAL_RCC_PLL2_DisableOutput(uint32_t output);
hal_status_t HAL_RCC_PLL2_Reset(void);
hal_rcc_pll_status_t HAL_RCC_PLL2_IsReady(void);
|
|
PLL2 macros |
#define __HAL_RCC_PLL2_ENABLE()
#define __HAL_RCC_PLL2_DISABLE()
#define __HAL_RCC_PLL2CLKOUT_ENABLE(..)
#define __HAL_RCC_PLL2CLKOUT_DISABLE(..)
#define __HAL_RCC_PLL2_PLLSOURCE_CONFIG(..)
#define __HAL_RCC_GET_PLL2_OSCSOURCE()
#define __HAL_RCC_PLL2_CONFIG(..)
#define __HAL_RCC_GET_PLL2CLKOUT_CONFIG(..)
#define __HAL_RCC_PLL2FRACN_ENABLE()
#define __HAL_RCC_PLL2FRACN_DISABLE()
#define __HAL_RCC_PLL2FRACN_CONFIG(..)
|
hal_status_t HAL_RCC_PLL2_Enable(void);
hal_status_t HAL_RCC_PLL2_Disable(void);
hal_status_t HAL_RCC_PLL2_EnableOutput(uint32_t output);
hal_status_t HAL_RCC_PLL2_DisableOutput(uint32_t output);
hal_status_t HAL_RCC_PLL2_Reset(void);
hal_rcc_pll_status_t HAL_RCC_PLL2_IsReady(void);
hal_status_t HAL_RCC_PLL2_SetFRACN(uint16_t fracn);
uint16_t HAL_RCC_PLL2_GetFRACN(void);
|
Peripheral-specific APIs for clock source selection ¶
In HAL1, peripheral clocks are configured using the
RCC_PeriphCLKInitTypeDef
structure and the
HAL_RCCEx_PeriphCLKConfig
function. This method is memory-intensive because it includes all peripheral instances, such as USART1, USART2, SPI3, and SPI4, and their possible clock sources in one structure.
HAL2 replaces this with dedicated functions for each peripheral, such as
HAL_RCC_ADCDAC_SetKernelClkSource
for ADC/DAC and
HAL_RCC_SAI1_SetKernelClkSource
for SAI1. This modular design improves code clarity and maintainability, and reduces memory footprint.
|
Example |
HAL1 |
HAL2 |
|---|---|---|
|
ADC/DAC clock configuration |
RCC_PeriphCLKInitTypeDef PeriphClkInit;
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADCDAC;
PeriphClkInit.AdcDacClockSelection = RCC_ADCDACCLKSOURCE_HSI;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
|
HAL_RCC_ADCDAC_SetKernelClkSource(HAL_RCC_ADCDAC_CLK_SRC_HSI);
|
|
SAI1 clock configuration |
GPIO_InitTypeDef GPIO_InitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI1;
PeriphClkInit.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLL2;
PeriphClkInit.PLL2.PLL2Source = RCC_PLLSOURCE_MSI;
PeriphClkInit.PLL2.PLL2M = 1;
PeriphClkInit.PLL2.PLL2N = 48;
PeriphClkInit.PLL2.PLL2P = 17;
PeriphClkInit.PLL2.PLL2Q = 2;
PeriphClkInit.PLL2.PLL2R = 2;
PeriphClkInit.PLL2.PLL2RGE = RCC_PLLVCIRANGE_0;
PeriphClkInit.PLL2.PLL2FRACN = 0;
PeriphClkInit.PLL2.PLL2ClockOut = RCC_PLL2_DIVP;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
|
hal_rcc_pll_config_t pll2_config;
pll2_config.pll_source = HAL_RCC_PLLSOURCE_MSIS;
pll2_config.pll_m = 1;
pll2_config.pll_n = 48;
pll2_config.pll_p = 17;
pll2_config.pll_q = 2;
pll2_config.pll_r = 2;
pll2_config.pll_fracn = 0;
HAL_RCC_PLL2_SetConfig(&pll2_config);
HAL_RCC_PLL2_Enable();
HAL_RCC_PLL2_EnableOutput(HAL_RCC_PLL2_OUTPUT_P);
HAL_RCC_SAI1_SetKernelClkSource(HAL_RCC_SAI1_CLK_SOURCE_PLL2P);
|
Introduce specialized IRQ handlers ¶
In HAL1, the
HAL_PPP_IRQHandler
function manages all possible peripheral interrupts, clears related flags, and triggers all corresponding callbacks. For some drivers, like HAL TIM, this results in heavy processing and memory usage, as applications typically require only a subset of interrupts. Continuously checking all flags can be inefficient, especially in real-time applications.
HAL2 addresses this by keeping the global
HAL_PPP_IRQHandler
while introducing specialized IRQ handlers focused on specific processes or event groups. These specialized handlers manage only the interrupts, flags, and callbacks relevant to their scope, improving efficiency and reducing overhead.
|
Scope |
HAL1 |
HAL2 |
|---|---|---|
|
HAL TIM IRQ handler |
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim);
|
void HAL_TIM_IRQHandler(hal_tim_handle_t *htim);
void HAL_TIM_UPD_IRQHandler(hal_tim_handle_t *htim);
void HAL_TIM_CC_IRQHandler(hal_tim_handle_t *htim);
void HAL_TIM_BRK_TERR_IERR_IRQHandler(hal_tim_handle_t *htim);
void HAL_TIM_TRGI_COM_DIR_IDX_IRQHandler(hal_tim_handle_t *htim);
|
|
HAL LTDC IRQ handler |
void HAL_LTDC_IRQHandler(LTDC_HandleTypeDef *hltdc);
|
void HAL_LTDC_IRQHandler(hal_ltdc_handle_t *hltdc);
void HAL_LTDC_ERR_IRQHandler(hal_ltdc_handle_t *hltdc);
void HAL_LTDC_LineDetectionIRQHandler(hal_ltdc_handle_t *hltdc);
void HAL_LTDC_ReloadEventIRQHandler(hal_ltdc_handle_t *hltdc);
|