在嵌入式开发中,串口通信是最基础也最常用的外设接口之一。最近我在为RTL8720CM模组开发OTA升级功能时,遇到了一个令人头疼的问题:通过串口发送固件数据时,总是出现数据丢失现象。具体表现为每帧128字节的数据会偶发不完整,导致接收端MCU无法正确解析。
这个项目运行在TuyaOS环境下,虽然厂商对FreeRTOS进行了封装,但核心机制与标准FreeRTOS基本一致。系统提供了类似FreeRTOS的任务调度、队列等机制,只是API名称略有不同。开发板使用RTL8720CM芯片,串口配置为115200波特率,8位数据位,无校验,1位停止位。
关键点:在RTOS环境下,串口通信的稳定性不仅受硬件影响,更与任务调度机制密切相关。理解这一点对问题定位至关重要。
最初设计的OTA协议采用256字节的数据帧,但在实际测试中发现:
首先我计算了理论传输时间:
考虑到RTOS的任务调度时间片通常在1-10ms量级,256字节的传输时间确实过长,容易被打断。于是我将帧长缩减到128字节,但问题依然存在。
进一步检查发现平台串口驱动有以下限制:
这意味着:
我设计了一个专用发送任务和缓冲队列:
c复制#define UART_QUEUE_LENGTH 10
#define UART_QUEUE_ITEM_SIZE 128
QueueHandle_t xUartQueue;
void vUartSendTask(void *pvParameters) {
uint8_t ucData[UART_QUEUE_ITEM_SIZE];
while(1) {
if(xQueueReceive(xUartQueue, &ucData, portMAX_DELAY) == pdTRUE) {
uart_blocking_send(ucData, UART_QUEUE_ITEM_SIZE);
}
}
}
实际测试发现:
根据同事建议,尝试在发送前后关闭中断:
c复制portDISABLE_INTERRUPTS();
uart_blocking_send(pucData, 128);
portENABLE_INTERRUPTS();
结果:
经验分享:全局中断关闭是最后的解决手段,会破坏RTOS的实时性。不到万不得已不应使用。
通过逻辑分析仪发现关键现象:
于是将发送任务优先级提高到最高:
c复制xTaskCreate(vUartSendTask, "UartSend", 512, NULL, configMAX_PRIORITIES-1, NULL);
调整后:
在默认优先级下,串口发送任务可能被以下情况打断:
每次打断都会导致:
115200波特率下:
提高优先级后:
另一个潜在问题是:
改进后的发送流程:
c复制void vSendData(uint8_t *pucData, size_t xLength) {
uint8_t ucLocalBuffer[128];
memcpy(ucLocalBuffer, pucData, xLength);
taskENTER_CRITICAL();
uart_blocking_send(ucLocalBuffer, xLength);
taskEXIT_CRITICAL();
}
结合所有优化点,完整解决方案包括:
关键代码结构:
c复制typedef struct {
uint8_t ucData[128];
size_t xLength;
} UartFrame_t;
void vUartSendTask(void *pvParameters) {
UartFrame_t xFrame;
while(1) {
if(xQueueReceive(xUartQueue, &xFrame, portMAX_DELAY)) {
taskENTER_CRITICAL();
uart_blocking_send(xFrame.ucData, xFrame.xLength);
taskEXIT_CRITICAL();
}
}
}
void vSendOtaData(uint8_t *pucData, size_t xLength) {
UartFrame_t xFrame;
memcpy(xFrame.ucData, pucData, xLength);
xFrame.xLength = xLength;
xQueueSend(xUartQueue, &xFrame, portMAX_DELAY);
}
对于20KB的OTA固件传输:
示例协议帧结构:
code复制| 0x55 | 0xAA | 长度L | 长度H | 数据... | CRC8 |
长期运行建议:
在实际开发中,我总结了以下关键经验:
优先级设置黄金法则:
串口使用注意事项:
调试技巧:
性能权衡:
这个案例让我深刻认识到,在RTOS环境下,单纯的"代码正确"是不够的,必须充分考虑任务调度对时序的影响。通过合理设置任务优先级和适当的保护机制,可以显著提升通信可靠性。