1. 课程导论:为什么需要软件架构?
各位同学好,我是这门课的主讲。在嵌入式这行摸爬滚打了十几年,我见过太多项目从意气风发走向一地鸡毛。今天咱们开篇第一讲,不急着上代码,先聊聊一个根本问题:嵌入式系统到底需不需要软件架构?
我的答案是:不仅需要,而且比纯软件领域更需要。为什么?你想想看,一个跑在 Linux 服务器上的程序崩了,最多重启一下。但一个嵌入式设备——比如心脏起搏器、汽车 ECU、工业控制器——如果软件架构混乱导致崩溃,那可能就是人命关天的大事。
1.1 嵌入式系统的特殊性
嵌入式系统不是“小一号的 PC”。它有自己独特的脾气。我刚开始做嵌入式时,把 PC 上的那套开发习惯直接搬过来,结果被现实狠狠教育了一顿。
具体来说,嵌入式系统有三大特殊性:
| 特性 | 描述 | 对架构的影响 |
|---|---|---|
| 资源受限 | RAM 可能只有几 KB~几百 KB,Flash 几 MB~几十 MB,CPU 主频几十 MHz~几百 MHz | 架构必须轻量,不能引入过多抽象层,避免内存碎片和栈溢出 |
| 实时性 | 任务必须在规定时间内完成,否则系统失效(硬实时 vs 软实时) | 架构必须支持优先级调度、中断响应、时间确定性 |
| 可靠性 | 设备可能连续运行数年不重启,环境恶劣(高温、振动、电磁干扰) | 架构必须包含看门狗、冗余、错误恢复、状态机等机制 |
说白了,嵌入式架构师的工作就是在“资源有限”和“要求无限”之间找平衡。我曾经在一个只有 32KB RAM 的项目上做 TCP/IP 协议栈,那感觉就像在针尖上跳舞——每一字节都得精打细算。
1.2 没有架构的后果
有人可能会说:“我写个单片机程序,就几个功能,要什么架构?”嗯,我年轻时也这么想。
结果呢?项目做到一半,需求变了。客户说:“老王,加个 OTA 升级功能吧。”我一看代码,所有功能都揉在一个 main.c 里,全局变量满天飞,中断服务函数里调了延时函数……改一个地方崩三个地方。最后只能重写。
这就是典型的“架构债务”。没有架构的代码,前期跑得飞快,后期寸步难行。
1.3 课程目标与学习方法
这门课的目标很明确:让你掌握嵌入式软件架构设计的核心方法,尤其是接口定义和内聚设计。
具体来说,学完这门课,你应该能:
- 理解分层架构、模块化、接口隔离等原则在嵌入式场景下的落地方式
- 掌握如何定义清晰、稳定的模块接口(API、回调、消息、共享内存)
- 学会用状态机、事件驱动、任务调度等模式组织代码
- 具备评估和重构现有嵌入式软件架构的能力
学习方法上,我建议你:
- 动手实践——每章都有代码示例,别只看,要自己敲一遍
- 带着问题学——想想你当前项目中的痛点,看课程能不能解决
- 多问为什么——架构设计没有标准答案,只有 trade-off
1.4 本章知识体系
下面这张图概括了本章的核心脉络,也是整门课的知识地图:
1.5 一个简单的例子
为了让你直观感受“有架构”和“没架构”的区别,我举个 LED 闪烁的例子。
没有架构的写法:
// main.c
#include "stm32f1xx.h"
void delay(volatile uint32_t count) {
while(count--);
}
int main(void) {
RCC->APB2ENR |= (1 << 4); // 使能 GPIOC 时钟
GPIOC->CRH = 0x33333333; // 配置为推挽输出
while(1) {
GPIOC->ODR ^= (1 << 13); // 翻转 PC13
delay(500000);
}
}
这段代码能跑,但问题很多:硬件寄存器直接操作、延时用空循环、没有模块划分。如果明天要改成 PWM 控制亮度,或者换一个 MCU,基本得重写。
有架构的写法:
// led.h
#ifndef LED_H
#define LED_H
#include <stdint.h>
void led_init(void);
void led_on(void);
void led_off(void);
void led_toggle(void);
#endif
// led.c
#include "led.h"
#include "stm32f1xx.h"
void led_init(void) {
RCC->APB2ENR |= (1 << 4);
GPIOC->CRH = 0x33333333;
}
void led_on(void) {
GPIOC->ODR &= ~(1 << 13);
}
void led_off(void) {
GPIOC->ODR |= (1 << 13);
}
void led_toggle(void) {
GPIOC->ODR ^= (1 << 13);
}
// main.c
#include "led.h"
#include "timer.h"
int main(void) {
led_init();
timer_init(500); // 500ms 定时
while(1) {
// 主循环做其他事情
}
}
// timer.c 中定时回调
void timer_callback(void) {
led_toggle();
}
看到了吗?通过定义清晰的接口(led.h),我们把硬件操作封装起来。上层代码只关心“开灯”“关灯”“翻转”,不关心具体怎么操作寄存器。这就是接口隔离和模块化的雏形。