星標公眾號,讓嵌入式知識 “投喂” 不停歇!
大家好,我是雜燴君。
最近后臺好多朋友催更呀~
有人說 “能不能聊聊 STM32、GD32 這些常用單片機的代碼分層設計?寫項目時總亂糟糟的”!

這不就安排上了~ 今天咱們就聚焦 “通用嵌入式軟件架構分層” 這個實用技巧,聊聊怎么把項目代碼分層得明明白白的、維護起來不頭疼~
嵌入式系統中的 Arch-Platform-Target 三層抽象是一種常用的軟件架構設計模式,用于提高代碼的可移植性和可維護性。

Arch架構支持層是最底層,與硬件直接相關。它包含了針對特定處理器架構的代碼,例如ARM、MIPS等。這一層通常包括中斷處理、上下文切換、內存管理單元(MMU)配置、緩存控制等。Arch層為上層提供了統一的硬件抽象接口。
主要職責:
Platform平臺抽象層位于Arch層之上,Target層之下。通過Arch層提供的接口來訪問硬件,同時為Target層提供統一的平臺服務接口。
主要職責:
Target目標應用層屬于上層,實現具體的業務邏輯和功能。Target層通過Platform層提供的服務來訪問硬件,因此當硬件平臺改變時,只需要修改Platform層和Arch層,
而Target層的代碼可以保持相對不變。
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)單獨放,防止業務碰到寄存器。
下面結合 STM32 + RTOS 給出一個體現 Arch-Platform-Target 的職責分離的目錄規劃和代碼示例:
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/arm/cortex-m0/arch_port.c

Arch 層只負責把內核時鐘和中斷接好,具體任務調度邏輯由 FreeRTOS 內核完成。更換 RTOS 時,Arch 層需要少量調整。
platform/stm32f072_nucleo/bsp_gpio.c

Platform 層對外暴露 platform_led_*等統一接口,Target 層不感知 HAL 細節。
target/app/app_led.c

target/app/main.c

Target 層只依賴 Platform 的初始化與業務 API,后續如果換成 GD32 或更換板載外設,僅需改動 Platform 與 Arch,不影響業務代碼。
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。
osal/osal_xxx.c,實現與 osal.h 一致的 API。Arch 層改為調用新RTOS的啟動入口,并按新RTOS要求設置 SysTick/中斷優先級;移除 vTaskStartScheduler 相關邏輯。-DUSE_RTTHREAD)。嵌入式軟件中,合理的分層設計可以提升可移植、可維護、可測試性。可以用 Arch/Platform/OSAL/Services/Target 分層隔離硬件差異、RTOS 差異與業務邏輯。
分層帶來的實際收益:
platform_led_toggle),對 Target 暴露一致接口,避免業務代碼直接依賴 HAL 細節。如果覺得有幫助,歡迎點贊、在看、轉發三連。我篩選了一些非常實用的嵌入式書籍,有需要的朋友可掃碼購買:

猜你喜歡:
這幾個嵌入式軟件開源項目值得深度復刻!
嵌入式Linux多線程編程注意事項
常見嵌入式軟件崩潰類型:12種典型場景!
嵌入式中代碼執行時間測量的幾種方法!
點擊閱讀原文,查看我給大家篩選的熱門嵌入式書籍!