当前位置: 首页 > article >正文

HAL库中断的理解

在 STM32 中,使用 HAL 库 进行中断管理时,通常会遵循一系列步骤来确保中断能够正确触发并被处理。下面是 HAL 库中断的整个流程,最后我将通过一个实例来加深理解。

HAL 库中断处理流程

  1. 配置中断

    • 在中断能够被触发之前,必须先配置相应的外设以及中断控制器。这通常包括设置外设的中断使能、设置中断优先级、启用 NVIC 中断等。
  2. 启用外设的中断使能

    • 外设(如 UART、CAN、TIM 等)需要启用相应的中断使能标志。例如,在 UART 的情况下,需要设置 USART_CR1_RXNEIE 来启用接收中断。
  3. 启用 NVIC 中断

    • 在 STM32 中,每个中断源(外设)都有一个与之关联的中断向量,并且需要通过 NVIC(嵌套向量中断控制器)来配置。通过 NVIC_EnableIRQ 函数将中断使能并设置优先级。
  4. 中断触发

    • 一旦外设中断标志被设置(例如数据接收完成、定时器溢出等),并且 NVIC 配置正确,外设会触发中断请求,CPU 会跳转到中断服务函数(ISR)
  5. 中断服务函数(ISR)

    • 当中断触发时,系统会调用相应的中断服务函数。例如,UART 的接收中断会触发 USART1_IRQHandler,定时器中断会触发 TIM1_UP_IRQHandler 等。
    • 在中断服务函数内部,通常会调用 HAL 库中的中断处理函数(如 HAL_UART_IRQHandlerHAL_TIM_IRQHandler 等),该函数会检查外设中断标志并调用相应的回调函数。
  6. HAL 中断处理

    • HAL 库的中断处理函数(如 HAL_UART_IRQHandlerHAL_CAN_IRQHandler 等)会检查外设的中断标志并确定是哪种类型的中断(如接收完成、中断发送、错误等)。
    • 根据不同的中断类型,HAL 会清除相应的中断标志,并调用用户定义的回调函数来处理具体的中断事件。
  7. 清除中断标志

    • 中断标志会在处理完中断后被清除,以避免重复触发。例如,HAL 中的 HAL_UART_IRQHandler 会清除 UART 的接收中断标志。
  8. 中断回调函数

    • 每个外设通常会提供一个或多个回调函数,供用户实现自定义的中断处理逻辑。例如,HAL_UART_RxCpltCallback 处理接收完成的逻辑,HAL_TIM_PeriodElapsedCallback 处理定时器溢出的逻辑等。

HAL 中断处理示例

假设我们有一个 UART 接收中断的例子,下面是一个简单的流程和代码示例:

1. 初始化外设(UART)

首先,我们需要配置 UART 和中断,使能接收中断。

void UART_Init(void)
{
    // 初始化 UART 外设
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&huart1);
    
    // 启用接收中断
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
    
    // 启用 NVIC 中断
    HAL_NVIC_EnableIRQ(USART1_IRQn);
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
}
2. 配置中断服务函数

在中断服务函数中,我们会调用 HAL 提供的 HAL_UART_IRQHandler 来处理 UART 的中断。

void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&huart1);  // 调用 HAL UART 中断处理函数
}
这里传入&huart1是为了传入串口1的结构体,从而检测标志位确定是哪种中断,然后调用对应的回调函数。
3. HAL 中断处理和回调函数

HAL_UART_IRQHandler 中,具体会根据接收到的数据调用回调函数。这里我们实现一个接收完成的回调函数 HAL_UART_RxCpltCallback

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        // 处理接收到的数据(例如,将接收到的数据存储到缓冲区)
        // 此处的 rxBuffer 是用户定义的接收缓冲区
        printf("Received Data: %s\n", rxBuffer);
        
        // 重新启动接收(例如使用 DMA 或继续轮询)
        HAL_UART_Receive_IT(&huart1, rxBuffer, RX_BUFFER_SIZE);
    }
}
4. 启动接收(使用中断)

