调试
1.打印
printf:FreeRTOS 工程里使用了 microlib,里面实现了 printf 函数。
我们只需实现一下函数即可使用printf:
int fputc( int ch, FILE *f );
2.断言
- 断言用于检查程序中的某些条件是否为真。当断言条件为假时,程序会触发断言失败,通常会进入一个预先定义的错误处理流程,例如停止程序运行、输出错误信息等
#include "FreeRTOS.h"
#include "task.h"
void vTaskFunction( void *pvParameters )
{
// 任务函数的具体实现
for( ;; )
{
// 任务循环
}
}
int main( void )
{
TaskHandle_t xTaskHandle;
BaseType_t xReturned;
// 创建任务
xReturned = xTaskCreate( vTaskFunction, "TaskName", 100, NULL, 1, &xTaskHandle );
// 使用断言检查任务创建是否成功
configASSERT( xReturned == pdPASS );
// 启动调度器
vTaskStartScheduler();
for( ;; );
}
- 在调用 xTaskCreate() 函数创建任务后,使用 configASSERT() 宏检查任务创建是否成功。如果任务创建失败(xReturned 不等于 pdPASS),则触发断言失败
- 若失败,会进入一个预先定义的错误处理流程,如
#include <stdio.h>
#define configASSERT( x ) if( ( x ) == 0 ) { \
printf("Assertion failed at file %s, line %d\n", __FILE__, __LINE__); \
taskDISABLE_INTERRUPTS(); \
for( ;; ); \
}
- 如果断言失败发生在 main.c 文件的第 20 行,会输出类似 Assertion failed at file main.c, line 20 的信息
- 接着调用 taskDISABLE_INTERRUPTS() 函数禁用所有中断,使系统进入无中断响应状态。
3.Malloc Hook 函数
编程时,一般的逻辑错误都容易解决。难以处理的是内存越界、栈溢出等。
内存越界经常发生在堆的使用过程中,堆,就是使用malloc得到的内存。并没有很好的方法检测内存越界,但是可以提供一些回调函数
使用 pvPortMalloc 失败时,如果在 FreeRTOSConfig.h 里配置configUSE_MALLOC_FAILED_HOOK 为 1,会调用:
void vApplicationMallocFailedHook( void );
4.栈溢出 Hook 函数
当 FreeRTOS 检测到栈溢出时,会自动调用这个函数。开发者可以在这个函数中实现一些处理逻辑
要启用栈溢出检测功能,需要在 FreeRTOSConfig.h 文件中进行相应的配置:
#define configCHECK_FOR_STACK_OVERFLOW 2
0:禁用栈溢出检测功能。
1:使用简单的栈边界检查方法。(多用)
2:使用简单的栈边界检查方法,并且在任务切换时也进行检查,检测更频繁
函数
void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName );
参数说明:
xTask:指向发生栈溢出的任务句柄。
pcTaskName:指向发生栈溢出的任务名称的字符串指针。
简单示例
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
{
printf("Stack overflow detected in task: %s\n", pcTaskName);
// 可以在这里添加更多的处理逻辑,例如重启系统
// NVIC_SystemReset();
}
优化
1.栈使用情况
可以使用以下函数查看是还有多少空余的栈空间
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
// 任务函数
void vTaskFunction( void *pvParameters )
{
UBaseType_t uxHighWaterMark;
for( ;; )
{
// 模拟任务的一些工作
for( int i = 0; i < 1000; i++ )
{
// 这里可以添加更复杂的任务逻辑
}
// 获取当前任务的栈高水位线
uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
printf("Stack high water mark of current task: %lu\n", (unsigned long)uxHighWaterMark);
// 任务延时
vTaskDelay( pdMS_TO_TICKS( 1000 ) );
}
}
int main( void )
{
// 创建任务
xTaskCreate( vTaskFunction, "TaskName", 1000, NULL, 1, NULL );
// 启动调度器
vTaskStartScheduler();
// 如果调度器启动失败,程序会执行到这里
for( ;; );
}
vTaskList :获得任务的统计信息,形式为可读的字符串。注意,pcWriteBuffer必须足够大。
void vTaskList( signed char *pcWriteBuffer );
可读信息格式如下
2.任务运行时间统计
配置(可在cubemx中配置)
#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_TRACE_FACILITY 1
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
在vTaskSwitchContext()函数中
接着利用vTaskGetRunTimeStats:获得任务的运行信息,形式为可读的字符串。注意,pcWriteBuffer 必须足够大。
void vTaskGetRunTimeStats( signed char *pcWriteBuffer );
可读信息格式如下: