C语言回调函数初始化与触发方法
第一种用法:
typedef void (*dev_uart1_callback)(uint32_t status);
-
typedef
:关键字,用于为现有类型定义别名。 -
void (*dev_uart1_callback)
:声明一个名为dev_uart1_callback
的函数指针。 -
(uint32_t status)
:该函数指针指向的函数接受一个uint32_t
类型的参数status
,且返回值为void
(无返回值)。
-
定义函数指针类型:
将dev_uart1_callback
定义为一种类型,表示“指向参数为uint32_t
、返回值为void
的函数”的指针。 -
简化代码:
允许直接使用dev_uart1_callback
类型声明变量,而无需重复复杂的函数指针语法。
3. 使用场景
示例 1:声明函数指针变量
dev_uart1_callback my_callback; // 声明一个函数指针变量
示例 2:定义匹配的函数
void my_uart1_handler(uint32_t status) {
// 处理 status(例如:检查UART1的中断状态)
}
示例 3:将函数赋值给指针
my_callback = my_uart1_handler; // 指向具体函数
示例 4:作为参数传递
void register_callback(dev_uart1_callback cb) {
// 注册回调函数到UART1驱动
}
register_callback(my_uart1_handler); // 传递回调函数
4. 底层意义
-
回调机制:
在嵌入式系统中,UART(串口)常用于异步通信。通过定义回调函数,可以在特定事件(如数据接收完成、发送就绪或错误发生)时自动调用用户定义的逻辑,例如:-
状态变化(如
status
表示中断标志) -
异步事件处理(如接收缓冲区满)
-
-
硬件抽象:
此类定义常见于驱动层代码,用于将硬件事件(如UART中断)与应用程序逻辑解
5. 参数 uint32_t status
的典型含义
-
可能表示 UART状态寄存器 的值,例如:
-
位 0: 接收数据就绪
-
位 1: 发送缓冲区空
-
位 2: 校验错误
-
其他位: 特定硬件相关的状态
-
以下是一个完整的C语言示例,展示从初始化到触发UART1回调函数的全流程。代码包含硬件驱动模拟、回调注册和事件触发机制举例说明:
#include <stdio.h>
#include <stdint.h>
//--------------------- 类型定义 ---------------------
typedef void (*dev_uart1_callback)(uint32_t status);
//--------------------- 模拟硬件寄存器 -----------------
typedef struct {
volatile uint32_t STATUS; // 状态寄存器
volatile uint32_t DATA; // 数据寄存器
} UART1_TypeDef;
#define UART1 ((UART1_TypeDef*)0x40000000) // 假设UART1硬件地址
//--------------------- 驱动结构体 ---------------------
typedef struct {
dev_uart1_callback callback; // 回调函数指针
uint8_t initialized; // 初始化标志
} UART1_Driver;
//--------------------- 全局驱动实例 -------------------
static UART1_Driver uart1_driver = {0};
//--------------------- 驱动API实现 --------------------
// 初始化UART1并注册回调
void UART1_Init(dev_uart1_callback cb) {
if (!uart1_driver.initialized) {
// 模拟硬件初始化
UART1->STATUS = 0;
UART1->DATA = 0;
uart1_driver.callback = cb; // 注册回调
uart1_driver.initialized = 1;
printf("[UART1] Initialized with callback registered\n");
}
}
// 模拟硬件中断服务函数(由硬件自动调用)
void UART1_IRQHandler(void) {
if (uart1_driver.callback != NULL) {
uint32_t status = UART1->STATUS; // 读取真实状态寄存器
uart1_driver.callback(status); // 触发回调
}
// 硬件自动清除中断标志...
}
//--------------------- 用户回调实现 --------------------
// 用户定义的回调函数(严格匹配签名)
void User_UART1_Callback(uint32_t status) {
printf("[Callback] Status: 0x%08X\n", status);
// 状态位解析(示例)
if (status & 0x01) {
printf(" RX Data Ready!\n");
// 读取数据寄存器:uint8_t data = UART1->DATA;
}
if (status & 0x02) {
printf(" TX Buffer Empty!\n");
}
if (status & 0x04) {
printf(" Parity Error!\n");
}
}
//--------------------- 主程序流程 ---------------------
int main() {
// 1. 初始化驱动并注册回调
UART1_Init(User_UART1_Callback);
// 2. 模拟硬件事件(实际由中断触发)
printf("\nSimulating RX event...\n");
UART1->STATUS = 0x01; // 设置RX就绪状态位
UART1_IRQHandler(); // 模拟中断触发
printf("\nSimulating TX Empty + Error...\n");
UART1->STATUS = 0x06; // 0b0110 (TX空 + 校验错)
UART1_IRQHandler();
return 0;
}
第二种结构体用法:
typedef void (*DetectionCallback)(int event_type);
typedef struct {
// 配置参数
uint16_t fft_size;
uint16_t sample_rate;
// 时间参数
uint32_t detect_start;
uint32_t last_Audio;
DetectionCallback callback;
} AudioDetector;
AudioDetector *detector = NULL;
// 注册回调函数
void detector_register_callback(int event_type)
{
ESP_LOGI(TAG,"---alarm:%d----", event_type);
}
// 初始化检测器并设置回调函数
void init_audio_detector() {
detector = (AudioDetector*)malloc(sizeof(AudioDetector));
if (detector) {
// 初始化配置参数
detector->fft_size = 1024;
detector->sample_rate = 16000;
// 初始化时间参数
detector->detect_start = 0;
detector->last_Audio = 0;
// 注册回调函数(关键步骤)
detector->callback = detector_register_callback; // 直接赋值函数指针
}
}
// 触发回调函数(通常在检测到事件时调用)
void process_audio_event(int event_type) {
if (detector && detector->callback) { // 安全检查
// 通过函数指针调用回调函数
detector->callback(event_type); // 传入事件类型参数
// 这行代码等价于直接调用:
// detector_register_callback(event_type);
}
}
// 使用示例
void some_operation() {
init_audio_detector();
// 当检测到事件时(示例:事件类型 1)
process_audio_event(1); // 将会打印 ---alarm:1----
// 另一个事件示例(事件类型 2)
process_audio_event(2); // 将会打印 ---alarm:2----
}
关键点说明:
-
初始化回调函数:
-
通过直接将函数指针赋值给结构体成员:
detector->callback = detector_register_callback
-
注意函数签名必须完全匹配:返回值和参数类型要一致
-
触发回调的要素:
-
需要先进行空指针检查(detector 和 detector->callback)
-
通过函数指针调用:
detector->callback(event_type)
-
可以传递不同的事件类型参数(示例中的 1 和 2)
-
内存安全:
-
使用 malloc 分配内存后应该确保最终调用 free
-
在复杂的应用场景中建议添加互斥锁保护
-
可以使用 assert(detector != NULL) 进行调试断言