我找了很久才发现。。。

原因:由于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

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