本质:

前期增加一点设计成本

⬇️

后期降低大量维护成本


🔴 第一道坎:复杂度失控

⭐ 问题本质

很多人觉得:

  • 单片机代码就几千行,应该不会太复杂

实际上:

⚠️ 嵌入式复杂度取决于外设和接口数量,而不是代码量。

一个设备可能同时包含:

  • UART

  • SPI

  • I2C

  • CAN

  • Flash

  • OTA

  • 按键

  • LED

  • 传感器

每增加一个模块:

📈 复杂度 +1

模块之间开始交互后:

📈📈 复杂度指数增长


✅ 架构解决方案

通过:

  • 模块化

  • 分层设计

  • 接口隔离

实现:

✅ 独立理解

✅ 独立修改

✅ 独立测试

❌ 不做架构

所有代码堆一起:

while(1)
{
    wifi_connect();
​
    temp = ds18b20_read();
​
    oled_show(temp);
​
    oled_show_time();
​
    key_scan();
​
    if(key_pressed)
    {
        wifi_reconnect();
    }
}

如果现在需求变了:

DS18B20
↓
换成DHT11

你可能要改:

main.c
oled.c
wifi.c
config.c

到处找。

✅ 模块化

把功能拆开:

wifi.c
oled.c
sensor.c
key.c

主函数:

while(1)
{
    Sensor_Task();
    Display_Task();
    Wifi_Task();
    Key_Task();
}

此时:

独立理解

想看温度采集:

直接看sensor.c

不用管OLED。

独立修改

DS18B20换DHT11:

只改:

sensor.c
sensor.h

其它模块不用动。

独立测试

直接测试:

Sensor_Task();

看温度是否正确。

✅ 分层设计

进一步升级:

Application
↓
Driver
↓
Hardware

例如:

天气显示
↓
OLED驱动
↓
SPI
↓
STM32

业务层:

DisplayTemp(25);

根本不知道:

SPI怎么发
GPIO怎么控制

如果以后:

OLED
↓
TFT屏

只改驱动层:

oled.c

业务层不动。

✅ 接口隔离

例如:

先定义接口:

float Sensor_ReadTemp(void);

业务层:

temp = Sensor_ReadTemp();

DS18B20实现:

float Sensor_ReadTemp(void)
{
    return DS18B20_Read();
}

DHT11实现:

float Sensor_ReadTemp(void)
{
    return DHT11_Read();
}

业务层代码:

temp = Sensor_ReadTemp();

一行都不用改。



🔵 第二道坎:硬件变化

⭐ 问题本质

嵌入式开发中:

  • MCU缺货

  • 芯片升级

  • 供应商替换

  • 产品迭代

都是常态。


✅ 架构解决方案 HAL硬件抽象层

核心思想:

🟢 业务代码不碰寄存器,只调用接口。

例如:

uart_send(data);

而不是:

USART1->DR = data;

⭐分层骨架

  app
   ↓
 service
   ↓
 driver 
   ↓
HAL接口层
   ↓
芯片寄存器

更换芯片:

✅ 只修改HAL

✅ 业务代码保持不变

抽象不是免费的

代价:

  • ROM增加

  • RAM增加

  • 调用开销增加

高速路径:

  • ADC采样

  • PWM控制

  • 电机控制

允许适度访问底层。

举个最简单的例子

直接操作寄存器

USART1->DR = data;

CPU执行:

写寄存器
↓
结束

可能就几条汇编指令。

加HAL

uart_send(data);

内部:

void uart_send(uint8_t data)
{
    USART1->DR = data;
}

CPU执行:

调用函数
↓
保存现场
↓
压栈
↓
跳转
↓
执行代码
↓
返回

比直接写:

USART1->DR = data;

多了额外开销。

为什么ROM增加

以前:

USART1->DR = data;

1行代码。


现在:

uart_send()
uart_recv()
uart_init()
uart_deinit()

