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

1 前言

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

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

2 函数调用方式

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

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

2.1 直接调用

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

int SumFun(int a, int b)
{
    return a + b;
}

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

2.1 间接调用

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

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

函数指针的用法如下:

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 什么场景下使用

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

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

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

/************* 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);

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

// 回调函数:串口数据处理
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,否则会引起程序异常。

Original: https://www.cnblogs.com/const-zpc/p/16364419.html
Author: 大橙子疯
Title: 嵌入式软件架构设计-函数调用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/581952/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 自动化集成:Jenkins管理工具详解

    前言:该系列文章,围绕持续集成:Jenkins+Docker+K8S相关组件,实现自动化管理源码编译、打包、镜像构建、部署等操作; 本篇文章主要描述Jenkins基础用法。 一、J…

    Linux 2023年5月27日
    099
  • Linux——配置主从数据库服务

    主从数据库 Linux中,数据库服务有三种:互为主主,互为主从,一主一从(主从数据库) 服务名 mariadb 协议名 mysql 进程名称 mysqld 端口号 3306 一、改…

    Linux 2023年5月27日
    0101
  • Docker Compose 安装

    方法一:这种方式相对简单,但是由于网络问题,常常安装不上,并且经常会断开,可以尝试使用网络上提供的国内安装的方法将@version 替换为要安装的版本号 sudo curl -L …

    Linux 2023年6月13日
    085
  • 软件工程 结构法方法 第2篇随笔

    建立系统的功能模型图 首先:建立系统环境图,确定系统边界 其中: 数据流为:销售的商品,日销售额等;三个输入流,三个输出流 ​ 数据源为:营业员,经理,收款员 ​ 数据潭为:经理,…

    Linux 2023年6月7日
    092
  • Python eval()函数

    The eval() takes three parameters: expression – this string as parsed and evaluated …

    Linux 2023年6月8日
    088
  • 【设计模式】Java设计模式-适配器模式

    【设计模式】Java设计模式 – 适配器模式 😄 不断学习才是王道🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆原创作品,更多关注我CSDN: 一…

    Linux 2023年6月6日
    0143
  • Linux中安装JDK详细步骤

    一、下载Linux版本的JDK 进入官网下载对应的JDK,下载之前需要先登录 官网地址 -> https://www.oracle.com/ 登录成功后,找到对应的下载位置 …

    Linux 2023年6月7日
    096
  • Base-64字符串无效,The input is not a valid Base-64 string as it contains a non-base 64 character

    base64规则: 字符串只可能包含A-Z,a-z,0-9,+,/,=字符 字符串长度是4的倍数 =只会出现在字符串最后,可能没有或者一个等号或者两个等号 首先,C# 做上传文件的…

    Linux 2023年6月7日
    0106
  • Mybatis源码解读-插件

    注册 xml方式的注册,是在XMLConfigBuilder#pluginElement完成的。 不明觉厉的同学,请参考上一篇文章:Mybatis源码解读-配置加载和Mapper的…

    Linux 2023年6月7日
    087
  • Centos7(非LVM)扩容根目录

    Centos7(非LVM)扩容根目录 系统安装在了VM上,so首先对虚拟机进行扩容: 1、进入VM,选择”虚拟机——>设置” 2、选择硬盘,看到扩展为…

    Linux 2023年6月8日
    0136
  • Wireshark 使用教程

    Wireshark是非常流行的网络封包分析软件,可以截取各种网络数据包,并显示数据包详细信息。常用于开发测试过程各种问题定位。本文主要内容包括: 1、Wireshark软件下载和安…

    Linux 2023年6月8日
    076
  • CentOS——安装Redis 6.0版本

    一、 Centos7 yum install -y http://rpms.famillecollet.com/enterprise/remi-release -7.rpm 如图 …

    Linux 2023年5月28日
    079
  • Java基础之接口篇

    Overload和Override的区别?重载Overload:表示同⼀个类中可以有多个名称相同的⽅法,但这些⽅法的参数列表各不相同,参 数个数或类型不同 重写Override:表…

    Linux 2023年6月7日
    081
  • linux 安装docker

    https://www.cnblogs.com/kingsonfu/p/11576797.html posted @2022-04-29 17:52 银河星光 阅读(10 ) 评论…

    Linux 2023年6月13日
    083
  • vsftpd配置FTP服务器(Centos7.x安装)

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月7日
    075
  • 解析库的使用

    使用 Xpath 使用 pyquery from pyquery import PyQuery as pq doc = pq(html) print(doc) PyQuery对象可…

    Linux 2023年6月7日
    080
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球