随着现代处理器性能与资源的日益强大,许多项目实际仅使用了其一小部分能力。这种情况下,为嵌入式项目引入一个清晰的软件框架,不仅能充分利用硬件资源,更是提升代码可维护性与团队协作效率的关键。本文将深入剖析一种通用的嵌入式软件架构分层设计思想,并以STM32结合RTOS的项目为例,展示如何实现代码的清晰分层与高效管理。
在嵌入式系统设计中,Arch-Platform-Target三层抽象是一种经典且高效的软件架构模式,其核心目标是增强代码的可移植性与可维护性。

1.1 Arch层(架构支持层)
Arch层作为最底层,直接与处理器硬件架构相关。它封装了针对特定CPU核心(如ARM Cortex-M、MIPS)的代码,包括中断向量表、上下文切换、内存管理单元(MMU)配置、缓存控制等,为上层提供统一的硬件架构抽象接口。
主要职责:
- CPU架构相关代码(启动文件、汇编例程)
- 编译器/汇编器特定支持
- 核心系统初始化(时钟、异常处理)
Platform层位于Arch层之上,Target层之下。它通过调用Arch层提供的接口来访问硬件,并对芯片外设驱动进行封装和抽象,从而为目标应用层提供统一、稳定的硬件访问API,有效屏蔽底层硬件差异。
主要职责:
- 硬件抽象和驱动封装(如GPIO、UART、I2C)
- 提供统一的板级硬件访问接口
- 管理板级资源配置与初始化
1.3 Target层(目标应用层)
Target层是架构的最上层,专注于实现具体的业务逻辑和功能应用。它通过调用Platform层提供的服务来间接访问硬件资源。这种设计确保了当硬件平台发生变更时,只需调整Platform层和Arch层,而Target层的核心业务代码可以最大限度地保持稳定,无需重写。
2. 实际项目中的分层演进
Arch-Platform-Target是核心思想,在实际项目中,结构会根据复杂度进行演进,可能引入OSAL(操作系统抽象层)、Services(基础服务组件)等模块。
对于资源受限、功能单一的小型嵌入式项目(使用单一RTOS、外设较少),分层可以简化。Platform层可能直接包含OSAL和Services的功能,目录结构简洁,便于快速开发。如下图所示:

对于中大型或未来可能更换RTOS、芯片的平台,则需要更清晰的职责分离。OSAL(专注抽象不同RTOS的API)、Services(管理通用组件)会独立于Platform层,同属中间层。Platform则专注板级硬件与外设封装。结构示例如下:

进一步细分各层职责如下:

- Arch:负责启动流程、异常处理、系统时基。
- BSP/Platform:管理板级时钟、引脚复用、外设驱动抽象。
- OSAL:对任务、信号量、消息队列、内存管理等RTOS核心功能进行统一适配。
- Services/Middleware:提供日志系统、命令行接口、键值存储、文件系统、网络协议栈、OTA升级、安全加解密等通用服务。
- Target/App:实现具体的业务领域逻辑。
- 可选层:有时会将MCU厂商的HAL库与驱动框架(如设备树)单独剥离,确保业务代码完全不直接接触寄存器,实现彻底解耦。
3. STM32+RTOS项目分层设计实战
下面以一个基于STM32和RTOS的实际工程为例,展示如何通过合理的目录规划与编码约束来体现Arch-Platform-Target的分层思想。
3.1 工程目录设计
一个清晰的目录结构是良好架构的直观体现。遵循分层思想,我们可以这样组织项目,这也是一个优秀的软件工程目录结构规划的实践。
stm32_project/
├── arch/ # CPU/架构相关层
│ └── arm/cortex-m0/
│ ├── startup_gcc.s # 启动文件与中断向量表
│ ├── system_stm32f0xx.c # 时钟与系统初始化
│ └── arch_port.c # SysTick、临界区等架构级封装
├── platform/ # 平台/板级支持层
│ └── stm32f072_nucleo/
│ ├── bsp_clock.c
│ ├── bsp_gpio.c
│ ├── bsp_uart.c
│ └── platform_init.c # 统一的平台初始化入口
├── osal/ # 操作系统抽象层
│ ├── osal.h # 统一的任务/互斥锁/队列接口
│ ├── osal_freertos.c # FreeRTOS适配实现
│ └── osal_port.h # 基础类型、错误码定义
├── services/ # 系统服务与组件层
│ ├── log/
│ └── kv/
├── external/ # 第三方库
│ ├── lwip/
│ ├── mbedtls/
│ └── littlefs/
├── target/ # 目标应用/业务层
│ └── app/
│ ├── main.c # 应用初始化、任务创建、启动调度
│ └── app_led.c # 具体业务逻辑实现
├── freertos/ # RTOS内核源码与移植文件
│ ├── CMSIS/
│ ├── portable/GCC/ARM_CM0/
│ └── FreeRTOSConfig.h
└── drivers/ # MCU厂商HAL库
└── stm32f0xx_hal/
分层约束原则:
- Arch层 仅处理与CPU核心架构强相关的初始化,不涉及具体业务外设。
- Platform层 负责对芯片外设进行二次封装,并向Target层暴露统一的、板级相关的API(如
platform_led_toggle)。
- Target层 只允许依赖Platform层提供的接口,禁止直接引用HAL库函数或操作寄存器。
3.2 关键代码示例
Arch 层:提供SysTick时基驱动
arch/arm/cortex-m0/arch_port.c 负责为RTOS提供时钟心跳。

