27、嵌入式系统架构:裸机架构、RTOS集成、低功耗设计

嵌入式系统架构这个话题,说实话,是很多工程师从入门到进阶的一道坎。我做了十几年嵌入式开发,见过太多人在裸机和RTOS之间反复横跳,也见过不少产品因为低功耗设计不到位,电池续航直接腰斩。今天咱们就把这三个核心问题掰开揉碎了讲清楚。

一、裸机架构:简单但不简陋

裸机架构,说白了就是没有操作系统的程序。主循环+中断,这就是它的全部。很多人觉得裸机就是"低级"的代名词,其实不然。我做过一个工业传感器项目,每秒采样1000次,用裸机跑得稳稳的,换成RTOS反而因为任务切换开销导致采样抖动。

裸机架构的核心模式:

  • 超级循环(Super Loop):while(1)里轮询各个任务
  • 前后台系统:中断处理紧急事件,主循环处理非实时任务
  • 状态机驱动:每个模块用状态机管理,避免全局变量满天飞

来看一个典型的前后台系统代码框架:

// 前后台系统示例
volatile uint8_t g_flag_adc_ready = 0;
volatile uint8_t g_flag_uart_rx = 0;

void ADC_IRQHandler(void) {
    // 前台:中断服务,只做最紧急的事
    adc_value = ADC->DR;
    g_flag_adc_ready = 1;  // 设置标志,通知后台处理
}

void UART_IRQHandler(void) {
    rx_buffer[rx_index++] = USART->DR;
    g_flag_uart_rx = 1;
}

int main(void) {
    // 初始化硬件
    system_init();
    
    while(1) {
        // 后台:主循环处理非实时任务
        if(g_flag_adc_ready) {
            g_flag_adc_ready = 0;
            process_adc_data(adc_value);  // 滤波、换算等
        }
        
        if(g_flag_uart_rx) {
            g_flag_uart_rx = 0;
            process_uart_command();  // 解析命令
        }
        
        // 其他轮询任务
        led_toggle();
        check_temperature();
    }
}

我的经验:裸机架构最适合任务少、实时性要求高、资源受限的场景。我曾经在一个Cortex-M0芯片上跑4路PID控制,裸机+定时器中断的方案比RTOS方案节省了60%的RAM。

二、RTOS集成:让复杂系统有序运转

当项目复杂度上来,比如同时要处理网络协议栈、GUI、多个传感器、文件系统,裸机就有点力不从心了。这时候RTOS的价值就体现出来了。

RTOS的核心思想是"分时复用"。把CPU时间切成片,每个任务分到自己的时间片。但这里有个坑——很多人以为用了RTOS就不用管时序了,这是大错特错。

避坑指南:我曾经接手过一个项目,工程师把20多个任务全放在同一个优先级,结果系统跑起来后,高频率的任务把低频率的任务饿死了。后来我重新设计了优先级,把关键任务(如电机控制)放在最高优先级,日志打印这种放在最低优先级,系统才稳定下来。

RTOS集成的几个关键点:

  • 任务划分:每个任务做一件事,不要贪多。我习惯把任务执行时间控制在5ms以内
  • 同步机制:信号量、消息队列、事件标志组,选对工具很重要
  • 内存管理:静态分配优先,动态分配要谨慎
// FreeRTOS任务创建示例
void vTaskMotorControl(void *pvParameters) {
    TickType_t xLastWakeTime = xTaskGetTickCount();
    
    for(;;) {
        // 每10ms执行一次
        vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(10));
        
        // 读取编码器
        encoder_value = read_encoder();
        
        // PID计算
        pwm_output = pid_calculate(target_speed, encoder_value);
        
        // 设置PWM
        set_motor_pwm(pwm_output);
    }
}

void vTaskSensorRead(void *pvParameters) {
    for(;;) {
        // 等待传感器数据就绪信号量
        if(xSemaphoreTake(xSensorSemaphore, portMAX_DELAY) == pdTRUE) {
            sensor_data = read_sensor();
            // 发送到处理任务
            xQueueSend(xSensorQueue, &sensor_data, 0);
        }
    }
}

三、低功耗设计:从硬件到软件的全面考量

低功耗设计,说白了就是"不该干活的时候别干活,该干活的时候快点干完继续睡"。我做过一个电池供电的物联网终端,目标续航3年。刚开始设计时功耗超标3倍,后来一步步优化才达标。

