0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

嵌入式软件架构设计之消息交互

汽车电子技术 来源:大橙子疯嵌入式 作者: 大橙子疯 2023-02-15 15:44 次阅读

前言

在熟悉任务调度、程序分层和模块化编程关于软件架构、分层和模块设计后,除了函数调用设计中出现的情况外,还会遇到同层模块之前如何进行消息交互,通常是应用层之间。

比如一个设备通过架构设计包含人机交互应用层模块(一般会调用按键和显示屏等功能驱动模块)和通信应用层模块(一般调用串口、CAN网络ESP8266等功能驱动模块),两个同层之间的模块如果需要互传数据,一般都是调用各自头文件提供的接口(模块对外提供的接口尽量不要使用全局变量,防止其他模块擅自修改),这样就造成了耦合

设计思路

上述情况,也可以采用回调函数的实现方式进行模块解耦,但是需要引入新的内容,即公共模块Commoon层(包含第三方功能库)。图片

公共模块主要有各模块都需要使用的类型定义、结构体定义、通用函数或常用宏定义等(通常属于基础类的功能,不会受功能需求和不同平台的影响)。

基于公共模块,为了解决各模块之前的数据交互,可以通过公共模块实现基础类的功能达到各应用层模块解耦的目的。

参考消息队列的方式,可以实现一个生产者/消费者的功能模块(这种可以称作观察者模式,即存在观察者和被观察者),即某一模块更新数据后,其他模块可以第一时间得到通知更新(采用回调函数的方式实现)

看图:

图片

Callback是一个指针数组变量,每个数组成员都是函数指针类型的变量,通过函数Notify_Attach拿到了应用层代码函数OnSaveParam(...)OnUpdateParam(...)的函数地址,之后人机交互模块调用了Notify_EventNotify,从而调用Callback ,调用方式和直接调用 OnFunction(...) 存在些许差异,因为是数组,所有需要[]取函数地址,为了保证系统运行安全,调用前要确保 Callback[i] 不为NULL,否则会引起程序异常。

从上述看,也许有人感觉这样处理反而复杂了,直接调用不香吗?(上述人机交互模块属于被观察者,参数和其他模块属于观察者)

有以下几个好处:

  1. 避免各模块相互调用,可完成解耦
  2. 即使 观察者 模块其中一个被移除,也不用修改 被观察者 或者 其他观察者 代码,保证系统稳定
  3. 新增一个 观察者 模块,也不需要修改 被观察者 代码,保证系统稳定

当然这种方式也有缺点:

  1. 如果回调函数过多,或者某一个 观察者 的回调函数执行时间很长,肯定会影响到其他观察者 模块的通知时间,甚至影响 被观察者 模块的正常运行
  2. 如果 观察者 和 被观察者 之间有循环依赖,就会导致他们循环调用,导致系统死机

避免方式:

  1. 回调函数中一定要保证执行的时间短,不能有执行时间长的功能,甚至延时(一般回调中处理数据更新等执行时间短的即可,数据更新后的需要花时间处理的可以在主循环执行)
  2. 观察者回调函数中尽量避免执行其他观察者的回调函数,防止循环调用

示例代码

下面简单实现人机交互模块在某种情况下需要保存参数,具体如何保存参数由参数模块实现,人机交互模块通过事件通知模块告知参数模块需要保存数据。

初步来看,可能中间多了一个,嫌实现麻烦,不如直接调用;但是从后期功能扩展和解耦来看,这是很有必要的。

事件通知模块

头文件定义

#ifndef _NOTIFY_H_
#define _NOTIFY_H_


#include 


/**
  * @brief 应用模块ID枚举定义
  *
  */
typedef enum
{
    NOTIFY_ID_HMI = 0,   // 人机交互模块
    NOTIFY_ID_SYS_PARAM, // 参数管理模块

    NOTIFY_ID_TOTAL
} NotifyId_e;

/**
  * @brief 事件类型枚举定义
  *
  */
typedef enum
{
    NOTIFY_EVENT_PARAM_UPDATE,     // 参数更新事件, 对应结构体 PrramUpdateInfo_t

    NOTIFY_EVENT_TOTAL
} NotifyEvent_e;


typedef struct
{
    uint16_t addr;
    uint32_t param;
}PrramUpdateInfo_t;


