当前位置: 首页 > news >正文

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

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

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

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.hskmm.com/?act=detail&tid=36915

相关文章:

  • DP 基础题乱做
  • [题解]P4616 [COCI 2017/2018 #5] Pictionary
  • 二三级区别
  • 第九章-Where-1S-tHe-Hacker
  • CF 2023D Many Games
  • 2025.10.22考试记录
  • 2025多校冲刺CSP模拟赛7 题目分析
  • Typora的多端同步方案,如何多台计算机共享md文件?Windows和Mac通过定时执行git来同步markdown文件
  • Trie树
  • Seg T
  • 2025.10.22总结 - A
  • 蛋白表达系统的技术布局与应用
  • 软件工程学习日志2025.10.22
  • CF2077B Finding OR Sum
  • 10月22日
  • OOP-实验二
  • P2272 [ZJOI2007] 最大半连通子图
  • 2025年,哪些微信公众号排版工具能带来效率变革?
  • 我对软件工程的理解
  • PCB线圈生成工具
  • 软件工程第三次作业--结对项目
  • AI股票预测分析报告 - 2025年10月22日
  • CF2144D
  • 折腾笔记[33]-使用uiautomation自动重复读取图片(被控程序为.net框架)
  • switch的简单运用
  • 软工第三次作业——结对项目
  • 10.22总结
  • AutoGen框架入门:5个核心概念搭建智能体协作系统
  • 使用google上colab编辑器
  • 16