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

前言在熟悉任务调度、程序分层和模块化编程关于软件架构、分层和模块设计后,除了函数调用设计中出现的情况外,还会遇到同层模块之前如何进行消息交互,通常是应用层之间。
比如一个设备通过架构设计包含人机交互应用层模块(一般会调用按键和显示屏等功能驱动模块)和通信应用层模块(一般调用串口、can和网络esp8266等功能驱动模块),两个同层之间的模块如果需要互传数据,一般都是调用各自头文件提供的接口(模块对外提供的接口尽量不要使用全局变量,防止其他模块擅自修改),这样就造成了耦合。
设计思路上述情况,也可以采用回调函数的实现方式进行模块解耦,但是需要引入新的内容,即公共模块commoon层(包含第三方功能库)。
公共模块主要有各模块都需要使用的类型定义、结构体定义、通用函数或常用宏定义等(通常属于基础类的功能,不会受功能需求和不同平台的影响)。
基于公共模块,为了解决各模块之前的数据交互,可以通过公共模块实现基础类的功能达到各应用层模块解耦的目的。
参考消息队列的方式,可以实现一个生产者/消费者的功能模块(这种可以称作观察者模式,即存在观察者和被观察者),即某一模块更新数据后,其他模块可以第一时间得到通知更新(采用回调函数的方式实现)
看图:
callback是一个指针数组变量,每个数组成员都是函数指针类型的变量,通过函数notify_attach拿到了应用层代码函数onsaveparam(...) 和onupdateparam(...)的函数地址,之后人机交互模块调用了notify_eventnotify,从而调用callback ,调用方式和直接调用 onfunction(...) 存在些许差异,因为是数组,所有需要[]取函数地址,为了保证系统运行安全,调用前要确保 callback[i] 不为null,否则会引起程序异常。
从上述看,也许有人感觉这样处理反而复杂了,直接调用不香吗?(上述人机交互模块属于被观察者,参数和其他模块属于观察者)
有以下几个好处:
避免各模块相互调用,可完成解耦即使 观察者 模块其中一个被移除,也不用修改 被观察者 或者 其他观察者 代码,保证系统稳定新增一个 观察者 模块,也不需要修改 被观察者 代码,保证系统稳定当然这种方式也有缺点:
如果回调函数过多,或者某一个 观察者 的回调函数执行时间很长,肯定会影响到其他观察者 模块的通知时间,甚至影响 被观察者 模块的正常运行如果 观察者 和 被观察者 之间有循环依赖,就会导致他们循环调用,导致系统死机避免方式:
回调函数中一定要保证执行的时间短,不能有执行时间长的功能,甚至延时(一般回调中处理数据更新等执行时间短的即可,数据更新后的需要花时间处理的可以在主循环执行)观察者回调函数中尽量避免执行其他观察者的回调函数,防止循环调用示例代码下面简单实现人机交互模块在某种情况下需要保存参数,具体如何保存参数由参数模块实现,人机交互模块通过事件通知模块告知参数模块需要保存数据。
初步来看,可能中间多了一个,嫌实现麻烦,不如直接调用;但是从后期功能扩展和解耦来看,这是很有必要的。
事件通知模块头文件定义
#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 = 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 addr, pinfo->param);// 保存参数 } break; default: break; } return 0;}人机交互应用层模块示例通信,作为被观察者通知/发送参数保存的消息。
#include notify.hvoid 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));}

可控硅在红外遥控开关中的应用及工作原理说明
MSP430单片机通用系统复位与隔离电路设计
电动车电池寿命短的主要原因
把算法用RTL实现该怎么做?
华为宣布耀星计划全面升级,10亿美元激励和扶持全球开发者创新
嵌入式软件架构设计之消息交互
基于AVLCRUISE软件仿真轮胎动态半径的计算
告别价格战时代 家电零售江湖争战愈发激烈
Linux下模拟网络时延和丢包神器介绍
mate40鸿蒙系统怎么升级
基于Cloud RAN和移动边缘计算的良好平衡支持无线宽带服务
分布式交流380V并网光伏发电监测系统解决方案
优步在德国被迫禁止服务 并称优步没有牌照
Mentor出售内忧,西门子现金收购
什么是全桥整流电路,全桥整流电路的工作原理是什么,全桥整流电路的计算公式
ADMV7810CHIPS功率放大器
鸿蒙系统不再“孤军奋战” 华为、美的强强联手
小鹏P7、小鹏G3新版车型开售 均采用磷酸铁锂电池
Orbit通过AR技术,将教学内容实现3D可视化
人工智能的以后会如何