一、嵌入式软件架构概述

嵌入式软件架构,说白了就是一套组织代码的规则。我做了十几年嵌入式开发,见过太多项目因为一开始没想好架构,后期改得痛不欲生。今天咱们就来聊聊这个东西到底是什么,为什么值得你花时间琢磨。

1.1 什么是嵌入式软件架构

嵌入式软件架构,是代码的骨架。它决定了你的任务怎么跑、数据怎么流、模块之间怎么通信。一个好的架构,能让你的代码像搭积木一样清晰。一个烂架构,嗯,就像一团乱麻,扯都扯不开。

我个人习惯把架构理解为三个层次:

  • 宏观架构:整个系统的模块划分,比如传感器采集、通信协议、控制算法各自独立
  • 微观架构:单个模块内部怎么组织,比如用状态机还是回调函数
  • 运行时架构:任务调度方式,轮询还是RTOS

核心观点:架构不是画出来的,是跑出来的。你在纸上画得再漂亮,跑起来崩了就是白搭。

1.2 为什么需要架构设计

你可能会问:小项目也要架构吗?我的回答是:要,但可以简单点。

我记得刚入行时接手过一个温控器项目,前任工程师把所有代码都写在main函数里,两千多行。加一个功能要改五个地方,改完还经常出bug。后来我花了三天重构,分了采集、控制、显示三个模块,代码量反而少了,维护起来轻松多了。

架构设计的好处,我总结了几点:

好处 说明 我踩过的坑
可维护性 改一个功能不影响其他模块 曾经改了一个定时器参数,结果通信协议崩了
可移植性 换芯片时只改底层驱动 从STM32F1换到F4,重写了整个应用层
可测试性 每个模块能单独测 没有架构的项目,测试只能全系统跑
团队协作 多人并行开发不打架 两个人同时改一个文件,合并时想哭

避坑指南:我曾经见过一个团队,花了两周画架构图,结果代码写出来跟架构完全两码事。架构要落地,别只停留在PPT上。

1.3 常见架构模式

嵌入式领域常见的架构模式就那么几种,各有各的适用场景。我按复杂度从低到高给你捋一遍。

1.3.1 轮询架构

最简单的架构。主循环里挨个检查每个任务,该执行就执行。代码长这样:

while(1) {
    check_sensor();   // 检查传感器
    process_data();   // 处理数据
    update_display(); // 更新显示
    handle_comm();    // 处理通信
}

优点:简单、无并发问题、资源占用少。
缺点:实时性差,一个任务卡住,后面全等。

我建议这种架构只用在非常简单的系统里,比如一个LED灯控制器。但凡有多个传感器或通信需求,就别用轮询了。

1.3.2 前后台架构

前台是中断,后台是主循环。中断处理紧急事件,主循环处理常规任务。

// 前台:中断服务
void TIM_IRQHandler() {
    flag_1ms = 1;  // 只设标志,不做复杂处理
}

// 后台:主循环
while(1) {
    if(flag_1ms) {
        flag_1ms = 0;
        scan_key();  // 扫描按键
    }
    if(flag_10ms) {
        flag_10ms = 0;
        update_display();  // 更新显示
    }
}

这种架构在工业控制里很常见。我做过的一个PLC项目就是前后台架构,中断处理高速输入,主循环处理逻辑运算。效果不错,但要注意中断服务程序要短,别在里面做复杂运算。

经验之谈:前后台架构里,中断里只做三件事:读寄存器、设标志、清中断。其他都交给主循环。

1.3.3 状态机架构

状态机适合处理有明确状态转换的系统。比如一个按键处理:

typedef enum {
    STATE_IDLE,
    STATE_PRESSED,
    STATE_LONG_PRESS,
    STATE_RELEASED
} key_state_t;

key_state_t state = STATE_IDLE;

void key_process() {
    switch(state) {
        case STATE_IDLE:
            if(key_pressed()) state = STATE_PRESSED;
            break;
        case STATE_PRESSED:
            if(key_hold_time > 1000) state = STATE_LONG_PRESS;
            else if(key_released()) state = STATE_RELEASED;
            break;
        // ...
    }
}

状态机的优势是逻辑清晰,每个状态做什么一目了然。我在做智能门锁项目时,整个开锁流程用了五个状态机嵌套,调试起来比纯if-else爽多了。

1.3.4 RTOS架构

实时操作系统,把任务拆成多个线程,由内核调度。适合复杂系统:

void task_sensor(void *arg) {
    while(1) {
        read_sensor();
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void task_control(void *arg) {
    while(1) {
        control_algorithm();
        vTaskDelay(pdMS_TO_TICKS(50));
    }
}

RTOS的好处是任务隔离、实时性好、资源管理方便。但代价是内存占用大、调试复杂、有优先级反转等问题。

我个人建议:任务数超过5个,或者有严格实时要求(毫秒级),才上RTOS。别为了用RTOS而用RTOS。

1.3.5 事件驱动架构

系统不主动轮询,而是等事件来了再处理。事件可以是中断、消息、定时器触发。

// 事件处理器
void event_handler(event_t event) {
    switch(event.type) {
        case EVT_KEY_PRESS:
            handle_key(event.data);
            break;
        case EVT_SENSOR_READY:
            process_sensor_data(event.data);
            break;
        case EVT_TIMEOUT:
            reset_system();
            break;
    }
}

事件驱动架构的优点是低功耗、响应快。我在做物联网终端时就用这种架构,平时休眠,有事件才唤醒,电池能用一年。

1.4 架构模式对比

我把这几种架构放在一起对比,方便你选型:

架构模式 复杂度 实时性 资源占用 适用场景
轮询 低 差 极低 简单控制、LED灯
前后台 中 中 低 工业控制、家电
状态机 中 中 低 协议解析、按键处理
RTOS 高 好 高 复杂系统、多任务
事件驱动 中高 好 中 低功耗、物联网

1.5 架构选型思路

选架构不是越复杂越好。我见过有人用RTOS做个温度计,纯属杀鸡用牛刀。我的建议是:

  • 任务少于3个:轮询或前后台就够了
  • 有明确状态转换:上状态机
  • 任务多且实时要求高:RTOS
  • 低功耗是关键:事件驱动

重要提醒:架构选型要考虑团队能力。你一个人能驾驭RTOS,不代表整个团队都能。我曾经在一个项目里用了复杂的消息队列架构,结果新同事接手时完全看不懂,最后不得不重构。

1.6 本章知识体系

下面这张图把本章的核心内容串起来了,你可以对照着回顾:

嵌入式软件架构知识体系 嵌入式软件架构 什么是架构 为什么需要架构 常见架构模式 宏观架构 微观架构 运行时架构 可维护性 可移植性 可测试性 团队协作 轮询 前后台 状态机 RTOS 事件驱动 选型原则:够用就好,别过度设计 团队能力 > 技术先进性

这张图把架构的三个方面和五种模式串在了一起。你可以在实际项目中对照着看,选型时心里就有谱了。

我的建议:初学者先从前后台架构入手,把中断和主循环的配合练熟。等遇到瓶颈了,再学RTOS或事件驱动。别一上来就搞复杂的,容易劝退。