如何把业务逻辑和硬件驱动剥离?别让 main 函数里全是 HAL 库调用
摘要:很多项目的
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 |
重写 |
重写 |
|
硬件改动 |
业务逻辑受影响 |
业务逻辑无感知 |
|
可读性 |
混杂硬件细节 |
只有逻辑流程 |
|
测试 |
必须接硬件 |
可 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。
关注我的新专栏《嵌入式系统架构实战》,下一篇我们将深入解析 《事件驱动架构:用消息队列代替全局变量》。
👉 下一篇预告:《事件驱动架构:用消息队列代替全局变量》
原创文章,转载请注明出处。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)