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

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----
}

关键点说明:

  1. 初始化回调函数:

  • 通过直接将函数指针赋值给结构体成员:detector->callback = detector_register_callback

  • 注意函数签名必须完全匹配:返回值和参数类型要一致

  1. 触发回调的要素:

  • 需要先进行空指针检查(detector 和 detector->callback)

  • 通过函数指针调用:detector->callback(event_type)

  • 可以传递不同的事件类型参数(示例中的 1 和 2)

  1. 内存安全:

  • 使用 malloc 分配内存后应该确保最终调用 free

  • 在复杂的应用场景中建议添加互斥锁保护

  • 可以使用 assert(detector != NULL) 进行调试断言

相关文章:

  • 【存储管理—动态不等长存储资源分配算法】
  • 实验一:Linux静态路由
  • JavaScript性能优化:从青铜到王者的进阶之路
  • pip安装包时出现网络问题的坑
  • MCPHub:一站式MCP服务器聚合平台
  • 摄像头模组RGB/IR模组
  • C++初阶-string类的简单应用
  • 这些单词有什么内在联系吗?
  • 【AI】关于模型部署方案MindIE Server和vllm
  • uni-app 引入vconsole web端正常,安卓端报错 Cannot read property ‘sendBeacon‘ of undefined
  • 【25软考网工】第五章(8)路由协议RIP、OSPF
  • 深入理解 Node.js 模块化(CommonJS):原理、用法与避坑指南
  • 一文走进GpuGeek | conda常用命令
  • STM32开发GPIO
  • 【JMeter技巧】GET请求如何传递Body参数?版本兼容性详解场景需求
  • ✨WordToCard使用分享✨
  • 编写程序,统计两会政府工作报告热词频率,并生成词云
  • 前端取经路——工程化渡劫:八戒的构建之道
  • 深度学习中的autograd与jacobian
  • 分布式id的两大门派!时钟回拨问题的解决方案!
  • 宣布停火后,印控克什米尔地区再次传出爆炸声
  • 成就彼此,照亮世界:“中欧建交50周年论坛”在沪成功举行
  • 肖峰读《从塞北到西域》︱拉铁摩尔的骆驼
  • 伤员回归新援融入,海港逆转海牛重回争冠集团
  • 虚假认定实质性重组、高估不良债权价值,原中国华融资产重庆分公司被罚180万元
  • 追光|铁皮房、土操场,这有一座“筑梦”摔跤馆