• 正文
  • 相关推荐
申请入驻 产业图谱

通用嵌入式软件架构分层设计思想!

12/23 14:42
819
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

大家好,我是杂烩君。最近后台好多朋友催更呀~

有人说 “能不能聊聊 STM32、GD32 这些常用单片机的代码分层设计?写项目时总乱糟糟的”!

这不就安排上了~ 今天咱们就聚焦 “通用嵌入式软件架构分层” 这个实用技巧,聊聊怎么把项目代码分层得明明白白的、维护起来不头疼~

1. Arch-Platform-Target三层抽象

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

1.1 Arch层(架构支持层)

Arch架构支持层是最底层,与硬件直接相关。它包含了针对特定处理器架构的代码,例如ARM、MIPS等。这一层通常包括中断处理、上下文切换、内存管理单元(MMU)配置、缓存控制等。Arch层为上层提供了统一的硬件抽象接口。

主要职责:

    CPU架构相关代码编译器/汇编器支持核心系统初始化

1.2 Platform层(平台抽象层)

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 + RTOS 给出一个体现 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 负责芯片外设封装(GPIOUARTI2C 等)和板级资源命名,向 Target 暴露统一 API。Target 只依赖 Platform 提供的接口做业务,不直接引用 HAL/寄存器。

3.2 关键代码示例

Arch 层:SysTick 驱动 FreeRTOS 时基

arch/arm/cortex-m0/arch_port.c

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

Platform 层:封装 LED

platform/stm32f072_nucleo/bsp_gpio.c

Platform 层对外暴露 platform_led_*等统一接口,Target 层不感知 HAL 细节。

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"

intosal_thread_create(osal_thread_t *t, constchar *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;
}

voidosal_thread_delay_ms(uint32_t ms)
{
    vTaskDelay(pdMS_TO_TICKS(ms));
}

voidosal_start_scheduler(void)
{
    vTaskStartScheduler();
}

intosal_mutex_create(osal_mutex_t *m)
{
    *m = xSemaphoreCreateMutex();
    return *m ? OSAL_OK : OSAL_ERR_FAIL;
}

intosal_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;
}

voidosal_mutex_unlock(osal_mutex_t m)
{
    xSemaphoreGive((SemaphoreHandle_t)m);
}

intosal_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;
}

intosal_queue_send(osal_queue_t q, constvoid *item, uint32_t timeout_ms)
{
    return xQueueSend(q, item, pdMS_TO_TICKS(timeout_ms)) == pdTRUE ? OSAL_OK : OSAL_ERR_TIMEOUT;
}

intosal_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(F0 -> F4 或 GD32)时,Target 基本不变,只需更新 Arch/Platform。可维护:外设封装集中在 Platform,避免业务层散落 HAL 调用。可测试:Platform API 可在仿真或 PC Mock 中替换实现,便于单元测试业务逻辑。

4.1 QA

    1. Q:OSAL 是必须的吗?
    1. A:若项目只用一种 RTOS,可以不加 OSAL;但预期会换 RTOS(如 FreeRTOS ⇄ RT-Thread),建议在早期就放 OSAL,后续只需替换适配文件,业务无需改动。
    1. Q:Platform 层和驱动 HAL 的区别?
    1. A:HAL 是芯片厂的寄存器封装;Platform 层在 HAL 之上做二次封装并用统一命名(如 platform_led_toggle),对 Target 暴露一致接口,避免业务代码直接依赖 HAL 细节。

如果觉得有帮助,欢迎点赞、在看、转发三连。

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

本公众号专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,公众号内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!