• 嵌入式软件架构设计-函数调用


    1 前言

    函数调用很好理解,即使刚学没多久的朋友也知道函数调用是怎么实现的,即调用一个已经封装好的函数,实现某个特定的功能。

    把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口,然后在其他地方调用即可


    2 函数调用方式

    函数调用难道还能怎么调用?不就封装好直接调用吗???

    函数调用方式分为两种:直接调用间接调用

    2.1 直接调用

    直接调用就是我们平常使用的方式,下面的方式就属于直接调用了。

    copy
    int SumFun(int a, int b)
    {
        return a + b;
    }
    
    int main()
    {
        // 直接调用定义好的函数
        int sum = SumFun(5, 6);
        printf("sum=%d", sum);
        return 0;
    }

    2.1 间接调用

    间接调用在初学时很难使用到,这是通过函数指针的方式实现的。

    函数指针本质是一个指针变量,是一个指向函数的指针(函数本身也是有地址的,指向的是函数入口);
    指针函数本质是一个函数,其返回值为指针。

    函数指针的用法如下:

    copy
    typedef int (*FunctionCB)(int, int);
    
    int SumFun(int a, int b)
    {
        return a + b;
    }
    
    int main()
    {
        // 将定义好的函数赋值给函数指针
        FunctionCB pfnSum = SumFun;
    
        // 通过函数指针间接调用
        int sum = pfnSum(5, 6);
        printf("sum=%d", sum);
        return 0;
    }

     

    3 什么场景下使用

    函数指针在软件架构分层设计中十分重要,因为分层设计中有一个设计原则,那就是下层函数不能直接调用上层函数,那么可以通过函数指针的方式实现;一般称上层通过函数指针赋值给下层的函数为回调函数

    什么情况会存在需要下层程序需要调用上层程序的呢?
    比如串口数据接收,虽然可以通过查询的方式接收,但是远不及通过串口中断的方式接收及时,当接收完成时,需要立即通知上层读取数据进行处理,而不是等待上层程序查询读取。

    如何实现呢?
    比如硬件抽象层/驱动层中的串口模块实现函数

    copy
    /************* UART.c 文件 ****************/
    static UartRecvCB sg_pfnUartRecv;
    
    // 设置数据帧接收处理回调函数
    void UART_SetRecvCallback(UartRecvCB pfnUartRecv)
    {
        sg_pfnUartRecv = pfnUartRecv;
    }
    
    void UART_Task(void)
    {
        if (RecvEnd)
        {
            // 数据一帧接收完成立即调用
            if (sg_pfnUartRecv != NULL)
            {
                sg_pfnUartRecv(UartRecvBuf, UartRecvLength);
            }
        }
    }
    
    /************* UART.h 文件 ****************/
    typedef void (*UartRecvCB)(const char *, int);
    
    extern void UART_SetRecvCallback(UartRecvCB pfnUartRecv);
    extern void UART_Task(void);
    折叠

    应用层代码中实现回调函数,并调用下层函数。

    copy
    // 回调函数:串口数据处理
    void OnUartRecvProcess(const char *pBuf, int length)
    {
        // 处理串口数据
        printf("Recv: %s", pBuf);
    }
    
    int main()
    {
        UART_SetRecvCallback(OnUartRecvProcess);
    
        while(1)
        {
            if (TimeFlag)
            {
                UART_Task();
            }
        }
    }

    上述示例中通过函数指针的方式间接调用了应用层的函数,而且并不违背分层设计原则。
    如果看代码不能立即理解的话,可以尝试通过下图理解:

     Callback 是一个函数指针类型的变量,通过函数 FML_TIME_Attach 拿到了应用层代码函数 OnFunction(...) 的函数地址,之后在定时器中断函数中根据触发条件调用 Callback 即可,调用方式和直接调用 OnFunction(...) 没有太大差异,只不过名字不一样(可以理解成取了一个别名),为了保证系统运行安全,调用前要确保  Callback 不为 NULL,否则会引起程序异常。

     

  • 相关阅读:
    【chrome基础】Chrome、Chromium、libcef、electron版本关系大揭秘!
    在将对象 => JSON格式时,无法序列化部分属性
    金仓数据库KingbaseES客户端编程接口指南-Nodejs(3. Nodejs驱动使用说明)
    在微信小程序上做一个「博客园年度总结」:解决前端获取接口数据太慢的一种思路
    一种清洁机器人设计及仿真
    前端学习笔记 第1集:WebStorm 安装、vue3.0 安装、npm安装
    每天分享一点运维小知识---pm2工具
    Observability:如何使用 Elastic Agents 把定制的日志摄入到 Elasticsearch 中
    消息队列的一些场景及源码分析,RocketMQ使用相关问题及性能优化
    【每日一题Day345】LC2562找出数组的串联值 | 模拟
  • 原文地址:https://www.cnblogs.com/const-zpc/p/16364419.html