FreeRTOS实时操作系统之可视化追踪调试

目录
  • 前言
  • 1.使能可视化追踪和运行时间统计功能
  • 2.获取任务信息并格式化
  • 3.添加到命令解释列表

前言

用RTOS编程,为每个任务分配多大的堆栈空间就成了一项技术活:分配多了浪费系统资源,分配少了又恐怕会发生堆栈溢出。由于中断和抢占式调度器的存在,我们要估算出一个任务需要多少堆栈是非常困难的,今天我们就介绍一种方法,来获取每个任务的剩余堆栈空间。本文以NXP LPC177x_8x系列微控制器为例。

我们将这个功能做成一个命令,添加到FreeRTOS使用任务通知实现命令行解释器一文介绍的命令解释列表中。当程序运行一段时间后,我们在SecureCRT软件中输入命令“task”后回车,能看到如图1-1所示的任务信息。这里只有两个任务,其中堆栈一列中的数字,代表对应任务剩余的堆栈空间,单位是StackType_t类型,这个类型在移植层定义,一般定义为4字节。

图1-1:任务信息

1.使能可视化追踪和运行时间统计功能

如图1-1所示,要实现堆栈使用量信息以及CPU使用率信息,必须将FreeRTOSConfig.h文件中的两个宏设置为1:

         #define configUSE_TRACE_FACILITY          1
         #define configGENERATE_RUN_TIME_STATS 1

第一个宏用来使能可视化追踪功能,第二个宏用来使能运行时间统计功能。如果第二个宏设置为1,则下面两个宏必须被定义:

portCONFIGURE_TIMER_FOR_RUN_TIME_STATS():

用户程序需要提供一个基准时钟函数,函数完成初始化基准时钟功能,这个函数要被define到宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()上。

这是因为运行时间统计需要一个比系统节拍中断频率还要高分辨率的基准定时器,否则,统计可能不精确。

基准定时器中断频率要比统节拍中断快10~100倍。基准定时器中断频率越快,统计越精准,但能统计的运行时间也越短(比如,基准定时器10ms中断一次,8位无符号整形变量可以计到2.55秒,但如果是1秒中断一次,8位无符号整形变量可以统计到255秒)。

portGET_RUN_TIME_COUNTER_VALUE():

用户程序需要提供一个返回基准时钟当前“时间”的函数,这个函数要被define到宏portGET_RUN_TIME_COUNTER_VALUE()上。

我们使用定时器1来产生基准时钟,定时器1初始化函数为:

/**
* 初始化计时定时器1,用于OS任务运行时间统计
*/
void init_timer1_for_runtime_state(void)
{
    TIM_TIMERCFG_Type Timer0CfgType;
    Timer0CfgType.PrescaleOption=TIM_PRESCALE_USVAL;        //预分频的单位是微秒
    Timer0CfgType.PrescaleValue=500;                        //预分频后为500微秒,
    TIM_Init(LPC_TIM1,TIM_TIMER_MODE,&Timer0CfgType);
    LPC_TIM1->TCR=0x01;
}

定时器1被配置成每隔500微秒,TC寄存器值增一。我们将定时器1的 TC寄存器值作为基准时钟当前时间。当TC寄存器值溢出时,大概要经过24.8天,这对于我们这个应用是足够的。

在FreeRTOSConfig.h中,定义初始化基准定时器宏和获取当前时间宏:

extern void init_timer1_for_runtime_state(void);
#define TIMER1_TC         ( * ( ( volatile uint32_t * )0x40008008 ) )
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() init_timer1_for_runtime_state()
#define portGET_RUN_TIME_COUNTER_VALUE() TIMER1_TC

2.获取任务信息并格式化

获取每个任务的状态信息使用的是API函数uxTaskGetSystemState(),该函数定义为:

UBaseType_tuxTaskGetSystemState(
                       TaskStatus_t * constpxTaskStatusArray,
                       const UBaseType_tuxArraySize,
                       unsigned long * constpulTotalRunTime );

函数uxTaskGetSystemState()向TaskStatus_t结构体填充相关信息,系统中每一个任务的信息都可以填充到TaskStatus_t结构体数组中,数组大小由uxArraySize指定。结构体TaskStatus_t定义如下:

