HAL库中断回调机制

USART中断为例

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->SR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);

  ..........//省略大量代码直接跳到最后面

  /* UART in mode Transmitter ------------------------------------------------*/
  if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {
    UART_Transmit_IT(huart);
    return;
  }

  /* UART in mode Transmitter end --------------------------------------------*/
  if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {
    UART_EndTransmit_IT(huart);
    return;
  }
}
static HAL_StatusTypeDef UART_EndTransmit_IT(UART_HandleTypeDef *huart)
{
  /* Disable the UART Transmit Complete Interrupt */
  __HAL_UART_DISABLE_IT(huart, UART_IT_TC);

  /* Tx process is ended, restore huart->gState to Ready */
  huart->gState = HAL_UART_STATE_READY;

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  /*Call registered Tx complete callback*/
  huart->TxCpltCallback(huart);
#else
  /*Call legacy weak Tx complete callback*/
  HAL_UART_TxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

  return HAL_OK;
}

中断服务函数(ISR) (如 USART1_IRQHandler)内调用HAL_UART_IRQHandler(&huart1);此函数末尾调用UART_EndTransmit_IT(UART_HandleTypeDef *huart)函数,再在此函数能最终调用发送完成回调函数(两种方式实现回调)

两种回调模式

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    huart->TxCpltCallback(huart);      // 方式1:调用用户动态注册的回调函数
#else
    HAL_UART_TxCpltCallback(huart);    // 方式2:调用传统的弱定义回调函数
#endif

二、两种回调模式的区别

1. 动态注册回调模式 (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  • 触发机制
    通过函数指针动态注册回调函数,允许运行时灵活修改回调行为。
  • 配置方法

    c

    /* 用户代码中注册回调函数 */
    void My_TxCpltCallback(UART_HandleTypeDef *huart) {
        // 自定义处理逻辑
    }
    
    /* 在初始化后注册 */
    huart1.TxCpltCallback = My_TxCpltCallback;
  • 优势
    • 支持多个UART实例共用同一回调函数模板
    • 可在运行时切换不同回调实现
2. 传统弱定义回调模式 (USE_HAL_UART_REGISTER_CALLBACKS == 0)
  • 触发机制
    调用弱符号__weak void HAL_UART_TxCpltCallback(),用户通过重写弱函数实现自定义。
  • 配置方法

    c

    /* 用户重写弱函数(通常在main.c) */
    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
        if(huart->Instance == USART1) {
            // 处理USART1发送完成
        }
    }
  • 优势
    • 代码结构简单直观
    • 无需额外内存存储函数指针

三、关键宏定义 USE_HAL_UART_REGISTER_CALLBACKS

  • 定义位置
    stm32xx_hal_conf.h中配置:

    c

    #define USE_HAL_UART_REGISTER_CALLBACKS 1 // 启用动态注册模式
  • 工程影响
    • 设为1时:HAL库会为每个UART实例维护回调函数指针表(增加约20字节RAM开销)
    • 设为0时:使用传统弱函数方式(节省内存)

四、底层实现对比

特性动态注册回调模式传统弱函数模式
函数调用方式通过结构体中的函数指针(huart->TxCpltCallback)直接调用弱函数
内存开销每个UART实例需要存储函数指针无额外开销
灵活性支持运行时动态修改回调编译时固定
多实例区分通过注册不同的回调函数实现需在回调函数内手动检查huart->Instance

五、典型应用场景

1. 动态注册模式适用场景

c

// 案例:多个UART实例需要不同处理逻辑
void UART1_Callback(UART_HandleTypeDef *huart) { /* 处理UART1 */ }
void UART2_Callback(UART_HandleTypeDef *huart) { /* 处理UART2 */ }

// 初始化时分别注册
huart1.TxCpltCallback = UART1_Callback;
huart2.TxCpltCallback = UART2_Callback;
2. 传统弱函数模式适用场景

c

// 案例:单一UART实例或简单应用
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
    // 所有UART共用此回调
    if(huart->Instance == USART1) {
        // 特殊处理USART1
    }
}

六、配置注意事项

  1. 一致性原则
    同一工程中所有外设最好统一使用同一种模式(避免混用导致维护困难)。

  2. 模式切换代价
    从传统模式切换到动态注册模式需修改两点:

    • hal_conf.h中启用宏
    • 在代码中显式注册所有回调函数
  3. 错误处理
    动态注册模式下,若未注册回调直接启用中断,函数指针为NULL会导致硬故障。安全做法:

    c

    if(huart->TxCpltCallback != NULL) {
        huart->TxCpltCallback(huart);
    }

七、HAL库的实现演进

  • 早期版本:仅支持弱函数回调方式
  • HAL V1.2.0+ :引入动态注册机制,增强灵活性
  • 兼容性:宏定义确保新旧代码可共存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值