低功耗设计的几个层次:

层次 策略 典型做法
硬件层 选型与电路 低功耗MCU、DCDC转换器、分时供电
软件层 睡眠与唤醒 WFI指令、定时唤醒、事件唤醒
系统层 任务调度 批处理、动态频率调整、外设关断

低功耗设计核心原则:

  • 能睡就睡:空闲时立即进入低功耗模式
  • 能关就关:不用的外设及时关闭时钟
  • 能快就快:处理完数据立刻进入睡眠

来看一个典型的低功耗设计代码:

// 低功耗模式切换示例
void enter_sleep_mode(void) {
    // 关闭不用的外设时钟
    RCC->AHBENR &= ~(RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN);
    RCC->APB1ENR &= ~(RCC_APB1ENR_USART2EN | RCC_APB1ENR_I2C1EN);
    
    // 配置唤醒源
    RTC->CR |= RTC_CR_WUTE;  // 使能RTC唤醒
    NVIC_EnableIRQ(RTC_WKUP_IRQn);
    
    // 进入停止模式
    PWR->CR |= PWR_CR_LPDS;   // 低功耗深度睡眠
    PWR->CR |= PWR_CR_CWUF;   // 清除唤醒标志
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
    __WFI();  // 等待中断,进入睡眠
}

void RTC_WKUP_IRQHandler(void) {
    // 唤醒后恢复外设时钟
    RCC->AHBENR |= RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_I2C1EN;
    
    // 采集数据
    collect_sensor_data();
    
    // 如果需要发送,打开无线模块
    if(data_need_to_send()) {
        enable_wireless_module();
        send_data();
        disable_wireless_module();
    }
    
    // 再次进入睡眠
    enter_sleep_mode();
}

我的经验:低功耗优化是个迭代过程。先用电流表测出基线功耗,然后逐个模块关掉,看哪个模块耗电最多。我曾经发现一个GPIO上拉电阻没配置好,导致漏电流多了50uA,优化后整机功耗降了30%。

四、三种架构的选择策略

你可能会问:到底该选哪种架构?我的建议是看项目需求:

  • 裸机:任务少于5个,实时性要求高(微秒级),资源极度受限(如8位MCU)
  • RTOS:任务多于10个,需要复杂同步,有网络/文件系统等中间件
  • 混合架构:关键任务用裸机中断处理,非关键任务用RTOS管理

嗯,这里要注意一点:不要为了用RTOS而用RTOS。我见过有人在一个只有2个任务的系统里硬塞了FreeRTOS,结果代码量翻倍,调试难度大增,得不偿失。

嵌入式系统架构选择决策树 裸机架构 超级循环 + 中断 任务数 < 5 实时性:微秒级 资源:极低 RTOS架构 任务调度 + 同步 任务数 > 10 实时性:毫秒级 资源:中等 混合架构 裸机 + RTOS 结合 关键任务:裸机 非关键任务:RTOS 资源:灵活 低功耗设计贯穿所有架构 睡眠模式 | 外设关断 | 动态频率 | 批处理 资源极度受限 任务复杂需同步 关键与非关键并存 8位MCU, 2KB RAM Cortex-M4, 64KB+ RAM Cortex-M0+, 16KB RAM

五、实战中的避坑指南

最后分享几个我踩过的坑:

  • 中断优先级:RTOS的滴答定时器中断优先级要设置合理,我曾经设得太高,导致其他中断响应延迟
  • 堆栈大小:RTOS每个任务都要分配独立堆栈,我习惯留30%余量,否则跑着跑着就栈溢出了
  • 低功耗与调试:进入低功耗模式后,调试器可能连不上芯片。我一般会在调试阶段禁用低功耗,量产时再开启
  • 外设初始化顺序:先初始化时钟,再初始化外设,最后初始化RTOS。顺序错了可能死机

我曾经踩过的一个大坑:在一个RTOS项目中,我在中断服务函数里调用了vTaskDelay(),结果系统直接卡死。后来才想起来,中断里不能调用阻塞型API。正确的做法是在中断里发信号量,让任务去处理。

嵌入式系统架构没有银弹。裸机、RTOS、低功耗设计,这三者不是互斥的,而是可以组合使用的。关键是要理解每种方案的适用场景和代价。嗯,希望今天的分享能帮你少走一些弯路。


公众号:蓝海资料掘金营,微信deep3321