初始化之后,我们可以启动 UART 的接收功能,并使用中断模式进行数据接收。

int main(void)
{
    HAL_Init();
    
    // 初始化 UART
    UART_Init();
    
    // 启动 UART 接收中断
    HAL_UART_Receive_IT(&huart1, rxBuffer, RX_BUFFER_SIZE);
    
    while (1)
    {
        // 主循环中的其他任务
    }
}

总结

在 STM32 HAL 库中,中断的处理流程大致如下:

  1. 配置外设和中断。
  2. 启用外设的中断使能并配置 NVIC。
  3. 中断触发时,系统会调用中断服务函数。
  4. 在中断服务函数中,调用 HAL 库的中断处理函数(如 HAL_UART_IRQHandler)。
  5. HAL_UART_IRQHandler 会根据外设的中断标志调用对应的回调函数(如 HAL_UART_RxCpltCallback)。
  6. 在回调函数中处理具体的业务逻辑,例如读取接收到的数据、重新启动接收等。

通过这个流程,STM32 HAL 库能高效、简洁地处理中断,并使用户能够灵活地在回调函数中处理各种事件。


是的,您理解得完全正确。在 STM32 的 HAL 库中,传入 &huart1UART_HandleTypeDef 类型的结构体)到中断处理函数(例如 HAL_UART_IRQHandler)是为了指定操作的 UART 外设,并通过该结构体来访问相关的状态信息、标志位和配置。这样,HAL 可以根据外设的中断标志确定是哪种中断,并调用相应的回调函数。

具体流程如下:

1. 传入 &huart1 结构体的目的

  • &huart1 是一个指向 UART_HandleTypeDef 结构体的指针,huart1 是为 UART1 外设配置的句柄(例如 huart1.Instance = USART1;)。这个结构体包含了有关 UART1 外设的所有信息,包括配置参数(如波特率、数据位、停止位等)以及当前的运行状态(如接收、发送缓冲区、错误标志等)。

  • 在中断服务函数 USART1_IRQHandler 中,传入 &huart1 是为了让 HAL 函数知道具体是哪一个 UART 外设发生了中断,以便后续处理。

2. 中断标志检测和回调调用

  • 当中断触发时(例如数据接收完成、发送完成或错误发生),STM32 的硬件会设置对应的中断标志。为了处理这些中断,HAL_UART_IRQHandler 会通过读取 huart1 结构体中 huart1.Instance->ISR(即 UART 状态寄存器)来获取中断标志。

  • HAL_UART_IRQHandler 会根据这些中断标志来确定是哪种类型的中断。常见的中断标志包括:

    • 接收完成中断 (UART_ISR_RXNE):接收到数据时触发。
    • 发送完成中断 (UART_ISR_TXE):发送缓冲区可用时触发。
    • 错误中断(如溢出、噪声错误等)。
  • 根据检测到的标志,HAL_UART_IRQHandler 会调用相应的回调函数。例如:

    • HAL_UART_RxCpltCallback:当接收到数据时,会调用此回调函数来处理接收到的数据。
    • HAL_UART_TxCpltCallback:当数据发送完成时,会调用此回调函数。
    • HAL_UART_ErrorCallback:如果发生错误(如帧错误、溢出错误等),会调用此回调函数。

3. 回调函数

回调函数是在 HAL_UART_IRQHandler 内部根据中断标志被自动调用的,用户只需要实现这些回调函数,来完成具体的中断事件处理。例如:

  • 接收完成回调 (HAL_UART_RxCpltCallback)

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
        // 检查 huart1
        if (huart->Instance == USART1)
        {
            // 处理接收到的数据
            // 假设数据已经存储在 rxBuffer 中
            printf("Received data: %s\n", rxBuffer);
            
            // 重新启动接收(例如,继续接收下一条数据)
            HAL_UART_Receive_IT(&huart1, rxBuffer, RX_BUFFER_SIZE);
        }
    }
    
  • 发送完成回调 (HAL_UART_TxCpltCallback)

    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
    {
        if (huart->Instance == USART1)
        {
            // 发送完成后,可以进行其他操作,如启动新的数据发送
            printf("Data transmission completed\n");
        }
    }
    

