嵌入式软件分层架构设计 - lucky

news/2025/10/22 21:45:43/文章来源:https://www.cnblogs.com/luckyTS/p/19158767

一、什么是嵌入式分层架构

“嵌入式分层架构”并不是一个全新的架构类型,而是指在嵌入式系统开发中应用和实现分层架构的设计模式,它继承了通用分层架构的所有核心思想和优点,但根据嵌入式系统的独特约束和需求进行了调整和优化,把整个软件系统,按照职责和依赖关系,垂直地划分成若干个层次。每个层次专注于解决一部分特定的问题,并且只与它相邻的层次进行交互。

1、核心概念

嵌入式分层架构是一种软件设计方法,它将嵌入式固件(Firmware)划分为多个层次分明的模块。每一层都构建于下层之上,并为上层提供服务,同时隐藏其具体的实现细节(如硬件操作)。
其核心思想与通用分层架构一致:分离关注点、降低耦合、提高可维护性和可移植性。

2、典型的嵌入式分层架构

层级 职责 特点 补充
硬件驱动层 (Driver Layer / HAL - Hardware Abstraction Layer) 这是最底层,直接和硬件打交道。负责操作MCU的寄存器,初始化外设(GPIO, UART, SPI, I2C, ADC等),提供最基本的硬件读写函数。比如HAL_SPI_Transmit(),GPIO_SetPinLevel()。 与具体硬件型号紧密相关。很多芯片厂商会提供官方的库,就属于这一层。它的目标是屏蔽硬件细节,向上层提供稳定、统一的硬件操作接口。 这一层应该尽量保持“纯粹”,只做硬件操作,不掺杂任何业务逻辑。
板级支持包层 (BSP - Board Support Package) 这一层是可选的,但强烈推荐。它建立在驱动层之上,关联了具体的电路板设计。它知道哪个GPIO引脚连接到了LED灯,哪个SPI接口连接到了特定的传感器。它提供的是面向“板子上某个具体功能单元”的接口。比如BSP_LED_On(LED_ID_STATUS),BSP_ReadTemperature()。 它调用驱动层的函数,但封装了板级的特定信息。换块板子,可能驱动层不用大改,但BSP层几乎肯定要改。 让应用层代码彻底与具体引脚、具体外设通道解耦。应用层只需要关心“状态灯”亮灭,而不需要知道这个灯接在哪个引脚。
中间件层 (Middleware Layer) 提供一些通用的、与具体业务关联不大但又必不可少的软件服务。比如:实时操作系统 (RTOS),文件系统 (FileSystem),通信协议栈 (TCP/IP, Modbus, CANopen),图形库 (GUI),算法库 (PID控制器, 滤波算法) 等。 通常是可复用的软件组件,不直接依赖硬件(或者其硬件依赖已被下层封装)。它们为应用层提供更高级的服务支撑。 抽象程度很高,能在各个项目中安插通用的:LVGL数学库\快速傅里叶变换库
应用层 (Application Layer) 这是软件系统最顶层,实现产品的最终业务逻辑和功能。它决定了设备“做什么”和“怎么做”。比如,一个温控器的应用层代码会包含读取温度(调用BSP层接口)、执行PID算法(可能调用中间件层的算法库)、控制加热/制冷设备(调用BSP层接口)、响应用户按键(调用BSP层接口)、更新显示(可能调用中间件图形库或BSP层接口)等逻辑。 高度定制化,与具体产品需求紧密相关。它应该只调用下层提供的接口,绝对禁止直接操作硬件寄存器或调用最底层的HAL函数。那是“越级指挥”,架构师看了想打人。

3、总体架构图

image

4、分层结构说明

1、黄金法则:单向依赖

分层架构有一个极其重要的原则:依赖关系是单向的。通常是上层依赖下层。应用层可以调用中间件层、BSP层或驱动层提供的接口。
中间件层可以调用BSP层或驱动层。
BSP层调用驱动层。
驱动层直接操作硬件。
反过来?不行!下层不应该知道上层的存在,更不能直接调用上层的函数。(回调函数和事件通知机制是特殊情况,它们是预先约定好的“反向注册”,本质上还是维持了依赖的清晰性)。

2、规则

1. 命名1)文件名添加前缀:硬件抽象层( hal_ )、功能模块层( fml_ )、应用接口层( api_ )、业务逻辑层( bll_ )、应用层( app_ );2)api接口命名规则:api_功能名称;2. 调用1)同一级别的层相互独立,互不影响、互不干扰、互不关联,不能相互调用,只能调用下层接口;2)各个层之间不能跨层调用,如:在业务逻辑层不能直接调用功能模块层的代码 ,仅能调用应用接口层代码;3. 增删1)新增接口要求与整体规则统一,后续只能增加,不允许修改、不允许删除;2)每次新增接口,需在文件头备注 (作者、版本信息、状态功能);

