硬汉嵌入式论坛

 找回密码
 立即注册
查看: 7782|回复: 10
收起左侧

[FreeRTOS教程] 第8章 FreeRTOS调试方法(打印任务执行情况)

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-8-17 15:48:34 | 显示全部楼层 |阅读模式
第8章       FreeRTOS调试方法(打印任务执行情况)


    本章节为大家介绍FreeRTOS的调试方法,这里的调试方法主要是教会大家如何获取任务的执行情况,通过获取的任务信息,可以进一步的配置和优化工程,这种方法非常实用,建议初学者必须掌握。
    本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407以及F429。
8.1 串口打印调试说明
8.2 STM32F103实现串口打印调试
8.3 STM32F407实现串口打印调试
8.4 STM32F429实现串口打印调试
8.5总结
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-17 15:54:24 | 显示全部楼层
8.1  串口打印调试说明


       很多时候,我们需要了解任务的执行状态,任务栈的使用情况以及各个任务的CPU使用率,这时就需要用到官方提供的两个函数vTaskList和vTaskGetRunTimeStats。用户就可以通过这两个函数获得任务的执行情况。
      获取了任务执行情况后,可以通过串口将其打印出来,当然,也可以通过任何其它方式将其显示出来。本教程配套的例子统一采用串口打印的方式显示任务的执行情况另外有一点要特别注意,这种调试方式仅限测试目的,实际项目中不要使用,这种测试方式比较影响系统实时性
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-17 15:57:50 | 显示全部楼层
8.2   STM32F103实现串口打印调试


    为了获取FreeRTOS的任务信息,需要创建一个定时器,这个定时器的时间基准精度要高于系统时钟节拍,这样得到的任务信息才准确。这里提供的函数仅用于测试目的,切不可将其用于实际项目,原因有两点:
u FreeRTOS的系统内核没有对总的计数时间做溢出保护。
u 定时器中断是50us进入一次,比较影响系统性能。
    这里使用的是32位变量来保存50us一次的计数值,最大支持计数时间:2^32 * 50us / 3600s =59.6分钟。运行时间超过了59.6分钟将不准确。
    具体在FreeRTOS的工程中如何做才可以实现任务信息获取呢?下面分三步进行说明。

8.2.1     使能相关宏定义


   需要在FreeRTOSConfig.h文件中使能如下宏定义:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. /* Run time and task stats gathering related definitions. */
  7. #define configUSE_TRACE_FACILITY                    1
  8. #define configGENERATE_RUN_TIME_STATS               1
  9. #define configUSE_STATS_FORMATTING_FUNCTIONS        1
  10. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()    (ulHighFrequencyTimerTicks = 0ul)
  11. #define portGET_RUN_TIME_COUNTER_VALUE()            ulHighFrequencyTimerTicks
复制代码
其中变量ulHighFrequencyTimerTicks是需要用户去定义的,我们这里是将其定义在了定时器初始化文件SysInfoTest.c里面。

