64.单片机回调函数实战:从原理到模块化工程落地

在嵌入式开发中,回调函数是实现模块化、解耦代码的核心技术之一。它通过函数指针的方式,将 "业务逻辑" 与 "底层处理" 分离,让代码更易维护、扩展。本文将结合视频中的核心知识点,通过完整代码示例,带你从原理到实战掌握回调函数在单片机开发中的应用。

一、回调函数的核心本质

回调函数的本质是将函数地址作为参数传递给另一个函数,当满足特定条件时,该函数会被自动调用。这种方式可以避免大量的条件判断语句,让代码结构更清晰,实现 "定义匹配格式的函数,满足条件自动执行" 的效果。

二、回调函数的实现步骤

1. 定义回调函数指针类型

首先需要定义一个回调函数的指针类型,用于规定回调函数的参数和返回值格式。

复制代码
// 定义回调函数指针类型:参数为int,返回值为void
typedef void (*CallBackFunc)(int val);

2. 编写通用处理函数

通用处理函数接收回调函数指针作为参数,在合适的时机调用回调函数。

复制代码
// 通用设备处理函数:接收检测数据和回调函数
void DeviceHandle(int data, CallBackFunc cb)
{
    printf("设备检测数值:%d,触发回调\n", data);
    // 调用传入的回调函数
    cb(data);
}

3. 编写自定义回调函数

根据业务需求编写不同的回调函数,这些函数必须与定义的回调函数指针类型格式一致。

复制代码
// 自定义回调1:报警处理
void AlarmCb(int val)
{
    printf("警告:数值%d超出阈值!\n", val);
}

// 自定义回调2:正常处理
void NormalCb(int val)
{
    printf("运行正常,当前数值:%d\n", val);
}

4. 调用通用处理函数

在主函数中,将不同的回调函数地址传递给通用处理函数,实现不同的业务逻辑。

复制代码
int main(void)
{
    // 传递报警回调
    DeviceHandle(85, AlarmCb);
    // 传递正常回调
    DeviceHandle(25, NormalCb);
    return 0;
}

三、单片机开发中的四大常用场景

回调函数在单片机开发中应用广泛,以下是四个典型场景:

1. 外部中断回调:不同按键按下执行对应动作

在单片机外部中断中,不同按键按下时可以触发不同的回调函数,实现按键功能的模块化。

复制代码
// 定义按键回调函数指针类型
typedef void (*KeyCallBack)(void);

// 按键1回调
void Key1Handler(void)
{
    printf("按键1按下,执行功能1\n");
}

// 按键2回调
void Key2Handler(void)
{
    printf("按键2按下,执行功能2\n");
}

// 外部中断处理函数
void EXTI_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // 按键1按下,调用对应回调
        Key1Handler();
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
    if(EXTI_GetITStatus(EXTI_Line1) != RESET)
    {
        // 按键2按下,调用对应回调
        Key2Handler();
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}

3. 多级菜单回调:切换页面时绑定不同显示函数

在 OLED/LCD 等显示设备的多级菜单中,切换页面时可以绑定不同的显示回调函数,实现菜单的模块化。

复制代码
// 定义菜单回调函数指针类型
typedef void (*MenuCallBack)(void);

// 主菜单显示回调
void MainMenuDisplay(void)
{
    printf("显示主菜单\n");
}

// 设置菜单显示回调
void SettingMenuDisplay(void)
{
    printf("显示设置菜单\n");
}

// 菜单切换函数
void MenuSwitch(MenuCallBack cb)
{
    // 清屏
    printf("清屏\n");
    // 调用对应菜单显示回调
    cb();
}

// 主函数中切换菜单
int main(void)
{
    // 显示主菜单
    MenuSwitch(MainMenuDisplay);
    // 切换到设置菜单
    MenuSwitch(SettingMenuDisplay);
    return 0;
}

4. 定时器回调:定时执行不同任务

在定时器中断中,不同的定时任务可以绑定不同的回调函数,实现定时任务的模块化。

复制代码
// 定义定时器回调函数指针类型
typedef void (*TimerCallBack)(void);

// 定时任务1回调
void TimerTask1(void)
{
    printf("定时任务1执行\n");
}

// 定时任务2回调
void TimerTask2(void)
{
    printf("定时任务2执行\n");
}

// 定时器中断处理函数
void TIM_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
        static uint32_t count = 0;
        count++;
        if(count % 1000 == 0)
        {
            // 1秒执行一次任务1
            TimerTask1();
        }
        if(count % 2000 == 0)
        {
            // 2秒执行一次任务2
            TimerTask2();
        }
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

四、回调函数的核心优势

  1. 功能解耦:底层处理代码与业务逻辑分离,新增功能时无需修改底层代码,只需新增回调函数即可。
  2. 工程维护轻松:代码结构清晰,每个功能对应一个回调函数,便于调试和维护。
  3. 灵活性高:可以根据不同的场景动态切换回调函数,实现不同的业务逻辑。

五、注意事项

回调函数的参数和返回值必须与定义的函数指针类型完全一致,否则会导致编译错误。

六、总结

回调函数是单片机开发中实现模块化的重要技术,通过函数指针的方式,将业务逻辑与底层处理分离,让代码更易维护、扩展。本文通过完整的代码示例,介绍了回调函数的实现步骤和在单片机开发中的四大常用场景,希望能帮助你掌握这一核心技术。

关注我,持续更新技术文章

相关推荐
晓蛋3 天前
c语言指的是什么意思
c语言·编译器·编程开发·集成开发环境·程序实例
不悔哥3 天前
开源OV-Watch:怎么做一个智能手表
单片机·开源·嵌入式
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
傲世仙尊3 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
XiHongShi20163 天前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
牵猫散步的鱼儿3 天前
重载、重写(覆盖)、重定义区别
c语言
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
phltxy3 天前
C 语言指针:从内存地址到灵活的数据访问
c语言
优信电子3 天前
AIT1001 非接触式红外测温模块 STM32 驱动开发实战:从引脚到代码全解析
stm32·嵌入式·温度传感器·红外测温·非接触式测温·ait1001
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++