二、为什么在嵌入式系统中使用分层架构

优点

1. 清晰度:代码结构清晰,逻辑分明。想看硬件操作?去驱动层。想看业务流程?去应用层。各司其职,阅读和理解代码的难度直线下降。2. 提高可维护性和可读性:代码结构清晰,开发者可以快速定位问题。例如,如果是传感器数据读取错误,问题大概率在驱动层;如果是逻辑控制错误,则在应用层。新成员更容易上手,因为他们只需理解自己负责的层级接口。3. 极强的可移植性:这是最核心的优点。当需要更换主控MCU(例如从STM32换到NXP的芯片)或硬件平台时,你只需要重写或适配最底层的硬件驱动层和HAL层;上层的服务层和应用层代码几乎不需要任何修改即可复用,极大地减少了开发工作量,保护了核心业务逻辑的投资。4. 增强可测试性:可以对每一层进行单元测试和模拟。例如:在PC上模拟运行应用层代码时,可以创建一个“模拟”的服务层,让它返回预设的传感器数据,从而在不具备真实硬件的情况下验证应用逻辑的正确性(称为硬件在环测试或仿真)。5. 便于团队协作:硬件工程师和软件工程师可以并行工作。硬件工程师负责提供硬件并开发底层驱动,软件工程师基于定义好的服务层接口开发应用逻辑。

缺点

1. 性能开销:函数调用穿越多个层会带来额外的执行时间和栈空间开销。对于性能极其敏感、时序要求苛刻的场景(如纳秒级的中断服务程序),这种开销可能是不可接受的。解决方案:在关键路径上,允许“越级”调用(例如,在应用层直接调用经过高度优化的驱动函数),但这会牺牲一定的架构纯洁性。2. 资源消耗:分层结构可能需要更多的代码空间(ROM)和内存(RAM)来维护各层的接口和数据缓冲。这在资源极度受限的8位/16位MCU上可能是个问题。解决方案:根据项目资源和性能要求权衡分层的粒度。在资源紧张的系统中,层次可以划分得更粗一些。3. 设计复杂:前期需要投入更多时间进行良好的接口设计。设计不当的接口可能在后期导致层与层之间产生意想不到的耦合,反而失去分层的意义。

三、在项目中落地分层思想

1、目录结构

在工程里,创建对应的文件夹,比如 Drivers, BSP, Middleware, Application。把相应层的文件放进去。清晰明了。接口设计 (.h 文件):每一层都要有清晰的头文件 (.h) 来定义它向上层提供的公共函数和数据结构。这是层与层之间的“合同”。头文件里只放“能给别人看的”,内部实现细节封装在对应的 .c 文件里,使用 static 关键字限制作用域。数据流:尽量通过函数参数和返回值来传递数据。避免滥用全局变量,它们是耦合的温床。如果层间需要共享复杂状态,可以考虑定义结构体,通过指针传递。

四、总结

嵌入式分层架构是一种极其重要且实用的设计范式,尤其适用于:产品生命周期长,未来可能更换芯片或硬件。
团队需要协作开发。
系统复杂度较高,需要良好的结构来管理。
项目需要较高的可测试性和可靠性。
尽管它带来微小的性能和资源开销,但其在可维护性、可移植性和协作效率方面带来的巨大优势,使得它成为中大型嵌入式项目事实上的标准架构选择。
开发者需要做的是根据具体项目的资源约束和性能要求,找到分层粒度与执行效率之间的最佳平衡点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/943771.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DP 基础题乱做