8.2.2     精度高于滴答定时器的时钟初始化


    这里采用STM32F103内部的TIM6实现50us一次的中断,在中断函数里面对变量ulHighFrequencyTimerTicks进行计数操作,以供FreeRTOS系统使用,具体实现的代码如下:
  1. #include "bsp.h"
  2. /* 定时器频率,50us一次中断 */
  3. #define  timerINTERRUPT_FREQUENCY    20000
  4. /* 中断优先级 */
  5. #define  timerHIGHEST_PRIORITY       1
  6. /* 被系统调用 */
  7. volatile uint32_t ulHighFrequencyTimerTicks = 0UL;
  8. /*
  9. *********************************************************************************************************
  10. *    函 数 名: vSetupTimerTest
  11. *    功能说明: 创建定时器
  12. *    形    参: 无
  13. *    返 回 值: 无
  14. *********************************************************************************************************
  15. */
  16. void vSetupSysInfoTest(void)
  17. {
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);
  19. }
  20. /*
  21. *********************************************************************************************************
  22. *    函 数 名: TIM6_IRQHandler
  23. *    功能说明: TIM6中断服务程序。
  24. *    形    参: 无
  25. *    返 回 值: 无
  26. *********************************************************************************************************
  27. */
  28. void TIM6_IRQHandler( void )
  29. {
  30.      if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
  31.      {
  32.          ulHighFrequencyTimerTicks++;
  33.          TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
  34.      }
  35. }
  36. /***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
复制代码
其中函数bsp_SetTIMforInt在文件bsp_tim_pwm.c里面进行了实现,源代码如下:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_SetTIMforInt
  4. *    功能说明: 配置TIM和NVIC,用于简单的定时中断. 开启定时中断。 中断服务程序由应用程序实现。
  5. *    形    参: TIMx : 定时器
  6. *               _ulFreq : 定时频率 (Hz)。 0 表示关闭。
  7. *               _PreemptionPriority : 中断优先级分组
  8. *               _SubPriority : 子优先级
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. void bsp_SetTIMforInt(TIM_TypeDef* TIMx, uint32_t _ulFreq, uint8_t _PreemptionPriority, uint8_t _SubPriority)
  13. {
  14.      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  15.      uint16_t usPeriod;
  16.      uint16_t usPrescaler;
  17.      uint32_t uiTIMxCLK;
  18.      /* 使能TIM时钟 */
  19.      if ((TIMx == TIM1) || (TIMx == TIM8))
  20.      {
  21.          RCC_APB2PeriphClockCmd(bsp_GetRCCofTIM(TIMx), ENABLE);
  22.      }
  23.      else
  24.      {
  25.          RCC_APB1PeriphClockCmd(bsp_GetRCCofTIM(TIMx), ENABLE);
  26.      }
  27.      if (_ulFreq == 0)
  28.      {
  29.          TIM_Cmd(TIMx, DISABLE);     /* 关闭定时输出 */
  30.          /* 关闭TIM定时更新中断 (Update) */
  31.          {
  32.               NVIC_InitTypeDef NVIC_InitStructure; /* 中断结构体在 misc.h 中定义 */
  33.               uint8_t irq = 0;   /* 中断号, 定义在 stm32f4xx.h */
  34.               if (TIMx == TIM1)
  35.                    irq = TIM1_UP_IRQn;
  36.               else if (TIMx == TIM2)
  37.                    irq = TIM2_IRQn;
  38.               else if (TIMx == TIM3)
  39.                    irq = TIM3_IRQn;
  40.               else if (TIMx == TIM4)
  41.                    irq = TIM4_IRQn;
  42.               else if (TIMx == TIM5)
  43.                    irq = TIM5_IRQn;
  44.               else if (TIMx == TIM6)
  45.                    irq = TIM6_IRQn;
  46.               else if (TIMx == TIM7)
  47.                    irq = TIM7_IRQn;
  48.               else if (TIMx == TIM8)
  49.                    irq = TIM8_UP_IRQn;
  50.               NVIC_InitStructure.NVIC_IRQChannel = irq;
  51.               NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = _PreemptionPriority;
  52.               NVIC_InitStructure.NVIC_IRQChannelSubPriority = _SubPriority;
  53.               NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  54.               NVIC_Init(&NVIC_InitStructure);
  55.          }      
  56.          return;
  57.      }
  58.     /*-----------------------------------------------------------------------
  59.          system_stm32f4xx.c 文件中 static void SetSysClockToHSE(void) 函数对时钟的配置如下:
  60.               //HCLK = SYSCLK
  61.               RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  62.                
  63.               //PCLK2 = HCLK
  64.               RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
  65.             
  66.               //PCLK1 = HCLK
  67.               RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;
  68.          APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13,TIM14
  69.          APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11
  70.      ----------------------------------------------------------------------- */
  71.      if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM9) || (TIMx == TIM10) || (TIMx == TIM11))
  72.      {
  73.          /* APB2 定时器 */
  74.          uiTIMxCLK = SystemCoreClock;
  75.      }
  76.      else /* APB1 定时器 .  */
  77.      {
  78.          uiTIMxCLK = SystemCoreClock;     // SystemCoreClock / 2;
  79.      }
  80.      if (_ulFreq < 100)
  81.      {
  82.          usPrescaler = 10000 - 1;                       /* 分频比 = 1000 */
  83.          usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值 */
  84.      }
  85.      else if (_ulFreq < 3000)
  86.      {
  87.          usPrescaler = 100 - 1;                          /* 分频比 = 100 */
  88.          usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;  /* 自动重装的值 */
  89.      }
  90.      else /* 大于4K的频率,无需分频 */
  91.      {
  92.          usPrescaler = 0;                     /* 分频比 = 1 */
  93.          usPeriod = uiTIMxCLK / _ulFreq - 1;  /* 自动重装的值 */
  94.      }
  95.      /* Time base configuration */
  96.      TIM_TimeBaseStructure.TIM_Period = usPeriod;
  97.      TIM_TimeBaseStructure.TIM_Prescaler = usPrescaler;
  98.      TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  99.      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  100.      TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  101.      TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
  102.      TIM_ARRPreloadConfig(TIMx, ENABLE);
  103.      /* TIM Interrupts enable */
  104.      TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
  105.      /* TIMx enable counter */
  106.      TIM_Cmd(TIMx, ENABLE);
  107.      /* 配置TIM定时更新中断 (Update) */
  108.      {
  109.          NVIC_InitTypeDef NVIC_InitStructure; /* 中断结构体在 misc.h 中定义 */
  110.          uint8_t irq = 0;   /* 中断号, 定义在 stm32f4xx.h */
  111.          if (TIMx == TIM1)
  112.               irq = TIM1_UP_IRQn;
  113.          else if (TIMx == TIM2)
  114.               irq = TIM2_IRQn;
  115.          else if (TIMx == TIM3)
  116.               irq = TIM3_IRQn;
  117.          else if (TIMx == TIM4)
  118.               irq = TIM4_IRQn;
  119.          else if (TIMx == TIM5)
  120.               irq = TIM5_IRQn;
  121.          else if (TIMx == TIM6)
  122.               irq = TIM6_IRQn;
  123.          else if (TIMx == TIM7)
  124.               irq = TIM7_IRQn;
  125.          else if (TIMx == TIM8)
  126.               irq = TIM8_UP_IRQn;
  127.          NVIC_InitStructure.NVIC_IRQChannel = irq;
  128.          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = _PreemptionPriority;
  129.          NVIC_InitStructure.NVIC_IRQChannelSubPriority = _SubPriority;
  130.          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  131.          NVIC_Init(&NVIC_InitStructure);
  132.      }
  133. }
