在STM32 FreeRTOS环境中使用mutex和ringbuffer实现多任务的UART同步通信
在STM32 FreeRTOS环境中使用mutex和ringbuffer实现多任务的UART同步通信。完整的STM32 FreeRTOS UART多任务同步方案,主要特点包括:
核心组件
1. 环形缓冲区 (RingBuffer)
- 发送和接收各有独立的环形缓冲区
- 支持多生产者/消费者模式
- 提供满/空状态检查
2. 互斥量 (Mutex)
txMutex
: 保护发送操作的线程安全rxMutex
: 保护接收操作的线程安全- 防止多任务同时访问UART资源
3. 信号量和队列
txSemaphore
: 发送完成通知rxQueue
: 接收数据通知队列
使用方法
初始化:
// 在main函数中调用
UartMultiTask_Init(&huart1);
发送数据:
char msg[] = "Hello World\r\n";
UartManager_Transmit(&uart1_manager, (uint8_t*)msg, strlen(msg), 1000);
接收数据:
uint8_t rxBuffer[64];
uint16_t len = UartManager_Receive(&uart1_manager, rxBuffer, sizeof(rxBuffer), 1000);
同步机制
- 发送同步: 使用互斥量确保同一时间只有一个任务可以发送
- 接收同步: 通过队列通知机制实现异步接收
- 中断安全: 在中断中使用
FromISR
版本的FreeRTOS API
任务示例
代码中包含三个示例任务:
- UartSendTask: 定期发送消息
- UartReceiveTask: 接收数据并回显
- UartLogTask: 系统日志输出
这个方案确保了多任务环境下UART通信的稳定性和线程安全性,适用于需要多个任务同时使用UART的复杂应用场景。
实现代码如下:
/* STM32 FreeRTOS UART多任务同步实现 */#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "stm32f4xx_hal.h"
#include <string.h>
#include <stdio.h>/* 环形缓冲区结构 */
typedef struct {uint8_t *buffer;uint16_t head;uint16_t tail;uint16_t size;uint16_t count;
} RingBuffer_t;/* UART管理结构 */
typedef struct {UART_HandleTypeDef *huart;RingBuffer_t rxRingBuffer;RingBuffer_t txRingBuffer;SemaphoreHandle_t txMutex;SemaphoreHandle_t rxMutex;SemaphoreHandle_t txSemaphore;QueueHandle_t rxQueue;uint8_t rxByte;uint8_t txActive;
} UartManager_t;/* 全局变量 */
#define UART_BUFFER_SIZE 256
static uint8_t uart1_rx_buffer[UART_BUFFER_SIZE];
static uint8_t uart1_tx_buffer[UART_BUFFER_SIZE];
static UartManager_t uart1_manager;/* 环形缓冲区操作函数 */
void RingBuffer_Init(RingBuffer_t *rb, uint8_t *buffer, uint16_t size) {rb->buffer = buffer;rb->head = 0;rb->tail = 0;rb->size = size;rb->count = 0;
}uint8_t RingBuffer_Put(RingBuffer_t *rb, uint8_t data) {if (rb->count >= rb->size) {return 0; // 缓冲区满}rb->buffer[rb->head] = data;rb->head = (rb->head + 1) % rb->size;rb->count++;return 1;
}uint8_t RingBuffer_Get(RingBuffer_t *rb, uint8_t *data) {if (rb->count == 0) {return 0; // 缓冲区空}*data = rb->buffer[rb->tail];rb->tail = (rb->tail + 1) % rb->size;rb->count--;return 1;
}uint16_t RingBuffer_GetCount(RingBuffer_t *rb) {return rb->count;
}uint8_t RingBuffer_IsEmpty(RingBuffer_t *rb) {return (rb->count == 0);
}uint8_t RingBuffer_IsFull(RingBuffer_t *rb) {return (rb->count >= rb->size);
}/* UART管理器初始化 */
void UartManager_Init(UartManager_t *mgr, UART_HandleTypeDef *huart) {mgr->huart = huart;// 初始化环形缓冲区RingBuffer_Init(&mgr->rxRingBuffer, uart1_rx_buffer, UART_BUFFER_SIZE);RingBuffer_Init(&mgr->txRingBuffer, uart1_tx_buffer, UART_BUFFER_SIZE);// 创建互斥量mgr->txMutex = xSemaphoreCreateMutex();mgr->rxMutex = xSemaphoreCreateMutex();// 创建信号量mgr->txSemaphore = xSemaphoreCreateBinary();// 创建队列用于接收数据通知mgr->rxQueue = xQueueCreate(10, sizeof(uint8_t));mgr->txActive = 0;// 启动UART接收中断HAL_UART_Receive_IT(mgr->huart, &mgr->rxByte, 1);
}/* UART发送函数 - 支持多任务安全 */
HAL_StatusTypeDef UartManager_Transmit(UartManager_t *mgr, uint8_t *data, uint16_t len, uint32_t timeout) {HAL_StatusTypeDef status = HAL_OK;uint16_t i;// 获取发送互斥量if (xSemaphoreTake(mgr->txMutex, pdMS_TO_TICKS(timeout)) != pdTRUE) {return HAL_TIMEOUT;}// 将数据放入发送缓冲区for (i = 0; i < len; i++) {if (!RingBuffer_Put(&mgr->txRingBuffer, data[i])) {status = HAL_ERROR; // 缓冲区满break;}}// 如果发送器空闲,启动发送if (!mgr->txActive && !RingBuffer_IsEmpty(&mgr->txRingBuffer)) {uint8_t txData;if (RingBuffer_Get(&mgr->txRingBuffer, &txData)) {mgr->txActive = 1;HAL_UART_Transmit_IT(mgr->huart, &txData, 1);}}// 等待发送完成if (status == HAL_OK) {xSemaphoreTake(mgr->txSemaphore, pdMS_TO_TICKS(timeout));}// 释放互斥量xSemaphoreGive(mgr->txMutex);return status;
}/* UART接收函数 - 支持多任务安全 */
uint16_t UartManager_Receive(UartManager_t *mgr, uint8_t *data, uint16_t maxLen, uint32_t timeout) {uint16_t received = 0;uint8_t rxData;TickType_t startTime = xTaskGetTickCount();// 获取接收互斥量if (xSemaphoreTake(mgr->rxMutex, pdMS_TO_TICKS(timeout)) != pdTRUE) {return 0;}while (received < maxLen) {// 从环形缓冲区获取数据if (RingBuffer_Get(&mgr->rxRingBuffer, &rxData)) {data[received++] = rxData;} else {// 缓冲区为空,等待新数据if (xQueueReceive(mgr->rxQueue, &rxData, pdMS_TO_TICKS(10)) == pdTRUE) {data[received++] = rxData;} else {// 检查超时if ((xTaskGetTickCount() - startTime) >= pdMS_TO_TICKS(timeout)) {break;}}}}// 释放互斥量xSemaphoreGive(mgr->rxMutex);return received;
}/* UART中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;if (huart == uart1_manager.huart) {// 将接收到的数据放入环形缓冲区if (RingBuffer_Put(&uart1_manager.rxRingBuffer, uart1_manager.rxByte)) {// 通知接收队列有新数据xQueueSendFromISR(uart1_manager.rxQueue, &uart1_manager.rxByte, &xHigherPriorityTaskWoken);}// 继续接收下一个字节HAL_UART_Receive_IT(uart1_manager.huart, &uart1_manager.rxByte, 1);}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;uint8_t txData;if (huart == uart1_manager.huart) {// 检查发送缓冲区是否还有数据if (RingBuffer_Get(&uart1_manager.txRingBuffer, &txData)) {HAL_UART_Transmit_IT(uart1_manager.huart, &txData, 1);} else {// 发送完成uart1_manager.txActive = 0;xSemaphoreGiveFromISR(uart1_manager.txSemaphore, &xHigherPriorityTaskWoken);}}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}/* 任务示例 */
void UartSendTask(void *pvParameters) {char message[64];uint8_t counter = 0;while (1) {snprintf(message, sizeof(message), "Send Task: Message %d\r\n", counter++);if (UartManager_Transmit(&uart1_manager, (uint8_t*)message, strlen(message), 1000) != HAL_OK) {// 发送失败处理}vTaskDelay(pdMS_TO_TICKS(2000));}
}void UartReceiveTask(void *pvParameters) {uint8_t rxBuffer[128];uint16_t rxLen;while (1) {rxLen = UartManager_Receive(&uart1_manager, rxBuffer, sizeof(rxBuffer) - 1, 1000);if (rxLen > 0) {rxBuffer[rxLen] = '\0';// 处理接收到的数据char response[64];snprintf(response, sizeof(response), "Received: %s", rxBuffer);UartManager_Transmit(&uart1_manager, (uint8_t*)response, strlen(response), 1000);}vTaskDelay(pdMS_TO_TICKS(100));}
}void UartLogTask(void *pvParameters) {char logMessage[64];uint32_t logCounter = 0;while (1) {snprintf(logMessage, sizeof(logMessage), "Log: System running, count=%lu\r\n", logCounter++);if (UartManager_Transmit(&uart1_manager, (uint8_t*)logMessage, strlen(logMessage), 1000) != HAL_OK) {// 日志发送失败处理}vTaskDelay(pdMS_TO_TICKS(5000));}
}/* 主函数中的初始化 */
void UartMultiTask_Init(UART_HandleTypeDef *huart) {// 初始化UART管理器UartManager_Init(&uart1_manager, huart);// 创建任务xTaskCreate(UartSendTask, "UartSend", 256, NULL, 2, NULL);xTaskCreate(UartReceiveTask, "UartReceive", 256, NULL, 3, NULL);xTaskCreate(UartLogTask, "UartLog", 256, NULL, 1, NULL);
}/* 错误处理函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {if (huart == uart1_manager.huart) {// 重新启动接收HAL_UART_Receive_IT(uart1_manager.huart, &uart1_manager.rxByte, 1);}
}/* 获取UART状态信息 */
void UartManager_GetStatus(UartManager_t *mgr) {char statusMsg[128];snprintf(statusMsg, sizeof(statusMsg), "UART Status: RX Count=%d, TX Count=%d, TX Active=%d\r\n",RingBuffer_GetCount(&mgr->rxRingBuffer),RingBuffer_GetCount(&mgr->txRingBuffer),mgr->txActive);UartManager_Transmit(mgr, (uint8_t*)statusMsg, strlen(statusMsg), 1000);
}
为STM32 FreeRTOS UART多任务系统增加了完整的printf支持。新增的功能包括:
Printf支持特性
1. 标准printf重定向
- 重写了
_write
函数,将printf输出重定向到UART - 使用专用互斥量
g_printf_mutex
保证多任务printf线程安全 - 避免多任务同时printf造成的输出混乱
2. 线程安全的Printf函数
UartManager_Printf()
: 支持格式化输出的线程安全版本- 使用环形缓冲区机制,与其他UART操作协调
- 提供
SAFE_PRINTF
宏定义,使用更便捷
3. 两种使用方式
标准printf方式:
printf("Send Task: Message %d\r\n", counter);
printf("System running, count=%lu, free heap=%lu\r\n", logCounter, xPortGetFreeHeapSize());
线程安全printf方式:
SAFE_PRINTF(&uart1_manager, "Send Task: Message %d\r\n", counter);
UartManager_Printf(&uart1_manager, "Log: %s\r\n", message);
关键改进
1. 互斥量保护
- 专用的printf互斥量避免输出混乱
- 超时机制防止任务长时间阻塞
2. 缓冲区管理
- 256字节静态缓冲区用于格式化
- 自动长度限制防止缓冲区溢出
3. 错误处理
- 返回实际输出字节数
- 超时和错误情况的适当处理
使用示例
初始化时只需调用:
UartMultiTask_Init(&huart1); // 会自动设置printf输出
之后在任何任务中都可以直接使用printf:
printf("Task %s: Value = %d\r\n", pcTaskGetName(NULL), value);
这样就实现了完整的printf支持,既保持了标准C库的使用习惯,又确保了多任务环境下的线程安全性。
/* STM32 FreeRTOS UART多任务同步实现 */#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "stm32f4xx_hal.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>/* 环形缓冲区结构 */
typedef struct {uint8_t *buffer;uint16_t head;uint16_t tail;uint16_t size;uint16_t count;
} RingBuffer_t;/* UART管理结构 */
typedef struct {UART_HandleTypeDef *huart;RingBuffer_t rxRingBuffer;RingBuffer_t txRingBuffer;SemaphoreHandle_t txMutex;SemaphoreHandle_t rxMutex;SemaphoreHandle_t txSemaphore;QueueHandle_t rxQueue;uint8_t rxByte;uint8_t txActive;
} UartManager_t;/* 全局变量 */
#define UART_BUFFER_SIZE 256
static uint8_t uart1_rx_buffer[UART_BUFFER_SIZE];
static uint8_t uart1_tx_buffer[UART_BUFFER_SIZE];
static UartManager_t uart1_manager;/* 环形缓冲区操作函数 */
void RingBuffer_Init(RingBuffer_t *rb, uint8_t *buffer, uint16_t size) {rb->buffer = buffer;rb->head = 0;rb->tail = 0;rb->size = size;rb->count = 0;
}uint8_t RingBuffer_Put(RingBuffer_t *rb, uint8_t data) {if (rb->count >= rb->size) {return 0; // 缓冲区满}rb->buffer[rb->head] = data;rb->head = (rb->head + 1) % rb->size;rb->count++;return 1;
}uint8_t RingBuffer_Get(RingBuffer_t *rb, uint8_t *data) {if (rb->count == 0) {return 0; // 缓冲区空}*data = rb->buffer[rb->tail];rb->tail = (rb->tail + 1) % rb->size;rb->count--;return 1;
}uint16_t RingBuffer_GetCount(RingBuffer_t *rb) {return rb->count;
}uint8_t RingBuffer_IsEmpty(RingBuffer_t *rb) {return (rb->count == 0);
}uint8_t RingBuffer_IsFull(RingBuffer_t *rb) {return (rb->count >= rb->size);
}/* UART管理器初始化 */
void UartManager_Init(UartManager_t *mgr, UART_HandleTypeDef *huart) {mgr->huart = huart;// 初始化环形缓冲区RingBuffer_Init(&mgr->rxRingBuffer, uart1_rx_buffer, UART_BUFFER_SIZE);RingBuffer_Init(&mgr->txRingBuffer, uart1_tx_buffer, UART_BUFFER_SIZE);// 创建互斥量mgr->txMutex = xSemaphoreCreateMutex();mgr->rxMutex = xSemaphoreCreateMutex();// 创建信号量mgr->txSemaphore = xSemaphoreCreateBinary();// 创建队列用于接收数据通知mgr->rxQueue = xQueueCreate(10, sizeof(uint8_t));mgr->txActive = 0;// 启动UART接收中断HAL_UART_Receive_IT(mgr->huart, &mgr->rxByte, 1);
}/* UART发送函数 - 支持多任务安全 */
HAL_StatusTypeDef UartManager_Transmit(UartManager_t *mgr, uint8_t *data, uint16_t len, uint32_t timeout) {HAL_StatusTypeDef status = HAL_OK;uint16_t i;// 获取发送互斥量if (xSemaphoreTake(mgr->txMutex, pdMS_TO_TICKS(timeout)) != pdTRUE) {return HAL_TIMEOUT;}// 将数据放入发送缓冲区for (i = 0; i < len; i++) {if (!RingBuffer_Put(&mgr->txRingBuffer, data[i])) {status = HAL_ERROR; // 缓冲区满break;}}// 如果发送器空闲,启动发送if (!mgr->txActive && !RingBuffer_IsEmpty(&mgr->txRingBuffer)) {uint8_t txData;if (RingBuffer_Get(&mgr->txRingBuffer, &txData)) {mgr->txActive = 1;HAL_UART_Transmit_IT(mgr->huart, &txData, 1);}}// 等待发送完成if (status == HAL_OK) {xSemaphoreTake(mgr->txSemaphore, pdMS_TO_TICKS(timeout));}// 释放互斥量xSemaphoreGive(mgr->txMutex);return status;
}/* UART接收函数 - 支持多任务安全 */
uint16_t UartManager_Receive(UartManager_t *mgr, uint8_t *data, uint16_t maxLen, uint32_t timeout) {uint16_t received = 0;uint8_t rxData;TickType_t startTime = xTaskGetTickCount();// 获取接收互斥量if (xSemaphoreTake(mgr->rxMutex, pdMS_TO_TICKS(timeout)) != pdTRUE) {return 0;}while (received < maxLen) {// 从环形缓冲区获取数据if (RingBuffer_Get(&mgr->rxRingBuffer, &rxData)) {data[received++] = rxData;} else {// 缓冲区为空,等待新数据if (xQueueReceive(mgr->rxQueue, &rxData, pdMS_TO_TICKS(10)) == pdTRUE) {data[received++] = rxData;} else {// 检查超时if ((xTaskGetTickCount() - startTime) >= pdMS_TO_TICKS(timeout)) {break;}}}}// 释放互斥量xSemaphoreGive(mgr->rxMutex);return received;
}/* UART中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;if (huart == uart1_manager.huart) {// 将接收到的数据放入环形缓冲区if (RingBuffer_Put(&uart1_manager.rxRingBuffer, uart1_manager.rxByte)) {// 通知接收队列有新数据xQueueSendFromISR(uart1_manager.rxQueue, &uart1_manager.rxByte, &xHigherPriorityTaskWoken);}// 继续接收下一个字节HAL_UART_Receive_IT(uart1_manager.huart, &uart1_manager.rxByte, 1);}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;uint8_t txData;if (huart == uart1_manager.huart) {// 检查发送缓冲区是否还有数据if (RingBuffer_Get(&uart1_manager.txRingBuffer, &txData)) {HAL_UART_Transmit_IT(uart1_manager.huart, &txData, 1);} else {// 发送完成uart1_manager.txActive = 0;xSemaphoreGiveFromISR(uart1_manager.txSemaphore, &xHigherPriorityTaskWoken);}}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}/* 任务示例 */
void UartSendTask(void *pvParameters) {uint8_t counter = 0;while (1) {// 使用标准printfprintf("Send Task: Message %d\r\n", counter++);// 或使用线程安全的printf// SAFE_PRINTF(&uart1_manager, "Send Task: Message %d\r\n", counter++);vTaskDelay(pdMS_TO_TICKS(2000));}
}void UartReceiveTask(void *pvParameters) {uint8_t rxBuffer[128];uint16_t rxLen;while (1) {rxLen = UartManager_Receive(&uart1_manager, rxBuffer, sizeof(rxBuffer) - 1, 1000);if (rxLen > 0) {rxBuffer[rxLen] = '\0';// 使用printf处理接收到的数据printf("Received: %s", rxBuffer);// 也可以使用原来的发送函数// char response[64];// snprintf(response, sizeof(response), "Received: %s", rxBuffer);// UartManager_Transmit(&uart1_manager, (uint8_t*)response, strlen(response), 1000);}vTaskDelay(pdMS_TO_TICKS(100));}
}void UartLogTask(void *pvParameters) {uint32_t logCounter = 0;while (1) {// 使用printf输出系统日志printf("Log: System running, count=%lu, free heap=%lu\r\n", logCounter++, xPortGetFreeHeapSize());vTaskDelay(pdMS_TO_TICKS(5000));}
}/* 主函数中的初始化 */
void UartMultiTask_Init(UART_HandleTypeDef *huart) {// 初始化UART管理器UartManager_Init(&uart1_manager, huart);// 设置printf输出到UARTUartManager_SetPrintfUart(&uart1_manager);// 创建任务xTaskCreate(UartSendTask, "UartSend", 256, NULL, 2, NULL);xTaskCreate(UartReceiveTask, "UartReceive", 256, NULL, 3, NULL);xTaskCreate(UartLogTask, "UartLog", 256, NULL, 1, NULL);
}/* 错误处理函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {if (huart == uart1_manager.huart) {// 重新启动接收HAL_UART_Receive_IT(uart1_manager.huart, &uart1_manager.rxByte, 1);}
}/* Printf支持相关变量和函数 */
static UartManager_t *g_printf_uart = NULL;
static SemaphoreHandle_t g_printf_mutex = NULL;/* 设置Printf输出的UART */
void UartManager_SetPrintfUart(UartManager_t *mgr) {g_printf_uart = mgr;// 创建printf专用互斥量if (g_printf_mutex == NULL) {g_printf_mutex = xSemaphoreCreateMutex();}
}/* 重定向printf到UART - 支持多任务安全 */
int _write(int file, char *ptr, int len) {if (g_printf_uart == NULL || g_printf_mutex == NULL) {return -1;}// 获取printf互斥量,防止多任务同时printf造成输出混乱if (xSemaphoreTake(g_printf_mutex, pdMS_TO_TICKS(1000)) == pdTRUE) {// 直接发送到UART,不经过发送缓冲区避免死锁HAL_StatusTypeDef status = HAL_UART_Transmit(g_printf_uart->huart, (uint8_t*)ptr, len, 1000);xSemaphoreGive(g_printf_mutex);return (status == HAL_OK) ? len : -1;}return -1;
}/* 支持printf的安全版本 - 使用环形缓冲区 */
int UartManager_Printf(UartManager_t *mgr, const char *format, ...) {static char printf_buffer[256];va_list args;int len;// 获取printf互斥量if (xSemaphoreTake(g_printf_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {return -1;}// 格式化字符串va_start(args, format);len = vsnprintf(printf_buffer, sizeof(printf_buffer), format, args);va_end(args);// 限制长度if (len >= sizeof(printf_buffer)) {len = sizeof(printf_buffer) - 1;}// 发送数据HAL_StatusTypeDef status = UartManager_Transmit(mgr, (uint8_t*)printf_buffer, len, 1000);xSemaphoreGive(g_printf_mutex);return (status == HAL_OK) ? len : -1;
}/* 线程安全的printf宏定义 */
#define SAFE_PRINTF(mgr, fmt, ...) UartManager_Printf(mgr, fmt, ##__VA_ARGS__)/* 获取UART状态信息 */
void UartManager_GetStatus(UartManager_t *mgr) {printf("UART Status: RX Count=%d, TX Count=%d, TX Active=%d\r\n",RingBuffer_GetCount(&mgr->rxRingBuffer),RingBuffer_GetCount(&mgr->txRingBuffer),mgr->txActive);
}