恶补基础 DP. CF1061C 多样性 转移是经典的子序列 DP,考虑前 \(i\) 个数,子序列长度为 \(j\) 的方案数. 转移: \[f_{i,j}=\begin{cases} f_{i-1,j-1}+f_{i-1,j}&j\mid a_i\\ f_{i-1,j}&otherwise. \end{cas…

20232315 2025-2026-1 《网络与系统攻防技术》实验二实验报告

20232315 2025-2026-1 《网络与系统攻防技术》实验二实验报告20232315 2025-2026-1 《网络与系统攻防技术》实验二实验报告 目录一、实验基本信息二、实验内容三、实验要求四、实验过程4.1使用netcat获取主机操作Shell…

2025-10-21 XQQ Round 赛后总结

赛时心路历程开 T1,然后 think 一会就秒掉了。大洋里一遍过。 开 T2,然后 think 一会就秒掉了。大洋里也是一遍过。 15:48 think T3,结果假了。但是注意到在 \(m=0\) 的时候是对的,10 分也是分。 16:25 想不到 T3。…

《中华人民共和国网络安全法》第二十一条这一核心考点

《网络安全法》第二十一条 考点精析 法条原文:国家实行网络安全等级保护制度。网络运营者应当按照网络安全等级保护制度的要求,履行下列安全保护义务,保障网络免受干扰、破坏或者未经授权的访问,防止网络数据泄露或…

二三级区别

等保第二级 vs 第三级核心差异及考点精析 核心关系:第二级是 “指导防护” ,第三级是 “强制管控、增强审计” 。从二级到三级是质的飞跃,而非简单量的增加,这正是考试的重点。核心差异对比与考点解析控制领域 核心…

小红书 404 重定向

function clientRedirect(e) { window.location.href = e, setTimeout(function() { window.location.href = e }, 100), setTimeout(function() { …

第九章-Where-1S-tHe-Hacker

第九章-Where-1S-tHe-Hacker 1、通过本地 PC RDP到服务器并且找到黑客ID 为多少,将黑客ID 作为 FLAG 提交; 这里一定一定要先把windows安全中心关了。不然会影响后门题目正常做从这里看到了管理员账户和密码,我们先看…

CF 2023D Many Games

huiyoude上界卡不来,怎么这么菜??? 下文的概率都是指的 \(\frac{p}{100}\)。 首先有一个想法就是直接背包,但是 \(\prod p\) 涉及不小的数的乘积并不好放入状态,所以考虑设 \(f_s\) 表示 \(\sum w = s\) 的最大概…

2025.10.22考试记录

T1 题意 在 \(n\) 瓶水中有1瓶毒药,\(m\)次实验,每次选择 \(k\) 瓶水测试其中是否有毒药,给出实验结果,试判断能否辨别出毒药,若能则给出最早得出结论的实验批次,否则求出可能成为毒药的编号。 分析 提前一天就被…

2025多校冲刺CSP模拟赛7 题目分析

T1 题目概述 求: \[\sum_{i=1}^n\sum_{j=i}^n[\gcd(i,j)=i\text{ xor } j] \]其中 \(n\leq 10^7\)。 分析 赛时没有做出来(doge)。 其实细想一下还是可以做的。 不难想到枚举 \(d=\gcd(i,j)\),那么 \(i\text{ xor …

学习资源

关于科研方向以及自己学习的一些经验借鉴资源 LLM 度盘有资料 【1080P】安德烈卡帕西:深入探索像ChatGPT这样的大语言模型|Andrej Karpathy 【2025最新】LLM大模型零基础全套入门保姆级课程,台大李宏毅《生成式人工…

CMC-C# Visual Studio2022 中不能进入断点設置方法

CMC-C# Visual Studio2022 中不能进入断点設置方法

[题解]P4616 [COCI 2017/2018 #5] Pictionary

P4616 [COCI 2017/2018 #5] Pictionary 我们发现,第 \(i\) 天会让所有为 \((m-i+1)\) 倍数的节点相互连通。可以将 \((m-i+1)\) 向它所有的倍数连边,效果是相同的。 我们规定边权为 \(i\)。 那么对于建好的图,我们不…

2025.10.22总结 - A

今天上了离散和马原,感觉还可以,加油

蛋白表达系统的技术布局与应用

引言 蛋白表达技术是分子生物学与生物制药研究的核心支撑之一。不同的蛋白表达系统在转录调控、翻译后修饰以及生产效率上各具特点。对于科研与产业化生产而言,系统选择不仅影响产量,更决定蛋白的结构与功能完整性。…

10月22日

今日学习了离散数学的逻辑结构与马克思主义的原理概论。离散数学严谨的符号推理,与马克思主义宏观的历史规律,分别从微观与宏观维度锻炼了我的抽象与辩证思维。二者虽领域迥异,却共同揭示了世界运行的内在逻辑与秩序…

软件工程学习日志2025.10.22

今日目标让后端服务稳定启动,加载最新静态资源与 WebSocket 逻辑。 修复聊天气泡左右归属判定错误(所有消息都在右侧)。 提升右侧气泡内昵称与时间的可读性(颜色对比度不足)。实施过程多次尝试本地启动方式,修正…

Seg T

交换律类任意一个标记向下时,两种标记都要下放 求max 时,query的无关子树返回极小值 pushdown和update的修改应当相同(对于结合律类时,可能update时对同一个tag的处理不同(如+&时对 add 有+有),对应到pushd…

Typora的多端同步方案,如何多台计算机共享md文件?Windows和Mac通过定时执行git来同步markdown文件

Windows和Mac通过定时执行git来同步markdown文件。 在阅读文章前,务必给typora配好图床,图床真的很爽,去网上搜搜教程二十分钟配好。 配置git免密码推拉 首先需要给git配置免登录,使用ssh推拉代码。如果不想看废话…

OOP-实验二

实验任务一 源代码 T.h点击查看代码 #pragma once#include <string>// 类T: 声明 class T { // 对象属性、方法 public:T(int x = 0, int y = 0); // 普通构造函数T(const T &t); // 复制构造函数T(T &am…