复制代码

8.2.3     获取任务执行情况


    通过前面8.2.1小节和8.2.2小节的设置后,大家就可以在工程中通过FreeRTOS的两个函数vTaskListvTaskGetRunTimeStats获取任务的执行情况。这里我们分成简单的两步进行说明:
u 第1步:先做精度高于滴答定时器的时钟初始化
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
u 第2步:初始化好以后就可以在FreeRTOS的任务中使用了。这里将任务信息获取功能放在了任务vTaskTaskUserIF里面去实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* 其他的键值不处理 */
  35.                    default:                    
  36.                        break;
  37.               }
  38.          }
  39.         
  40.          vTaskDelay(20);
  41.      }
  42. }
复制代码

8.2.4     串口打印效果


    STM32F103配套的完整工程是:V4-301_FreeRTOS实验_串口调试方法(打印任务执行情况)串口打印的效果如下(波特率115200,数据位8,奇偶校验位无,停止位1):
8.1.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
另外要注意剩余栈的单位是word,即4字节。比如vTaskUserIF任务的剩余栈是321,代表321*4 = 1284字节。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-17 16:02:03 | 显示全部楼层
8.3  STM32F407实现串口打印调试


    为了获取FreeRTOS的任务信息,需要创建一个定时器,这个定时器的时间基准精度要高于系统时钟节拍,这样得到的任务信息才准确。这里提供的函数仅用于测试目的,切不可将其用于实际项目,原因有两点:
u FreeRTOS的系统内核没有对总的计数时间做溢出保护。
u 定时器中断是50us进入一次,比较影响系统性能。
    这里使用的是32位变量来保存50us一次的计数值,最大支持计数时间:2^32 * 50us / 3600s =59.6分钟。运行时间超过了59.6分钟将不准确。
    具体在FreeRTOS的工程中如何做才可以实现任务信息获取呢?下面分三步进行说明。

8.3.1     使能相关宏定义


    需要在FreeRTOSConfig.h文件中使能如下宏定义:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. /* Run time and task stats gathering related definitions. */
  7. #define configUSE_TRACE_FACILITY                    1
  8. #define configGENERATE_RUN_TIME_STATS               1
  9. #define configUSE_STATS_FORMATTING_FUNCTIONS        1
  10. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()    (ulHighFrequencyTimerTicks = 0ul)
  11. #define portGET_RUN_TIME_COUNTER_VALUE()            ulHighFrequencyTimerTicks
复制代码

其中变量ulHighFrequencyTimerTicks是需要用户去定义的,我们这里是将其定义在了定时器初始化文件SysInfoTest.c里面。

