关于FreeRTOS进行重定向printf打印出现卡死问题
·
我找了很久才发现。。。
原因:由于printf的栈消耗超出了任务初始分配的栈空间,导致栈溢出和系统崩溃。增加堆栈大小直接解决了这个问题,但更好的方法是优化printf实现和控制并发访问。
方案一:直接修改堆栈大小
解决方案:![]()

创建任务时,首先需要将Stack Size从原来的128修改为512。
其次将TOTAL_HEAP_SIZE从默认的3078Byte修改为8000Byte
通过上述的解决方法,即可增加增加堆栈大小来解决问题。
上面的解决方法是“暴力”解决大部分问题,但是如果还是不行,还有其他方案。
| 方案 | 栈消耗 | 线程安全 | 实时性 | 内存使用 |
|---|---|---|---|---|
| 原始printf方案 | 高(数百字节) | 不安全 | 差 | 栈空间 |
| 增加堆栈方案 | 高但足够 | 不安全 | 差 | 栈空间浪费 |
| 当前myprintf方案 | 低(<50字节) | 安全(互斥锁) | 一般 | 静态缓冲区 |
| 优化版(DMA) | 低 | 安全 | 好 | 静态/动态缓冲区 |
互斥锁解决资源竞争
osMutexAcquire(debugPrint_MutexHandle, osWaitForever) == osOK
vsnprintf限制缓冲区大小
static char buffer[256];
vsnprintf(buffer, sizeof(buffer), fmt, args);
使用静态缓冲区减少栈消耗
分离格式化与发送逻辑
void myprintf(...) {
// 1. 获取互斥锁
// 2. 格式化字符串(内存安全)
// 3. 调用发送函数
}
static void debugPrintUART(const char *pData) {
// 纯发送逻辑
HAL_UART_Transmit(&huart1, (uint8_t*)&pData[0], strlen(pData), 100);
}
方案二完整代码:
#include "main.h"
#include "FreeRTOS.h"
#include "stdarg.h"
#include "usart.h"
osMutexId_t debugPrint_MutexHandle;
// 打印信息备用手段
void myprintf(const char *fmt, ...)
{
if(osMutexAcquire(debugPrint_MutexHandle, osWaitForever) == osOK)
{
static char buffer[256];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
debugPrintUART(buffer);
osMutexRelease(debugPrint_MutexHandle);
}
}
static void debugPrintUART(const char *pData)
{
HAL_UART_Transmit(&huart1, (uint8_t*)&pData[0], strlen(pData), 100);
}
方案三:创建环形缓存区进行串口数据打印
方案三我就不赘述了(最后有链接),直接放代码。
#define BUFFER_SIZE 1024 /* 环形缓冲区大小 根据实际的数据大小进行调整 */
/* 定义环形缓冲区结构体 */
typedef struct {
uint8_t buffer[BUFFER_SIZE]; /* 缓冲区 */
volatile uint32_t head; /* 写入指针 */
volatile uint32_t tail; /* 读取指针 */
SemaphoreHandle_t mutex; /* 互斥锁,保证线程安全 */
} ringbuffer_t;
/* 初始化环形缓冲区 */
void RingBuffer_Init(ringbuffer_t *rb) {
rb->head = 0;
rb->tail = 0;
rb->mutex = xSemaphoreCreateMutex();
if (rb->mutex == NULL) {
printf("Mutex creation failed.\n");
while (1);
}
}
/* 判断环形缓冲区是否为空 */
int32_t RingBuffer_IsEmpty(ringbuffer_t *rb) {
/* head == tail 为空 */
return (rb->head == rb->tail);
}
/* 判断环形缓冲区是否已满 */
int32_t RingBuffer_IsFull(ringbuffer_t *rb) {
return ((rb->head + 1) % BUFFER_SIZE == rb->tail);
}
/* 向环形缓冲区写入数据 */
int32_t RingBuffer_Write(ringbuffer_t *rb, char data) {
int32_t result = pdFALSE;
if (xSemaphoreTake(rb->mutex, portMAX_DELAY) == pdTRUE) {
if (!RingBuffer_IsFull(rb)) {
rb->buffer[rb->head] = data;
rb->head = (rb->head + 1) % BUFFER_SIZE;
result = pdTRUE;
}
xSemaphoreGive(rb->mutex); /* 释放互斥锁 */
}
return result;
}
/* 从环形缓冲区读取数据 */
int32_t RingBuffer_Read(ringbuffer_t *rb, char *data) {
int32_t result = pdFALSE;
if (xSemaphoreTake(rb->mutex, portMAX_DELAY) == pdTRUE) {
if (!RingBuffer_IsEmpty(rb)) {
*data = rb->buffer[rb->tail];
rb->tail = (rb->tail + 1) % BUFFER_SIZE;
result = pdTRUE;
}
xSemaphoreGive(rb->mutex); /* 释放互斥锁 */
}
return result;
}
/* 格式化并写入数据的函数 */
void RingBufferWriteFormatted(ringbuffer_t* rb, const char* format, ...) {
va_list args;
va_start(args, format);
/* 使用 vsnprintf 来格式化字符串到局部缓冲区 */
char localBuffer[BUFFER_SIZE]; // 根据需要调整大小
int written = vsnprintf(localBuffer, sizeof(localBuffer), format, args);
// 检查格式化是否成功
if (written > 0) {
// 将格式化后的字符串逐字符写入环形缓冲区
for (int i = 0; i < written; ++i) {
if (!RingBuffer_Write(rb, localBuffer[i])) {
break;
}
}
}
va_end(args);
}
/* 创建一个全局环形缓冲区实例 */
ringbuffer_t ringBuffer = {
.head = 0,
.tail = 0,
.mutex = NULL
};
/* 数据填充任务 测试任务向队列填充数据依次进行添加 */
void DataFillTask(void *para) {
RingBufferWriteFormatted(&ringBuffer," 1 test test test %d\r\n",0x88);
RingBufferWriteFormatted(&ringBuffer, "2 test size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "3 xx size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "4 00 size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "5 11 size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "6 22 size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "7 33 size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "8 44 size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "9 55 size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "10 66 size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "11 ww size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "12 xiao size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer, "13 bai size value %d\r\n", 0x88);
RingBufferWriteFormatted(&ringBuffer,"*************DataFillTask******************\n");
while (1) {
vTaskDelay(200);
}
}
/* 数据读取和打印任务 */
void PrintTask(void *Param) {
char data;
while (1) {
/* 读取缓冲区,有数据就打印 */
if (RingBuffer_Read(&ringBuffer, &data)) {
printf("%c",data);
// printf("Data '%c' read from buffer.\n", data); /* 从缓冲区去取数据并打印 */
vTaskDelay(30);
}
}
}
方案三参考链接 https://blog.csdn.net/qq_56992543/article/details/147411025?fromshare=blogdetail&sharetype=blogdetail&sharerId=147411025&sharerefer=PC&sharesource=2401_84570224&sharefrom=from_link
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)