当前位置: 首页 > news >正文

12 FreeRTOS 调试与优化

1、调试

1.1 打印

        在FreeRTOS工程中使用了microlib,里面实现了printf函数。

        只需要实现一下以下函数即可使用printf。

int fputc(int ch; FILE *f);

        假如要从串口实现打印函数:

int fputc( int ch, FILE *f )
{//指定串口USART_TypeDef* USARTx = USART1;//等待数据发送出去,数据发送完时SR的bit7=1while((USARTx->SR & (1<<7)) == 0);//往DR寄存器中写入字符USARTx->DR = ch;return ch;
}

1.2 断言

        一般的C库中,断言就是一个函数。

void assert(scalar expression);

        它的作用是:确认expression必须为真,如果expression为假的话就中止程序。

        在FreeRTOS中,使用configASSERT(),比如:

#define configASSERT(x)  if (!x) while(1);

        也可以让它提供更多的信息:

#define configASSERT(x)  \if (!x) \{printf("%s %s %d\r\n", __FILE__, __FUNCTION__, __LINE__); \while(1); \}

1.3 Trace

        FreeRTOS中定义了很多trace开头的宏,这些宏被放在系统个关键位置。

        它们一般都是空的宏,这不会影响代码:不影响编程处理的程序大小、不影响运行时间。

        我们要调试某些功能时,可以修改宏:修改某些标记变量、打印信息等待。

1.4 Malloc Hook函数

        编程时,一般的逻辑错误都容易解决。难以处理的是内存越界、栈溢出等。

        内存越界经常发生在堆的使用过程中:堆,就是使用malloc得到的内存。

        并没有很好的方法检测内存越界,但是可以提供一些回调函数:使用pvPortMalloc失败时,如果在FreeRTOSConfig.h里配置`configUSE_MALLOC_FAILED_HOOK`为1,会调用:

void vApplicationMallocFailedHook( void );

1.5 栈溢出Hook函数

        在切换任务(vTaskSwitchContext)时调用taskCHECK_FOR_STACK_OVERFLOW来检测栈是否溢出,如果溢出会调用:

void vApplicationStackOverflowHook( TaskHandle_t xTask, char * pcTaskName );

        判断栈溢出可以有两种方法:

        方法一:当前任务被切换出去之前,它的整个运行现场都被保存在栈里,这时很可能就是它对栈的使用到达了峰值。这方法很高效,但是并不精确。比如:任务在运行过程中调用了函数A大量地使用了栈,调用完函数A后才被调度。 

        方法二:创建任务时,它的栈被填入固定的值,比如:0xa5。检测栈里最后16字节的数据,如果不是0xa5的话表示栈即将、或者已经被用完了。没有方法1快速,但是也足够快。能捕获几乎所有的栈溢出。为什么是几乎所有?可能有些函数使用栈时,非常凑巧地把栈设置为0xa5:几乎不可能

2、优化

        在Windows中,当系统卡顿时我们可以查看任务管理器找到最消耗CPU资源的程序。

        在FreeRTOS中,我们也可以查看任务使用CPU的情况、使用栈的情况,然后针对性地进行优化。

2.1 栈使用情况

        在创建任务时分配了栈,可以填入固定的数值比如0xa5,以后可以使用以下函数查看"栈的高水位",也就是还有多少空余的栈空间。原理是:从栈底往栈顶逐个字节地判断,它们的值持续是0xa5就表示它是空闲的。 

//xTask 哪个任务  
//返回值 //任务运行时、任务被切换时,都会用到栈。栈里原来值(0xa5)就会被覆盖。//逐个函数从栈的尾部判断栈的值连续为0xa5的个数,它就是任务运行过程中空闲内存容量的最小值。//注意:假设从栈尾开始连续为0xa5的栈空间是N字节,返回值是N/4。
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );

2.2 任务运行时间统计

        对于同优先级的任务,它们按照时间片轮流运行:你执行一个Tick,我执行一个Tick。是否可以在Tick中断函数中,统计当前任务的累计运行时间?不行!很不精确,因为有更高优先级的任务就绪时,当前任务还没运行一个完整的Tick就被抢占了。

        我们需要比Tick更快的时钟,比如Tick周期时1ms,我们可以使用另一个定时器,让它发生中断的周期时0.1ms甚至更短。使用这个定时器来衡量一个任务的运行时间,原理如下图所示:

        切换到Task1时,使用更快的定时器记录当前时间T1;Task1被切换出去时,使用更快的定时器记录当前时间T4;(T4-T1)就是它运行的时间,累加起来。关键点:在vTaskSwitchContext函数中,使用更快的定时器统计运行时间。

