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. 灵活性高:可以根据不同的场景动态切换回调函数,实现不同的业务逻辑。

五、注意事项

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

六、总结

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

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

相关推荐
黑客-秋凌2 小时前
使用Python+selenium实现第一个自动化测试脚本
开发语言·自动化测试·软件测试·python·selenium·测试工具
geovindu2 小时前
CSharp: Iterative Algorithms
开发语言·后端·算法·c#·.net·迭代算法
XTIOT6662 小时前
零部件 DPM 码扫码枪生产厂家:工业 DPM 设备多工位组网与 MES/ERP 数据集成方案
网络·单片机·嵌入式硬件·物联网·计算机视觉
一只月月鸟呀3 小时前
移动端tap与click的区别 && 点透事件
开发语言·前端·javascript
雨落在了我的手上3 小时前
Java数据结构(六):链表的介绍
java·开发语言·数据结构
振南的单片机世界3 小时前
PSC分频、ARR定周期、OPM停一次:基本定时器三要素
stm32·单片机·嵌入式硬件
影寂ldy3 小时前
C# HttpClient 分片下载、断点续传、暂停取消(完整项目知识点)
开发语言·c#
独隅3 小时前
DevEco Code Plan+Build 双 Agent 协同开发效率实测
java·开发语言
JERRY. LIU3 小时前
VS CODE进行NRF CONNECT项目开发出现变量无法链接跳转问题及解决方法
数据库·嵌入式硬件·物联网·iot·嵌入式实时数据库
KaMeidebaby4 小时前
卡梅德生物技术快报|抗体筛选:抗体筛选中的噬菌体展示四参数调优——从流程设计到数据验证
开发语言·前端·javascript