HAL and LL drivers’ files

The HAL and LL drivers consist of the following files:

hal/
  ├── stm32tnxx/
      ├── _htmresc/
      ├── .config/
      ├── deprecated/
      ├── doc/
      ├── hal/
      |   ├── stm32tnxx_hal_def.h
      |   ├── stm32tnxx_hal_ppp.c
      |   ├── stm32tnxx_hal_ppp.h
      |   ├── stm32tnxx_hal.c
      |   ├── stm32tnxx_hal.h
      |   ├── stm32tnxx_ppp_core.c
      |   └── stm32tnxx_ppp_core.h
      ├── ll/
      |   ├── stm32tnxx_ll_ppp.h
      ├── os_port/
      |   ├── freertos/
      |   |   ├── stm32_hal_os.c
      |   |   └── stm32_hal_os.h
      |   └── no_os/
      |       ├── stm32_hal_os.c
      |       └── stm32_hal_os.h
      ├── templates/
      |   ├── common/
      |   |   ├── stm32_assert.h
      |   |   ├── stm32_ll.h
      |   |   └── stm32tnxx_hal_conf.h
      |   ├── os_port/
      |   |   ├── stm32_hal_os.c
      |   |   └── stm32_hal_os.h
      |   └── timebases/
      |       ├── mx_rtc.c
      |       ├── mx_rtc.h
      |       ├── mx_timx.c
      |       ├── mx_timx.h
      |       └── stm32_hal_timebase.h
      ├── timebases/
      |   ├── stm32_hal_timebase_rtc_alarm.c
      |   ├── stm32_hal_timebase_rtc_wakeup.c
      |   └── stm32_hal_timebase_tim.c
      ├── utils/
      |   ├── i2c_timing/
      |   |   ├── stm32_utils_i2c.c
      |   |   └── stm32_utils_i2c.h
      |   └── i3c_timing/
      ├── .gitignore
      ├── license.md
      ├── README.md
      ├── Release_Notes.html
      └── STMicroelectronics.stm32tnxx_hal.pdsc

stm32_hal header file

The stm32_hal.h is the entry point header file for any application using the HAL. The stm32_hal.h file includes (in the following order):

  • The stm32tnxx_hal.h

  • The stm32_hal_os.h under the conditional flag USE_HAL_MUTEX

  • The different HAL PPP modules stm32tnxx_hal_ppp.h under the conditional flag USE_HAL_PPP_MODULE

    • Note: The HAL system modules (RCC, GPIO, PWR, DMA) are included first.

  • The stm32_assert.h under the conditional flag USE_ASSERT_DBG_PARAM or USE_ASSERT_DBG_STATE

Note

The stm32_assert.h file is provided as a template. It can be copied into the application and customized by users if needed.

#ifndef STM32_HAL_H
#define STM32_HAL_H

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32tnxx_hal.h"

#if defined(USE_HAL_MUTEX) && (USE_HAL_MUTEX == 1)
#include "stm32_hal_os.h"
#endif /* USE_HAL_MUTEX == 1 */

#if defined(USE_HAL_RCC_MODULE) && (USE_HAL_RCC_MODULE == 1)
#include "stm32tnxx_hal_rcc.h"
#endif /* USE_HAL_RCC_MODULE==1U */

#if defined(USE_HAL_GPIO_MODULE) && (USE_HAL_GPIO_MODULE == 1)
#include "stm32tnxx_hal_gpio.h"
#endif /* USE_HAL_GPIO_MODULE==1U */

#if defined(USE_HAL_DMA_MODULE) && (USE_HAL_DMA_MODULE == 1)
#include "stm32tnxx_hal_dma.h"
#endif /* USE_HAL_DMA_MODULE==1U */
...
#if defined(USE_HAL_PPP_MODULE) && (USE_HAL_PPP_MODULE == 1)
#include "stm32tnxx_hal_ppp.h"
#endif /* USE_HAL_PPP_MODULE==1U */
...
#if defined(USE_FULL_ASSERT)
#ifndef USE_ASSERT_DBG_PARAM
#define USE_ASSERT_DBG_PARAM
#endif /* !USE_ASSERT_DBG_PARAM */
#ifndef USE_ASSERT_DBG_STATE
#define USE_ASSERT_DBG_STATE
#endif /* !USE_ASSERT_DBG_STATE */
#endif /* USE_FULL_ASSERT */

#if defined(USE_ASSERT_DBG_PARAM) || defined(USE_ASSERT_DBG_STATE)
#include "stm32_assert.h"
#else
#define ASSERT_DBG_PARAM(expr) ((void) 0U)
#define ASSERT_DBG_STATE(state,val) ((void)0U)
#endif /* USE_ASSERT_DBG_PARAM || USE_ASSERT_DBG_STATE */

#ifdef __cplusplus
}
#endif

#endif /* STM32_HAL_H */

stm32tnxx_hal module

