STM32基于FreeRTOS的多任务调度开发(传感器采集/数据发送)

1. 系统架构设计

采用生产者-消费者模型实现任务解耦:

  • 生产者任务:传感器数据采集(周期性执行)
  • 消费者任务:数据打包发送(事件驱动)
  • 通信机制:使用队列传递数据,实现任务同步
graph LR
A[传感器采集任务] -->|写入队列| B[数据队列]
B -->|读取数据| C[数据发送任务]

2. 关键组件实现

(1)队列创建(main.c)

#include "FreeRTOS.h"
#include "queue.h"

#define QUEUE_LENGTH 10
#define ITEM_SIZE sizeof(SensorData)

QueueHandle_t xDataQueue;

int main(void) {
  HAL_Init();
  SystemClock_Config();
  
  // 创建数据队列
  xDataQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);
  
  // 创建任务
  xTaskCreate(vSensorTask, "Sensor", 128, NULL, 3, NULL);
  xTaskCreate(vSendTask, "Sender", 128, NULL, 2, NULL);
  
  vTaskStartScheduler();
  while(1);
}

(2)传感器采集任务(周期性)

void vSensorTask(void *pvParameters) {
  SensorData data;
  const TickType_t xDelay = pdMS_TO_TICKS(100); // 100ms周期
  
  for(;;) {
    // 1. 读取传感器(示例)
    data.timestamp = xTaskGetTickCount();
    data.value = read_adc(ADC_CHANNEL);
    
    // 2. 写入队列(阻塞式)
    if(xQueueSend(xDataQueue, &data, portMAX_DELAY) != pdPASS) {
      // 错误处理
    }
    
    // 3. 精确周期延迟
    vTaskDelayUntil(&xLastWakeTime, xDelay);
  }
}

(3)数据发送任务(事件驱动)

void vSendTask(void *pvParameters) {
  SensorData rxData;
  
  for(;;) {
    // 1. 阻塞等待队列数据
    if(xQueueReceive(xDataQueue, &rxData, portMAX_DELAY) == pdPASS) {
      
      // 2. 数据处理(示例)
      uint8_t buffer[20];
      int len = snprintf(buffer, sizeof(buffer), "T:%lu V:%d", 
                        rxData.timestamp, rxData.value);
      
      // 3. 通过UART发送
      HAL_UART_Transmit(&huart1, buffer, len, HAL_MAX_DELAY);
    }
  }
}

3. 关键配置参数
参数 推荐值 说明
采集任务优先级 3 (较高) 保证实时性
发送任务优先级 2 (中等) 低于采集任务
队列长度 10-20 根据数据速率调整
采集周期 50-200ms 取决于传感器特性
任务堆栈大小 128-256字 根据函数调用深度调整
4. 优化策略
  1. 双缓冲技术

    // 在采集任务中
    static SensorData buffer[2];
    static uint8_t activeBuf = 0;
    
    // 采集周期结束时切换缓冲区
    xQueueSend(xDataQueue, &buffer[activeBuf], 0);
    activeBuf ^= 1;  // 切换缓冲区
    

  2. 带超时的队列接收

    // 在发送任务中
    if(xQueueReceive(xDataQueue, &data, pdMS_TO_TICKS(50)) == pdPASS) {
      // 处理数据
    } else {
      // 发送心跳包维持连接
    }
    

  3. 优先级反转预防

    • 使用互斥锁保护共享资源
    • 考虑优先级继承机制:
      SemaphoreHandle_t xUartMutex = xSemaphoreCreateMutex();
      
      // 发送前获取互斥锁
      if(xSemaphoreTake(xUartMutex, portMAX_DELAY) == pdTRUE) {
        HAL_UART_Transmit(...);
        xSemaphoreGive(xUartMutex);
      }
      

5. 调试技巧
  1. 使用FreeRTOS的uxTaskGetStackHighWaterMark()监控堆栈使用
  2. 通过vTaskList()获取任务状态信息
  3. 利用STM32的硬件定时器验证任务执行周期精度
  4. 在队列操作前后添加调试引脚电平变化,用示波器观察时序

注意事项

  1. 避免在中断服务程序(ISR)中直接调用xQueueSend(),应使用xQueueSendFromISR()
  2. 任务优先级设置需确保传感器采集不被阻塞
  3. 使用configASSERT()验证RTOS API调用结果
  4. 低功耗设计时,合理使用eTaskStateGet()管理任务状态
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