8.3.2     精度高于滴答定时器的时钟初始化


    这里采用STM32F407内部的TIM6实现50us一次的中断,在中断函数里面对变量ulHighFrequencyTimerTicks进行计数操作,以供FreeRTOS系统使用,具体实现的代码如下:
  1. #include "bsp.h"
  2. /* 定时器频率,50us一次中断 */
  3. #define  timerINTERRUPT_FREQUENCY    20000
  4. /* 中断优先级 */
  5. #define  timerHIGHEST_PRIORITY       1
  6. /* 被系统调用 */
  7. volatile uint32_t ulHighFrequencyTimerTicks = 0UL;
  8. /*
  9. *********************************************************************************************************
  10. *    函 数 名: vSetupTimerTest
  11. *    功能说明: 创建定时器
  12. *    形    参: 无
  13. *    返 回 值: 无
  14. *********************************************************************************************************
  15. */
  16. void vSetupSysInfoTest(void)
  17. {
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);
  19. }
  20. /*
  21. *********************************************************************************************************
  22. *    函 数 名: TIM6_DAC_IRQHandler
  23. *    功能说明: TIM6中断服务程序。
  24. *    形    参: 无
  25. *    返 回 值: 无
  26. *********************************************************************************************************
  27. */
  28. void TIM6_DAC_IRQHandler ( void )
  29. {
  30.      if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
  31.      {
  32.          ulHighFrequencyTimerTicks++;
  33.          TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
  34.      }
  35. }
  36. /***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
复制代码

其中函数bsp_SetTIMforInt在文件bsp_tim_pwm.c里面进行了实现,源代码如下:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_SetTIMforInt
  4. *    功能说明: 配置TIM和NVIC,用于简单的定时中断. 开启定时中断。 中断服务程序由应用程序实现。
  5. *    形    参: TIMx : 定时器
  6. *               _ulFreq : 定时频率 (Hz)。 0 表示关闭。
  7. *               _PreemptionPriority : 中断优先级分组
  8. *               _SubPriority : 子优先级
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. void bsp_SetTIMforInt(TIM_TypeDef* TIMx, uint32_t _ulFreq, uint8_t _PreemptionPriority, uint8_t _SubPriority)
  13. {
  14.      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  15.      uint16_t usPeriod;
  16.      uint16_t usPrescaler;
  17.      uint32_t uiTIMxCLK;
  18.      /* 使能TIM时钟 */
  19.      if ((TIMx == TIM1) || (TIMx == TIM8))
  20.      {
  21.          RCC_APB2PeriphClockCmd(bsp_GetRCCofTIM(TIMx), ENABLE);
  22.      }
  23.      else
  24.      {
  25.          RCC_APB1PeriphClockCmd(bsp_GetRCCofTIM(TIMx), ENABLE);
  26.      }
  27.      if (_ulFreq == 0)
  28.      {
  29.          TIM_Cmd(TIMx, DISABLE);     /* 关闭定时输出 */
  30.          /* 关闭TIM定时更新中断 (Update) */
  31.          {
  32.               NVIC_InitTypeDef NVIC_InitStructure; /* 中断结构体在 misc.h 中定义 */
  33.               uint8_t irq = 0;   /* 中断号, 定义在 stm32f4xx.h */
  34.               if (TIMx == TIM1)
  35.                    irq = TIM1_UP_IRQn;
  36.               else if (TIMx == TIM2)
  37.                    irq = TIM2_IRQn;
  38.               else if (TIMx == TIM3)
  39.                    irq = TIM3_IRQn;
  40.               else if (TIMx == TIM4)
  41.                    irq = TIM4_IRQn;
  42.               else if (TIMx == TIM5)
  43.                    irq = TIM5_IRQn;
  44.               else if (TIMx == TIM6)
  45.                    irq = TIM6_IRQn;
  46.               else if (TIMx == TIM7)
  47.                    irq = TIM7_IRQn;
  48.               else if (TIMx == TIM8)
  49.                    irq = TIM8_UP_IRQn;
  50.               NVIC_InitStructure.NVIC_IRQChannel = irq;
  51.               NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = _PreemptionPriority;
  52.               NVIC_InitStructure.NVIC_IRQChannelSubPriority = _SubPriority;
  53.               NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  54.               NVIC_Init(&NVIC_InitStructure);
  55.          }      
  56.          return;
  57.      }
  58.     /*-----------------------------------------------------------------------
  59.          system_stm32f4xx.c 文件中 static void SetSysClockToHSE(void) 函数对时钟的配置如下:
  60.               //HCLK = SYSCLK
  61.               RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  62.                
  63.               //PCLK2 = HCLK
  64.               RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
  65.             
  66.               //PCLK1 = HCLK
  67.               RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;
  68.          APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13,TIM14
  69.          APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11
  70.      ----------------------------------------------------------------------- */
  71.      if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM9) || (TIMx == TIM10) || (TIMx == TIM11))
  72.      {
  73.          /* APB2 定时器 */
  74.          uiTIMxCLK = SystemCoreClock;
  75.      }
  76.      else /* APB1 定时器 .  */
  77.      {
  78.          uiTIMxCLK = SystemCoreClock;     // SystemCoreClock / 2;
  79.      }
  80.      if (_ulFreq < 100)
  81.      {
  82.          usPrescaler = 10000 - 1;                       /* 分频比 = 1000 */
  83.          usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值 */
  84.      }
  85.      else if (_ulFreq < 3000)
  86.      {
  87.          usPrescaler = 100 - 1;                          /* 分频比 = 100 */
  88.          usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;  /* 自动重装的值 */
  89.      }
  90.      else /* 大于4K的频率,无需分频 */
  91.      {
  92.          usPrescaler = 0;                     /* 分频比 = 1 */
  93.          usPeriod = uiTIMxCLK / _ulFreq - 1;  /* 自动重装的值 */
  94.      }
  95.      /* Time base configuration */
  96.      TIM_TimeBaseStructure.TIM_Period = usPeriod;
  97.      TIM_TimeBaseStructure.TIM_Prescaler = usPrescaler;
  98.      TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  99.      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  100.      TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  101.      TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
  102.      TIM_ARRPreloadConfig(TIMx, ENABLE);
  103.      /* TIM Interrupts enable */
  104.      TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
  105.      /* TIMx enable counter */
  106.      TIM_Cmd(TIMx, ENABLE);
  107.      /* 配置TIM定时更新中断 (Update) */
  108.      {
  109.          NVIC_InitTypeDef NVIC_InitStructure; /* 中断结构体在 misc.h 中定义 */
  110.          uint8_t irq = 0;   /* 中断号, 定义在 stm32f4xx.h */
  111.          if (TIMx == TIM1)
  112.               irq = TIM1_UP_IRQn;
  113.          else if (TIMx == TIM2)
  114.               irq = TIM2_IRQn;
  115.          else if (TIMx == TIM3)
  116.               irq = TIM3_IRQn;
  117.          else if (TIMx == TIM4)
  118.               irq = TIM4_IRQn;
  119.          else if (TIMx == TIM5)
  120.               irq = TIM5_IRQn;
  121.          else if (TIMx == TIM6)
  122.               irq = TIM6_IRQn;
  123.          else if (TIMx == TIM7)
  124.               irq = TIM7_IRQn;
  125.          else if (TIMx == TIM8)
  126.               irq = TIM8_UP_IRQn;
  127.          NVIC_InitStructure.NVIC_IRQChannel = irq;
  128.          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = _PreemptionPriority;
  129.          NVIC_InitStructure.NVIC_IRQChannelSubPriority = _SubPriority;
  130.          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  131.          NVIC_Init(&NVIC_InitStructure);
  132.      }
  133. }
