当前位置:首页 > > 大橙子疯嵌入式
[导读]在熟悉任务调度、程序分层和模块化编程关于软件架构、分层和模块设计后,除了函数调用设计中出现的情况外,还会遇到同层模块之前如何进行消息交互,通常是应用层之间。

前言

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

比如一个设备通过架构设计包含人机交互应用层模块(一般会调用按键和显示屏等功能驱动模块)和通信应用层模块(一般调用串口、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));
}




本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

本届年会将在上海(11月13-14日)、北京(11月19-20日)和深圳(11月27-28日)举行,面向嵌入式设计工程师推出25门技术课程

关键字: 嵌入式 MCU 模拟

上海2025年9月5日 /美通社/ -- 由纽伦堡会展(上海)有限公司举办的上海国际嵌入式会议将于 2025 年 10 月 16-17 日在上海世博展览馆举办。 此次会议将由三个版块组成:嵌入式技术会议、汽...

关键字: 嵌入式 CE CHINA EMBEDDED

从外部看,电子系统仿佛一个统一的学科或设备,各组成部分协同工作,浑然一体。然而揭开表象,其内在却是另一番景象:一个碎片化、多层次的世界——其中每一层都独立且复杂,衍生出各自特有的工具、专家、工作流程,甚至哲学体系。

关键字: 嵌入式 电子系统 半导体

8位单片机在嵌入式设计领域已经成为半个多世纪以来的主流选择。尽管嵌入式系统市场日益复杂,8位单片机依然不断发展,积极应对新的挑战和系统需求。如今,Microchip推出的8位PIC®和AVR®单片机系列,配备了先进的独立...

关键字: 单片机 嵌入式 CPU

深圳2025年8月28日 /美通社/ -- 8月26日,2025 ELEXCON深圳国际电子展盛大启幕。本届大会以"All for AI"为主题,深圳市德...

关键字: AI 工业级 SSD 嵌入式

深圳2025年8月26日 /美通社/ -- 8月26日,由博闻创意会展主办的 第22届深圳国际电子展暨嵌入式展(elexcon2025)在深圳(福田)会展中心隆重开幕。 作为中国电子与嵌入式技术领域的专业大展,本届展会...

关键字: 嵌入式 电子 高通 AI

模块化设计作为一种将系统拆分为独立、可复用组件的方法,能够在低代码平台中实现功能的灵活组合,并最大限度地提升系统性能。本文将探讨如何通过模块化设计,使得低代码平台既能快速适应变化,又能保持高效稳定的运行。

关键字: 模块化设计 嵌入式

CPU亲和度通过限制进程或线程可以运行的CPU核心集合,使得它们只能在指定的CPU核心上执行。这可以减少CPU缓存的失效次数,提高缓存命中率,从而提升系统性能。

关键字: Linux 嵌入式

加密算法分对称加密和非对称算法,其中对称加密算法的加密与解密密钥相同,非对称加密算法的加密密钥与解密密钥不同,此外,还有一类不需要密钥的散列算法。

关键字: 算法 嵌入式

从本质上讲,算法是一种有条不紊、分步骤解决问题或完成任务的方法。无论是简单的数字相加公式,还是复杂的机器学习协议,算法都是软件应用的基础,确保任务能够高效有效地执行。

关键字: 算法 嵌入式
关闭