typedef struct xTASK_STATUS
{
   /* 任务句柄*/
   TaskHandle_t xHandle;
   /* 指针,指向任务名*/
   const signed char *pcTaskName;
   /*任务ID,是一个独一无二的数字*/
   UBaseType_t xTaskNumber;
   /*填充结构体时,任务当前的状态(运行、就绪、挂起等等)*/
   eTaskState eCurrentState;
   /*填充结构体时,任务运行(或继承)的优先级。*/
   UBaseType_t uxCurrentPriority;
   /* 当任务因继承而改变优先级时,该变量保存任务最初的优先级。仅当configUSE_MUTEXES定义为1有效。*/
   UBaseType_t uxBasePriority;
   /* 分配给任务的总运行时间。仅当宏configGENERATE_RUN_TIME_STATS为1时有效。*/
   unsigned long ulRunTimeCounter;
   /* 从任务创建起,堆栈剩余的最小数量,这个值越接近0,堆栈溢出的可能越大。 */
   unsigned short usStackHighWaterMark;
}TaskStatus_t;

注意,这个函数仅用来调试用,调用此函数会挂起所有任务,直到函数结束后才恢复挂起的任务,因此任务可能被挂起很长时间。在文件FreeRTOSConfig.h中,宏configUSE_TRACE_FACILITY必须设置为1,此函数才有效。

由于我们不使用动态内存分配策略,所以实现定义了最大任务个数并预先分配好了存储任务状态信息的数组:

#defineMAX_TASK_NUM        5
TaskStatus_tpxTaskStatusArray[MAX_TASK_NUM];

正确调用函数uxTaskGetSystemState()后,任务的信息会被放在TaskStatus_t结构体中,我们需要将这些信息格式化为容易阅读的形式,并共通过串口打印到屏幕。完成这些功能的函数叫做get_task_state(),代码如下所示:

/*获取OS任务信息*/
voidget_task_state(int32_t argc,void *cmd_arg)
{
    const chartask_state[]={'r','R','B','S','D'};
    volatile UBaseType_t uxArraySize, x;
    uint32_t ulTotalRunTime,ulStatsAsPercentage;
    /* 获取任务总数目 */
    uxArraySize = uxTaskGetNumberOfTasks();
   if(uxArraySize>MAX_TASK_NUM)
    {
        MY_DEBUGF(CMD_LINE_DEBUG,("当前任务数量过多!\n"));
    }
    /*获取每个任务的状态信息 */
    uxArraySize = uxTaskGetSystemState(pxTaskStatusArray, uxArraySize, &ulTotalRunTime );
    #if (configGENERATE_RUN_TIME_STATS==1)
    MY_DEBUGF(CMD_LINE_DEBUG,("任务名      状态  ID    优先级  堆栈    CPU使用率\n"));

    /* 避免除零错误 */
    if( ulTotalRunTime > 0 )
    {
        /* 将获得的每一个任务状态信息部分的转化为程序员容易识别的字符串格式 */
        for( x = 0; x < uxArraySize; x++ )
        {
            char tmp[128];
            /* 计算任务运行时间与总运行时间的百分比。*/
            ulStatsAsPercentage =(uint64_t)(pxTaskStatusArray[ x ].ulRunTimeCounter)*100 / ulTotalRunTime;
            if( ulStatsAsPercentage > 0UL )
            {
               sprintf(tmp,"%-12s%-6c%-6d%-8d%-8d%d%%",pxTaskStatusArray[ x].pcTaskName,task_state[pxTaskStatusArray[ x ].eCurrentState],
                                                                       pxTaskStatusArray[ x ].xTaskNumber,pxTaskStatusArray[ x].uxCurrentPriority,
                                                                       pxTaskStatusArray[ x ].usStackHighWaterMark,ulStatsAsPercentage);
            }
            else
            {
                /* 任务运行时间不足总运行时间的1%*/
                sprintf(tmp,"%-12s%-6c%-6d%-8d%-8dt<1%%",pxTaskStatusArray[x ].pcTaskName,task_state[pxTaskStatusArray[ x ].eCurrentState],
                                                                       pxTaskStatusArray[ x ].xTaskNumber,pxTaskStatusArray[ x].uxCurrentPriority,
                                                                       pxTaskStatusArray[ x ].usStackHighWaterMark);
            }
           MY_DEBUGF(CMD_LINE_DEBUG,("%s\n",tmp));
        }
    }
    MY_DEBUGF(CMD_LINE_DEBUG,("任务状态:   r-运行  R-就绪  B-阻塞  S-挂起  D-删除\n"));
    #endif //#if (configGENERATE_RUN_TIME_STATS==1)
}

