FreeRTOS 调试方法(printf---打印任务执行情况)

以下转载自安富莱电子: http://forum.armfly.com/forum.php

摘自:https://www.cnblogs.com/yangguang-it/p/7149048.html

本章节为大家介绍 FreeRTOS 的调试方法,这里的调试方法主要是教会大家如何获取任务的执行情况,通过获取的任务信息,可以进一步的配置和优化工程,这种方法非常实用,建议初学者必须掌握。 
串口打印调试说明
很多时候,我们需要了解任务的执行状态,任务栈的使用情况以及各个任务的 CPU 使用率,这时就
需要用到官方提供的两个函数 vTaskList  vTaskGetRunTimeStats。用户就可以通过这两个函数获得任
务的执行情况。
获取了任务执行情况后,可以通过串口将其打印出来,当然,也可以通过任何其它方式将其显示出来。
本教程配套的例子统一采用串口打印的方式显示任务的执行情况。另外有一点要特别注意,这种调试方式
仅限测试目的,实际项目中不要使用,这种测试方式比较影响系统实时性
为了获取 FreeRTOS 的任务信息,需要创建一个定时器,这个定时器的时间基准精度要高于系统时钟
节拍,这样得到的任务信息才准确。这里提供的函数仅用于测试目的,切不可将其用于实际项目,原因有两点:

1. FreeRTOS 的系统内核没有对总的计数时间做溢出保护。
2. 定时器中断是 50us 进入一次,比较影响系统性能。
这里使用的是 32 位变量来保存 50us 一次的计数值,最大支持计数时间:2^32 * 50us / 3600s =
59.6 分钟。 运行时间超过了 59.6 分钟将不准确。
具体在 FreeRTOS 的工程中如何做才可以实现任务信息获取呢? 
使能相关宏定义
需要在 FreeRTOSConfig.h 文件中使能如下宏定义:

复制代码
/***freertosconfig.h***/

extern volatile uint32_t ulHighFrequencyTimerTicks ;
/* Run time and task stats gathering related definitions. */
#define configUSE_TRACE_FACILITY    1
#define configGENERATE_RUN_TIME_STATS                1
#define configUSE_STATS_FORMATTING_FUNCTIONS         1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()     (ulHighFrequencyTimerTicks = 0ul) 
#define portGET_RUN_TIME_COUNTER_VALUE()             ulHighFrequencyTimerTicks
复制代码

之前的博客:

使能了宏定义之后,必要完成portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()和portGET_RUN_TIME_COUNTER_VALUE()的定义,那么我们究竟应该如何定义并使用它们呢?

freertos官方网站给了提示(点击这里查看更多):

本次实验采用上面的做法,下面还有一个例子,但是没有上面好用:

现在我使用第一种方式,STM32F429,TIM6基本定时器,产生一个50us周期的中断,进一次中断,计数器的值加1:

由于在进入

/* 第三步:启动FreeRTOS,开始多任务调度,启动成功则不返回 */
vTaskStartScheduler();(在这个函数中调用了portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() )

这个函数之前,定时器计数已经执行计数功能很多次了,为了保证我们的基石相对准确,我们在启动freertos的API函数中将其置零,这也就是为什么那个宏#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 要这样操作这个计数器ulHighFrequencyTimerTicks=0UL;这样从任务开启时,计数器从零开始计数,而portGET_RUN_TIME_COUNTER_VALUE()是为了得到最后的计数值,可以是一个函数,返回之前用户自定义的计数器的值,也可以直接把那个计数器的值进行宏替换,在uxTaskGetSystemState()函数调用形式如下:

可以知道,我们只是需要这个计数器的值作为右值最后打印显示。(一个比较好的参考,点击这里)-(stack overflow参考)-(博客参考)。

有了前面的铺垫之后,再来说说我们的验证试验:

功能描述:按下K1按键,打印出任务信息,函数如下:

