摘要:很多项目的 main.c里充斥着 HAL_GPIO_WritePin()HAL_UART_Transmit()。这种写法导致换一颗 MCU,业务逻辑必须重写。本文将演示如何通过 BSP 适配层(Board Support Package),实现“换芯片不改业务”。


一、反面教材(地狱写法)

这是绝大多数入门项目的现状。

// main.c
void Process_User_Input(void)
{
    // 业务直接依赖 STM32 HAL
    if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET) {
        HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
        HAL_UART_Transmit(&huart1, data, len, 100);
    }
}

痛点:

  • 换芯片(STM32 -> GD32/NXP/ESP32):main.c必须全改。

  • 硬件改动(LED 换引脚):业务逻辑被迫修改。

  • 单元测试无法脱离硬件。


二、进阶写法(架构思维)

核心思想:业务只认接口,不认硬件。

1. 定义 BSP 接口(.h 文件)

这是业务和硬件的契约。

// bsp_led.h
#pragma once
void BSP_LED_On(void);
void BSP_LED_Off(void);

// bsp_uart.h
#pragma once
void BSP_UART_Send(uint8_t *data, uint16_t len);

2. 实现 BSP 适配层(.c 文件)

这里是脏活累活集中的地方,专门对接 HAL 库。

// bsp_led.c
#include "bsp_led.h"
#include "stm32f1xx_hal.h"

void BSP_LED_On(void)
{
    // 只有这里知道 LED 接在哪个引脚
    HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
}

void BSP_LED_Off(void)
{
    HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
}

3. 纯净的业务逻辑(main.c)

main.c现在完全不知道​ STM32 的存在。

// main.c
#include "bsp_led.h"
#include "bsp_uart.h"

void Process_User_Input(void)
{
    // 纯粹的“如果按键按下,则点灯并发送数据”
    if (Is_Key_Pressed()) {
        BSP_LED_On();       // 调用抽象接口
        BSP_UART_Send(...); // 调用抽象接口
    }
}

三、进阶收益

维度

地狱写法

架构写法

换 MCU

重写 main.c

重写 bsp_xxx.c

硬件改动

业务逻辑受影响

业务逻辑无感知

可读性

混杂硬件细节

只有逻辑流程

测试

必须接硬件

可 Mock BSP 接口


四、工程级最佳实践

1. 禁止业务层包含 HAL 头文件

规则main.c里绝对不能出现 #include "stm32xxxx_hal.h"

2. 参数传递用“值”,不用“句柄”

BSP 接口不要暴露 HAL 的句柄(如 UART_HandleTypeDef)。

// 错误:暴露了 HAL 句柄
void BSP_UART_Send(UART_HandleTypeDef *huart, ...);

// 正确:隐藏硬件细节
void BSP_UART_Send(uint8_t ch, uint8_t *data, uint16_t len);

3. 宏定义隔离硬件

bsp_config.h中定义硬件特征。

#define BOARD_TYPE_STM32F103
//#define BOARD_TYPE_GD32F103

五、总结 Checklist

  • [ ] 是否建立了 bsp_xxx.c/h文件夹?

  • [ ] main.c是否完全脱离了 HAL 库调用?

  • [ ] 业务逻辑是否只依赖 BSP 接口?

  • [ ] 更换 MCU 是否只需要替换 BSP 层?


六、写在最后(关注我,少走弯路)

我是 gqqsherry666,一个拒绝调包、专注工程落地的嵌入式架构师。

只会调库的程序员,离开原厂芯片就失业;

懂得分层的架构师,换颗芯片只是换个 BSP。

关注我的新专栏《嵌入式系统架构实战》,下一篇我们将深入解析 《事件驱动架构:用消息队列代替全局变量》

👉 下一篇预告:《事件驱动架构:用消息队列代替全局变量


原创文章,转载请注明出处。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