zoukankan      html  css  js  c++  java
  • FreeRTOS 调试方法(printf---打印任务执行情况)

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

    本章节为大家介绍 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个任务状态。

  • 相关阅读:
    Visual Prolog 的 Web 专家系统 (7)
    spring来源理解-BeanFactory子类XmlBeanFactory创建过程
    Threejs 它可以在建立其内部房间效果可见
    Android AlarmManager报警的实现
    什么是注册表和注册表中的作用
    精致的外观Request
    数据仓库的工作日记_创纪录的(一)
    servlet api.jar是干什么的?
    servlet-api-2.5.jar
    用poi-3.6-20091214.jar 实现java给excel资料加密
  • 原文地址:https://www.cnblogs.com/yangguang-it/p/7149048.html
Copyright © 2011-2022 走看看