1. 课程导论与分层架构全景:为什么需要分层?
各位同学好,我是这门课的主讲。今天咱们聊聊嵌入式软件分层这件事。
先问大家一个问题:你写过的嵌入式代码,最怕什么?
我个人最怕的是——改一个LED闪烁频率,结果把串口通信搞崩了。听起来离谱?我在项目中遇到过不止一次。裸机开发时代,这种牵一发动全身的噩梦,几乎是家常便饭。
1.1 从裸机开发到分层架构的演进
咱们先看看裸机开发长什么样。说白了,就是一个 main.c 里塞满所有东西:
// 典型的裸机代码(别这么写)
void main(void) {
init_clock();
init_gpio();
init_uart();
init_timer();
while(1) {
// 读传感器
uint8_t data = read_sensor();
// 处理数据
uint16_t value = data * 10 + offset;
// 显示到LCD
lcd_display(value);
// 同时还要处理按键
if(button_pressed()) {
// 改显示模式
display_mode++;
}
delay_ms(100);
}
}
这种写法有什么问题?我总结了几点:
- 耦合严重:传感器、LCD、按键全搅在一起
- 移植困难:换个MCU,整个文件重写
- 调试痛苦:出问题不知道是硬件还是逻辑的锅
- 团队协作难:两个人改同一个文件,合并时想哭
我记得刚入行那会儿,带我的老工程师看我写的代码,叹了口气说:“你这代码,就像一碗浆糊。”当时不服气,后来自己踩了坑才明白——裸机开发在小项目里还能凑合,一旦项目超过5000行,维护成本指数级上升。
1.2 经典三层架构概览:HAL、DRV、APP
那怎么解决这个问题?分层。嵌入式领域最经典的分层方式就是三层架构:
| 层级 | 全称 | 职责 | 典型内容 |
|---|---|---|---|
| APP | Application Layer | 业务逻辑、状态机、协议解析 | 用户需求实现、算法、流程控制 |
| DRV | Driver Layer | 硬件抽象、寄存器操作、中断处理 | GPIO、UART、SPI、I2C驱动 |
| HAL | Hardware Abstraction Layer | 统一接口、平台无关、资源管理 | 设备句柄、读写接口、配置结构体 |
你想想看,这三层的关系就像一家公司:
- APP 是老板,只管“做什么”
- HAL 是部门经理,负责“怎么安排”
- DRV 是一线员工,负责“具体干”
老板不需要知道员工用什么工具,员工也不需要关心老板的战略规划。各司其职,互不干扰。
1.3 驱动分离的核心思想
驱动分离,说白了就是让硬件驱动和上层逻辑彻底解耦。我习惯用一个比喻:
驱动就像插座,上层应用就像电器。 你换个插座(换MCU),电器插上去照样用。前提是——插座接口要标准化。
驱动分离的核心原则就三条:
- 接口统一:所有驱动暴露相同的API,比如
drv_open()、drv_read()、drv_write() - 数据隐藏:上层不直接操作寄存器,只通过句柄或指针访问
- 配置独立:硬件参数(引脚、时钟、波特率)放在单独配置文件里
来看一个实际例子。假设你要从STM32换到GD32,传统做法是:
// 传统做法:到处改
// stm32_uart.c
USART1->DR = data;
// 改成
// gd32_uart.c
USART0->DATA = data;
如果用了驱动分离:
// 上层代码完全不用动
int ret = uart_write(handle, data, len);
// 底层只需要实现这个接口
// stm32版本
int uart_write(void *handle, uint8_t *data, uint32_t len) {
// STM32寄存器操作
}
// gd32版本
int uart_write(void *handle, uint8_t *data, uint32_t len) {
// GD32寄存器操作
}
嗯,这里要注意:驱动分离不是银弹。小项目(比如一个传感器+一个LED)强行分层反而增加复杂度。我建议项目超过3个外设,或者需要跨平台移植时,再上分层架构。
1.4 分层架构全景图
下面这张图,是我自己画的分层架构全景。你可以看到数据是怎么从硬件一路流到应用的:
这张图你看懂了吗?数据从硬件往上走,命令从应用往下传。每一层都只和相邻层打交道,绝不越级。
1.5 为什么我坚持用分层架构
说了这么多,总结一下分层架构的好处:
- 可移植性:换MCU只改DRV层,APP和HAL基本不动
- 可测试性:可以单独测试每一层,不用等硬件就绪
- 可维护性:出问题定位快,改代码影响范围小
- 团队协作:不同人负责不同层,并行开发不冲突
我记得有一次,客户临时要求换MCU型号。因为用了分层架构,我只花了两天就完成了驱动移植,APP层一行代码没改。客户都惊了,问我们是不是早就知道要换芯片。其实哪有什么预知,不过是架构设计得好罢了。
好了,这一章的内容就到这里。分层架构是后面所有章节的基础,理解透了,后面的驱动分离、接口设计、测试策略才能学得扎实。