复制代码


8.3.3     获取任务执行情况


     通过前面8.2.1小节和8.2.2小节的设置后,大家就可以在工程中通过FreeRTOS的两个函数vTaskListvTaskGetRunTimeStats获取任务的执行情况。这里我们分成简单的两步进行说明:
u 第1步:先做精度高于滴答定时器的时钟初始化
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码

u 第2步:初始化好以后就可以在FreeRTOS的任务中使用了。这里将任务信息获取功能放在了任务vTaskTaskUserIF里面去实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* 其他的键值不处理 */
  35.                    default:                    
  36.                        break;
  37.               }
  38.          }
  39.         
  40.          vTaskDelay(20);
  41.      }
  42. }
复制代码


8.3.4     串口打印效果


    STM32F407配套的完整工程是:V5-301_FreeRTOS实验_串口调试方法(打印任务执行情况)串口打印的效果如下(波特率115200,数据位8,奇偶校验位无,停止位1):
8.2.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
另外要注意剩余栈的单位是word,即4字节。比如vTaskUserIF任务的剩余栈是321,代表321*4 = 1284字节。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-17 16:05:54 | 显示全部楼层
8.4  STM32F429实现串口打印调试


    为了获取FreeRTOS的任务信息,需要创建一个定时器,这个定时器的时间基准精度要高于系统时钟节拍,这样得到的任务信息才准确。这里提供的函数仅用于测试目的,切不可将其用于实际项目,原因有两点:
u FreeRTOS的系统内核没有对总的计数时间做溢出保护。
u 定时器中断是50us进入一次,比较影响系统性能。
    这里使用的是32位变量来保存50us一次的计数值,最大支持计数时间:2^32 * 50us / 3600s =59.6分钟。运行时间超过了59.6分钟将不准确。
    具体在FreeRTOS的工程中如何做才可以实现任务信息获取呢?下面分三步进行说明。

8.4.1     使能相关宏定义


    需要在FreeRTOSConfig.h文件中使能如下宏定义:
  1. /* Ensure stdint is only used by the compiler, and not the assembler. */
  2. #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
  3. #include <stdint.h>
  4. extern volatile uint32_t ulHighFrequencyTimerTicks;
  5. #endif
  6. /* Run time and task stats gathering related definitions. */
  7. #define configUSE_TRACE_FACILITY                    1
  8. #define configGENERATE_RUN_TIME_STATS               1
  9. #define configUSE_STATS_FORMATTING_FUNCTIONS        1
  10. #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()    (ulHighFrequencyTimerTicks = 0ul)
  11. #define portGET_RUN_TIME_COUNTER_VALUE()            ulHighFrequencyTimerTicks
复制代码
其中变量ulHighFrequencyTimerTicks是需要用户去定义的,我们这里是将其定义在了定时器初始化文件SysInfoTest.c里面。