Arch层的职责纯粹是连接硬件时钟与RTOS内核,业务调度逻辑由RTOS完成。更换RTOS时,仅需修改此层。
platform/stm32f072_nucleo/bsp_gpio.c 在HAL库之上进行抽象。

Platform层对外暴露如platform_led_on/off/toggle等稳定接口,彻底隐藏HAL的GPIO_PinState等细节,Target层无需感知。
Target 层:实现业务逻辑
target/app/app_led.c 业务代码清晰简洁。

target/app/main.c 负责应用初始化和任务创建。

Target层代码完全不关心当前是STM32还是其他芯片,它只调用Platform层定义的接口。未来更换硬件平台,业务代码几乎无需改动。
OSAL 层:统一RTOS抽象接口
osal/osal.h 定义统一的API。

osal/osal_freertos.c 实现FreeRTOS适配。
#include "osal.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
int osal_thread_create(osal_thread_t *t, const char *name,
osal_thread_entry_t entry, void *arg,
uint16_t stack_words, uint8_t priority) {
if (xTaskCreate(entry, name, stack_words, arg, priority, (TaskHandle_t *)t) != pdPASS)
return OSAL_ERR_FAIL;
return OSAL_OK;
}
void osal_thread_delay_ms(uint32_t ms) {
vTaskDelay(pdMS_TO_TICKS(ms));
}
void osal_start_scheduler(void) {
vTaskStartScheduler();
}
int osal_mutex_create(osal_mutex_t *m) {
*m = xSemaphoreCreateMutex();
return *m ? OSAL_OK : OSAL_ERR_FAIL;
}
// ... 其他互斥锁、队列函数的适配实现
OSAL层抽象了线程、延时、互斥锁、消息队列等核心机制,并定义统一的错误码。若项目需要从FreeRTOS迁移至RT-Thread或Zephyr等其他实时操作系统(RTOS),只需新增对应的osal_rtthread.c适配文件,并修改构建配置,业务层代码可保持原样。
切换RTOS的要点:
- 新增适配文件:实现新RTOS下
osal.h中定义的所有API。
- 调整启动与时基:修改Arch层,使其调用新RTOS的启动入口,并按新RTOS要求配置SysTick中断优先级。
- 更新构建系统:替换RTOS源码目录,在编译选项中切换宏定义(如从
-DUSE_FREERTOS改为-DUSE_RTTHREAD)。
- 统一语义:在OSAL内部处理不同RTOS在优先级数值方向、超时单位等方面的差异,保证给业务层提供一致的逻辑视图。
4. 总结
在嵌入式软件开发中,采用合理的分层架构是提升项目可移植性、可维护性和可测试性的基石。通过Arch/Platform/OSAL/Services/Target的层次化设计,能够有效地隔离硬件差异、RTOS差异与核心业务逻辑。
分层设计带来的实际收益包括:
- 可移植性:更换MCU(如STM32F0 -> STM32F4 或 GD32)时,Target层业务代码基本不变,主要工作集中在更新Arch层和Platform层的驱动适配。
- 可维护性:所有硬件相关代码被集中封装在Platform层,避免了业务代码中散落大量HAL库调用,使得阅读、调试和修改更为高效。
- 可测试性:Platform层提供的API可以在仿真环境或PC端通过Mock对象替换实现,从而方便地对Target层的纯业务逻辑进行单元测试。
常见问题(QA)
-
Q:OSAL层是必须的吗?
A:如果项目明确只使用一种RTOS且没有更换计划,可以直接使用该RTOS的原生API,OSAL层不是强制必需的。但如果项目存在未来更换RTOS(如从FreeRTOS切换到RT-Thread)的可能性,建议在项目初期就引入OSAL层。这样后续更换时只需替换适配层实现,业务代码无需任何修改,保护了投资。
-
Q:Platform层和芯片厂商的HAL库有什么区别?
A:HAL库是芯片厂商提供的,用于封装寄存器操作的硬件抽象层,其接口通常与芯片系列绑定。Platform层则是在HAL库(或直接寄存器操作)之上进行的二次封装,其目标是针对“本板卡”的具体硬件(如某个LED、某个串口)提供稳定、统一的业务接口(如platform_led_toggle),目的是让Target层业务代码完全脱离对特定HAL库或芯片的依赖。