多了很多函数。

这些函数最终都会编译进:

Flash(ROM)

为什么调用开销增加

假设电机控制:

控制周期:50us


如果你每次:

Motor_Control() (app)
    ↓
Driver_PWM_Set() (driver)
     ↓
HAL_PWM_Set()     (hal)
     ↓
Register_Write()   (寄存器)

经过:

3层调用

每层都压栈、出栈。

累计:

几微秒

都有可能。

🎯 本质

🟢 HAL解决的是可移植性问题。


🟡 第三道坎:实时性与可维护性的矛盾

⭐ 问题本质

嵌入式必须满足实时性:

  • 电机控制

  • 通信协议

  • 传感器采样

  • 按键扫描

都有严格时间要求。


✅ 架构解决方案

找到平衡点:

实时性
   ↔
可维护性

三种主流架构

image-20260607145957440

① 前后台架构

结构:

中断
 ↓
主循环

特点:

  • 简单

  • 资源占用小

适用:

  • 家电

  • 玩具

  • 仪表


② 时间触发架构(软件定时器+工作队列)

结构:

1ms任务
10ms任务
100ms任务

特点:

✅ 时序可预测

适用:

  • 汽车电子

  • 医疗设备


③ RTOS架构

优点:

✅ 灵活

缺点:

  • 死锁

  • 优先级反转

  • 栈溢出

适用:

  • 中大型项目



🟣 第四道坎:状态爆炸

⭐ 问题本质

例如充电器:

  • 待机

  • 检测

  • 慢充

  • 快充

  • 满电

  • 过温

  • 过压

  • 故障恢复

状态越来越多。


❌ 不做状态管理

假设有个电池管理系统(BMS),一开始状态很简单:

STATE_IDLE        待机
STATE_CHARGE      充电
STATE_FULL        满电

代码:

if(state == STATE_IDLE)
{
    if(charger_insert)
    {
        state = STATE_CHARGE;
    }
}

if(state == STATE_CHARGE)
{
    Charge_Run();

    if(battery_full)
    {
        state = STATE_FULL;
    }
}

if(state == STATE_FULL)
{
    Charge_Stop();
}

完全没问题。


第一次需求变更

老板说:

增加温度保护

温度超过60℃:

停止充电
进入保护状态

你加:

if(state == STATE_CHARGE)
{
    if(temp > 60)
    {
        state = STATE_PROTECT;
    }
}

以及:

if(state == STATE_PROTECT)
{
    Charge_Stop();
}

还能接受。


第二次需求变更

增加:

过压保护

于是:

if(state == STATE_CHARGE)
{
    if(temp > 60)
    {
        state = STATE_PROTECT;
    }

    if(voltage > 4.25)
    {
        state = STATE_PROTECT;
    }
}

第三次需求变更

增加:

过流保护

继续:

if(state == STATE_CHARGE)
{
    if(temp > 60)
        state = STATE_PROTECT;

    if(voltage > 4.25)
        state = STATE_PROTECT;

    if(current > 20)
        state = STATE_PROTECT;
}

第四次需求变更

增加:

故障恢复

要求:

温度恢复正常
↓
自动恢复充电

于是:

if(state == STATE_PROTECT)
{
    if(temp < 50)
    {
        state = STATE_CHARGE;
    }
}

第五次需求变更(增加状态)

增加:

涓流充电

老板说:

电压太低
先涓流
再快充

状态变成:

IDLE
↓
TRICKLE
↓
FAST_CHARGE
↓
FULL

这时候灾难来了。

原来:

if(state == STATE_CHARGE)

出现了几十处。

你得思考:

涓流充电算不算充电?

这里:

if(state == STATE_CHARGE)
{
    Led_Blink();
}

要不要改?


这里:

if(state == STATE_CHARGE)
{
    Save_Charge_Time();
}

要不要改?


这里:

if(state == STATE_CHARGE)
{
    Can_Send_Status();
}