8.4.2     精度高于滴答定时器的时钟初始化


    这里采用STM32F429内部的TIM6实现50us一次的中断,在中断函数里面对变量ulHighFrequencyTimerTicks进行计数操作,以供FreeRTOS系统使用,具体实现的代码如下:
  1. #include "bsp.h"
  2. /* 定时器频率,50us一次中断 */
  3. #define  timerINTERRUPT_FREQUENCY    20000
  4. /* 中断优先级 */
  5. #define  timerHIGHEST_PRIORITY       1
  6. /* 被系统调用 */
  7. volatile uint32_t ulHighFrequencyTimerTicks = 0UL;
  8. /*
  9. *********************************************************************************************************
  10. *    函 数 名: vSetupTimerTest
  11. *    功能说明: 创建定时器
  12. *    形    参: 无
  13. *    返 回 值: 无
  14. *********************************************************************************************************
  15. */
  16. void vSetupSysInfoTest(void)
  17. {
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);
  19. }
  20. /*
  21. *********************************************************************************************************
  22. *    函 数 名: TIM6_DAC_IRQHandler
  23. *    功能说明: TIM6中断服务程序。
  24. *    形    参: 无
  25. *    返 回 值: 无
  26. *********************************************************************************************************
  27. */
  28. void TIM6_DAC_IRQHandler ( void )
  29. {
  30.      if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
  31.      {
  32.          ulHighFrequencyTimerTicks++;
  33.          TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
  34.      }
  35. }
  36. /***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/
复制代码
其中函数bsp_SetTIMforInt在文件bsp_tim_pwm.c里面进行了实现,源代码如下:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_SetTIMforInt
  4. *    功能说明: 配置TIM和NVIC,用于简单的定时中断. 开启定时中断。 中断服务程序由应用程序实现。
  5. *    形    参: TIMx : 定时器
  6. *               _ulFreq : 定时频率 (Hz)。 0 表示关闭。
  7. *               _PreemptionPriority : 中断优先级分组
  8. *               _SubPriority : 子优先级
  9. *    返 回 值: 无
  10. *********************************************************************************************************
  11. */
  12. void bsp_SetTIMforInt(TIM_TypeDef* TIMx, uint32_t _ulFreq, uint8_t _PreemptionPriority, uint8_t _SubPriority)
  13. {
  14.      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  15.      uint16_t usPeriod;
  16.      uint16_t usPrescaler;
  17.      uint32_t uiTIMxCLK;
  18.      /* 使能TIM时钟 */
  19.      if ((TIMx == TIM1) || (TIMx == TIM8))
  20.      {
  21.          RCC_APB2PeriphClockCmd(bsp_GetRCCofTIM(TIMx), ENABLE);
  22.      }
  23.      else
  24.      {
  25.          RCC_APB1PeriphClockCmd(bsp_GetRCCofTIM(TIMx), ENABLE);
  26.      }
  27.      if (_ulFreq == 0)
  28.      {
  29.          TIM_Cmd(TIMx, DISABLE);     /* 关闭定时输出 */
  30.          /* 关闭TIM定时更新中断 (Update) */
  31.          {
  32.               NVIC_InitTypeDef NVIC_InitStructure; /* 中断结构体在 misc.h 中定义 */
  33.               uint8_t irq = 0;   /* 中断号, 定义在 stm32f4xx.h */
  34.               if (TIMx == TIM1)
  35.                    irq = TIM1_UP_IRQn;
  36.               else if (TIMx == TIM2)
  37.                    irq = TIM2_IRQn;
  38.               else if (TIMx == TIM3)
  39.                    irq = TIM3_IRQn;
  40.               else if (TIMx == TIM4)
  41.                    irq = TIM4_IRQn;
  42.               else if (TIMx == TIM5)
  43.                    irq = TIM5_IRQn;
  44.               else if (TIMx == TIM6)
  45.                    irq = TIM6_IRQn;
  46.               else if (TIMx == TIM7)
  47.                    irq = TIM7_IRQn;
  48.               else if (TIMx == TIM8)
  49.                    irq = TIM8_UP_IRQn;
  50.               NVIC_InitStructure.NVIC_IRQChannel = irq;
  51.               NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = _PreemptionPriority;
  52.               NVIC_InitStructure.NVIC_IRQChannelSubPriority = _SubPriority;
  53.               NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
  54.               NVIC_Init(&NVIC_InitStructure);
  55.          }      
  56.          return;
  57.      }
  58.     /*-----------------------------------------------------------------------
  59.          system_stm32f4xx.c 文件中 static void SetSysClockToHSE(void) 函数对时钟的配置如下:
  60.               //HCLK = SYSCLK
  61.               RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  62.                
  63.               //PCLK2 = HCLK
  64.               RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
  65.             
  66.               //PCLK1 = HCLK
  67.               RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;
  68.          APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13,TIM14
  69.          APB2 定时器有 TIM1, TIM8 ,TIM9, TIM10, TIM11
  70.      ----------------------------------------------------------------------- */
  71.      if ((TIMx == TIM1) || (TIMx == TIM8) || (TIMx == TIM9) || (TIMx == TIM10) || (TIMx == TIM11))
  72.      {
  73.          /* APB2 定时器 */
  74.          uiTIMxCLK = SystemCoreClock;
  75.      }
  76.      else /* APB1 定时器 .  */
  77.      {
  78.          uiTIMxCLK = SystemCoreClock;     // SystemCoreClock / 2;
  79.      }
  80.      if (_ulFreq < 100)
  81.      {
  82.          usPrescaler = 10000 - 1;                       /* 分频比 = 1000 */
  83.          usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1; /* 自动重装的值 */
  84.      }
  85.      else if (_ulFreq < 3000)
  86.      {
  87.          usPrescaler = 100 - 1;                          /* 分频比 = 100 */
  88.          usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;  /* 自动重装的值 */
  89.      }
  90.      else /* 大于4K的频率,无需分频 */
  91.      {
  92.          usPrescaler = 0;                     /* 分频比 = 1 */
  93.          usPeriod = uiTIMxCLK / _ulFreq - 1;  /* 自动重装的值 */
  94.      }
  95.      /* Time base configuration */
  96.      TIM_TimeBaseStructure.TIM_Period = usPeriod;
  97.      TIM_TimeBaseStructure.TIM_Prescaler = usPrescaler;
  98.      TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  99.      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  100.      TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  101.      TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
  102.      TIM_ARRPreloadConfig(TIMx, ENABLE);
  103.      /* TIM Interrupts enable */
  104.      TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
  105.      /* TIMx enable counter */
  106.      TIM_Cmd(TIMx, ENABLE);
  107.      /* 配置TIM定时更新中断 (Update) */
  108.      {
  109.          NVIC_InitTypeDef NVIC_InitStructure; /* 中断结构体在 misc.h 中定义 */
  110.          uint8_t irq = 0;   /* 中断号, 定义在 stm32f4xx.h */
  111.          if (TIMx == TIM1)
  112.               irq = TIM1_UP_IRQn;
  113.          else if (TIMx == TIM2)
  114.               irq = TIM2_IRQn;
  115.          else if (TIMx == TIM3)
  116.               irq = TIM3_IRQn;
  117.          else if (TIMx == TIM4)
  118.               irq = TIM4_IRQn;
  119.          else if (TIMx == TIM5)
  120.               irq = TIM5_IRQn;
  121.           else if (TIMx == TIM6)
  122.               irq = TIM6_IRQn;
  123.          else if (TIMx == TIM7)
  124.               irq = TIM7_IRQn;
  125.          else if (TIMx == TIM8)
  126.               irq = TIM8_UP_IRQn;
  127.          NVIC_InitStructure.NVIC_IRQChannel = irq;
  128.          NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = _PreemptionPriority;
  129.          NVIC_InitStructure.NVIC_IRQChannelSubPriority = _SubPriority;
  130.          NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  131.          NVIC_Init(&NVIC_InitStructure);
  132.      }
  133. }