3.添加到命令解释列表

在FreeRTOS使用任务通知实现命令行解释器一文我们讲过了命令表,这里只需要将get_task_state()函数添加到命令列表中,命令设置为”task”,代码如下所示:

/*命令表*/
const cmd_list_structcmd_list[]={
/*   命令    参数数目    处理函数        帮助信息                                  */
    {"?",       0,     handle_help,     "?                                  -打印帮助信息"},
    {"reset",   0,     handle_reset,    "reset                              -重启控制器"},
    {"arg",     8,     handle_arg,      "arg<arg1> <arg2> ...               -测试用,打印输入的参数"},
    {"hello",   0,     printf_hello,    "hello                              -打印HelloWorld!"},
    {"task",    0,     get_task_state,  "task                               -获取任务信息"},
};

以上就是FreeRTOS实时操作系统之可视化追踪调试的详细内容,更多关于FreeRTOS可视化追踪调试的资料请关注我们其它相关文章!

(0)

相关推荐

  • 使用FreeRTOS遇到死等异常的解决

    目录 问题场景: 追溯代码: 分析代码 问题场景: 在使用apollo3时,调试时发现在ADC中断中一发送信号量就卡住. 追溯代码: 追溯代码发现其实是在ADC中断中调用xQueueGenericSendFromISR就卡住,卡住位置如下 这个宏定义如下 继续往里看,发现卡在下面位置 此断言如下 所以打印看到的条件是0>=128,所以就while(1);卡在这里了 分析代码 这是获得ipsr寄存器的值,保存在ulCurrentInterrupt变量,那ipsr寄存器代表的是什么呢,这里有写htt

  • FreeRTOS实时操作系统的任务应用函数详解

    目录 1.获取任务系统状态 1.1函数描述 1.2参数描述 1.3返回值 1.4用法举例 2.获取当前任务句柄 2.1函数描述 2.2返回值 3.获取空闲任务句柄 3.1函数描述 3.2返回值 4.获取任务堆栈最大使用深度 4.1函数描述 4.2参数描述 4.3返回值 4.4用法举例 5.获取任务状态 5.1函数描述 5.2参数描述 5.3返回值 6.获取任务描述内容 6.1函数描述 6.2参数描述 6.3返回值 7.获取系统节拍次数 7.1函数描述 7.2返回值 8.获取调度器状态 8.1函数

  • FreeRTOS实时操作系统的任务通知方法

    目录 前言 1.发送通知-方法1 1.1函数描述 1.2参数描述 1.3返回值 2.发送通知-方法2 2.1函数描述 2.2参数描述 2.3用法举例 3.获取通知 3.1函数描述 3.2参数描述 3.3返回值 4.等待通知 4.1函数描述 4.2参数描述 4.3返回值 4.4用法举例 5.任务通知并查询 5.1函数描述 5.2参数描述 5.3返回值 前言 注:本文介绍任务通知的基础知识,详细源码分析见FreeRTOS进阶<FreeRTOS高级篇8---FreeRTOS任务通知分析> 每个RTO

  • FreeRTOS实时操作系统的任务创建和删除

    目录 前言 1.任务创建 1.1函数描述 1.2参数描述 1.3返回值 1.4用法举例 2.任务删除 2.1任务描述 2.2参数描述 前言 在FreeRTOS移植到Cortex-M3硬件平台的文章中,我们已经见过任务创建API,但那篇文章的重点在于如何移植FreeRTOS,本文将重点放在任务的创建和删除API函数上面. 任务创建和删除API函数位于文件task.c中,需要包含task.h头文件. 1.任务创建 1.1函数描述 BaseType_t xTaskCreate( TaskFunctio

  • FreeRTOS使用任务通知实现命令行解释器

    目录 前言 1.编码风格 2.一些准备工作 2.1串口硬件驱动 2.2一个类printf函数 3.使用任务通知 4.数据结构 4.1与命令有关的数据结构 4.2与分析命令有关数据结构 5.串口接收中断处理函数 6.命令行分析任务 6.1去除无效字符和控制字符 6.2参数分析 6.3定义命令回调函数 6.3.1不带参数的命令回调函数举例 6.3.2带参数的命令行回调函数举例 6.5命令行分析任务实现 7.使用的串口工具 7.1设置串口参数 7.2设置新行模式 7.3设置本地回显 8.测试 8.1无

  • FreeRTOS实时操作系统的内核控制示例解析

    目录 前言 1.强制上下文切换宏 2.进入临界区宏 3.退出临界区宏 4.禁止可屏蔽中断宏 5.使能可屏蔽中断宏 6.启动调度器 6.1函数描述 7.停止调度器 7.1函数描述 8.挂起调度器 8.1函数描述 9.恢复被挂起的调度器 9.1函数描述 9.2返回值 9.3用法举例 10.调整系统节拍 10.1函数描述 10.2参数描述 10.3用法举例 前言 内核控制的一些功能需要移植层提供,为了方便移植,这些API函数用宏来实现,比如上下文切换.进入和退出临界区.禁止和使能可屏蔽中断.内核控制函

  • FreeRTOS软件定时器apollo中断状态判断

    问题场景 开发中发现FreeRTOS软件定时器不走了,具体表现在软件定时器中断进不去. 分析问题 观察发现只有在某个任务执行期间,FreeRTOS的软件定时器才会不走,其他任务执行时正常,排查后是此任务的优先级比定时器任务高,且占用时间比较长,导致任务切不出去. 解决问题 在FreeRTOSConfig.h中修改定时器任务优先级为最高解决问题 apollo中断状态判断 在看apollo3 代码时发现下面这个函数 void WsfSetOsSpecificEvent(void) { if(xRad

  • FreeRTOS实时操作系统的任务概要讲解

    目录 1. 任务和协程(Co-routines) 1.1任务的特性 1.2任务概要 2. 任务状态 3.任务优先级 4.实现一个任务 5.空闲任务和空闲任务钩子(idle task和Idle Task hook) 5.1空闲任务 5.2空闲任务钩子 1. 任务和协程(Co-routines) 应用程序可以使用任务也可以使用协程,或者两者混合使用,但是任务和协程使用不同的API函数,因此在任务和协程之间不能使用同一个队列或信号量传递数据. 通常情况下,协程仅用在资源非常少的微处理器中,特别是RAM

  • FreeRTOS实时操作系统之可视化追踪调试

    目录 前言 1.使能可视化追踪和运行时间统计功能 2.获取任务信息并格式化 3.添加到命令解释列表 前言 用RTOS编程,为每个任务分配多大的堆栈空间就成了一项技术活:分配多了浪费系统资源,分配少了又恐怕会发生堆栈溢出.由于中断和抢占式调度器的存在,我们要估算出一个任务需要多少堆栈是非常困难的,今天我们就介绍一种方法,来获取每个任务的剩余堆栈空间.本文以NXP LPC177x_8x系列微控制器为例. 我们将这个功能做成一个命令,添加到FreeRTOS使用任务通知实现命令行解释器一文介绍的命令解释

  • FreeRTOS实时操作系统队列的API函数讲解

    目录 FreeRTOS为操作队列提供了非常丰富的API函数,包括队列的创建.删除,灵活的入队和出队方式.带中断保护的入队和出队等等.下面就来详细讲述这些API函数. 1.获取队列入队信息数目1.1函数描述 UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue ); 返回队列中存储的信息数目.具有中断保护的版本为uxQueueMessagesWaitingFromISR(),原型为:UBaseType_t uxQueueMessagesW

  • FreeRTOS实时操作系统特点介绍

    目录 1.什么是FreeRTOS? 2.为什么择 选择 FreeRTOS ? 3.FreeRTOS 特点 FreeRTOS资料与源码下载 FreeRTOS源码文件介绍 1.什么是FreeRTOS? Free 即免费的,RTOS 全称是 Real Time Operating System,中文就是实时操作系统.注意,RTOS 不是指某一个确定的系统,而是指一类系统.比如 uC/OS,FreeRTOS,RTX,RT-Thread 等这些都是 RTOS 类操作系统. 操作系统允许多个任务同时运行,这

  • freertos实时操作系统空闲任务阻塞延时示例解析

    阻塞态:如果一个任务当前正在等待某个外部事件,则称它处于阻塞态. rtos中的延时叫阻塞延时,即任务需要延时的时候,会放弃CPU的使用权,进入阻塞状态.在任务阻塞的这段时间,CPU可以去执行其它的任务(如果其它的任务也在延时状态,那么 CPU 就将运行空闲任务),当任务延时时间到,重新获取 CPU 使用权,任务继续运行. 空闲任务:处理器空闲的时候,运行的任务.当系统中没有其他就绪任务时,空闲任务开始运行,空闲任务的优先级是最低的. 空闲任务 定义空闲任务: #define portSTACK_

  • freertos实时操作系统临界段保护开关中断及进入退出

    目录 中断的基础知识 嵌套: 优先级: 中断的悬起与解悬: 咬尾中断Tail‐Chaining: 晚到的高优先级异常: 进入临界段和退出临界段 中断的基础知识 嵌套: 嵌套向量中断控制器 NVIC(Nested Vectored Interrupt Controller与内核是紧耦合的.提供如下的功能:可嵌套中断支持.向量中断支持.动态优先级调整支持.中断延迟大大缩短. 中断可屏蔽. 所有的外部中断和绝大多数系统异常均支持可嵌套中断.异常都可以被赋予不同的优先级,当前优先级被存储在 xPSR的专

  • FreeRTOS实时操作系统多任务管理基础知识

    目录 什么是多任务系统? FreeRTOS  任务与协程 1.任务(Task) 的特性 2.协程(Co-routine)的特性 任务状态 运行态 就绪态 阻塞态 挂起态 任务优先级 任务实现 任务控制块 任务堆栈 RTOS 系统的核心就是任务管理,FreeRTOS 也不例外,而且大多数学习 RTOS 系统的工程师或者学生主要就是为了使用 RTOS 的多任务处理功能,初步上手 RTOS 系统首先必须掌握的也是任务的创建.删除.挂起和恢复等操作,由此可见任务管理的重要性. 什么是多任务系统? 回想一

  • FreeRTOS实时操作系统临界段保护场合示例

    目录 临界段保护场合 非中断场合 中断场合 临界段保护场合 FreeRTOS中临界段保护有2种场合,中断和非中断,通过关中断(或者关部分中断)来实现临界保护. 非中断场合 task.h 中 #define taskENTER_CRITICAL()portENTER_CRITICAL() #define taskEXIT_CRITICAL()portEXIT_CRITICAL() portmacro.h中 #define portENTER_CRITICAL()vPortEnterCritical

  • FreeRTOS实时操作系统Cortex-M内核使用注意事项

    前言 在阅读本文之前,有两个定义在FreeRTOSConfig.h中的宏,你必须先明白它们是什么意思,<FreeRTOS内核配置说明>一文中,讲解了这两个宏: configKERNEL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY FreeRTOS与Cortex-M内核可谓是绝配,以至于让移植和使用FreeRTOS都变得更简单起来.根据FreeRTOS官方反馈,在Cortex-M内核上使用FreeRTOS大多数的问题点是由不正确

  • FreeRTOS实时操作系统空闲任务的阻塞延时实现

    目录 什么是阻塞延时.为什么需要空闲任务 空闲任务的实现 阻塞延时的实现 xTicksToDelay 递减 SysTick初始化 仿真 什么是阻塞延时.为什么需要空闲任务 RTOS中的延时叫阻塞延时,即任务需要延时时,任务会放弃cpu使用权,cpu转而去做其他的事,当任务延时时间到后,任务重新请求获得cpu使用权.但当所有的任务都处于阻塞后,为了不让cpu空闲没事干就需要一个空闲任务让cpu干活. 空闲任务的实现 空闲任务实现和创建普通任务没区别,空闲任务在调用vTaskStartSchedul

  • FreeRTOS实时操作系统信号量基础

    目录 前言 1.信号量简介 2.二进制信号量 3.计数信号量 4.互斥量 5.递归互斥量 前言 本文介绍信号量的基础知识,详细源码分析见<FreeRTOS进阶FreeRTOS信号量分析> 1.信号量简介 FreeRTOS的信号量包括二进制信号量.计数信号量.互斥信号量(以后简称互斥量)和递归互斥信号量(以后简称递归互斥量). 我们可以把互斥量和递归互斥量看成特殊的信号量.互斥量和信号量在用法上不同: 信号量用于同步,任务间或者任务和中断间同步:互斥量用于互锁,用于保护同时只能有一个任务访问的资

随机推荐