单片机软件架构概述:什么是软件架构、为什么需要架构、常见架构模式对比

大家好,我是老李。做单片机开发十几年了,踩过的坑比吃过的盐还多。今天咱们聊聊软件架构——这个听起来有点虚,但实际上能救命的东西。

很多人觉得,单片机嘛,不就是写个循环,加点中断,完事了?嗯,我刚开始也是这么想的。直到有一次,一个智能家居项目,功能越加越多,代码改到后面我自己都看不懂了。客户要加个新功能,我愣是改了一个星期,还改出了三个bug。从那以后,我彻底明白了:没有架构的代码,就是给自己挖坑。

什么是软件架构?

说白了,软件架构就是代码的骨架。它决定了你的程序怎么组织、模块之间怎么通信、数据怎么流动。

我习惯把架构比作盖房子。你想想看,没有设计图纸,直接砌砖,能盖出高楼吗?能,但大概率是危房。软件架构就是那张图纸。

具体到单片机,架构要回答几个问题:

  • 主循环里该放什么?
  • 中断里该处理什么?
  • 任务之间怎么同步?
  • 数据怎么共享?

这些问题想清楚了,代码写起来就顺了。

为什么需要架构?

我遇到过不少工程师,上来就写代码,写到一半发现逻辑乱了。为什么会这样?因为没有架构。

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

  • 可维护性:三个月后,你还能看懂自己的代码。我曾经接手过一个项目,前任工程师离职了,代码没有架构,我花了整整两周才理清楚逻辑。那两周,我每天都在骂人。
  • 可扩展性:加功能不用大改。我记得有个项目,客户临时要加个蓝牙控制。因为有架构,我只加了一个模块,两天就搞定了。同事问我怎么这么快?我说,架构好,改起来就是爽。
  • 可靠性:减少bug。架构清晰了,逻辑就不容易乱。我有个习惯,写代码前先画架构图。画清楚了再动手,bug率至少降一半。
  • 团队协作:大家分工明确。你写驱动,我写应用,他写协议栈。各干各的,互不干扰。

核心观点:没有架构的项目,前期省时间,后期加倍还。我见过太多项目死在维护阶段。

常见架构模式对比

单片机开发中,最常见的架构模式有三种:前后台系统、RTOS(实时操作系统)、状态机。咱们一个一个说。

1. 前后台系统

这是最传统的架构。前台是中断,后台是主循环。中断处理紧急事件,主循环处理常规任务。

代码大概长这样:

void main(void)
{
    // 初始化
    SystemInit();
    
    while(1)
    {
        // 后台任务
        Task1();
        Task2();
        Task3();
    }
}

// 中断服务函数
void TIM_IRQHandler(void)
{
    // 前台处理
    Flag_Tick = 1;
}

优点很明显:简单、资源占用少。我刚开始做开发时,用的就是这种架构。一个8位单片机,跑得飞起。

但缺点也很致命:实时性差。如果某个任务卡住了,后面的任务都得等。我曾经在一个项目中,因为一个延时函数没写好,导致整个系统响应慢了200毫秒。客户投诉说按键不灵敏,我查了三天才找到原因。

适用场景:功能简单、任务少、对实时性要求不高的项目。比如电子钟、温度计、简单的遥控器。

避坑指南:我曾经在前后台系统中,把大量计算放在中断里。结果中断时间太长,主循环几乎没机会跑。系统直接死机了。记住:中断里只做最紧急的事,别做复杂计算。

2. RTOS(实时操作系统)

RTOS就是给单片机装了个小系统。任务可以独立运行,互不干扰。常用的有FreeRTOS、uC/OS、RT-Thread等。

代码示例:

void Task_LED(void *pvParameters)
{
    while(1)
    {
        LED_Toggle();
        vTaskDelay(500);  // 延时500ms
    }
}

void Task_UART(void *pvParameters)
{
    while(1)
    {
        UART_Send("Hello\n");
        vTaskDelay(1000);
    }
}

int main(void)
{
    xTaskCreate(Task_LED, "LED", 128, NULL, 1, NULL);
    xTaskCreate(Task_UART, "UART", 128, NULL, 1, NULL);
    vTaskStartScheduler();
}

RTOS的好处是:任务独立、实时性好、代码清晰。我做过一个多传感器采集项目,用了RTOS后,每个传感器一个任务,互不干扰。调试起来特别方便。

但RTOS也有代价:占用资源多、学习成本高。我记得第一次用FreeRTOS时,光是理解任务调度就花了一周。而且,如果任务优先级设置不当,容易出现优先级反转的问题。

适用场景:功能复杂、任务多、对实时性要求高的项目。比如无人机、机器人、工业控制器。

个人经验:我建议初学者先从前后台系统开始,等遇到瓶颈了再上RTOS。别一上来就搞RTOS,容易把自己搞晕。

3. 状态机

状态机是一种思想,不是具体的系统。它把系统分成若干个状态,每个状态处理特定的事情。状态之间通过事件切换。

代码示例:

typedef enum
{
    STATE_IDLE,
    STATE_RUNNING,
    STATE_ERROR
} SystemState_t;

SystemState_t currentState = STATE_IDLE;

void StateMachine_Run(void)
{
    switch(currentState)
    {
        case STATE_IDLE:
            // 空闲处理
            if(Event_Start)
                currentState = STATE_RUNNING;
            break;
            
        case STATE_RUNNING:
            // 运行处理
            if(Event_Error)
                currentState = STATE_ERROR;
            if(Event_Stop)
                currentState = STATE_IDLE;
            break;
            
        case STATE_ERROR:
            // 错误处理
            if(Event_Reset)
                currentState = STATE_IDLE;
            break;
    }
}

状态机的优点是:逻辑清晰、容易调试。我做过一个按键扫描程序,用状态机实现了单击、双击、长按的识别。代码写得很干净,后来同事接手,一看就懂。

缺点呢?状态多了,管理起来麻烦。我曾经做过一个通信协议解析,状态有20多个,画状态图都画了半天。

适用场景:逻辑复杂、有明确状态切换的项目。比如按键处理、通信协议解析、菜单系统。

三种架构对比

特性 前后台系统 RTOS 状态机
资源占用 极低 较高
实时性 中等
代码复杂度 中等
可维护性
学习成本 中等
适用场景 简单项目 复杂项目 逻辑控制

怎么选?

说实话,没有最好的架构,只有最合适的。我个人的选择原则是:

  • 如果项目功能少于5个,用前后台系统。简单直接,不折腾。
  • 如果项目功能多、实时性要求高,上RTOS。别犹豫,后期改架构更痛苦。
  • 如果项目逻辑复杂、状态切换多,用状态机。哪怕配合前后台系统也行。

另外,这三种架构不是互斥的。我做过一个项目,主架构是RTOS,但某个任务内部用了状态机。两者结合,效果很好。

最后说一句:架构是工具,不是目的。别为了用架构而用架构。我见过有人用RTOS做电子钟,结果资源不够,还得换芯片。何必呢?

好了,这一章就聊到这里。下一章咱们聊聊怎么从零开始设计一个软件架构。到时候我会拿一个实际项目做例子,手把手教大家。