嵌入式软件架构设计中,如何实现高效的消息交互机制?

2026-04-11 09:421阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1918个文字,预计阅读时间需要8分钟。

嵌入式软件架构设计中,如何实现高效的消息交互机制?

在单片机软件开发过程中,如何更好地实现各模块的数据交互,降低耦合度,主要涉及以下几个方面:

1. 前因:明确软件架构、分层和模块化设计的重要性。

2.任务调度:采用优先级和定时器机制,合理分配任务,提高系统响应速度。

3.程序分层:将程序划分为独立的层次,如硬件抽象层、驱动层、业务逻辑层等。

4.模块化编程:将功能划分为模块,实现代码重用,降低维护难度。

5.软件架构:采用分层架构,如MVC(模型-视图-控制器),确保系统可扩展性。

6.模块设计:在设计模块时,注重接口定义,确保模块间的高内聚和低耦合。

7.函数调用:合理设计函数调用,避免冗余调用,降低系统复杂度。

通过以上方法,可以有效提高单片机软件开发的效率和质量。

讲述在单片机软件开发过程中如何更好地实现各个模块的数据交互,降低耦合 1、前言

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

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


2、解决思路

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

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

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

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

看图:

嵌入式软件架构设计中,如何实现高效的消息交互机制?

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

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

有以下几个好处:

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

当然这种方式也有缺点:

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

避免方式:

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

3、示例代码

事件通知模块头文件

#ifndef _NOTIFY_H_ #define _NOTIFY_H_ #include <stdint.h> /** * @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 <string.h> 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)); }

本文共计1918个文字,预计阅读时间需要8分钟。

嵌入式软件架构设计中,如何实现高效的消息交互机制?

在单片机软件开发过程中,如何更好地实现各模块的数据交互,降低耦合度,主要涉及以下几个方面:

1. 前因:明确软件架构、分层和模块化设计的重要性。

2.任务调度:采用优先级和定时器机制,合理分配任务,提高系统响应速度。

3.程序分层:将程序划分为独立的层次,如硬件抽象层、驱动层、业务逻辑层等。

4.模块化编程:将功能划分为模块,实现代码重用,降低维护难度。

5.软件架构:采用分层架构,如MVC(模型-视图-控制器),确保系统可扩展性。

6.模块设计:在设计模块时,注重接口定义,确保模块间的高内聚和低耦合。

7.函数调用:合理设计函数调用,避免冗余调用,降低系统复杂度。

通过以上方法,可以有效提高单片机软件开发的效率和质量。

讲述在单片机软件开发过程中如何更好地实现各个模块的数据交互,降低耦合 1、前言

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

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


2、解决思路

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

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

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

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

看图:

嵌入式软件架构设计中,如何实现高效的消息交互机制?

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

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

有以下几个好处:

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

当然这种方式也有缺点:

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

避免方式:

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

3、示例代码

事件通知模块头文件

#ifndef _NOTIFY_H_ #define _NOTIFY_H_ #include <stdint.h> /** * @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 <string.h> 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)); }