1. 驱动层设计思想:为什么需要封装?
做单片机开发好些年头了,我见过太多人一上来就对着寄存器猛写。不是说不行,但项目一复杂,你就知道苦头在哪了。
先讲个我自己的经历。几年前做一个多传感器采集的项目,用了 STM32F4 系列。刚开始图省事,直接在 main 函数里操作寄存器。功能倒是跑通了,结果客户说要换芯片型号。好家伙,那代码改得我头皮发麻——每个外设的寄存器地址都不一样,中断号也不同,几乎等于重写。
从那以后,我养成了一个习惯:写驱动必须先想封装。说白了,就是给底层硬件穿件「外套」,让上层代码不直接跟寄存器打交道。
1.1 为什么需要封装?三个核心理由
你想想看,封装到底解决了什么问题?我总结了三件事:
- 隔离变化——硬件换了你不用重写业务逻辑
- 降低复杂度——上层调用者不需要知道寄存器怎么配
- 提高复用——写好的驱动模块,下个项目直接拿来用
举个例子,你用 GPIO 控制一个 LED 灯亮灭。不封装的话,代码可能是这样:
// 直接操作寄存器
GPIOA->BSRR = (1 << 5); // 点亮 PA5
封装之后呢?
// 调用驱动接口
led_on(LED_RED);
哪个好维护?一目了然。而且如果哪天你把 LED 换到了 PB3,只需要改驱动层,应用层代码一行不动。
1.2 分层架构:应用层、驱动层、HAL层
说到分层,很多人觉得是「高大上」的东西。其实没那么玄乎。我习惯把单片机软件分成三层,每层各司其职。
下面这张图能帮你快速理解三层的关系:
嗯,这里要注意:箭头方向是调用关系。应用层调用驱动层,驱动层调用HAL层,HAL层操作寄存器。反过来不行——底层不能反过来调用上层。
1.3 各层职责划分
我见过不少新手把三层混在一起写。比如在应用层里直接配定时器预分频系数,或者在驱动层里写业务逻辑。这都是坏习惯。
下面这个表格,把每层该干什么、不该干什么说清楚:
| 层级 | 职责 | 典型内容 | 不该做的事 |
|---|---|---|---|
| 应用层 | 实现业务逻辑 | 状态机、协议解析、用户交互 | 直接操作寄存器、配置时钟 |
| 驱动层 | 封装外设功能 | LED驱动、按键扫描、传感器读写 | 写业务逻辑、直接操作寄存器 |
| HAL层 | 屏蔽硬件差异 | GPIO配置、定时器初始化、中断注册 | 实现业务逻辑、调用应用层函数 |
1.4 一个实际例子:LED驱动
光说理论太干,咱们看个具体的。假设你要控制一个LED灯,三层分别怎么写?
HAL层:封装GPIO的初始化操作
// hal_gpio.c
void hal_gpio_init(GPIO_TypeDef* port, uint16_t pin, uint32_t mode) {
GPIO_InitTypeDef gpio_cfg = {0};
gpio_cfg.Pin = pin;
gpio_cfg.Mode = mode;
gpio_cfg.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(port, &gpio_cfg);
}
驱动层:封装LED的开关行为
// drv_led.c
#include "hal_gpio.h"
#define LED_PORT GPIOB
#define LED_PIN GPIO_PIN_5
void led_init(void) {
hal_gpio_init(LED_PORT, LED_PIN, GPIO_MODE_OUTPUT_PP);
}
void led_on(void) {
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);
}
void led_off(void) {
HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);
}
应用层:只管业务逻辑
// app_main.c
#include "drv_led.h"
void app_task(void) {
led_init();
while(1) {
led_on();
delay_ms(500);
led_off();
delay_ms(500);
}
}
你看,应用层根本不知道LED接在哪个引脚,也不知道GPIO怎么配。这就是封装的价值。
1.5 什么时候可以「偷懒」?
当然,不是所有场景都需要严格分层。我个人的经验是:
- 小项目(几百行代码)——可以适当简化,但至少把硬件操作封装成函数
- 中等项目(几千行)——建议严格分层,尤其是多人协作时
- 大型项目(上万行)——必须分层,否则后期维护成本会让你崩溃
说白了,分层架构不是「教条」,而是帮你管理复杂度的工具。用得好,事半功倍;用得僵,反而增加工作量。
嗯,这一章就聊到这儿。记住一句话:写驱动的时候,多想想「如果换芯片了,我这代码能直接复用吗?」——有这个意识,你就已经走在正确的路上了。