typedef int (*EventNotifyCB)(NotifyId_e id, NotifyEvent_e eEvent, const void *pData, uint32_t length);


extern void Notify_Init(void);

extern int Notify_Attach(NotifyId_e id, NotifyEvent_e eEvent, EventNotifyCB pfnCallback);
extern int Notify_Detach(NotifyId_e id, NotifyEvent_e eEvent);
extern int Notify_EventNotify(NotifyId_e id, NotifyEvent_e eEvent, const void *pData, uint32_t length);

#endif /* _NOTIFY_H_ */

源文件实现

#include "notify.h"
#include 

static EventNotifyCB sg_pfnCallback[NOTIFY_ID_TOTAL][NOTIFY_EVENT_TOTAL];

/**
  * @brief      事件初始化
  *
  */
void Notify_Init(void)
{
    memset(sg_pfnCallback, 0, sizeof(sg_pfnCallback));
}

/**
  * @brief      添加事件监听通知
  *
  * @param[in]  id          应用模块ID
  * @param[in]  eEvent      事件
  * @param[in]  pfnCallback 回调函数
  * @return     0,成功; -1,失败
  */
int Notify_Attach(NotifyId_e id, NotifyEvent_e eEvent, EventNotifyCB pfnCallback)
{
    if (id >= 0 && id < NOTIFY_ID_TOTAL && eEvent < NOTIFY_EVENT_TOTAL)
    {
        sg_pfnCallback[id][eEvent] = pfnCallback;
        return 0;
    }

    return -1;
}

/**
  * @brief      删除事件监听通知
  *
  * @param[in]  id          应用模块ID
  * @param[in]  eEvent      事件
  * @return     0,成功; -1,失败
  */
int Notify_Detach(NotifyId_e id, NotifyEvent_e eEvent)
{
    if (id >= 0 && id < NOTIFY_ID_TOTAL && eEvent < NOTIFY_EVENT_TOTAL)
    {
        sg_pfnCallback[id][eEvent] = 0;
        return 0;
    }

    return -1;
}

/**
  * @brief      事件通知
  *
  * @param[in]  id          应用模块ID
  * @param[in]  eEvent      事件类型
  * @param[in]  pData       消息内容
  * @param[in]  length      消息长度
  * @return     0,成功; -1,失败
  */
int Notify_EventNotify(NotifyId_e id, NotifyEvent_e eEvent, const void *pData, uint32_t length)
{
    int i;

    if (eEvent < NOTIFY_EVENT_TOTAL)
    {
        for (i = 0; i < NOTIFY_ID_TOTAL; i++)
        {
            if (sg_pfnCallback[i][eEvent] != 0)
            {
                sg_pfnCallback[i][eEvent](id, eEvent, pData, length);
            }
        }

        return 0;
    }

    return -1;
}

参数应用层模块

示例通信,作为观察者监听参数保存的消息。

#include "notify.h"

static int Param_OnNotifyProc(NotifyId_e id, NotifyEvent_e eEvent, const void *pData, uint32_t length);

void Param_Init(void)
{
    Notify_Attach(NOTIFY_ID_SYS_PARAM, NOTIFY_EVENT_PARAM_UPDATE, Param_OnNotifyProc);
}

// 事件回调处理
int Param_OnNotifyProc(NotifyId_e id, NotifyEvent_e eEvent, const void *pData, uint32_t length)
{
    switch (eEvent)
    {
    case NOTIFY_EVENT_PARAM_UPDATE:
        {
            PrramUpdateInfo_t *pInfo = (PrramUpdateInfo_t *)pData;
            SaveParam(pInfo->addr, pInfo->param);// 保存参数
        }
        break;
    default:
        break;
    }

    return 0;
}

人机交互应用层模块

示例通信,作为被观察者通知/发送参数保存的消息。

#include "notify.h"

void Hmi_Init(void)
{

}