The stm32tnxx_hal module is limited to the following services:

  • HAL Init and De-Init

  • Tick/Time base management

  • An API to retrieve the device unique ID

  • An API to retrieve the HAL version

The stm32tnxx_hal module APIs and services are consistent across all STM32 series and are limited to the aforementioned services. The implementation of these APIs might vary from one series to another. Nevertheless, the API list and prototypes remain the same across all series.

Unlike HAL1, the specific SoC services APIs (e.g., DBGMCU, SYSCFG, SBS, etc.) are not provided within the stm32tnxx_hal module. Instead, they are offered by the following modules:

  • The APIs that are related to the DBGMCU are provided by the HAL module: stm32tnxx_hal_dbgmcu.h and stm32tnxx_hal_dbgmcu.c

  • The APIs that are related to the SBS are provided by the HAL module: stm32tnxx_hal_sbs.h and stm32tnxx_hal_sbs.c

  • ART/PREFETCH APIs are available within the HAL flash module

  • EXTI APIs (if any) are provided by the HAL EXTI driver

The stm32tnxx_hal module provides the common set of APIs for all STM32 series:

hal_status_t HAL_Init(void);
hal_status_t HAL_DeInit(void);
hal_status_t HAL_InitTick(hal_tickFreq_t  tick_freq, uint32_t tick_priority);
hal_status_t HAL_UpdateCoreClock(void);
void HAL_IncTick(void);
void HAL_Delay(uint32_t Delay);
void HAL_Delay_NoISR(uint32_t delay_ms);
uint32_t HAL_GetTick(void);
uint32_t HAL_GetTickPrio(void);
hal_tickFreq_t HAL_GetTickFreq(void);
void HAL_SuspendTick(void);
void HAL_ResumeTick(void);
uint32_t HAL_GetVersion(void);
hal_status_t HAL_GetDeviceUniqueID(hal_device_uid_t *uid);

stm32tnxx_hal_def.h module