复制代码

8.4.3     获取任务执行情况


    通过前面8.2.1小节和8.2.2小节的设置后,大家就可以在工程中通过FreeRTOS的两个函数vTaskListvTaskGetRunTimeStats获取任务的执行情况。这里我们分成简单的两步进行说明:
u 第1步:先做精度高于滴答定时器的时钟初始化
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.      /*
  12.        在启动调度前,为了防止初始化STM32外设时有中断服务程序执行,这里禁止全局中断(除了NMI和HardFault)。
  13.        这样做的好处是:
  14.        1. 防止执行的中断服务程序中有FreeRTOS的API函数。
  15.        2. 保证系统正常启动,不受别的中断影响。
  16.        3. 关于是否关闭全局中断,大家根据自己的实际情况设置即可。
  17.        在移植文件port.c中的函数prvStartFirstTask中会重新开启全局中断。通过指令cpsie i开启,__set_PRIMASK(1)
  18.        和cpsie i是等效的。
  19.      */
  20.      __set_PRIMASK(1);
  21.    
  22.      /* 硬件初始化 */
  23.      bsp_Init();
  24.    
  25.      /* 1. 初始化一个定时器中断,精度高于滴答定时器中断,这样才可以获得准确的系统信息 仅供调试目的,实际项
  26.            目中不要使用,因为这个功能比较影响系统实时性。
  27.         2. 为了正确获取FreeRTOS的调试信息,可以考虑将上面的关闭中断指令__set_PRIMASK(1); 注释掉。
  28.      */
  29.      vSetupSysInfoTest();
  30.    
  31.      /* 创建任务 */
  32.      AppTaskCreate();
  33.    
  34.     /* 启动调度,开始执行任务 */
  35.     vTaskStartScheduler();
  36.      /*
  37.        如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
  38.        heap空间不足造成创建失败,此要加大FreeRTOSConfig.h文件中定义的heap大小:
  39.        #define configTOTAL_HEAP_SIZE        ( ( size_t ) ( 17 * 1024 ) )
  40.      */
  41.      while(1);
  42. }
