什么是回调函数
回调函数就是一个被作为参数传递的函数。在C语言中,回调函数只能使用函数指针实现,在C++、Python、ECMAScript等更现代的编程语言中还可以使用仿函数或匿名函数。
工作机制
- ⑴定义一个回调函数;
- ⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
- ⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
应用案例
(1)应用层:通过调用hal层的函数,将回调函数注册到hal层
(2)硬件hal层:通过异步调用这个callback函数,实现将数据上报到应用层
(3)应用层:根据硬件层上报的采集数据,实现业务逻辑判断和执行
函数类型分类
1.输入性函数( 异步调用的函数
)
输入型函数,我们称为响应式函数。比如说接收串口的数据。我们不知道数据什么时候来。
再比如:按键检测函数,我们不知道什么时候会按下按键,name就需要定义成响应式函数来实现
#include <stdio.h>
// 定义按键枚举类型
typedef enum {
BUTTON_NONE,
BUTTON_A,
BUTTON_B,
BUTTON_C
} Button;
// 定义按键事件处理器类型
typedef void (*ButtonEventHandler)(void);
// 按键事件处理函数:处理按键A事件
void handle_button_a() {
printf("按下了按钮A,执行功能A\n");
}
// 按键事件处理函数:处理按键B事件
void handle_button_b() {
printf("按下了按钮B,执行功能B\n");
}
// 按键事件处理函数:处理按键C事件
void handle_button_c() {
printf("按下了按钮C,执行功能C\n");
}
// 模拟按键输入函数
Button get_button_input() {
// 模拟获取按键输入
return BUTTON_A; // 假设当前按下了按钮A
}
int main() {
// 声明按键事件处理器数组,并初始化为相应的处理函数
ButtonEventHandler event_handlers[] = {
handle_button_a,
handle_button_b,
handle_button_c
};
// 获取按键输入
Button button_pressed = get_button_input();
// 根据按键输入选择相应的事件处理器并执行
if (button_pressed >= BUTTON_A && button_pressed <= BUTTON_C) {
// 获取事件处理器索引
int index = button_pressed - 1;
// 调用相应的事件处理器函数
event_handlers[index]();
} else {
printf("无效的按键输入\n");
}
return 0;
}
#include <stdio.h>
// 定义温度回调函数的函数指针类型
typedef void (*TemperatureCallback)(float);
// 模拟温度传感器读取函数
float read_temperature_sensor() {
// 模拟读取温度传感器数据
return 25.5; // 返回模拟的温度值
}
// 温度回调函数:打印当前温度
void print_temperature(float temperature) {
printf("当前温度为 %.2f 摄氏度\n", temperature);
}
// 温度回调函数:记录当前温度到日志文件
void log_temperature(float temperature) {
FILE *log_file = fopen("temperature_log.txt", "a");
if (log_file != NULL) {
fprintf(log_file, "%.2f\n", temperature);
fclose(log_file);
} else {
printf("无法打开日志文件\n");
}
}
// 主函数:设置回调函数,并定期读取温度传感器数据调用回调函数
int main() {
// 定义回调函数指针变量
TemperatureCallback callback;
// 设置回调函数为打印温度
callback = print_temperature;
// 定时读取温度传感器数据,并调用回调函数处理
while (1) {
// 模拟定时读取温度传感器数据
float temperature = read_temperature_sensor();
// 调用回调函数处理数据
callback(temperature);
// 等待一段时间再次读取温度数据
delay(1000); // 假设延时函数 delay 表示等待1秒
}
return 0;
}
在Javascript中也有类似的回调机制
- (函数A: greetUser作为参数传入另一个函数B:getUserInput,函数A在函数B的内部某个时间节点被触发调用,这就是Javascript的异步操作):
// 模拟异步操作:获取用户输入
function getUserInput(callback) {
// 模拟延时,以模拟异步操作
setTimeout(function() {
var userInput = prompt("请输入您的名字:");
callback(userInput); // 将用户输入作为参数传递给回调函数
}, 1000); // 假设延时1秒
}
// 回调函数:处理用户输入
function greetUser(username) {
if (username) {
alert("你好," + username + "!");
} else {
alert("您没有输入名字。");
}
}
// 调用 getUserInput 函数,并传入 greetUser 回调函数
getUserInput(greetUser);
2.输出型函数
大家不知道有没用过C语言的库函数,比如说sizeof()
获取数据长度的函数,memcpy()
是内存拷贝函数,我们调用这个函数之后呢,就能完成相应的功能,还有我们基于单片机的一些程序函数,比如说控制LED状态,继电器状态等,LCD驱动等。那么我们称他们为输出型函数。
输出型函数使我们主导的角色,我们知道什么时候应该调用他。
3. 小结
所以通过两个类型的分析,我们知道,回调函数基本是用于输入型函数的处理,比如说串口数据的接收,那么数据就是输入到单片机里面的,按键检测,按键状态是输入到单片机的。
再比如说ADC值采集,ADC值也是输入到单片机莉的,那么他们输入的时间节点是未知的,这些就能够用回调函数来处理。
回调函数还有一个作用就是为了封装代码,比如说做芯片或者模组的厂家,我们拿典型的STM32举例,像外部中断,定时器,串口等中断函数都是属于回调函数。这种函数的目的是把采集到的数据传递给用户,或者说应用层。
所以回调函数的核心作用就是:
1、把数据从一个.c文件传递到另一个.C文件,而不是采用全局变量的方法
2、对于这种数据传递方式,回调函数更加便于代码的封装。