The stm32tnxx_hal_def.h provides essential configurations and definitions for the STM32 HAL. It includes the following:

  • HAL Configuration Definitions:

    • By including the stm32tnxx_hal_conf.h, it provides the necessary configuration settings for the HAL.

  • Essential Types and Definitions Related to STM32TN Devices:

    • By including the specific Device Family Pack (DFP) header file, such as stm32tnxx.h, it ensures the inclusion of essential types and definitions related to the given STM32TN devices.

    • Additionally, it includes other essential types by incorporating the standard headers stddef.h and math.h.

  • HAL Status Enumeration and Defines:

    • The hal_status_t enumeration type, used by the HAL APIs to return status, is defined within this file.

    • It also defines the HAL maximum delay (HAL_MAX_DELAY).

    • The file includes defines for the HAL Peripheral Clock enabling model, which are used to activate or deactivate the Peripheral Clock within various HAL_PPP_Init functions according to the user choices set in the stm32tnxx_hal_conf.h.

  • HAL_CHECK_UPDATE_STATE Macro:

    • The macro HAL_CHECK_UPDATE_STATE allows checking and updating the state with two flavors according to the USE_HAL_CHECK_PROCESS_STATE option set in the stm32tnxx_hal_conf.h.

      • An implementation with atomic check and update of the state from one original state to one destination state if USE_HAL_CHECK_PROCESS_STATE is defined to 1.

      • An implementation with direct assignment of the state to the new desired state without any check if USE_HAL_CHECK_PROCESS_STATE is defined to 0.

 /**  @file    stm32tnxx_hal_def.h */

 /* Define to prevent recursive inclusion -------------------------------------*/
 #ifndef STM32TNXX_HAL_DEF_H
 #define STM32TNXX_HAL_DEF_H

 #ifdef __cplusplus
 extern "C" {
 #endif
 /* Includes ------------------------------------------------------------------*/
 #include "stm32tnxx_hal_conf.h"

 #include "stm32tnxx.h"
 #include <stddef.h>
 #include <math.h>

 /* Exported types  ------------------------------------------------------------*/
 /**
   * @brief  HAL Status structures definition
   */
 typedef enum
 {
   HAL_OK            = 0xEAEAEAEAU, /* HAL operation completed successfully */
   HAL_ERROR         = 0xF5F5F5F5U, /* HAL operation completed with error   */
   HAL_BUSY          = 0x55555555U, /* HAL concurrent process ongoing       */
   HAL_INVALID_PARAM = 0xAAAAAAAAU, /* HAL invalid parameter                */
   HAL_TIMEOUT       = 0x5A5A5A5AU  /* HAL operation exceeds user timeout   */

 } hal_status_t;
 /* Exported defines  ------------------------------------------------------------*/
 #define HAL_MAX_DELAY      0xFFFFFFFFU
 #define HAL_TICK_INT_LOWEST_PRIORITY               ((1UL << __NVIC_PRIO_BITS) - 1UL)

 /**
   * @brief   HAL PPP clock model activation definition
   * @details These defines are used inside each HAL_PPP_Init function to indicate
   *          the clock model to be used.
   *          This model is declared inside stm32tnxx_hal_conf.h like this:
   *           #define USE_HAL_PPP_CLK_ENABLE_MODEL HAL_CLK_ENABLE_XXXX */
 #define HAL_CLK_ENABLE_NO                0U /*<! No clock activation in PPP                          */
 #define HAL_CLK_ENABLE_PERIPH_ONLY       1U /*<! PERIPH Clock activation only                        */
 #define HAL_CLK_ENABLE_PERIPH_PWR_SYSTEM 2U /*<! PERIPH Clock activation including PWR and/or system */
 ...

#endif /* STM32TNXX_HAL_DEF_H */

By providing these configurations and definitions, the stm32tnxx_hal_def.h ensures that the HAL is properly configured and ready to be used with the given STM32TN devices.

HAL PPP module files


  ├── hal/
  |   ├── stm32tnxx_hal_def.h
  |   ├── stm32tnxx_hal_ppp.c
  |   ├── stm32tnxx_hal_ppp.h
  |   ├── stm32tnxx_hal.c
  |   ├── stm32tnxx_hal.h
  |   ├── stm32tnxx_ppp_core.c
  |   └── stm32tnxx_ppp_core.h

HAL (Hardware Abstraction Layer) drivers provide a high-level, user-friendly interface for interacting with the microcontroller’s peripherals. They abstract the complexity of direct register manipulation, making it easier to develop applications without requiring in-depth knowledge of the hardware. HAL drivers are designed to be portable and scalable, supporting a wide range of STM32 series and peripherals.

The HAL is designed to offer several key benefits:

  • Ease of Use: The HAL abstracts the complexity of direct register manipulation, simplifying peripheral interaction for developers. This allows users to focus on application logic without dealing with low-level hardware details.

  • Portability: The HAL is designed for portability, enabling use across different STM32 series microcontrollers. This facilitates code reuse and scalability, allowing applications to be easily adapted to various hardware platforms.

  • Comprehensive Functionality: The HAL provides a wide range of functionalities, including initialization, configuration, control, and state management of peripherals. This ensures that all necessary operations for peripheral management are available, enhancing development efficiency.

File Naming Convention:

  • Header File: stm32tnxx_hal_ppp.h

  • Source File: stm32tnxx_hal_ppp.c

This module contains the implementation of the HAL functions for the PPP peripheral specific to the STM32TN series. It is composed of a header file and a source file that cover all the functional features supported by the given peripheral for the specified functional scope. These files together provide a standardized interface for interacting with the PPP peripheral on the STM32 TNXX series, ensuring consistency and ease of use across different projects and applications.

For example:

  • HAL CRC Module: The HAL CRC module of the STM32C5 series is composed of a source file (stm32c5xx_hal_crc.c) and a header file (stm32c5xx_hal_crc.h). It abstracts all the features of the CRC peripheral.

  • HAL UART Module: The HAL UART module is composed of a source file (stm32c5xx_hal_uart.c) and a header file (stm32c5xx_hal_uart.h). It abstracts all the UART functionalities (Universal Asynchronous Receiver/Transmitter) of the USART and LPUART peripherals.

The stm32tnxx_hal_ppp.h is the HAL PPP module header file. It contains:

  • Data Structures: Definitions of data structures used to configure and manage the PPP peripheral.

  • Enumerations: Enumerations for various configuration options and statuses for the PPP peripheral.

  • Defines: Various constants and configuration parameters related to the PPP peripheral.

  • Function Prototypes: Declarations of all the public functions implemented in the corresponding .c file.

The stm32tnxx_hal_ppp.c contains the implementation of the HAL (Hardware Abstraction Layer) functions for the PPP (Peripheral Placeholder) module specific to the STM32 TNXX series. It includes the following:

  • Initialization and De-initialization Functions: Functions to initialize and de-initialize the HAL PPP handle associated with a given instance of the PPP peripheral.

  • Configuration Functions: Functions to configure the PPP peripheral parameters.

  • Control Functions: Functions to control the operation of the PPP peripheral, such as starting, stopping, and managing interrupts.

  • Status Functions: Functions to check the status and handle errors for the PPP peripheral.

  • Callback Functions: Functions to handle callback events triggered by the PPP peripheral.

HAL Core files

Peripheral core drivers are designed to be accessed by one or more HAL drivers. They offer a set of APIs and services used by the upper HAL driver(s). From the user’s perspective, core drivers are not directly accessible; they are utilized exclusively by the HAL drivers built upon them.

Core drivers reside below the HAL layer and must not depend on any HAL driver or file. The only allowed dependency for core drivers is the device header file stm32tnxx.h.

To avoid confusion between user LL (Low-Layer) drivers and core drivers, the core driver files must follow a specific naming convention: stm32tnxx_ppp_core.c/h (where “tn” represents the series name and “ppp” represents the peripheral name, e.g., i2c).

Examples

  • stm32tnxx_dlyb_core.c/.h: The DeLay block core driver used by the HAL XSPI

  • stm32tnxx_usb_drd_core.c/.h: the USB core driver used by the HAL PCD and HCD drivers

HAL utilities

utils/
  ├── i2c_timing/
  |   ├── stm32_utils_i2c.c
  |   └── stm32_utils_i2c.h
  ├── i3c_timing/

HAL utility modules

These modules offer additional helper functions to support the HAL and LL drivers. For example, the utils/i2c_timing folder contains the files stm32_utils_i2c.c and stm32_utils_i2c.h, which provide functions to calculate the I2C timing values required by the HAL_I2C_SetConfig and HAL_I2C_SetTiming functions when using the HAL, and by the LL_I2C_SetTiming function when using the LL.

LL driver files

stm32tnxx_hal/
  ├── _htmresc/
  ├── .config/
  ├── deprecated/
  ├── hal/
  ├── ll/
  |   └── stm32tnxx_ll_ppp.h
  ├── os_port/
  ├── templates/
  ├── timebases/
  ├── utils/
  ├── .gitignore
  ├── LICENSE.md
  ├── LICENSE.txt
  ├── README.md
  ├── Release_Notes.html
  └── STMicroelectronics.stm32tnxx_hal.pdsc

STM32 LL (Low-Layer) drivers must provide a set of functions that allow direct access to the hardware registers of the microcontroller. These drivers are designed for performance and efficiency, offering a lightweight alternative to the HAL drivers. The LL drivers are particularly useful in applications where low-level control and optimization are crucial.

File Naming Convention:

  • Header File: stm32tnxx_ll_ppp.h

The LL (Low-Layer) drivers are organized under an “LL” folder. This folder provides only the LL header files, such as stm32tnxx_ll_ppp.h.

HAL OS port modules

os_port/
  ├── freertos/
  |   ├── stm32_hal_os.c
  |   └── stm32_hal_os.h
  └── no_os/
      ├── stm32_hal_os.c
      └── stm32_hal_os.h

A HAL OS abstraction layer is provided under the os_port folder in the files stm32_hal_os.c and stm32_hal_os.h. The purpose of these files is to offer the HAL an abstraction for OS services (OSAL). This includes semaphore services (create, take, release, and delete), as well as non-recursive mutex services (create, take, release, and delete).

The primary purpose of the HAL OS abstraction layer is to be used by the HAL PPP Acquire/Release APIs when the USE_HAL_MUTEX define is set to 1 . Additionally, the HAL OSAL can be utilized by part drivers’ IO interfaces or any applicative layer.

The stm32_hal_os.c and stm32_hal_os.h files must be provided with two implementations: NO_OS and FreeRTOS. The STM32 HAL OS port is included within the HAL pack as a component with two variants:

  • NO_OS: This variant has no other dependencies other than the HAL and the CMSIS core functions and APIs. It relies on the Cortex services to set and clear a semaphore object that is a U32 in an atomic way (depending on the Cortex, it can use Exclusive Load/Store instructions or critical sections by gating the interrupts).

  • FreeRTOS: This variant is based on FreeRTOS semaphore and mutex services. It depends on the corresponding FreeRTOS middleware CMSIS pack and components.

HAL Time Base modules

The HAL time base management is built by default on the Cortex SysTick timer and is based on the following HAL services, all defined as weak in the stm32tnxx_hal module, so users can override them to use their own time base.

Function

Description

__WEAK hal_status_t HAL_InitTick(hal_tick_freq_t tick_freq, uint32_t tick_priority)

This function configures the time base frequency and interrupt priority.

  • tick_freq Tick frequency with a hal_tick_freq_t type (to keep current value, use global variable uwTickFreq).

  • tick_priority Tick interrupt priority (to keep current value, use global variable uwTickPrio).

__WEAK void HAL_IncTick(void)

This function is called to increment time base global variable uwTick.

__WEAK void HAL_Delay(uint32_t delay_ms)

This function provides minimum delay (in milliseconds) based on variable incremented.

__WEAK uint32_t HAL_GetTick(void)

Provides a tick value in milliseconds.

uint32_t HAL_GetTickPrio(void)

This function returns a tick priority.

hal_tick_freq_t HAL_GetTickFreq(void)

Return tick frequency.

__WEAK void HAL_SuspendTick(void)

Suspend Tick increment.

__WEAK void HAL_ResumeTick(void)

Resume Tick increment.

Note

The different time base variables are defined in the common HAL file ( stm32tnxx_hal.c/h) as exportable variables as follows:

  • stm32tnxx_hal.c

volatile uint32_t uwTick;
uint32_t uwTickPrio = ((1UL << __NVIC_PRIO_BITS) - 1UL); /* Initial value: low priority */
hal_tick_freq_t uwTickFreq = HAL_TICK_FREQ_DEFAULT;      /* 1KHz */
  • stm32tnxx_hal.h

typedef enum
{
  HAL_TICK_FREQ_10HZ         = 100U,              /*!< HAL tick frequency 10Hz */
  HAL_TICK_FREQ_100HZ        =  10U,              /*!< HAL tick frequency 100Hz */
  HAL_TICK_FREQ_1KHZ         =   1U,              /*!< HAL tick frequency 1kHz */
  HAL_TICK_FREQ_DEFAULT      = HAL_TICK_FREQ_1KHZ /*!< HAL tick default frequency: 1kHz */
} hal_tick_freq_t;

extern volatile uint32_t        uwTick;    /*!< HAL tick counter current value (unit: ms) */
extern uint32_t                 uwTickPrio; /*!< HAL tick interrupt priority */
extern hal_tick_freq_t          uwTickFreq; /*!< HAL tick frequency */

As real-time operating systems rely on the Cortex System Timer (SysTick) as an RTOS time base, the STM32 HAL drivers must provide components allowing the use of an RTC or a timer as a HAL time base. The relevant components are:

  • stm32_hal_timebase_rtc_alarm.c

  • stm32_hal_timebase_rtc_wakeup.c

  • stm32_hal_timebase_tim.c

hal/
  ├── stm32tnxx/
    ├── _htmresc/
    ├── .config/
    ├── deprecated/
    ├── doc/
    ├── hal/
    ├── ll/
    ├── os_port/
    ├── templates/
    ├── timebases/
    |   ├── stm32_hal_timebase_rtc_alarm.c
    |   ├── stm32_hal_timebase_rtc_wakeup.c
    |   └── stm32_hal_timebase_tim.c
    ├── utils/
    ├── .gitignore
    ├── license.md
    ├── README.md
    ├── Release_Notes.html
    └── STMicroelectronics.stm32tnxx_hal.pdsc

These components are ready-to-use, read-only pattern components that are not configurable and do not require customization by the user. Each of these components provides an overridden implementation of the HAL tick functions, which can be based either on a TIMER, RTC alarm, or RTC wakeup time base. Below is an example of how this can be achieved using a TIMER:

 hal_tim_handle_t hal_tim_handle_t *htim_timebase = NULL;
 hal_status_t HAL_InitTick(hal_tick_freq_t tick_freq, uint32_t tick_priority)
 {
   hal_status_t status = HAL_ERROR;
   ASSERT_DBG_PARAM(IS_TICK_FREQ(tick_freq));
   ASSERT_DBG_PARAM(IS_TICK_PRIO(tick_priority));

   uwTickFreq = tick_freq;
   htim_timebase = mx_tim_timebase_config_tick(tick_freq);

   if (htim_timebase != HAL_OK)
   {
     if (tick_priority < (1UL << __NVIC_PRIO_BITS))
     {
#if defined(USE_HAL_TIM_REGISTER_CALLBACKS) && (USE_HAL_TIM_REGISTER_CALLBACKS == 1U)
       HAL_TIM_RegisterUpdateCallback(hTIM, TimeBase_TIM_UpdateCallback);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
       HAL_CORTEX_NVIC_SetPriority(TIM_TIMEBASE_IRQN, (hal_cortex_nvic_preemp_priority_t)tick_priority,
                                   (hal_cortex_nvic_sub_priority_t)0U);

       uwTickPrio = tick_priority;

       HAL_TIM_Start_IT(hTIM);

       status = HAL_OK;
     }
   }

   return status;
 }
  • Suspend and resume tick: HAL_SuspendTick/HAL_ResumeTick

void HAL_SuspendTick(void)
{
  /* Stop TIMx: */
  HAL_TIM_Stop_IT(htim_timebase);
}

void HAL_ResumeTick(void)
{
  /* Start the TIMx */
  HAL_TIM_Start_IT(htim_timebase);
}
  • Tick increment: HAL_IncTick is called within the HAL TIM update callback.

#if defined(USE_HAL_TIM_REGISTER_CALLBACKS) && (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
void TimeBase_TIM_UpdateCallback(TIM_HandleTypeDef *htim)
#else
void HAL_TIM_UpdateCallback(hal_tim_handle_t *htim)
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
{
  /* Prevent unused argument(s) compilation warning */
  STM32_UNUSED(htim);
  HAL_IncTick();
}
  • HAL_IncTick, HAL_GetTick, and HAL_Delay do not need to be overridden when using a TIMER or an RTC as a HAL time base because these functions deal with the global variables uwTick and uwTickFreq. The usage of these variables remains the same in all cases (default time base using CPU SysTick or time base using a TIMER or the RTC).

The TIMER and RTC time base modules require configuration functions for the corresponding peripheral used as the time base, which can either be the result of code generation or provided by the user based on the given TIM and RTC templates. This is facilitated through the following files:

  • mx_rtc.c/.h

  • mx_timx.c/.h

  • mx_hal_def.h

The mx_rtc.c/.h or mx_timx.c/.h files can either be generated based on user configuration of these peripherals or provided by the user as customized copies of the templates. The link between the stm32_hal_timebase_xxx.c files and the mx_rtc.c / mx_timx.c files is ensured through the hal_timebase.h aliases.

Below is an example of how this can be achieved using a TIMER:

/**
  ************************************************************************
  * @file    stm32_hal_timebase_tim.c
   ...
  *
  *************************************************************************
  */

#include "stm32_hal.h"
#include "stm32_hal_timebase.h"

/*! Check HAL tick frequency value */
#define IS_TICK_FREQ(freq) (((freq) == HAL_TICK_FREQ_10HZ)     \
                            || ((freq) == HAL_TICK_FREQ_100HZ) \
                            || ((freq) == HAL_TICK_FREQ_1KHZ))