// 需要保存参数
int Hmi_SaveProc(void)
{
    ParamUpdateInfo_t info;

    info.addr = 5;
    info.param = 20;

    Notify_EventNotify(NOTIFY_ID_HMI, NOTIFY_EVENT_HMI_UPDATE, &info, sizeof(ParamUpdateInfo_t));
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 软件架构
    +关注

    关注

    0

    文章

    60

    浏览量

    10224
  • 任务调度
    +关注

    关注

    0

    文章

    27

    浏览量

    9822
  • 模块化编程
    +关注

    关注

    4

    文章

    17

    浏览量

    7688
收藏 人收藏

    评论

    相关推荐

    诚聘嵌入式软件架构

    ,本科以上学历;2、三年以上嵌入式软件系统架构设计经验,同时有通信行业软件开发经验;3、精通C/C++语言,精通数据结构;熟悉QT\linux\android
    发表于 03-01 10:20

    嵌入式系统的软件架构设计!

    软件架构设计!2. 嵌入式环境下软件设计的特点要谈嵌入式软件
    发表于 08-10 07:46

    嵌入式软件架构设计常见的误解

    1.常见的误解1.1小型系统不需要架构设架构应当满足当前需求并适当的考虑重用和变更1.2 敏捷开发不需要框架 极限编程, 敏捷开发的出现使一些人误以为软件开发无需再做架构了。 这是
    发表于 10-27 09:22

    嵌入式Linux系统知识架构

    嵌入式Linux系统知识架构及层次嵌入式Linux系统构成及启动略析嵌入式Linux三剑客uboot技术
    发表于 10-27 07:22

    嵌入式软件架构设计的相关资料分享

    嵌入式软件架构的设计大多数嵌入式程序员学习编程,都是从开发板的附带例程开始。之后工作也会继续参考那些例程,很多编程习惯、方式也会受之影响。其实开发板式的编程方式与工作中实际需求的并不完
    发表于 10-28 06:15

    为何要进行嵌入式软件架构设计?如何设计?

    为何要进行嵌入式软件架构设计?如何进行嵌入式软件架构设计?
    发表于 11-01 06:31

    嵌入式软件架构设计的目的及思路

    【1】架构设计的目的1.应用的代码逻辑清晰,且避免重复造轮子。2.方便软件的移植。3.最大限度地复用。4.高内聚低耦合。 【2】嵌入式架构思路1.功能模块化设计获得需求------->
    发表于 11-08 06:41

    嵌入式UI架构设计相关资料下载

    嵌入式UI架构设计漫谈
    发表于 11-08 07:49

    嵌入式系统中的架构设计的理解

    【阅读这篇文章,你能了解到什么】1. 从事嵌入式开发12年的我,对架构设计的理解;2. 对嵌入式系统中的架构设计要刻意训练;3. 嵌入式系统
    发表于 11-08 08:23

    决定嵌入式系统软件架构的因素和架构的影响

    嵌入式系统软件架构设计目录1.前言42.决定架构的因素和架构的影响42.1.常见的误解52.1.1.小型的系统不需要
    发表于 11-08 06:54

    嵌入式软件架构设计的资料大合集

    一、感慨近公司新招了一个做嵌入式软件开发开发的童鞋,该童鞋是从上海的某一个上市公司出来的,因为我们这边人手不够,因此把他安排了去负责一个新产品的研发,前期让他负责加速度计、NB-IOT、舵机、外置
    发表于 11-09 07:50

    嵌入式软件架构设计资料分享

    作为程序员,我觉得如果要走的更远必须要成为工程师,毕竟年龄和资历都摆在那里了。所以就让我这个老程序员浅谈一下嵌入式软件架构设计。我参考的也是一篇博文。原图如下![在这里插入图片描述](?x-oss-process=image/w
    发表于 12-24 07:09

    嵌入式软件架构设

    嵌入式软件架构的设计,帮助我们建立合理,有效的软件架构
    发表于 11-09 17:34 19次下载

    嵌入式UI架构设计漫谈

    嵌入式UI架构设计漫谈
    发表于 11-03 17:36 15次下载
    <b class='flag-5'>嵌入式</b>UI<b class='flag-5'>架构设</b>计漫谈

    嵌入式系统软件架构设

    嵌入式系统软件架构设计目录1.前言42.决定架构的因素和架构的影响42.1.常见的误解52.1.1.小型的系统不需要
    发表于 11-03 18:21 29次下载
    <b class='flag-5'>嵌入式</b>系统<b class='flag-5'>软件</b><b class='flag-5'>架构设</b>计