6.单片机回调函数

一、回调函数的本质

回调函数本质上是一个通过函数指针调用的函数

把一个函数的地址(指针)传递给另一个函数,当特定事件发生时,这个指针会被用来调用对应的函数。

这种机制允许底层驱动(如 HAL 库)在事件触发时,自动通知上层应用程序,而无需上层主动查询("轮询")。

二、回调函数的作用

在 STM32 开发中,回调函数主要用于处理异步事件(无法预测何时发生的事件),核心作用是:

解耦:底层驱动和上层应用分离,底层负责检测事件,上层负责处理事件。

高效:避免CPU持续轮询事件状态,提高资源利用率。

模块化:不同功能的处理逻辑可以独立封装在不同的回调函数中。

三、典型应用场景

1. GPIO 外部中断回调

场景:按键按下/松开、传感器电平变化等。

工作流程

    1. 配置 GPIO 为中断模式(上升沿/下降沿触发)。
    2. 当中断发生时,硬件自动跳转到中断服务程序(ISR)。
    3. HAL 库的 ISR 会调用回调函数 HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

示例

复制代码
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == KEY_PIN) // 判断是哪个引脚触发中断
    {
        LED_Toggle(); // 翻转 LED 状态
    }
}

2. 定时器中断回调

场景:定时执行任务(如每隔1秒采集一次数据)。

工作流程

    1. 配置定时器并使能更新中断。
    2. 当定时器计数达到设定值时,触发中断。
    3. HAL 库调用回调函数 HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

示例

复制代码
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2) // 判断是哪个定时器触发
    {
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 翻转 LED
    }
}

3. UART 串口通信回调

场景:串口接收/发送数据完成后触发。

工作流程

    1. 配置 UART 并使能中断。
    2. 当串口接收/发送缓冲区满时,触发中断。
    3. HAL 库调用回调函数 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
  • 示例

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    if (huart->Instance == USART1)
    {
    HAL_UART_Transmit(huart, &rx_data, 1, 1000); // 回显接收的数据
    HAL_UART_Receive_IT(huart, &rx_data, 1); // 重新开启接收中断
    }
    }


4. ADC 转换完成回调

场景:ADC 采集完成后处理数据。

工作流程

    1. 配置 ADC 并使能转换完成中断。
    2. 当 ADC 转换完成时,触发中断。
    3. HAL 库调用回调函数 HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

示例

复制代码
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if (hadc->Instance == ADC1)
    {
        adc_value = HAL_ADC_GetValue(hadc); // 获取转换结果
    }
}

四、回调函数的执行时机

回调函数在中断上下文中执行,因此需要注意:

执行时间要短,避免阻塞其他中断。

不建议在回调函数中使用 printf 等耗时操作(可改用 DMA 或环形缓冲区)。

如果需要在回调函数中处理复杂逻辑,可通过信号量或消息队列通知任务处理。

五、回调函数的分类

函数调用分类:分为输出型和输入型两种类型(非标准分类)

输出型函数:

特点:调用者主导调用时机,明确知道何时需要调用

示例:C语言的sizeof()、memcpy()函数;单片机控制LED状态、继电器状态、LCD驱动等函数

输入型函数(响应式函数):

特点:响应外部事件,调用时机不可预知

示例:串口数据接收、按键检测、ADC采集等

实现方式:通过回调函数实现,在单片机领域应用广泛

六、回调函数的原理

实现机制:

函数指针:通过函数指针实现回调机制

固定名称:STM32库中预定义的中断回调函数名称(如外部中断0、定时器中断等)

执行流程:当特定事件发生时,程序自动跳转到对应的回调函数执行

实际应用:

数据传递:将硬件采集的数据传递给应用层

事件处理:处理异步事件,如中断服务

例如STM32的串口:

配置串口:

这里进行串口中断处理函数的指定:

也就是说,在串口发生中断时,会调用这个函数:

定义了虚函数,当用户没有编写回调函数时,编译工程也不会报错。

七、回调函数的原理

相关推荐
M78佐菲2 小时前
ARM学习笔记(1)
linux·arm开发·笔记·嵌入式硬件·学习
崇山峻岭之间8 小时前
HC32F460PETB的PB2的Vout123
单片机
恶魔泡泡糖8 小时前
stm32F103C8T6标准库串口接收之中断方式2
stm32·单片机·嵌入式硬件
充哥单片机设计8 小时前
【STM32开源项目】智能小区充电桩
stm32·单片机·嵌入式硬件·毕业设计·小区充电桩·智能小区充电桩
嵌入式-老费9 小时前
esp32开发与应用(如何解决SoC冷启动的问题)
单片机·嵌入式硬件
充哥单片机设计10 小时前
【STM32开源项目】智能语音台灯
stm32·单片机·嵌入式硬件·毕业设计·单片机设计·智能语音台灯
EatFan11 小时前
全栈自造 Status Deck(四):硬件终于到了,从点亮屏幕到跑通第一个 Demo
单片机·物联网·esp32
sunoo-22911 小时前
【嵌入式裸机开发Day04】i.MX6ULL 蜂鸣器驱动详解:从引脚地址查找到代码实现全流程
arm开发·单片机·嵌入式硬件·学习·串口·裸机
Brilliantwxx12 小时前
【STM32】 SPI阻塞式通信与HAL源码 (源码分析)
stm32·单片机·嵌入式硬件
Doraemomo12 小时前
IMX6ULL裸机开发——PWM
arm开发·嵌入式硬件·imx6ull