FREERTOS根本不能使用连续接收串口思想
示例代码:
void middle_task(void *pvParameters) {while (1) {// 获取互斥锁访问共享资源
// if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {// 处理串口数据HandleSerialData(Serial1_GetRxFlag, Serial1_GetRxDatas, 1);HandleSerialData(Serial2_GetRxFlag, Serial2_GetRxDatas, 1);HandleSerialData(Serial3_GetRxFlag, Serial3_GetRxDatas, 1);HandleSerialData(Serial4_GetRxFlag, Serial4_GetRxDatas, 1);HandleSerialData(Serial5_GetRxFlag, Serial5_GetRxDatas, 1);// xSemaphoreGive(xMutex);
// }vTaskDelay(20);}
}
void HandleSerialData(uint8_t (*Serial_GetRxFlag)(void), uint8_t* (*Serial_GetRxDatas)(uint8_t),char explore) {if(Serial_GetRxFlag() == 1) { RxDatas = Serial_GetRxDatas(3); if(RxDatas[0] == 0x06 && RxDatas[1] == 0x01 && RxDatas[2] == 0x66) { Run_up(); } if(RxDatas[0] == 0x06 && RxDatas[1] == 0x02 && RxDatas[2] == 0x66) { Run_down(); } if(RxDatas[0] == 0x06 && RxDatas[1] == 0x03 && RxDatas[2] == 0x66) { Run_left(); } if(RxDatas[0] == 0x06 && RxDatas[1] == 0x04 && RxDatas[2] == 0x66) { Run_right(); } if(RxDatas[0] == 0x06 && RxDatas[1] == 0x05 && RxDatas[2] == 0x66) { Run_forward(); } if(RxDatas[0] == 0x06 && RxDatas[1] == 0x06 && RxDatas[2] == 0x66) { Run_back(); }if(RxDatas[0] == 0x06 && RxDatas[1] == 0x07 && RxDatas[2] == 0x66) { Stepper_MoveSteps(10, 1, 5);}if(RxDatas[0] == 0x06 && RxDatas[1] == 0x08 && RxDatas[2] == 0x66) { Stepper_MoveSteps(10, 0, 5);
/* 获取多个数据 */
uint8_t* Serial1_GetRxDatas(int number)
{int timeout = 0;Serial1_RxDatas[0] = Serial1_GetRxData();for (int i = 1; i < number; i++){while (Serial1_GetRxFlag() == 0){if (++timeout > 2000) break;}Serial1_RxDatas[i] = Serial1_GetRxData();timeout = 0;}return Serial1_RxDatas;
}
这种连续接受思想在单线程芯片是可以用的,但在多线程或具有系统的单线程芯片中不行
在多线程环境中,这段代码存在严重的竞态条件(Race Condition)和线程安全问题。我来分析一下具体风险并提供改进方案。
问题分析
帧解析逻辑不完整:
只检查了3字节完整帧的情况(如
0x06 0x01 0x66
)没有处理帧不完整或数据错位的情况
没有清除接收标志,可能导致重复处理相同数据
数据接收方式不合理:
使用
Serial_GetRxDatas(3)
假设每次都能获取完整帧实际串口数据可能被分包传输,需要状态机处理
全局变量冲突:
RxDatas
是全局变量,被多个串口共享会导致数据覆盖缺少互斥锁保护共享数据
电机控制阻塞任务:
Run_up()
等函数直接调用可能导致任务阻塞没有使用队列解耦接收和控制逻辑
这段代码在多线程环境中存在以下隐患:
- 共享资源竞争:
Serial1_RxDatas
数组和接收标志Serial1_GetRxFlag()
被多个线程访问,但没有同步机制 - 原子性问题:
Serial1_GetRxFlag()
和Serial1_GetRxData()
的实现可能不是原子操作 - 数据一致性:读取过程中数据可能被其他线程修改
- 阻塞风险:
while
循环可能导致整个系统阻塞
改进方案
以下是几种适用于 FreeRTOS 的改进方案:
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"// 定义互斥锁
SemaphoreHandle_t xSerialMutex;// 初始化函数
void Serial1_Init(void) {// 创建互斥锁xSerialMutex = xSemaphoreCreateMutex();
}uint8_t* Serial1_GetRxDatas(int number)
{int timeout = 0;// 获取互斥锁xSemaphoreTake(xSerialMutex, portMAX_DELAY);Serial1_RxDatas[0] = Serial1_GetRxData();for (int i = 1; i < number; i++){while (Serial1_GetRxFlag() == 0){if (++timeout > 2000) break;// 释放CPU时间片taskYIELD();}Serial1_RxDatas[i] = Serial1_GetRxData();timeout = 0;}// 释放互斥锁xSemaphoreGive(xSerialMutex);return Serial1_RxDatas;
}