一下内容转载自安富莱电子:http://forum.armfly.com/forum.php本章节为大家介绍 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

FreeRTOS—printf打印任务执行情况

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

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

FreeRTOS—printf打印任务执行情况

FreeRTOS—printf打印任务执行情况

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

FreeRTOS—printf打印任务执行情况

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

FreeRTOS—printf打印任务执行情况

 

由于在进入

/* 第三步:启动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()函数调用形式如下:

FreeRTOS—printf打印任务执行情况

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

 

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

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

static void vTaskWork(void *pvParameters)
{
    uint8_t pcWriteBuffer[500];

    while(1)
    {            
        if (key1_flag==1)
        {
                    key1_flag=0;
                /* K1键按下 打印任务执行情况 */
                         
                    printf("=======================================================\r\n");
                    printf("任务名           任务状态   优先级      剩余栈   任务序号\r\n");
                    vTaskList((char *)&pcWriteBuffer);
                    printf("%s\r\n", pcWriteBuffer);
                
                    printf("\r\n任务名            运行计数              使用率\r\n");
                    vTaskGetRunTimeStats((char *)&pcWriteBuffer);
                    printf("%s\r\n", 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按键时,串口输出如下:

FreeRTOS—printf打印任务执行情况

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

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

 

相关文章: