一、回调函数的本质
回调函数本质上是一个通过函数指针调用的函数。
把一个函数的地址(指针)传递给另一个函数,当特定事件发生时,这个指针会被用来调用对应的函数。
这种机制允许底层驱动(如 HAL 库)在事件触发时,自动通知上层应用程序,而无需上层主动查询("轮询")。
二、回调函数的作用
在 STM32 开发中,回调函数主要用于处理异步事件(无法预测何时发生的事件),核心作用是:
解耦:底层驱动和上层应用分离,底层负责检测事件,上层负责处理事件。
高效:避免CPU持续轮询事件状态,提高资源利用率。
模块化:不同功能的处理逻辑可以独立封装在不同的回调函数中。
三、典型应用场景
1. GPIO 外部中断回调
场景:按键按下/松开、传感器电平变化等。
工作流程:
-
- 配置 GPIO 为中断模式(上升沿/下降沿触发)。
- 当中断发生时,硬件自动跳转到中断服务程序(ISR)。
- 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秒采集一次数据)。
工作流程:
-
- 配置定时器并使能更新中断。
- 当定时器计数达到设定值时,触发中断。
- 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 串口通信回调
场景:串口接收/发送数据完成后触发。
工作流程:
-
- 配置 UART 并使能中断。
- 当串口接收/发送缓冲区满时,触发中断。
- 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 采集完成后处理数据。
工作流程:
-
- 配置 ADC 并使能转换完成中断。
- 当 ADC 转换完成时,触发中断。
- 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的串口:
配置串口:


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

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


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

七、回调函数的原理

