Skip to content

RTOS 选型与架构

FreeRTOS 核心概念

任务管理

c
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"

// 任务优先级定义(数字越大优先级越高)
#define PRIORITY_MODBUS_POLL    5   // 高优先级:实时采集
#define PRIORITY_DATA_PROCESS   4   // 中优先级:数据处理
#define PRIORITY_MQTT_SEND      3   // 中优先级:数据上报
#define PRIORITY_HEARTBEAT      2   // 低优先级:心跳
#define PRIORITY_IDLE           1   // 最低:空闲任务

// 任务栈大小(字,1字=4字节)
#define STACK_MODBUS    512
#define STACK_MQTT      1024
#define STACK_PROCESS   512

// 任务句柄
TaskHandle_t xModbusTask = NULL;
TaskHandle_t xMQTTTask = NULL;

// Modbus 采集任务
void vModbusPollTask(void *pvParameters) {
    TickType_t xLastWakeTime = xTaskGetTickCount();
    const TickType_t xPeriod = pdMS_TO_TICKS(1000);  // 1秒周期

    for (;;) {
        // 精确周期执行(补偿执行时间)
        vTaskDelayUntil(&xLastWakeTime, xPeriod);

        // 读取 Modbus 数据
        ModbusData_t data;
        if (Modbus_ReadRegisters(&data) == MODBUS_OK) {
            // 发送到处理队列
            xQueueSend(xDataQueue, &data, 0);  // 不等待,丢弃旧数据
        }
    }
}

// 创建任务
void vCreateTasks(void) {
    xTaskCreate(
        vModbusPollTask,        // 任务函数
        "ModbusPoll",           // 任务名称(调试用)
        STACK_MODBUS,           // 栈大小
        NULL,                   // 参数
        PRIORITY_MODBUS_POLL,   // 优先级
        &xModbusTask            // 任务句柄
    );
}

队列通信

c
// 定义数据结构
typedef struct {
    uint32_t timestamp;
    float voltage;
    float current;
    float soc;
    uint16_t alarm_flags;
} ModbusData_t;

// 创建队列(10个元素的缓冲)
QueueHandle_t xDataQueue;

void vInitQueues(void) {
    xDataQueue = xQueueCreate(10, sizeof(ModbusData_t));
    configASSERT(xDataQueue != NULL);
}

