FreeRTOS调试

前言

在实际开发过程中,经常会遇见卡死的情况,大多数都是因为FreeRTOS的堆栈溢出造成,无论是分配给FreeRTOS的总Heap溢出,或者是单个任务的堆栈不够,都会造成卡死。

所以在FreeRTOS学会定位很重要,可以节省大量的时间,下面就来介绍一下常见的调式手段,在优化内存定位堆栈溢出有着重要作用

启用FreeRTOS堆栈溢出钩子

具体见FreeRTOS中钩子函数相关笔记

输出所有任务的信息

vTaskList() 是 FreeRTOS 提供的一个非常有用的调试API,可以输出所有任务的运行状态、剩余栈空间等信息,有助于我们监控对应任务堆栈是否足够,堆栈是否分配过多造成浪费等

要使用vTaskList需要配置这两个宏,具体位置见下方

1
2
#define configUSE_TRACE_FACILITY                1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
  • configUSE_TRACE_FACILITY: 打开后,FreeRTOS 内核会收集更多和任务相关的运行数据,并使部分调试/统计API可用,在FreeRTOSConfig.h中开启
  • configUSE_STATS_FORMATTING_FUNCTIONS: 启用后,FreeRTOS 会包含“格式化输出”相关的辅助函数,主要是把系统状态数据整理成表格字符串,在FreeRTOS.h开启

然后在某个任务中输出即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>

void printTaskList(void)
{
// 任务列表输出缓冲区,建议>512字节足够容纳所有任务信息
char taskListBuf[512];

// 输出表头
printf("Task Name\tState\tPrio\tStack\tNum\n");

// 获取任务信息
vTaskList(taskListBuf);

// 输出任务信息
printf("%s\n", taskListBuf);
}

// 你可以在某个任务里定时调用
void vDebugTask(void *pvParameters)
{
while(1)
{
printTaskList();
vTaskDelay(pdMS_TO_TICKS(3000)); // 每3秒输出一次
}
}

输出结果如下所示:

image-20250828123243143

  • Task Name:任务名(你创建任务时指定的字符串)
  • State:任务状态
    • R:运行态
    • B:阻塞态
    • S:挂起态
    • D:已被删除
    • X:就绪态
  • Prio:分配的优先级
  • Stack当前剩余最小栈空间(单位为字/word,通常1word = 4字节)
  • Num:任务号

输出FreeRTOS剩余Heap大小

我们可以在程序中输出FreeRTOS剩余Heap大小,监视是否发生Heap不够,Heap溢出的情况

这是我们在FreeRTOSConfig.h中配置的FreeRTOS使用Heap的总大小

image-20250828123926181

我们使用xPortGetFreeHeapSize即可获取剩余Heap的总字节数,可以结合vTaskList一起输出调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char taskListBuffer[512];


void SensorDataUpdateTask(void *argument)
{


while(1)
{
// 输出所有任务信息(剩余栈空间等),单位:Word(4Byte)
vTaskList(taskListBuffer);
SEGGER_RTT_printf(0,"%s\n", taskListBuffer);

// 输出FreeRTOS剩余Heap大小,单位:Byte
SEGGER_RTT_printf(0, "Free heap: %uByte\n", xPortGetFreeHeapSize());

osDelay(1000);
}
}

输出结果如下:

image-20250828124623168