复制代码
static void vTaskWork(void *pvParameters)
{

    uint8_t pcWriteBuffer[500];

    while(1)
    {
        
        
        if (key1_flag==1)
        {
                    key1_flag=0;
                /* K1键按下 打印任务执行情况 */
                         
                    printf("=======================================================
");
                    printf("任务名           任务状态   优先级      剩余栈   任务序号
");
                    vTaskList((char *)&pcWriteBuffer);
                    printf("%s
", pcWriteBuffer);
                
                    printf("
任务名            运行计数              使用率
");
                    vTaskGetRunTimeStats((char *)&pcWriteBuffer);
                    printf("%s
", pcWriteBuffer);
                
                /* 其他的键值不处理 */
    
            
        }
        
        vTaskDelay(20);
    }
}
复制代码

任务一,执行打印,任务二,LED闪烁,任务三,蜂鸣器

复制代码
static void AppTaskCreate(void)
{

        xTaskCreate(vTaskWork,       /* 任务函数  */
                 "vTaskWork",         /* 任务名    */
                 512,                   /* 任务栈大小,单位word,也就是4字节 */
                 NULL,                  /* 任务参数  */
                 1,                     /* 任务优先级*/
                 &xHandleTaskWork );  /* 任务句柄  */
    
    
    xTaskCreate( vTaskLed1,            /* 任务函数  */
                 "vTaskLed1",          /* 任务名    */
                 512,                 /* 任务栈大小,单位word,也就是4字节 */
                 NULL,                /* 任务参数  */
                 2,                   /* 任务优先级*/
                 &xHandleTaskLED1); /* 任务句柄  */
    
    xTaskCreate( vTaskBeep,             /* 任务函数  */
                 "vTaskBeep",           /* 任务名    */
                 512,                     /* 任务栈大小,单位word,也就是4字节 */
                 NULL,                   /* 任务参数  */
                 3,                       /* 任务优先级*/
                 &xHandleTaskBeep );  /* 任务句柄  */
    
    
}
复制代码
复制代码
void vTaskLed1(void *pvParameters)
{
    /* 任务都是一个无限,不能返回 */
    while(1)
    {
        LED3_ON;
    /* 阻塞延时,单位ms */        
        vTaskDelay( 500 );
        LED3_OFF;    
        vTaskDelay( 500 );
    }    
}
复制代码
复制代码
void vTaskBeep(void *pvParameters)
{
    /* 任务都是一个无限循环,不能返回 */
    while(1)
    {
        BEEP_ON;
    /* 阻塞延时,单位ms */        
        vTaskDelay( 20 );
        BEEP_OFF;    
        vTaskDelay( 500 );
    }    
}
复制代码

基本硬件初始化:

复制代码
static void BSP_Init(void)
{
    /*
     * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
     * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
     * 都统一用这个优先级分组,千万不要再分组,切忌。
     */
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
    
    /* LED 初始化 */
    LED_GPIO_Config();
    /*串口初始化*/
    Debug_USART_Config();
    /*按键初始化*/
    EXTI_Key_Config();
    /*定时器6初始化*/
    TIMx_Configuration();
  /* 蜂鸣器初始化 */
    Beep_GPIO_Config();    
}
复制代码

关于裸机部分的外设初始化配置不再赘述。这样,下载程序之后,LED灯闪烁并且蜂鸣器鸣叫,在按下k1按键时,串口输出如下:

 有了这个可以知道,我们512字的任务栈空间实在有点浪费,最后的剩余栈单位也是字。

最后,关于vTaskList((char *)&pcWriteBuffer);vTaskGetRunTimeStats((char *)&pcWriteBuffer);这个缓冲区的大小,官网说的,一个任务,大约40个字节足够,我们取50字节,所以定义的缓冲区大小uint8_t pcWriteBuffer[500];可以足够打印10个任务状态。

原文地址:https://www.cnblogs.com/LiuYanYGZ/p/13285100.html