2.3 涉及的代码

        要提前配置一下

#define configGENERATE_RUN_TIME_STATS 1
#define configUSE_TRACE_FACILITY    1
#define configUSE_STATS_FORMATTING_FUNCTIONS  1

        实现宏portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(),它用来初始化更快的定时器

        实现这两个宏之一,它们用来返回当前时钟值(更快的定时器)

                portGET_RUN_TIME_COUNTER_VALUE():直接返回时钟值

                portALT_GET_RUN_TIME_COUNTER_VALUE(Time):设置Time变量等于时钟值

        代码执行流程:

        (1)在启动调度器时,初始化更快的定时器

        (2)在任务切换时统计运行时间

        获得统计信息,可以使用下列函数:

        (1)uxTaskGetSystemState:对于每个任务它的统计信息都放在一个TaskStatus_t结构体里

        (2)vTaskList:得到的信息是可读的字符串

        (3)vTaskGetRunTimeStats:  得到的信息是可读的字符串

2.4 函数说明

        uxTaskGetSystemState:获得任务的统计信息

UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray,const UBaseType_t uxArraySize,uint32_t * const pulTotalRunTime );
参数描述
pxTaskStatusArray

向一个TaskStatus_t结构体数组,用来保存任务的统计信息。

有多少个任务?可以用`uxTaskGetNumberOfTasks()`来获得。

uxArraySize

数组大小、数组项个数,

必须大于或等于uxTaskGetNumberOfTasks()

pulTotalRunTime用来保存当前总的运行时间(更快的定时器),可以传入NULL
返回值

传入的pxTaskStatusArray数组,被设置了几个数组项。

注意:如果传入的uxArraySize小于uxTaskGetNumberOfTasks(),返回值就是0

        vTaskList :获得任务的统计信息,形式为可读的字符串。注意,pcWriteBuffer必须足够大。

void vTaskList( signed char *pcWriteBuffer );

        可读信息格式如下:

 

        vTaskGetRunTimeStats:获得任务的运行信息,形式为可读的字符串。注意,pcWriteBuffer必须足够大。

void vTaskGetRunTimeStats( signed char *pcWriteBuffer );

        可读信息如下:

相关文章:

  • Flutter 中的 SliverPrototypeExtentList 小部件:全面指南
  • TiDB-从0到1-分布式事务
  • 蓝桥杯2024国赛--备赛刷题题单
  • Linux--进程间通信(1)(匿名管道)
  • LabVIEW软件需求分析文档内容和编写指南
  • 2024最新群智能优化算法:大甘蔗鼠算法(Greater Cane Rat Algorithm,GCRA)求解23个函数,提供MATLAB代码
  • llama-factory微调大模型
  • 深入分析 Android Activity (六)
  • [C#]使用C#部署yolov8-seg的实例分割的tensorrt模型
  • vscode写html不会自动补全
  • 东方博宜1703 - 小明买水果
  • 数据结构:希尔排序
  • 身份认证与口令攻击
  • excel怎么对非数字求和汇总?
  • AI边缘计算盒子在智慧交通的应用
  • ----------
  • [数据结构]链表的实现在PHP中
  • 【5+】跨webview多页面 触发事件(二)
  • exports和module.exports
  • go append函数以及写入
  • HTML5新特性总结
  • Java编程基础24——递归练习
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • PV统计优化设计
  • vue-cli在webpack的配置文件探究
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 多线程事务回滚
  • 悄悄地说一个bug
  • 运行时添加log4j2的appender
  • 【干货分享】dos命令大全
  • ​你们这样子,耽误我的工作进度怎么办?
  • (C++20) consteval立即函数
  • (el-Date-Picker)操作(不使用 ts):Element-plus 中 DatePicker 组件的使用及输出想要日期格式需求的解决过程
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (二)正点原子I.MX6ULL u-boot移植
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (四)Android布局类型(线性布局LinearLayout)
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (一)Java算法:二分查找
  • (转)树状数组
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET NPOI导出Excel详解
  • .NET Remoting学习笔记(三)信道
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .NET的微型Web框架 Nancy
  • .NET上SQLite的连接
  • .NET下的多线程编程—1-线程机制概述
  • .net下简单快捷的数值高低位切换
  • .NET中的十进制浮点类型,徐汇区网站设计
  • .NET中分布式服务
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复
  • @configuration注解_2w字长文给你讲透了配置类为什么要添加 @Configuration注解
  • [ vulhub漏洞复现篇 ] JBOSS AS 5.x/6.x反序列化远程代码执行漏洞CVE-2017-12149