// 生产者:Modbus 采集任务
void vProducerTask(void *pvParameters) {
    ModbusData_t data;
    for (;;) {
        if (Modbus_ReadData(&data) == MODBUS_OK) {
            // 发送到队列,等待最多 10ms
            if (xQueueSend(xDataQueue, &data, pdMS_TO_TICKS(10)) != pdPASS) {
                // 队列满,记录丢弃事件
                xDropCount++;
            }
        }
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 消费者:数据处理任务
void vConsumerTask(void *pvParameters) {
    ModbusData_t data;
    for (;;) {
        // 等待队列数据(最多 5 秒)
        if (xQueueReceive(xDataQueue, &data, pdMS_TO_TICKS(5000)) == pdPASS) {
            ProcessData(&data);
            PublishToMQTT(&data);
        } else {
            // 超时,检查设备连接状态
            HandleCommTimeout();
        }
    }
}

互斥锁与信号量

c
// 互斥锁保护共享资源(串口)
SemaphoreHandle_t xUARTMutex;

void vInitMutex(void) {
    xUARTMutex = xSemaphoreCreateMutex();
}

// 安全的串口发送
BaseType_t UART_SendSafe(const uint8_t *data, size_t len) {
    // 获取互斥锁(最多等待 100ms)
    if (xSemaphoreTake(xUARTMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        HAL_UART_Transmit(&huart1, data, len, 1000);
        xSemaphoreGive(xUARTMutex);
        return pdTRUE;
    }
    return pdFALSE;  // 获取锁超时
}

// 二值信号量:中断通知任务
SemaphoreHandle_t xDataReadySemaphore;

// 中断服务程序(ISR)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    // 从 ISR 中释放信号量
    xSemaphoreGiveFromISR(xDataReadySemaphore, &xHigherPriorityTaskWoken);
    // 如果有更高优先级任务被唤醒,请求上下文切换
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// 等待数据就绪的任务
void vDataReceiveTask(void *pvParameters) {
    for (;;) {
        if (xSemaphoreTake(xDataReadySemaphore, pdMS_TO_TICKS(1000)) == pdTRUE) {
            ProcessReceivedData();
        }
    }
}

内存管理

c
// FreeRTOS 内存分配方案选择:
// heap_1: 只分配不释放(最简单,确定性)
// heap_2: 可释放,但有碎片(不推荐)
// heap_3: 封装 malloc/free(依赖 C 库)
// heap_4: 合并相邻空闲块(推荐)
// heap_5: 跨多个内存区域(大型系统)

// 推荐:heap_4,在 FreeRTOSConfig.h 中配置
#define configTOTAL_HEAP_SIZE    ((size_t)(64 * 1024))  // 64KB 堆

// 监控堆使用情况
void vPrintHeapStats(void) {
    HeapStats_t stats;
    vPortGetHeapStats(&stats);
    printf("Heap: free=%d, min_ever_free=%d, largest_block=%d\n",
           stats.xAvailableHeapSpaceInBytes,
           stats.xMinimumEverFreeBytesRemaining,
           stats.xSizeOfLargestFreeBlockInBytes);
}

// 监控任务栈使用情况
void vCheckStackUsage(void) {
    UBaseType_t watermark = uxTaskGetStackHighWaterMark(xModbusTask);
    if (watermark < 50) {  // 剩余不足 50 字(200字节)
        printf("WARNING: ModbusTask stack low! Watermark=%d words\n", watermark);
    }
}

RT-Thread 架构

c
// RT-Thread 线程创建(国产 RTOS,API 类似 POSIX)
#include <rtthread.h>

#define THREAD_PRIORITY     5
#define THREAD_STACK_SIZE   1024
#define THREAD_TIMESLICE    10

static rt_thread_t modbus_thread = RT_NULL;

static void modbus_thread_entry(void *parameter) {
    while (1) {
        // 采集数据
        modbus_poll();
        // 延时 1 秒
        rt_thread_mdelay(1000);
    }
}

int modbus_init(void) {
    modbus_thread = rt_thread_create(
        "modbus",
        modbus_thread_entry,
        RT_NULL,
        THREAD_STACK_SIZE,
        THREAD_PRIORITY,
        THREAD_TIMESLICE
    );

    if (modbus_thread != RT_NULL) {
        rt_thread_startup(modbus_thread);
    }
    return RT_EOK;
}

// RT-Thread 消息队列
static rt_mq_t data_mq = RT_NULL;

void init_message_queue(void) {
    data_mq = rt_mq_create("data_mq",
                            sizeof(ModbusData_t),  // 消息大小
                            20,                     // 最大消息数
                            RT_IPC_FLAG_FIFO);
}

// 发送消息
rt_mq_send(data_mq, &data, sizeof(ModbusData_t));

// 接收消息(等待 1 秒)
rt_mq_recv(data_mq, &data, sizeof(ModbusData_t), RT_TICK_PER_SECOND);

实时性保证

c
// 关键实时任务设计原则

// 1. 避免在高优先级任务中使用动态内存分配
// 错误:
void vHighPriorityTask(void *pvParameters) {
    char *buf = malloc(256);  // 可能阻塞!
    // ...
    free(buf);
}

// 正确:使用静态分配
void vHighPriorityTask(void *pvParameters) {
    static char buf[256];  // 静态分配,无阻塞
    // ...
}

// 2. 使用 configASSERT 检测错误
#define configASSERT(x) if((x) == 0) { taskDISABLE_INTERRUPTS(); for(;;); }

// 3. 看门狗集成
void vWatchdogTask(void *pvParameters) {
    for (;;) {
        // 喂狗
        HAL_IWDG_Refresh(&hiwdg);
        vTaskDelay(pdMS_TO_TICKS(500));  // 每 500ms 喂一次
    }
}

// 4. 任务运行时间统计(调试用)
// FreeRTOSConfig.h:
// #define configGENERATE_RUN_TIME_STATS 1
// #define configUSE_TRACE_FACILITY 1

void vPrintTaskStats(void) {
    char buf[1024];
    vTaskGetRunTimeStats(buf);
    printf("Task\t\tTime\t\t%%\n%s\n", buf);
}

OTA 升级方案

c
// 双分区 OTA 升级(Bootloader + App A + App B)

// Flash 分区布局:
// 0x08000000 - 0x0800FFFF: Bootloader (64KB)
// 0x08010000 - 0x0807FFFF: App A (448KB) ← 当前运行
// 0x08080000 - 0x080EFFFF: App B (448KB) ← 待升级
// 0x080F0000 - 0x080FFFFF: 升级标志 (64KB)

typedef struct {
    uint32_t magic;          // 0xDEADBEEF
    uint32_t version;
    uint32_t new_app_addr;
    uint32_t new_app_size;
    uint32_t crc32;
    uint8_t  upgrade_flag;   // 0=无升级, 1=待升级, 2=升级成功
} OTA_Flag_t;

// 应用层:接收固件并写入备用分区
void OTA_WriteChunk(uint32_t offset, const uint8_t *data, uint32_t len) {
    uint32_t target_addr = APP_B_START + offset;

    // 擦除(按扇区)
    if (offset == 0) {
        FLASH_EraseInitTypeDef erase = {
            .TypeErase = FLASH_TYPEERASE_SECTORS,
            .Sector = FLASH_SECTOR_APP_B,
            .NbSectors = APP_B_SECTORS,
            .VoltageRange = FLASH_VOLTAGE_RANGE_3
        };
        uint32_t error;
        HAL_FLASHEx_Erase(&erase, &error);
    }

    // 写入
    HAL_FLASH_Unlock();
    for (uint32_t i = 0; i < len; i += 4) {
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
                          target_addr + i,
                          *(uint32_t*)(data + i));
    }
    HAL_FLASH_Lock();
}

// 升级完成,设置标志并重启
void OTA_Commit(uint32_t version, uint32_t crc) {
    OTA_Flag_t flag = {
        .magic = 0xDEADBEEF,
        .version = version,
        .new_app_addr = APP_B_START,
        .crc32 = crc,
        .upgrade_flag = 1
    };
    // 写入标志区
    WriteOTAFlag(&flag);
    // 重启,Bootloader 将验证并切换
    NVIC_SystemReset();
}

褚成志的IoT笔记