复制代码
u 第2步:初始化好以后就可以在FreeRTOS的任务中使用了。这里将任务信息获取功能放在了任务vTaskTaskUserIF里面去实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: vTaskTaskUserIF
  4. *    功能说明: 接口消息处理。
  5. *    形    参: pvParameters 是在创建该任务时传递的形参
  6. *    返 回 值: 无
  7. *   优 先 级: 1  (数值越小优先级越低,这个跟uCOS相反)
  8. *********************************************************************************************************
  9. */
  10. static void vTaskTaskUserIF(void *pvParameters)
  11. {
  12.      uint8_t ucKeyCode;
  13.      uint8_t pcWriteBuffer[500];
  14.     while(1)
  15.     {
  16.          ucKeyCode = bsp_GetKey();
  17.         
  18.          if (ucKeyCode != KEY_NONE)
  19.          {
  20.               switch (ucKeyCode)
  21.               {
  22.                    /* K1键按下 打印任务执行情况 */
  23.                    case KEY_DOWN_K1:         
  24.                        printf("=================================================\\r\\n");
  25.                        printf("任务名      任务状态 优先级   剩余栈 任务序号\\r\\n");
  26.                        vTaskList((char *)&pcWriteBuffer);
  27.                        printf("%s\\r\\n", pcWriteBuffer);
  28.                   
  29.                        printf("\\r\\n任务名       运行计数         使用率\\r\\n");
  30.                        vTaskGetRunTimeStats((char *)&pcWriteBuffer);
  31.                        printf("%s\\r\\n", pcWriteBuffer);
  32.                        break;
  33.                   
  34.                    /* 其他的键值不处理 */
  35.                    default:                    
  36.                        break;
  37.               }
  38.          }
  39.         
  40.          vTaskDelay(20);
  41.      }
  42. }
复制代码

8.4.4     串口打印效果


    STM32F429配套的完整工程是:V6-301_FreeRTOS实验_串口调试方法(打印任务执行情况)串口打印的效果如下(波特率115200,数据位8,奇偶校验位无,停止位1):
8.3.jpg

上面截图中打印出来的任务状态字母B, R, D, S对应如下含义:
    #definetskBLOCKED_CHAR          ( 'B' )  任务阻塞
    #definetskREADY_CHAR           ( 'R' ) 任务就绪
    #definetskDELETED_CHAR           ( 'D' )  任务删除
    #definetskSUSPENDED_CHAR   ( 'S' ) 任务挂起
另外要注意剩余栈的单位是word,即4字节。比如vTaskUserIF任务的剩余栈是321,代表321*4 = 1284字节。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-8-17 16:06:23 | 显示全部楼层
8.5  总结

    本章节主要是指导大家如何获取任务的执行情况,非常的实用,建议初学者务必掌握。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2018-8-21 14:12:19 | 显示全部楼层
不错。。。。。。。。。。。。
回复

使用道具 举报

3

主题

12

回帖

21

积分

新手上路

积分
21
发表于 2018-12-25 13:52:19 | 显示全部楼层
多谢。。。。。。。。。
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2022-7-13 13:35:08 | 显示全部楼层
50 us 的中断非常影响性能,所以我自己重新设计了一个组件,利用系统切换钩子函数功能,实现对任务的执行时间的累计,1 s 计算一次,10 s 中找一次最大值。有以下两个优点:
1、计算过程及其简单,基本不占用 CPU
2、不怕溢出,可直接嵌入项目中长时间运行。 使用 DWT 外设(也可使用其它定时器)。
目前已经在 RT-thread 中实现了(以提交至官方仓库),并且在项目中长时间稳定运行,后续计划在 FreeRTOS 中实现。
链接:线程CPU使用率到底该如何计算?

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106718
QQ
发表于 2022-7-13 15:41:14 | 显示全部楼层
EmbeddedOsprey 发表于 2022-7-13 13:35
50 us 的中断非常影响性能,所以我自己重新设计了一个组件,利用系统切换钩子函数功能,实现对任务的执行时 ...

楼主位的是官方的玩法,改成用ThreadX或者uCOS的基于DWT的统计玩法就行。在任务切换的钩子函数统计各个任务即可。

很好实现。
回复

使用道具 举报

0

主题

43

回帖

43

积分

新手上路

积分
43
发表于 2024-3-27 09:24:09 | 显示全部楼层
很好 很强大
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-30 21:38 , Processed in 0.298636 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表