嵌入式系统中的Arch-Platform-Target三层抽象是一种常用的软件架构设计模式,用于提高代码的可移植性和可维护性。

1.1 Arch层(架构支持层)
Arch架构支持层是最底层,与硬件直接相关。它包含了针对特定处理器架构的代码,例如ARM、MIPS等。这一层通常包括中断处理、上下文切换、内存管理单元(MMU)配置、缓存控制等。Arch层为上层提供了统一的硬件抽象接口。
主要职责:
- CPU架构相关代码
- 编译器/汇编器支持
- 核心系统初始化
Platform平台抽象层位于Arch层之上,Target层之下。通过Arch层提供的接口来访问硬件,同时为Target层提供统一的平台服务接口。
主要职责:
- 硬件抽象和驱动封装
- 提供统一的硬件访问接口
- 屏蔽底层硬件差异
1.3 Target层(目标应用层)
Target目标应用层属于上层,实现具体的业务逻辑和功能。Target层通过Platform层提供的服务来访问硬件,因此当硬件平台改变时,只需要修改Platform层和Arch层,而Target层的代码可以保持相对不变。良好的任务调度设计有助于提升系统的实时性与稳定性,其思想在算法与系统设计中也有一席之地。
2. 实际项目常见分层思想
Arch-Platform-Target是个核心分层思想,实际项目中,可能还包含OSAL(系统抽象层)、Services(基础组件服务)等模块。
嵌入式小型项目(单一 RTOS、少量外设):Platform 下可能直接包含OSAL、Services,目录简单、上手快。如:

嵌入式中大型项目(可能换 RTOS/芯片):OSAL 、Services独立于 Platform,同属于中间层。Platform 专注板级与外设封装,OSAL 专注 RTOS API 抽象,Services专注于各种基础组件及中间件的管理。如:

进一步放大细分:

- Arch:启动、异常、时基。
- BSP/Platform:板级时钟、PinMux、外设驱动抽象。
- OSAL:任务/同步/队列/内存适配。
- Services/Middleware:log、cli/shell、kv/存储、文件系统、网络协议栈、OTA、安全等。
- Target/App:业务域。
- 可选:HAL(MCU 厂商层)与 Driver Framework(如 device tree/board cfg)单独放,防止业务碰到寄存器。
3. STM32+RTOS项目的分层设计案例
下面结合 STM32 + FreeRTOS 给出一个体现 Arch-Platform-Target 职责分离的目录规划和代码示例。
3.1 工程目录设计
stm32_project/
├── arch/ # CPU/架构相关
│ └── arm/cortex-m0/
│ ├── startup_gcc.s # 启动与向量表
│ ├── system_stm32f0xx.c # 时钟/系统初始化
│ └── arch_port.c # SysTick、临界段封装
├── platform/ # 平台/Board 支持
│ └── stm32f072/
│ ├── bsp_clock.c
│ ├── bsp_gpio.c
│ ├── bsp_uart.c
│ └── platform_init.c # 统一平台初始化入口
├── osal/ # OS 抽象层(屏蔽不同 RTOS)
│ ├── osal.h # 统一任务/互斥/队列接口
│ ├── osal_freertos.c # FreeRTOS 适配实现
│ └── osal_port.h # 基础类型、错误码
├── services/ # 常用系统组件
│ ├── log/
│ └── kv/
├── external/ # 第三方库(协议栈/文件系统/安全等)
│ ├── lwip/ # 轻量级网络协议栈
│ ├── mbedtls/
│ └── littlefs/
├── target/ # 业务/应用
│ └── app/
│ ├── main.c # 任务创建、启动调度
│ └── app_led.c # 具体业务
├── freertos/ # FreeRTOS 内核与移植
│ ├── CMSIS/ # 官方 CMSIS 头文件
│ ├── portable/GCC/ARM_CM0/ # FreeRTOS Cortex-M0 移植层
│ └── FreeRTOSConfig.h
└── drivers/ # MCU HAL 库
└── stm32f0xx_hal/
分层约束:
- Arch 仅处理与核心架构相关的启动、时钟、异常向量、SysTick 驱动,不直接操作业务外设。
- Platform 负责芯片外设封装(GPIO、UART、I2C 等)和板级资源命名,向 Target 暴露统一 API。
- Target 只依赖 Platform 提供的接口做业务,不直接引用 HAL/寄存器。
3.2 关键代码示例
Arch 层:SysTick 驱动 FreeRTOS 时基
arch/arm/cortex-m0/arch_port.c

Arch 层只负责把内核时钟和中断接好,具体任务调度逻辑由 FreeRTOS 内核完成。更换 RTOS 时,Arch 层需要少量调整。
platform/stm32f072_nucleo/bsp_gpio.c