/*! Check HAL tick priority value */
#define IS_TICK_PRIO(prio) ((prio) <= ((1UL << __NVIC_PRIO_BITS) - 1UL))

/* Private constants ---------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
extern hal_tick_freq_t uwTickFreq;
extern uint32_t uwTickPrio;

static hal_tim_handle_t *hTIM = NULL;

/* Private function prototypes -----------------------------------------------*/
static hal_status_t find_psc_arr(uint32_t tim_freq, uint32_t Update_event_freq, uint32_t *psc, uint32_t *arr);
static hal_status_t tim_timebase_config_tick(hal_tick_freq_t tick_freq);

#if defined(USE_HAL_TIM_REGISTER_CALLBACKS) && (USE_HAL_TIM_REGISTER_CALLBACKS == 1U)
void TimeBase_TIM_UpdateCallback(hal_tim_handle_t *htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */

/* Private functions ---------------------------------------------------------*/
static hal_status_t tim_timebase_config_tick(hal_tick_freq_t tick_freq)
{
  hal_status_t status;

  hal_tim_config_t  config;

  uint32_t tim_freq = HAL_TIM_GetClockFreq(timebase_gethandle());

  config.counter_mode           = HAL_TIM_COUNTER_UP;
  config.clock_division         = HAL_TIM_CLK_DIVISION_DIV1;
  config.repetition_counter     = 0;
  config.clock_sel.clock_source = HAL_TIM_CLK_INTERNAL;

  status = find_psc_arr(tim_freq, (1000UL/ (uint32_t)tick_freq) , &config.prescaler, &config.period);
  if(status == HAL_OK)
  {
    status= HAL_TIM_SetConfig(timebase_gethandle(), &config);
  }

  return status;
}

hal_status_t find_psc_arr(uint32_t tim_freq, uint32_t Update_event_freq, uint32_t *psc, uint32_t *arr)
{
    /*
    The update event period is calculated as follows:
    Update_event = TIM_CLK/((PSC + 1)*(ARR + 1)*(RCR + 1))

    Where: TIM_CLK = timer clock input
    PSC = 16-bit prescaler register (0..65535)
    ARR = 16/32-bit Autoreload register (1..65535)
    RCR = 16-bit repetition counter
    */
    uint32_t target;
    uint32_t ppsc, parr;

    if (Update_event_freq == 0 || tim_freq % Update_event_freq != 0)
    {
        return HAL_ERROR; // Invalid parameters
    }

    target = tim_freq / Update_event_freq;

    for (ppsc = 0; ppsc <= UINT16_MAX; ppsc++)
    {
        if (target % (ppsc + 1) == 0)
        {
            parr = (target / (ppsc + 1)) - 1;
            if (parr >= 1 && parr <= UINT16_MAX) // ARR min = 1, max = UINT16_MAX (65535)
            {
                *psc = (uint16_t)ppsc;
                *arr = (uint16_t)parr;
                return HAL_OK; // Found a valid pair
            }
        }
    }
    return HAL_ERROR;
}

/* Exported functions ---------------------------------------------------------*/
hal_status_t HAL_InitTick(hal_tick_freq_t tick_freq, uint32_t tick_priority)
{
  hal_status_t status = HAL_ERROR;
  ASSERT_DBG_PARAM(IS_TICK_FREQ(tick_freq));
  ASSERT_DBG_PARAM(IS_TICK_PRIO(tick_priority));

  if(HAL_TIM_GetState(timebase_gethandle()) == HAL_TIM_STATE_RESET)

  {
    if ( timebase_init() == NULL)
    {
      return HAL_ERROR;
    }
  }
  else
  {
    HAL_TIM_Stop_IT(timebase_gethandle());
  }

  status = tim_timebase_config_tick(tick_freq);

  if (status == HAL_OK)
  {
#if defined(USE_HAL_TIM_REGISTER_CALLBACKS) && (USE_HAL_TIM_REGISTER_CALLBACKS == 1U)
      HAL_TIM_RegisterUpdateCallback(timebase_gethandle(), TimeBase_TIM_UpdateCallback);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */

      uwTickFreq = tick_freq;
      uwTickPrio = tick_priority;

      HAL_CORTEX_NVIC_SetPriority(TIMEBASE_IRQ, (hal_cortex_nvic_preemp_priority_t)tick_priority, HAL_CORTEX_NVIC_SUB_PRIORITY_0);

      status = HAL_TIM_Start_IT(timebase_gethandle());
  }
  return status;
}

void HAL_SuspendTick(void)
{
  HAL_TIM_Stop_IT(hTIM);
}

void HAL_ResumeTick(void)
{
  HAL_TIM_Start_IT(hTIM);
}

#if defined(USE_HAL_TIM_REGISTER_CALLBACKS) && (USE_HAL_TIM_REGISTER_CALLBACKS == 1U)
void TimeBase_TIM_UpdateCallback(hal_tim_handle_t *htim)
#else
void HAL_TIM_UpdateCallback(hal_tim_handle_t *htim)
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
{
  STM32_UNUSED(htim);

  HAL_IncTick();
}

Note

HAL_IncTick, HAL_GetTick and HAL_Delay do not need to be overridden when using a TIMER or an RTC as a HAL time base. Indeed, these functions deal with the global variables uwTick and uwTickFreq.

The usage of these variables remains the same in all cases (default time base using CPU SysTick or time base using a TIMER or the RTC).

HAL specific coding rules and conventions

  • The HAL is based on a single time base that provides the timeouts and delays needed by internal processes; SysTick is used as the default time base but it can be replaced by any available peripheral that can provide the 1 ms HAL time base.

  • The rules related to the use of RTOS are respected across all the STM32Cube firmware components:

    • Re-entrancy.

    • Infinite loops must be timeout-based.

    • Shared resources must be protected.

  • Timeout is implemented to avoid blocking statements.

  • Timeout and delays are forbidden in interrupt context. If required in special cases, they are based on CPU cycle counting rather than the HAL standard time base to avoid priority conflicts, which might lead to the same bottleneck situations.

HAL naming rules

File names

stm32tnxx_hal_ppp (c/h)

Module name

HAL_PPP_MODULE

Function name

HAL_PPP_FeatureFunction_MODE

Handle name

hal_ppp_handle_t

Exported Configuration structure name

hal_ppp_config_t

Exported Enum name

hal_ppp_enumname_t

Exported Macros

HAL_PPP_{SUBBLOCK}_FUNCTION_BEHAVIOR ex: HAL_ADC_CALC_TEMPERATURE()

Private Macros

{IS}_PPP_{SUBBLOCK}_FUNCTION_BEHAVIOR ex: IS_ADC_THRESHOLD

Exported Defines

HAL_PPP_CATEGORY_ITEM ex : HAL_GPIO_PIN_0

Private Defines

PPP_CATEGORY_ITEM ex : I2C_XFER_TX_IT_MASK

The PPP prefix refers to the peripheral functional mode and not to the peripheral itself. For example, for the USART peripheral, PPP can be USART, UART or SMARTCARD depending on the peripheral mode.

  • No single/double underscore prefixes for any define/macro

Any HAL/LL define or macro starts with an underscore. In addition, a define or macro is all capital letters.

#define HAL_PPP_DEFINE
#define LL_PPP_DEFINE
  • Types are lower case and end with ``_t``

Any HAL typedef is lowercase and ends with xxx_t

Examples

  • HAL PPP Handle:

    struct hal_ppp_handle_s;
    typedef struct hal_ppp_handle_s hal_ppp_handle_t;
    struct hal_ppp_handle_s
    {
      (...)
      (...)
    };
    
  • HAL status type is named hal_status_t

    typedef enum
    {
      (...)
      (...)
    } hal_status_t;
    
  • Header file guards underscore prefixes are removed if existing

Header files use guards to avoid double inclusion issues. The guards are as follows:

  #ifndef FILENAME_H
  #define FILENAME_H

  (...)

  #endif /* FILENAME_H */

- Examples
#ifndef STM32TNXX_HAL_PPP_H
#define STM32TNXX_HAL_PPP_H

(...)

#endif /* STM32TNXX_HAL_PPP_H */
  • Use inline functions instead of big macros

As inline functions allow better debugging, they are used instead of macros. This approach ensures consistency while still enabling compiler optimizations. The only allowed exported macros are helper macros (macros that assist the user in calculating a result or preparing data to be fed to the HAL, e.g., HAL_ADC_CALC_TEMPERATURE).

Exported macros related to hardware register access are not allowed. Equivalent functionality is added to the LL layer or provided as inline HAL functions when no LL driver is available for the given PPP.

  • Use const keywords for non-modifiable pointers

The const qualifier is required to protect the content of user input data pointers that should not be modified. It is applied by adding the qualifier before the pointer type as follows: const <type> p_[pointer_name]

The const qualifier is applied in the following cases:

  • Configuration structure case

    /**
      * @brief  Configures the PPP according to the user parameters.
      * @param  hppp     Pointer to a hal_ppp_handle_t.
      * @param  p_config Pointer to the configuration structure.
      * @retval HAL_OK
      */
    hal_status_t HAL_PPP_SetConfig(hal_ppp_handle_t *hppp, const hal_ppp_config_t *p_config)
    {
      (...)
    }
    
  • User data buffers cases

    /**
      * @brief  API description.
      * @param  hppp              pointer to a hal_ppp_handle_t structure.
      * @param  p_{pointer_name_i} pointer to non-modifiable data buffer.
      * @param  p_{pointer_name_j} pointer to modifiable data buffer.
      * @param  size              amount of data to be sent and received in bytes.
      * @retval HAL_OK
      */
    hal_status_t HAL_PPP_{Process_i}_IT(hal_ppp_handle_t *hppp,
                                        const void   *p_{pointer_name_i},
                                        void         *p_{pointer_name_j},
                                        uint32_t     size_byte)
    {
      (...)
    }
    
  • Pointer to non-modifiable data in any type case

    /**
      * @brief  PPP handle structure definition
      */
    typedef struct
    {
      (...)
      const uint8_t *p_{pointer1_name}; /*!< Pointer to non-modifiable data buffer */
      uint8_t       *p_{pointer2_name}; /*!< Pointer to modifiable data buffer */
      (...)
    } hal_ppp_{type_name}_t;
    
  • Exported functions, macros, defines, and enums are prefixed by HAL or LL

ITEM

RULE

EXAMPLES

HAL Exported structure

prefixed by hal_

hal_ppp_handle_t

hal_ppp_config_t

LL Exported structure

prefixed by ll_

ll_dma_link_node_t

Fields within HAL/LL

structures

No prefix added

HAL Exported enumeration

Should be prefixed by hal_

hal_state_t

hal_ppp_state_t

LL Exported enumeration

No enumeration is deployed within LL

Possible values within HAL

enumeration

Exported constant outside

enumeration

prefixed by HAL_

HAL_DMA_HIGH_PRIORITY

HAL_PPP_XXXXXXXX

HAL exported APIs

prefixed by HAL_

HAL_DMA_Start()

HAL_PPP_xxxxx()

LL exported APIs (static

inline APIs)

Should be prefixed by LL_

LL_DMA_ConfigTransfer()

LL_PPP_xxxxx()

  • Structure, parameter, and argument names are in lowercase

ITEM

RULE

EXAMPLES

Typedef name

In lower case

hal_ppp_state_t

hal_ppp_t

Names of parameters of a structure

All in lower cases

typedef struct {
    uint8_t param1;
    uint32_t* p_buffer;
}

Argument in function

In lower case

hal_status_t HAL_PPP_{Process_i}_IT(hal_ppp_handle_t *hppp,
                          const void *p_{pointer_name_i},
                          void *p_{pointer_name_j},
                          uint32_t size_byte)
  • The HAL implementation must not use device RPN conditional defines

For both HAL and LL drivers, it is prohibited to use the device RPN define as compile-time conditional checks (e.g., #if defined(STM32C562xx) ). Instead, the implementation is based on dedicated defines for peripherals, features, bit definitions provided by the Device Family Pack (DFP) header files.

Examples:

  • Using peripheral instance define:

    typedef enum
    {
      /** TIM1 external trigger is connected to I/O */
      HAL_TIM_EXT_TRIG_TIM1_GPIO = LL_TIM_TIM1_ETRSOURCE_GPIO,
    #if defined (COMP2)
      /** TIM1 external trigger is connected to COMP2 output */
      HAL_TIM_EXT_TRIG_TIM1_COMP2 = LL_TIM_TIM1_ETRSOURCE_COMP2,
    #endif /* COMP2 */
      /** TIM2 external trigger is connected to MSIK */
    
      HAL_TIM_EXT_TRIG_TIM2_MSIK  = LL_TIM_TIM2_ETRSOURCE_MSIK,
      (...)
    } hal_tim_ext_trig_src_t;
    
  • Using PPP feature defines:

    #if defined(ADC_MULTIMODE_SUPPORT)
    static void adc_mm_reg_dma_data_transfer_stop_callback(hal_dma_handle_t *hdma);
    #endif /* ADC_MULTIMODE_SUPPORT */
    
  • Using register bit definition define:

    #if defined(DMA2D_FGPFCCR_CSS)
    IS_DMA2D_INPUT_YCBCR(input_color_mode)
    #endif /* DMA2D_FGPFCCR_CSS */
    

This approach simplifies the maintenance of HAL and LL drivers when new derivatives of the same series are introduced. In such cases, the Device Family Pack (DFP) is updated with the new device header file, which includes the necessary defines for peripherals and features.

As a result, HAL and LL drivers will automatically support the new device derivatives without requiring modifications, provided the peripherals and features remain consistent. Updates to HAL and LL drivers are only necessary if the new derivative introduces new use cases or features.

  • Local Variable Table Size: If using a local variable that is an array, the array size does not exceed 128 bytes.