Improved driver mechanisms ¶
Enhancements have been made to strengthen thread safety and prevent race conditions in specific functional scenarios.
Revised and optimized HAL state machine ¶
In HAL2, drivers built around handle objects feature an updated state machine implementation based on these principles:
State transition management: ensures controlled and proper progression between driver functional states.
Not a lock mechanism: state machines manage transitions only and do not control resource access.
Not for identifying functional modes: state machines are not meant to identify functional modes, such as I2C target/controller mode, which are managed independently.
Unitary states: Each state corresponds to a single process step, with the HAL PPP handle maintaining one state at a time to avoid mixing.
Dedicated assertions for state checks: state checks are performed through dedicated assertions. These reduce footprint and preserves performance when in non-debug mode.
Global state machine ¶
In HAL2, each HAL driver built around a handle implements a global state machine with these core states to manage driver functionality:
HAL_PPP_STATE_RESET = 0: default state before initialization or after
HAL_PPP_Init, indicating the driver is uninitialized.HAL_PPP_STATE_INIT: set by
HAL_PPP_Initafter resetting the handle object and linking it to the physical instance of the peripheral. The driver is initialized but not yet ready to start processes.HAL_PPP_STATE_IDLE: applied by configuration APIs like
HAL_PPP_SetConfig, indicating the driver is configured and ready for operations that do not require a subblock setup, such as UART transmit or I2C receive.HAL_PPP_STATE_FAULT: indicates a fatal, non-recoverable error requiring application-level recovery. Included only if such errors are possible for the peripheral.
The global state is stored within the HAL PPP handle structure, which links the state machine to the peripheral instance, and can be retrieved using
HAL_PPP_GetState.
Global state machine example ¶
For peripherals that support only one process at a time, such as I2C, the global state machine is extended with additional process-specific states. This allows the driver to accurately represent the current operation being executed by the peripheral.
|
HAL1 |
HAL2 |
|---|---|
typedef enum
{
/* Peripheral is not yet Initialized */
HAL_I2C_STATE_RESET = 0x00U,
/* Peripheral Initialized and ready for use */
HAL_I2C_STATE_READY = 0x20U,
/* An internal process is ongoing */
HAL_I2C_STATE_BUSY = 0x24U,
/* Data Transmission process is ongoing */
HAL_I2C_STATE_BUSY_TX = 0x21U,
/* Data Reception process is ongoing */
HAL_I2C_STATE_BUSY_RX = 0x22U,
/* Address Listen Mode is ongoing */
HAL_I2C_STATE_LISTEN = 0x28U,
/* Address Listen Mode and Data Transmission process is ongoing */
HAL_I2C_STATE_BUSY_TX_LISTEN = 0x29U,
/* Address Listen Mode and Data Reception process is ongoing */
HAL_I2C_STATE_BUSY_RX_LISTEN = 0x2AU,
/* Abort user request ongoing */
HAL_I2C_STATE_ABORT = 0x60U,
/* Timeout state */
HAL_I2C_STATE_TIMEOUT = 0xA0U,
/* Error */
HAL_I2C_STATE_ERROR = 0xE0U
} HAL_I2C_StateTypeDef;
typedef struct __I2C_HandleTypeDef
{
I2C_TypeDef *Instance;
__IO HAL_I2C_StateTypeDef State;
} I2C_HandleTypeDef;
HAL_I2C_StateTypeDef HAL_I2C_GetState(I2C_HandleTypeDef *hi2c);
|
typedef enum
{
/* Not yet Initialized */
HAL_I2C_STATE_RESET = (0UL),
/* Initialized but not yet configured */
HAL_I2C_STATE_INIT = (1UL << 31),
/* Initialized and a global config applied */
HAL_I2C_STATE_IDLE = (1UL << 30),
/* Data Transmission process is ongoing */
HAL_I2C_STATE_TX = (1UL << 29),
/* Data Reception process is ongoing */
HAL_I2C_STATE_RX = (1UL << 28),
/* Address Listen Mode is ongoing */
HAL_I2C_STATE_LISTEN = (1UL << 27),
/* Address Listen Mode and Data Transmission process is ongoing */
HAL_I2C_STATE_TX_LISTEN = (1UL << 26),
/* Address Listen Mode and Data Reception process is ongoing */
HAL_I2C_STATE_RX_LISTEN = (1UL << 25),
/* Abort user request ongoing */
HAL_I2C_STATE_ABORT = (1UL << 24),
} hal_i2c_state_t;
typedef struct hal_i2c_handle_s hal_i2c_handle_t;
struct hal_i2c_handle_s
{
hal_i2c_t instance;
volatile hal_i2c_state_t global_state;
};
hal_i2c_state_t HAL_I2C_GetState(const hal_i2c_handle_t *hi2c);
|
Simultaneous parallel processes ¶
For peripherals supporting multiple parallel processes, such as UART Tx/Rx, the global state machine includes only three basic states: RESET, INIT, and CONFIGURED. Process state transitions are managed by separate, dedicated state machines for each process. The global state can be retrieved using
HAL_PPP_GetState, while individual process states have their own getters, such as
HAL_UART_GetState,
HAL_UART_GetTxState, and
HAL_UART_GetRxState.
|
HAL1 |
HAL2 |
|---|---|
/* Peripheral is not initialized
Value is allowed for gState and RxState */
#define HAL_UART_STATE_RESET 0x00000000U
/* Peripheral Initialized and ready for use
Value is allowed for gState and RxState */
#define HAL_UART_STATE_READY 0x00000020U
/* An internal process is ongoing
Value is allowed for gState only */
#define HAL_UART_STATE_BUSY 0x00000024U
/* Data Transmission process is ongoing
Value is allowed for gState only */
#define HAL_UART_STATE_BUSY_TX 0x00000021U
/* Data Reception process is ongoing
Value is allowed for RxState only */
#define HAL_UART_STATE_BUSY_RX 0x00000022U
/* Data Transmission and Reception process is ongoing
Not to be used for neither gState nor RxState. Value is result
of combination (Or) between gState and RxState values */
#define HAL_UART_STATE_BUSY_TX_RX 0x00000023U
/* Timeout state
Value is allowed for gState only */
#define HAL_UART_STATE_TIMEOUT 0x000000A0U
/* Error
Value is allowed for gState only */
#define HAL_UART_STATE_ERROR 0x000000E0U
typedef uint32_t HAL_UART_StateTypeDef;
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance;
__IO HAL_UART_StateTypeDef gState;
__IO HAL_UART_StateTypeDef RxState;
} UART_HandleTypeDef;
HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart);
|
typedef enum
{
/*! Peripheral is not initialized */
HAL_UART_STATE_RESET = 0U,
/*! Peripheral is initialized but not configured */
HAL_UART_STATE_INIT = (1UL << 31U),
/*! Peripheral is initialized and a global config is set */
HAL_UART_STATE_CONFIGURED = (1UL << 30U),
} hal_uart_state_t;
typedef enum
{
/*! Data Reception process is in reset */
HAL_UART_RX_STATE_RESET = 1U,
/*! Data Reception process is idling */
HAL_UART_RX_STATE_IDLE = (1UL << 31U),
/*! Data Reception process is ongoing */
HAL_UART_RX_STATE_ACTIVE = (1UL << 30U),
/*! Data Reception process is aborting */
HAL_UART_RX_STATE_ABORT = (1UL << 29U),
} hal_uart_rx_state_t;
typedef enum
{
/*! Data Transmission process is in reset */
HAL_UART_TX_STATE_RESET = 1U,
/*! Data Transmission process is idling */
HAL_UART_TX_STATE_IDLE = (1UL << 31U),
/*! Data Transmission process is ongoing */
HAL_UART_TX_STATE_ACTIVE = (1UL << 30U),
/*! Data Transmission process is aborting */
HAL_UART_TX_STATE_ABORT = (1UL << 29U),
} hal_uart_tx_state_t;
typedef struct hal_uart_handle_s hal_uart_handle_t;
struct hal_uart_handle_s
{
hal_uart_t instance;
volatile hal_uart_state_t global_state;
volatile hal_uart_rx_state_t rx_state;
volatile hal_uart_tx_state_t tx_state;
};
hal_uart_state_t HAL_UART_GetState(const hal_uart_handle_t *huart);
hal_uart_tx_state_t HAL_UART_GetTxState(const hal_uart_handle_t *huart);
hal_uart_rx_state_t HAL_UART_GetRxState(const hal_uart_handle_t *huart);
|
Subinstance process management ¶
When the peripheral includes several subinstance processes, such as TIMER channels:
The global state machine contains a handle to the global state of the peripheral but not the subinstance states.
Each subinstance has its own dedicated state machine reflecting its specific state, such as the TIM channel state machine.
Sub-instance states must consider subblocks when applicable. For example, input compare (IC) and output compare (OC) are subblocks of the TIM peripheral.
|
HAL1 |
HAL2 |
|---|---|
typedef enum
{
/* Peripheral not yet initialized or disabled */
HAL_TIM_STATE_RESET = 0x00U,
/* Peripheral Initialized and ready for use */
HAL_TIM_STATE_READY = 0x01U,
/* An internal process is ongoing */
HAL_TIM_STATE_BUSY = 0x02U,
/* Timeout state */
HAL_TIM_STATE_TIMEOUT = 0x03U,
/* Reception process is ongoing */
HAL_TIM_STATE_ERROR = 0x04U
} HAL_TIM_StateTypeDef;
typedef enum
{
/* TIM Channel initial state */
HAL_TIM_CHANNEL_STATE_RESET = 0x00U,
/* TIM Channel ready for use */
HAL_TIM_CHANNEL_STATE_READY = 0x01U,
/* An internal process is ongoing on the TIM channel */
HAL_TIM_CHANNEL_STATE_BUSY = 0x02U
} HAL_TIM_ChannelStateTypeDef;
typedef enum
{
/* DMA Burst initial state */
HAL_DMA_BURST_STATE_RESET = 0x00U,
/* DMA Burst ready for use */
HAL_DMA_BURST_STATE_READY = 0x01U,
/* Ongoing DMA Burst */
HAL_DMA_BURST_STATE_BUSY = 0x02U
} HAL_TIM_DMABurstStateTypeDef;
typedef struct __TIM_HandleTypeDef
{
TIM_TypeDef *Instance;
__IO HAL_TIM_StateTypeDef State;
__IO HAL_TIM_ChannelStateTypeDef ChannelState[6];
__IO HAL_TIM_ChannelStateTypeDef ChannelNState[4];
} TIM_HandleTypeDef;
HAL_TIM_StateTypeDef
HAL_TIM_Base_GetState(TIM_HandleTypeDef *htim);
HAL_TIM_StateTypeDef
HAL_TIM_OC_GetState(TIM_HandleTypeDef *htim);
HAL_TIM_StateTypeDef
HAL_TIM_PWM_GetState(TIM_HandleTypeDef *htim);
HAL_TIM_StateTypeDef
HAL_TIM_IC_GetState(TIM_HandleTypeDef *htim);
HAL_TIM_StateTypeDef
HAL_TIM_OnePulse_GetState(TIM_HandleTypeDef *htim);
HAL_TIM_StateTypeDef
HAL_TIM_Encoder_GetState(TIM_HandleTypeDef *htim);
HAL_TIM_ChannelStateTypeDef
HAL_TIM_GetChannelState(TIM_HandleTypeDef *htim,
uint32_t Channel);
HAL_TIM_DMABurstStateTypeDef
HAL_TIM_DMABurstState(TIM_HandleTypeDef *htim);
|
typedef enum
{
/** Peripheral not yet initialized */
HAL_TIM_STATE_RESET = 0U,
/** Peripheral initialized but not yet configured */
HAL_TIM_STATE_INIT = (1UL << 31U),
/** Peripheral initialized and a global config applied */
HAL_TIM_STATE_IDLE = (1UL << 30U),
/** Counter is running */
HAL_TIM_STATE_ACTIVE = (1UL << 29U),
HAL_TIM_STATE_ACTIVE_SILENT = (HAL_TIM_STATE_ACTIVE | HAL_TIM_ACTIVE_SILENT),
} hal_tim_state_t;
/**
* @brief TIM Channel States definition
*/
typedef enum
{
/** TIM Channel initial state */
HAL_TIM_CHANNEL_STATE_RESET = (1UL << 31U),
/** TIM Channel ready for use as output channel */
HAL_TIM_OC_CHANNEL_STATE_IDLE = (1UL << 30U),
/** An internal process is ongoing on the TIM output channel */
HAL_TIM_OC_CHANNEL_STATE_ACTIVE = (1UL << 29U),
/** An internal process is ongoing on the TIM output channel in DMA silent mode */
HAL_TIM_OC_CHANNEL_STATE_ACTIVE_SILENT,
/** TIM Channel ready for use as input channel */
HAL_TIM_IC_CHANNEL_STATE_IDLE = (1UL << 28U),
/** An internal process is ongoing on the TIM input channel */
HAL_TIM_IC_CHANNEL_STATE_ACTIVE = (1UL << 27U),
/** An internal process is ongoing on the TIM input channel in DMA silent mode */
HAL_TIM_IC_CHANNEL_STATE_ACTIVE_SILENT
} hal_tim_channel_state_t;
typedef struct hal_tim_handle_s hal_tim_handle_t;
struct hal_tim_handle_s
{
hal_tim_t instance;
volatile hal_tim_state_t global_state;
volatile hal_tim_channel_state_t channel_states[HAL_TIM_CHANNELS];
};
hal_tim_state_t
HAL_TIM_GetState(const hal_tim_handle_t *htim);
hal_tim_channel_state_t
HAL_TIM_GetChannelState(const hal_tim_handle_t *htim,
hal_tim_channel_t channel);
|
Revised and optimized HAL API status returns ¶
The HAL API status returns have been reworked and optimized in HAL2 as follows:
|
HAL1 |
HAL2 |
|---|---|
typedef enum
{
HAL_OK = 0x00,
HAL_ERROR = 0x01,
HAL_BUSY = 0x02,
HAL_TIMEOUT = 0x03
} HAL_StatusTypeDef;
|
typedef enum
{
HAL_OK = 0x00000000U,
HAL_ERROR = 0xFFFFFFFFU,
HAL_BUSY = 0x55555555U,
HAL_INVALID_PARAM = 0xAAAAAAAAU,
HAL_TIMEOUT = 0x5A5A5A5AU
} hal_status_t;
|
In HAL2:
HAL_ERRORindicates an internal error, including intrinsic hardware timeouts.HAL_INVALID_PARAMsignals an invalid parameter that can lead to a hard fault or memory exception, such as a null pointer.HAL_BUSYis returned when a new process is started while another is still ongoing.HAL_TIMEOUTis used only when an operation exceeds the user-defined timeout, such as polling with a timeout parameter.
Note
In HAL2, the
hal_status_t
enumeration values have a minimum Hamming distance of at least 8 for function return values. This design complies with the secure coding rule requiring sensitive function return values to differ by at least 8 bits.
Revised and optimized HAL error management ¶
Error management in HAL2 largely follows the same principles as HAL1, with minor renaming. Drivers built around handles store the last occurred errors in a
last_error_codes
variable, which is reset during
HAL_PPP_Init
and at the start of each process. The HAL2
HAL_PPP_GetLastErrorCodes
function replaces the HAL1
HAL_PPP_GetError
function to retrieve these error codes.
A new HAL2 feature is optional error code storage and retrieval, controlled by the
USE_HAL_PPP_GET_LAST_ERRORS
compilation define statement. When set to 1 in the HAL configuration header file, such as
stm32u5xx_hal_conf.h, detailed error codes are available.
USE_HAL_PPP_GET_LAST_ERRORS
is disabled by default to save memory space.
In HAL2, error codes focus solely on process-related hardware errors:
HAL_PPP_ERROR_INVALID_PARAMandHAL_PPP_ERROR_INVALID_CALLBACKare removed. Invalid parameters or callbacks returnHAL_INVALID_PARAMwithout storing error codes.HAL_PPP_ERROR_TIMEOUTremains only if the peripheral has a hardware timeout flag; user-defined timeout expirations returnHAL_TIMEOUTwithout error code storage.Error codes are bitmapped and organized into common errors and specific process errors (for both single and parallel processes). They can be retrieved collectively using the
uint32_t HAL_PPP_GetLastErrors(hppp)function.
|
HAL1 |
HAL2 |
|---|---|
/* No error */
#define HAL_SPI_ERROR_NONE (0x00000000UL)
/* MODF error */
#define HAL_SPI_ERROR_MODF (0x00000001UL)
/* CRC error */
#define HAL_SPI_ERROR_CRC (0x00000002UL)
/* OVR error */
#define HAL_SPI_ERROR_OVR (0x00000004UL)
/* FRE error */
#define HAL_SPI_ERROR_FRE (0x00000008UL)
/* DMA transfer error */
#define HAL_SPI_ERROR_DMA (0x00000010UL)
/* Error on RXNE/TXE/BSY/FTLVL/FRLVL Flag */
#define HAL_SPI_ERROR_FLAG (0x00000020UL)
/* Error during SPI Abort procedure */
#define HAL_SPI_ERROR_ABORT (0x00000040UL)
/* Underrun error */
#define HAL_SPI_ERROR_UDR (0x00000080UL)
/* Timeout error */
#define HAL_SPI_ERROR_TIMEOUT (0x00000100UL)
/* Unknown error */
#define HAL_SPI_ERROR_UNKNOW (0x00000200UL)
/* Requested operation not supported */
#define HAL_SPI_ERROR_NOT_SUPPORTED (0x00000400UL)
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1UL)
/* Invalid Callback error */
#define HAL_SPI_ERROR_INVALID_CALLBACK (0x00000800UL)
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
typedef struct __SPI_HandleTypeDef
{
SPI_TypeDef *Instance;
__IO uint32_t ErrorCode;
} SPI_HandleTypeDef;
uint32_t HAL_SPI_GetError(SPI_HandleTypeDef *hspi);
|
#if (USE_HAL_SPI_GET_LAST_ERRORS == 1)
/* No error */
#define HAL_SPI_ERROR_NONE (0UL)
/* Mode fault error */
#define HAL_SPI_ERROR_MODF (0x01UL << 0U)
/* CRC error */
#define HAL_SPI_ERROR_CRC (0x01UL << 1U)
/* Overrun error */
#define HAL_SPI_ERROR_OVR (0x01UL << 2U)
/* Frame format error */
#define HAL_SPI_ERROR_FRE (0x01UL << 3U)
/* DMA transfer error */
#define HAL_SPI_ERROR_DMA (0x01UL << 4U)
/* Error during SPI Abort procedure */
#define HAL_SPI_ERROR_ABORT (0x01UL << 5U)
/* Underrun error */
#define HAL_SPI_ERROR_UDR (0x01UL << 6U)
/* Locked IO error */
#define HAL_SPI_ERROR_IO_LOCKED (0x01UL << 7U)
#endif /* USE_HAL_SPI_GET_LAST_ERRORS */
typedef struct hal_spi_handle_s hal_spi_handle_t;
struct hal_spi_handle_s
{
hal_spi_t instance;
#if (USE_HAL_SPI_GET_LAST_ERRORS == 1)
volatile uint32_t last_error_codes;
#endif /* USE_HAL_SPI_GET_LAST_ERRORS */
};
#if (USE_HAL_SPI_GET_LAST_ERRORS == 1)
uint32_t HAL_SPI_GetLastErrorCodes(const hal_spi_handle_t *hspi);
#endif /* USE_HAL_SPI_GET_LAST_ERRORS */
|
/* No error */
#define HAL_UART_ERROR_NONE (0x00000000U)
/* Parity error */
#define HAL_UART_ERROR_PE (0x00000001U)
/* Noise error */
#define HAL_UART_ERROR_NE (0x00000002U)
/* Frame error */
#define HAL_UART_ERROR_FE (0x00000004U)
/* Overrun error */
#define HAL_UART_ERROR_ORE (0x00000008U)
/* DMA transfer error */
#define HAL_UART_ERROR_DMA (0x00000010U)
/* Receiver Timeout error */
#define HAL_UART_ERROR_RTO (0x00000020U)
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/* Invalid Callback error */
#define HAL_UART_ERROR_INVALID_CALLBACK (0x00000040U)
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance;
__IO uint32_t ErrorCode;
} UART_HandleTypeDef;
uint32_t HAL_UART_GetError(UART_HandleTypeDef *huart);
|
#if (USE_HAL_UART_GET_LAST_ERRORS == 1)
/*! No error on RX */
#define HAL_UART_RECEIVE_ERROR_NONE (0UL)
/*! Parity error on RX */
#define HAL_UART_RECEIVE_ERROR_PE (0x1UL << 0)
/*! Noise error on RX */
#define HAL_UART_RECEIVE_ERROR_NE (0x1UL << 1U)
/*! Frame error on RX */
#define HAL_UART_RECEIVE_ERROR_FE (0x1UL << 2U)
/*! Overrun error on RX */
#define HAL_UART_RECEIVE_ERROR_ORE (0x1UL << 3U)
#if defined (USE_HAL_UART_DMA) && (USE_HAL_UART_DMA == 1U)
/*! DMA transfer error on RX */
#define HAL_UART_RECEIVE_ERROR_DMA (0x1UL << 4U)
#endif /* USE_HAL_UART_DMA */
/*! Receiver Timeout error on RX */
#define HAL_UART_RECEIVE_ERROR_RTO (0x1UL << 5U)
/*! No error on TX */
#define HAL_UART_TRANSMIT_ERROR_NONE (0UL << 16U)
/*! DMA transfer error on TX */
#define HAL_UART_TRANSMIT_ERROR_DMA (0x1UL << 17U)
#endif /* USE_HAL_UART_GET_LAST_ERRORS */
typedef struct hal_uart_handle_s hal_uart_handle_t;
struct hal_uart_handle_s
{
hal_uart_t instance;
#if (USE_HAL_UART_GET_LAST_ERRORS == 1)
/*! Last error codes on reception side */
volatile uint32_t last_reception_error_codes;
/*! Last error codes on transmission side */
volatile uint32_t last_transmission_error_codes;
#endif /* USE_HAL_UART_GET_LAST_ERRORS */
};
#if (USE_HAL_UART_GET_LAST_ERRORS == 1)
uint32_t HAL_UART_GetLastErrorCodes(const hal_uart_handle_t *huart);
#endif /* USE_HAL_UART_GET_LAST_ERRORS */
|