要不要改?


这里:

if(state == STATE_CHARGE)
{
    Fan_Control();
}

要不要改?


你必须全工程搜索:

STATE_CHARGE

可能搜出来:

87处

如果漏掉一个:

比如:

if(state == STATE_CHARGE)
{
    Can_Send_Status();
}

没改。

结果:

进入涓流充电
↓
CAN状态不上报
↓
上位机显示异常

Bug来了。


为什么会全崩

因为:

状态逻辑

散落在:

charge.c
can.c
led.c
fan.c
alarm.c
storage.c

每个文件都有:

if(state == xxx)

新增一个状态:

TRICKLE

意味着:

所有文件
所有if
全部检查

这就是:

If-Else地狱

✅ 架构解决方案

FSM有限状态机

状态统一管理:

switch(state)
{
    case IDLE:
        ...
        break;

    case TRICKLE:
        ...
        break;

    case FAST_CHARGE:
        ...
        break;

    case FULL:
        ...
        break;
}

状态转移统一写:

IDLE
↓ 插入充电器
TRICKLE

TRICKLE
↓ 电压达到3.0V
FAST_CHARGE

FAST_CHARGE
↓ 满电
FULL

以后新增:

PRE_CHARGE

只需要:

增加一个状态
增加几条转移规则

而不是:

全工程搜 STATE_CHARGE
87处逐个分析

常见小架构

事件驱动+消息队列

按键模块
 ↓
发送事件
 ↓
业务模块处理

实现解耦。


Ring Buffer

⭐ 嵌入式里的问题

例如串口接收:

MCU:

串口每1ms收到1字节

但是主循环:

10ms才处理一次数据

如果直接处理:

void USART_IRQHandler(void)
{
    data = USART1->DR;
}

那么:

1ms 来一个字节

而你:

10ms 才读一次

结果:

前面9个字节丢失

⭐ 解决办法:环形缓冲区

中断负责:

收到数据
↓
放入缓冲区

主循环负责:

从缓冲区取数据
↓
慢慢处理

结构:

UART
 ↓
中断
 ↓
RingBuffer
 ↓
主循环

这样:

接收快
处理慢
也不会丢数据

参数中心化

先看最原始的写法:

#define TEMP_LIMIT     80
#define SPEED_LIMIT    3000
#define CURRENT_LIMIT  20

代码里到处用:

if(temp > TEMP_LIMIT)

if(speed > SPEED_LIMIT)

if(current > CURRENT_LIMIT)

项目小的时候没问题。

问题1:参数到处都是

比如客户突然说:

温度保护从80℃改成75℃

你就得:

全项目搜索
TEMP_LIMIT

或者更离谱:

if(temp > 80)

这种魔数直接写死。

最后:

改一个参数
找半天代码

问题2:不同客户需求不同

客户A:

过温保护 80℃

客户B:

过温保护 90℃

客户C:

过温保护 70℃

如果全靠:

#define TEMP_LIMIT 80

那意味着:

每个客户一套代码

维护直接爆炸。

单独搞一个参数模块:

typedef struct
{
    uint16_t temp_limit;
    uint16_t speed_limit;
    uint16_t current_limit;
}sys_param_t;

sys_param_t g_param;

初始化:

g_param.temp_limit = 80;
g_param.speed_limit = 3000;
g_param.current_limit = 20;

业务代码:

if(temp > g_param.temp_limit)
{
    ...
}

🎯 本质

🟢 FSM解决的是状态复杂度问题。


🔥 最终总结

嵌入式软件架构本质解决四件事:

问题 解决方案
🔴 复杂度失控 模块化+分层+接口隔离
🔵 硬件变化 HAL抽象层
🟡 实时性矛盾 前后台 / 时间触发 / RTOS
🟣 状态爆炸 FSM状态机

常见小架构:事件驱动+消息队列,RingBuffer,参数中心化

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