Platform 层对外暴露 platform_led_* 等统一接口,Target 层不感知 HAL 细节。这种清晰的分层使基于STM32的嵌入式系统开发更具结构性和可维护性。
Target 层:创建任务并调用平台接口
target/app/app_led.c

target/app/main.c

Target 层只依赖 Platform 的初始化与业务 API,后续如果换成 GD32 或更换板载外设,仅需改动 Platform 与 Arch,不影响业务代码。
OSAL 层:统一 RTOS 抽象
osal/osal.h

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;
}
int osal_mutex_lock(osal_mutex_t m, uint32_t timeout_ms) {
return xSemaphoreTake((SemaphoreHandle_t)m, pdMS_TO_TICKS(timeout_ms)) == pdTRUE ? OSAL_OK : OSAL_ERR_TIMEOUT;
}
void osal_mutex_unlock(osal_mutex_t m) {
xSemaphoreGive((SemaphoreHandle_t)m);
}
int osal_queue_create(osal_queue_t *q, uint16_t item_size, uint16_t len) {
*q = xQueueCreate(len, item_size);
return *q ? OSAL_OK : OSAL_ERR_FAIL;
}
int osal_queue_send(osal_queue_t q, const void *item, uint32_t timeout_ms) {
return xQueueSend(q, item, pdMS_TO_TICKS(timeout_ms)) == pdTRUE ? OSAL_OK : OSAL_ERR_TIMEOUT;
}
int osal_queue_recv(osal_queue_t q, void *item, uint32_t timeout_ms) {
return xQueueReceive(q, item, pdMS_TO_TICKS(timeout_ms)) == pdTRUE ? OSAL_OK : OSAL_ERR_TIMEOUT;
}
OSAL 封装线程、延时、互斥、队列,并对外暴露统一的错误码(在 osal_port.h 中定义 OSAL_OK/OSAL_ERR_FAIL/OSAL_ERR_TIMEOUT)。若切换到 RT-Thread 或 Zephyr,仅需新增对应 osal_xxx.c 文件。
切换 RTOS 的要点:
- 新增适配文件:
osal/osal_xxx.c,实现与 osal.h 一致的 API。
- 调整启动与时基:Arch 层改为调用新RTOS的启动入口,并按新RTOS要求设置 SysTick/中断优先级;移除
vTaskStartScheduler 相关逻辑。
- 配置与链接:替换 RTOS 源码与配置文件(如移除 FreeRTOS 源,加入新RTOS源),在构建脚本中切换编译宏(例如
-DUSE_RTTHREAD)。
- 检查栈/优先级语义:如果不同RTOS优先级数值方向不同,适配时需在 OSAL 内部转换,保证业务侧传入的“逻辑优先级”保持一致。
- 队列/超时语义:确认阻塞超时单位(ms 或 tick),在 OSAL 内部做统一换算,避免业务层被不同 RTOS 语义影响。
4. 总结
嵌入式软件中,合理的分层设计是提升代码质量的关键。采用 Arch/Platform/OSAL/Services/Target 的分层架构,能有效隔离硬件差异、RTOS 差异与核心业务逻辑。
分层带来的实际收益:
- 可移植性:更换 MCU(如 STM32F0 -> STM32F4 或 GD32)时,Target 层业务代码基本不变,只需更新 Arch 与 Platform 层。
- 可维护性:外设驱动与硬件访问逻辑集中在 Platform 层,避免了 HAL 调用在业务代码中四处散落。
- 可测试性:Platform 层的 API 可以在仿真环境或 PC 端用 Mock 实现替换,便于对 Target 层的业务逻辑进行单元测试。
4.1 常见问题解答(Q&A)
-
Q:OSAL 层是必须的吗?
A: 如果项目确定只使用一种 RTOS,可以省略 OSAL 层,直接在业务中调用 RTOS 原生 API。但如果项目有更换 RTOS(如从 FreeRTOS 切换到 RT-Thread)的可能性,建议在项目早期引入 OSAL 层。这样后续更换时,只需替换 OSAL 的底层适配文件,上层业务代码无需任何改动。
-
Q:Platform 层和芯片厂商提供的 HAL 库有什么区别?
A: HAL 库是芯片厂商对寄存器操作的直接封装,通常与具体芯片型号强绑定。Platform 层则在 HAL 之上进行二次封装,目的是为 Target 层提供统一的、与具体板卡或外设相关的业务接口(如 platform_led_toggle)。这样可以避免业务代码直接依赖 HAL 的细节,当硬件更换时,只需修改 Platform 层的实现。