STM32中FreeRTOS同优先级三任务共用UART的问题解决
问题分析与解决方案
问题根源
同优先级任务采用时间片轮转调度,当第一个任务调用HAL_UART_Transmit(阻塞式,带100ms超时)时,UART进入忙状态;后续任务切换过来后,同样调用该函数时,UART未就绪,函数会在超时后返回错误(你未检查返回值,因此未察觉),导致发送失败。而不同优先级时,高优先级任务会完成整个发送流程后才让出CPU,因此所有任务都能成功发送。
以下是符合你要求的解决方案(不修改优先级、不用信号量/互斥锁、无循环等待就绪):
方案1:使用UART中断模式发送(HAL_UART_Transmit_IT)
将阻塞式发送替换为非阻塞的中断模式,任务发起发送请求后立即返回,UART通过中断完成传输,同优先级任务轮转时每个任务都能成功发起发送。
修改后的任务代码示例:
// 每个任务使用独立缓冲区,避免数据覆盖 static uint8_t t1_buf[] = "T1\r\n"; static uint8_t t2_buf[] = "T2\r\n"; static uint8_t t3_buf[] = "T3\r\n"; void StartDefaultTask(void const * argument) { for(;;) { // 单次检查UART状态,仅在就绪时发起发送 if(HAL_UART_GetState(&huart2) == HAL_UART_STATE_READY) { HAL_UART_Transmit_IT(&huart2, t1_buf, sizeof(t1_buf)-1); } osDelay(1000); } } void StartTask02(void const * argument) { for(;;) { if(HAL_UART_GetState(&huart2) == HAL_UART_STATE_READY) { HAL_UART_Transmit_IT(&huart2, t2_buf, sizeof(t2_buf)-1); } osDelay(1000); } } void StartTask03(void const * argument) { for(;;) { if(HAL_UART_GetState(&huart2) == HAL_UART_STATE_READY) { HAL_UART_Transmit_IT(&huart2, t3_buf, sizeof(t3_buf)-1); } osDelay(1000); } } // 必须实现UART发送完成回调函数 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART2) { // 可选:发送完成后的后续处理 } }
注意:需确保已开启UART的TX中断(CubeMX配置时勾选或代码中手动开启)。
方案2:使用DMA模式发送(HAL_UART_Transmit_DMA)
利用DMA硬件完成UART发送,完全不占用CPU资源,任务发起发送后立即返回,效率更高。
修改示例:
static uint8_t t1_buf[] = "T1\r\n"; static uint8_t t2_buf[] = "T2\r\n"; static uint8_t t3_buf[] = "T3\r\n"; void StartDefaultTask(void const * argument) { for(;;) { if(HAL_UART_GetState(&huart2) == HAL_UART_STATE_READY) { HAL_UART_Transmit_DMA(&huart2, t1_buf, sizeof(t1_buf)-1); } osDelay(1000); } } // 另外两个任务替换对应缓冲区即可 // 可选实现DMA发送完成回调 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART2) { // 可选:清理或标记操作 } }
注意:需确保CubeMX配置了UART的DMA发送通道,或代码中手动完成DMA初始化。
方案合规性说明
- 未修改任务优先级,保持
osPriorityNormal; - 未使用信号量、互斥锁,仅利用HAL库的非阻塞发送机制;
- 仅单次检查UART状态,无循环等待,符合通用单片机编程思路。
内容的提问来源于stack exchange,提问作者Nima Azad
相关产品推荐
相关产品推荐