4. 简化流程总结

  1. 配置 UART 和中断:在程序中配置 UART 外设并使能相应的中断。
  2. 传递外设句柄 (&huart1):当中断发生时,通过传入外设句柄(如 &huart1)来明确是哪一个外设引发的中断。
  3. 检测中断标志:在 HAL_UART_IRQHandler 中,HAL 会通过检查 huart->Instance->ISR 来识别发生的中断类型(如接收完成、发送完成、错误等)。
  4. 调用回调函数:根据中断类型,调用相应的回调函数(如 HAL_UART_RxCpltCallbackHAL_UART_TxCpltCallback 等)。
  5. 用户自定义处理逻辑:用户在回调函数中实现具体的逻辑(如处理接收到的数据、发送新数据等)。

代码示例

在下面的代码示例中,我们演示了如何通过中断接收 UART 数据并处理。

UART_HandleTypeDef huart1;
uint8_t rxBuffer[RX_BUFFER_SIZE];

// UART 初始化函数
void UART_Init(void)
{
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&huart1);

    __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);  // 启用接收中断
    HAL_NVIC_EnableIRQ(USART1_IRQn);  // 启用 USART1 的中断
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);  // 设置优先级
}

// USART1 中断处理函数
void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&huart1);  // 调用 HAL 中断处理函数
}

// 接收完成回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1)
    {
        // 处理接收到的数据
        printf("Received Data: %s\n", rxBuffer);

        // 重新启动接收
        HAL_UART_Receive_IT(&huart1, rxBuffer, RX_BUFFER_SIZE);
    }
}

int main(void)
{
    HAL_Init();
    UART_Init();
    HAL_UART_Receive_IT(&huart1, rxBuffer, RX_BUFFER_SIZE);  // 启动接收中断

    while (1)
    {
        // 主循环中的其他任务
    }
}

总结

  • 通过传入 &huart1(或其他外设的句柄)到 HAL_UART_IRQHandler,HAL 库可以通过该结构体访问外设的状态和中断标志,从而决定中断类型并调用相应的回调函数。
  • HAL_UART_IRQHandler 负责根据外设的中断标志判断是哪种中断(接收、发送或错误),然后调用对应的回调函数,用户可以在回调函数中实现具体的业务逻辑。

http://www.kler.cn/a/599052.html

相关文章:

  • 个人博客系统 --- 测试报告
  • linux--时区查看和修改
  • 深度学习2-线性回归表示
  • Elasticsearch 中的数据分片问题
  • Linux中查找标准库函数的定义
  • 【容器运维】docker搭建私有仓库
  • SpringBoot分布式定时任务实战:告别重复执行的烦恼
  • 蓝桥杯_DS1302时钟
  • 游戏引擎学习第174天
  • 【C++复习】——类型转换
  • git,openpnp - 根据安装程序打包名称找到对应的源码版本
  • LeetCode 3038 相同分数的最大操作数目I
  • 基于单片机的农作物自动灌溉系统
  • 蓝桥杯第九天 2022 省赛 第 4 题 最少刷题数
  • nt!KeWaitForMultipleObjects函数分析之一个例子ExpWorkerThreadBalanceManager
  • 【玩转全栈】---- Django 基于 Websocket 实现群聊(解决channel连接不了)
  • 【QA】QT事件处理流程是怎么样的?
  • Linux内核Netfilter框架分析
  • 【CC2530 教程 二】CC2530定时器实现微秒、毫秒、秒延时函数
  • 【Vue3入门1】04-计算属性 + 侦听器