FreeRTOS —— 11.开发者支持

11.1本章简介与适用范围

本章重点介绍了一组功能,这些功能可通过以下方式最大程度地提高生产率:

  • 提供有关应用程序行为方式的见解。
  • 突出优化机会。
  • 在发生错误的时刻捕获错误。

11.2 configASSERT()

在C语言中,宏assert()用于验证程序所做的断言(假设)。 该断言被写为一个C表达式,如果该表达式的值为假(0),则该断言被视为失败。 例如,清单163测试了指针pxMyPointer不是NULL的断言。

在这里插入图片描述

应用程序编写者通过提供assert()宏的实现来指定断言失败时要采取的措施。

FreeRTOS源代码不会调用assert(),因为assert()不适用于所有使用FreeRTOS进行编译的编译器。 相反,FreeRTOS源代码包含对名为configASSERT()的宏的大量调用,该宏可以由FreeRTOSConfig.h中的应用程序编写者定义,并且行为与标准C assert()完全相同。

失败的断言必须视为致命错误。 不要试图执行超过断言失败的行。

通过立即捕获并识别许多最常见的错误源,使用configASSERT()可以提高生产率。 强烈建议在开发或调试FreeRTOS应用程序时定义configASSERT()。

定义configASSERT()将大大有助于运行时调试,但也会增加应用程序代码的大小,因此会减慢其执行速度。 如果未提供configASSERT()的定义,则将使用默认的空定义,并且C预处理程序将完全删除对configASSERT()的所有调用。

示例configASSERT()定义

当在调试器的控制下执行应用程序时,清单164中所示的configASSERT()的定义很有用。发生在任何未通过断言的行上停止执行,因此未通过断言的行将是调试器在暂停 调试会话时显示的行。

在这里插入图片描述

当不在调试器的控制下执行应用程序时,清单165中所示的configASSERT()的定义很有用。 它打印出或记录失败声明的源代码行。 使用标准C __FILE__宏获取源文件的名称,并使用标准C __LINE__宏获取源文件中的行号,来标识未通过声明的行。

在这里插入图片描述

11.3 FreeRTOS+Trace

FreeRTOS + Trace是我们的合作伙伴公司Percepio提供的运行时诊断和优化工具。

FreeRTOS + Trace捕获有价值的动态行为信息,然后以互连的图形视图显示捕获的信息。 该工具还能够显示多个同步视图。

在分析,故障排除或仅优化FreeRTOS应用程序时,捕获的信息非常宝贵。

FreeRTOS + Trace可以与传统调试器并排使用,并以更高的基于时间的视角对调试器的视图进行补充。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

11.4调试相关的钩子(回调)功能

Malloc失败的钩子

第2章,堆内存管理中介绍了malloc失败的挂钩(或回调)。
定义malloc失败的钩子可确保在创建任务,队列,信号量或事件组的尝试失败时立即通知应用程序开发人员。

堆栈溢流钩

有关堆栈溢出挂钩的详细信息,请参见第12.3节“堆栈溢出”。
定义堆栈溢出挂钩可确保如果任务使用的堆栈量超过分配给任务的堆栈空间,则可通知应用程序开发人员。

11.5查看运行时和任务状态信息

任务运行时统计

任务运行时统计信息提供有关每个任务已接收的处理时间量的信息。 任务的运行时间是自应用程序启动以来,任务一直处于“运行”状态的总时间。

运行时统计信息旨在用作项目开发阶段的性能分析和调试工具。 它们提供的信息仅在用作运行时统计时钟的计数器溢出之前才有效。 收集运行时统计信息将增加任务上下文切换时间。

要获取二进制的运行时统计信息,请调用uxTaskGetSystemState()API函数。 要获取人类可读的ASCII表作为运行时统计信息,请调用vTaskGetRunTimeStats()帮助函数。

运行时统计时钟

运行时统计信息需要测量滴答周期的分数。 因此,RTOS滴答计数不用作运行时统计时钟,而是由应用程序代码提供。 建议使运行时统计时钟的频率比滴答中断的频率快10到100倍。 运行时统计时钟越快,统计将越准确,但是时间值溢出越早。

理想情况下,时间值将由自由运行的32位外设定时器/计数器生成,无需其他处理开销即可读取其值。 如果可用的外围设备和时钟速度无法使该技术成为可能,则替代的但效率较低的技术包括:

1.配置外设以所需的运行时统计时钟频率生成周期性中断,然后将生成的中断数作为运行时统计时钟。

如果定期中断仅用于提供运行时统计时钟,则此方法的效率非常低。 但是,如果应用程序已经使用了具有适当频率的周期性中断,则将已生成的中断数量添加到现有中断服务例程中既简单又有效。

2.通过使用自由运行的16位外设定时器的当前值作为32位值的最低有效16位,以及定时器溢出的次数作为32位值的最高值,来生成32位值。 有效的16位。

通过适当且稍微复杂的操作,可以通过将RTOS滴答计数与ARM Cortex-M SysTick计时器的当前值组合来生成运行时统计时钟。 FreeRTOS下载中的一些演示项目演示了如何实现此目的。

配置应用程序以收集运行时统计信息

表54详细列出了收集任务运行时统计信息所必需的宏。 宏原本是打算包含在RTOS端口层中的,这就是为什么宏带有前缀端口的原因,但事实证明,在FreeRTOSConfig.h中定义宏更为实用。

MacroDescription
configGENERATE_RUN_TIME_STATS必须在FreeRTOSConfig.h中将此宏设置为1。 当此宏设置为1时,调度程序将在适当的时间调用此表中详细说明的其他宏。
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()必须提供此宏来初始化用于提供运行时统计时钟的任何外设。
portGET_RUN_TIME_COUNTER_VALUE(), or portALT_GET_RUN_TIME_COUNTER_VALUE(Time)必须提供这两个宏之一,以返回当前的运行时统计时钟值。 这是自应用程序首次启动以来,应用程序已运行的总时间(以运行时统计时钟单位)。如果使用第一个宏,则必须将其定义为求出当前时钟值。 如果使用了第二个宏,则必须对其进行定义以设置其宏。

“时间”参数设置为当前时钟值。

uxTaskGetSystemState()API函数

uxTaskGetSystemState()在FreeRTOS调度程序的控制下,为每个任务提供状态信息的快照。 该信息以TaskStatus_t结构的数组形式提供,每个任务在数组中都有一个索引。 清单167和表56描述了TaskStatus_t。

在这里插入图片描述

Parameter NameDescription
pxTaskStatusArray指向TaskStatus_t结构数组的指针。该数组必须为每个任务至少包含一个TaskStatus_t结构。 可以使用uxTaskGetNumberOfTasks()API函数确定任务数。清单167中显示了TaskStatus_t结构,表56中描述了TaskStatus_t结构成员。
uxArraySizepxTaskStatusArray参数指向的数组的大小。 该大小指定为数组中索引的数量(数组中包含的TaskStatus_t结构的数量),而不是数组中的字节数。
pulTotalRunTime如果在FreeRTOSConfig.h中将configGENERATE_RUN_TIME_STATS设置为1,则* pulTotalRunTime由uxTaskGetSystemState()设置为自目标启动以来的总运行时间(由应用程序提供的运行时统计时钟定义)。pulTotalRunTime是可选的,如果不需要总运行时间,则可以将其设置为NULL。
Returned value返回由uxTaskGetSystemState()填充的TaskStatus_t结构的数量。返回的值应等于uxTaskGetNumberOfTasks()API函数返回的数字,但如果在uxArraySize参数中传递的值太小,则返回零。

在这里插入图片描述

Parameter Name/ Returned ValueDescription
xHandle结构中的信息与之相关的任务的句柄。
pcTaskName任务的可读文本名称。
xTaskNumber每个任务都有一个唯一的xTaskNumber值。如果应用程序在运行时创建和删除任务,则该任务可能具有与先前删除的任务相同的句柄。 提供了xTaskNumber,以允许应用程序代码和内核感知的调试器区分仍然有效的任务和具有与有效任务相同的句柄的已删除任务。
eCurrentState枚举类型,用于保存任务的状态。 eCurrentState可以是以下值之一:eRunning,eReady,eBlocked,eSuspended,eDeleted。从调用vTaskDelete()删除任务到空闲任务释放分配给已删除任务内部数据结构的内存之间的短时间内,仅报告任务处于eDeleted状态 并堆叠。 在那之后,该任务将不再以任何方式存在,并且尝试使用其句柄是无效的。
uxCurrentPriority调用uxTaskGetSystemState()时任务运行的优先级。 如果根据第7.3节“互斥对象(和二进制信号量)”中所述的优先级继承机制临时为任务分配了更高的优先级,则uxCurrentPriority只会高于应用程序编写者为任务分配的优先级。
uxBasePriority应用程序编写者分配给任务的优先级。 uxBasePriority仅在FreeRTOSConfig.h中的configUSE_MUTEXES设置为1时有效。
ulRunTimeCounter自创建任务以来,任务使用的总运行时间。 提供的总运行时间是绝对时间,该绝对时间使用应用程序编写器提供的时钟来收集运行时统计信息。 ulRunTimeCounter仅在FreeRTOSConfig.h中configGENERATE_RUN_TIME_STATS设置为1时有效。
usStackHighWaterMark任务的堆栈高水位线。 这是自创建任务以来该任务所剩余的最小堆栈空间。 这表明任务离堆栈溢出有多近了。 该值越接近零,任务越接近其堆栈溢出。 usStackHighWaterMark以字节为单位指定。

vTaskList()辅助函数

vTaskList()提供的任务状态信息与uxTaskGetSystemState()提供的信息类似,但是它以人类可读的ASCII表而不是二进制值数组的形式显示该信息。

vTaskList()是一个非常占用处理器的功能,会使调度程序暂停较长时间。 因此,建议将该功能仅用于调试目的,而不是在生产实时系统中使用。

如果在FreeRTOSConfig.h中将configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS都设置为1,则vTaskList()可用。

在这里插入图片描述

Parameter NameDescription
pcWriteBuffer指向字符缓冲区的指针,已格式化且人类可读的表将写入该字符缓冲区。 缓冲区必须足够大以容纳整个表,因为不执行边界检查。

由vTaskList()生成的输出示例如图88所示。在输出中:

  • 每行提供有关单个任务的信息。
  • 第一列是任务的名称。
  • 第二列是任务的状态,其中“ R”表示就绪,“ B”表示已阻止,“ S”表示已暂停,“ D”表示任务已删除。 从调用vTaskDelete()删除任务到空闲任务释放分配给已删除任务的内部数据结构的内存之间的短时间内,仅报告任务处于删除状态 并堆叠。 在那之后,该任务将不再以任何方式存在,并且尝试使用其句柄是无效的。
  • 第三列是任务的优先级。
  • 第四列是任务的堆栈高水位线。 请参阅表56中的usStackHighWaterMark的描述。
  • 第五列是分配给任务的唯一编号。 请参阅表56中对xTaskNumber的描述

vTaskGetRunTimeStats()辅助函数

vTaskGetRunTimeStats()格式将运行时统计信息收集到人类可读的ASCII表中。

vTaskGetRunTimeStats()是一个非常占用处理器的功能,会使调度程序暂停较长时间。 因此,建议将该功能仅用于调试目的,而不是在生产实时系统中使用。

如果在FreeRTOSConfig.h中将configGENERATE_RUN_TIME_STATS和configUSE_STATS_FORMATTING_FUNCTIONS都设置为1,则vTaskGetRunTimeStats()可用。

在这里插入图片描述

Parameter NameDescription
pcWriteBuffer指向字符缓冲区的指针,已格式化且人类可读的表将写入该字符缓冲区。 缓冲区必须足够大以容纳整个表,因为不执行边界检查。

由vTaskGetRunTimeStats()生成的输出示例如图89所示。在输出中:

  • 每行提供有关单个任务的信息。
  • 第一列是任务名称。
  • 第二列是任务在“运行”状态下花费的时间(绝对值)。 请参见表56中的ulRunTimeCounter描述。
  • 第三列是自目标启动以来,任务在“运行”状态下所花费的时间占总时间的百分比。 显示的百分比时间的总和通常将小于预期的100%,因为统计信息是使用整数计算收集和计算的,并向下舍入到最接近的整数值。

生成和显示运行时统计信息,一个有效的示例

本示例使用假设的16位计时器生成32位运行时统计时钟。 计数器配置为每当16位值达到最大值时都产生中断,从而有效地产生溢出中断。 中断服务程序计算溢出发生的次数。

通过使用溢出发生次数作为32位值的两个最高有效字节,并使用当前16位计数器值作为32位值的最低两个字节来创建32位值。 清单170中显示了中断服务例程的伪代码。

在这里插入图片描述

清单171显示了添加到FreeRTOSConfig.h中的行,以启用运行时统计信息的收集。
在这里插入图片描述

清单172中显示的任务每5秒打印出收集的运行时统计信息。

在这里插入图片描述

11.6跟踪钩子宏

跟踪宏是放置在FreeRTOS源代码中关键点的宏。 默认情况下,宏为空,因此不生成任何代码,并且没有运行时开销。 通过覆盖默认的空实现,应用程序编写者可以:

  • 将代码插入FreeRTOS,而无需修改FreeRTOS源文件。
  • 通过目标硬件上可用的任何方式输出详细的执行顺序信息。 跟踪宏出现在FreeRTOS源代码中足够的位置,以允许它们用于创建完整而详细的调度程序活动跟踪和性能分析日志。

可用跟踪钩子宏

在这里详细说明每个宏会占用太多空间。 表59详细列出了对应用程序编写者最有用的宏子集。

表59中的许多描述都引用了一个名为pxCurrentTCB的变量。 pxCurrentTCB是一个FreeRTOS私有变量,该变量将任务的句柄保持在“运行”状态,并且可用于从FreeRTOS / Source / tasks.c源文件调用的任何宏。

MacroDescription
traceTASK_INCREMENT_TICK(xTickCount)滴答计数增加后,在滴答中断期间调用。 xTickCount参数将新的滴答计数值传递到宏中。
traceTASK_SWITCHED_OUT()在选择要运行的新任务之前调用。 此时,pxCurrentTCB包含即将退出运行状态的任务的句柄。
traceTASK_SWITCHED_IN()在选择要运行的任务后调用。 此时,pxCurrentTCB包含即将进入运行状态的任务的句柄。
traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue)在尝试从空队列中读取或尝试“获取”空信号量或互斥锁之后,当前正在执行的任务进入“阻塞”状态之前立即调用。 pxQueue参数将目标队列或信号的句柄传递给宏。
traceBLOCKING_ON_QUEUE_SEND(pxQueue)在尝试写入已满的队列之后,当前正在执行的任务进入“阻塞”状态之前立即调用。 pxQueue参数将目标队列的句柄传递到宏中。
traceQUEUE_SEND(pxQueue)当队列发送或信号“给定”成功时,从xQueueSend(),xQueueSendToFront(),xQueueSendToBack()或任何信号“给定”函数中调用。 pxQueue参数将目标队列或信号的句柄传递给宏。
traceQUEUE_SEND_FAILED(pxQueue)当队列发送或信号“给定”操作失败时,从xQueueSend(),xQueueSendToFront(),xQueueSendToBack()或任何信号“给定”函数中调用。 如果队列已满,并且在指定的任何块时间段内都保持满状态,则队列发送或信号量’give’将失败。 pxQueue参数将目标队列或信号的句柄传递给宏。
traceQUEUE_RECEIVE(pxQueue)当队列接收或信号量“ take”成功执行时,可从xQueueReceive()或任何信号量“ take”函数中调用。 pxQueue参数将目标队列或信号的句柄传递给宏。
traceQUEUE_RECEIVE_FAILED(pxQueue)当队列或信号接收操作失败时,从xQueueReceive()或任何信号“获取”函数中调用。 如果队列接收或信号量为“空”,并且在指定的任何阻止时间内都保持为空,则队列接收或信号量“ take”操作将失败。 pxQueue参数将目标队列或信号的句柄传递给宏。
traceQUEUE_SEND_FROM_ISR(pxQueue)发送操作成功时,从xQueueSendFromISR()中调用。 pxQueue参数将目标队列的句柄传递到宏中。
traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue)发送操作失败时,从xQueueSendFromISR()中调用。 如果队列已满,则发送操作将失败。 pxQueue参数将目标队列的句柄传递到宏中。
traceQUEUE_RECEIVE_FROM_ISR(pxQueue)接收操作成功时,从xQueueReceiveFromISR()中调用。 pxQueue参数将目标队列的句柄传递到宏中。
traceQUEUE_RECEIVE_FROM_ISR_FAILED(pxQueue)当由于队列已为空而导致接收操作失败时,从xQueueReceiveFromISR()中调用。 pxQueue参数将目标队列的句柄传递到宏中。
traceTASK_DELAY_UNTIL()在调用任务进入“阻止”状态之前立即从vTaskDelayUntil()中调用。
traceTASK_DELAY()在调用任务进入“阻止”状态之前立即从vTaskDelay()中调用。

定义跟踪钩子宏

每个跟踪宏都有一个默认的空定义。 可以通过在FreeRTOSConfig.h中提供新的宏定义来覆盖默认定义。 如果跟踪宏定义变长或太复杂,则可以在新的头文件中实现它们,然后将其包含在FreeRTOSConfig.h中。

根据软件工程最佳实践,FreeRTOS维护严格的数据隐藏策略。 跟踪宏允许将用户代码添加到FreeRTOS源文件中,因此,跟踪宏可见的数据类型将不同于应用程序代码可见的数据类型:

  • 在FreeRTOS / Source / tasks.c源文件中,任务句柄是指向描述任务(任务的任务控制块,或TCB)的数据结构的指针。 在FreeRTOS / Source / tasks.c源文件之外,任务句柄是指向void的指针。

  • 在FreeRTOS / Source / queue.c源文件中,队列句柄是指向描述队列的数据结构的指针。 在FreeRTOS / Source / queue.c源文件之外,队列句柄是指向void的指针。

如果跟踪宏直接访问通常私有的FreeRTOS数据结构,则需要格外小心,因为私有数据结构可能会在FreeRTOS版本之间发生变化。

FreeRTOS感知调试器插件

提供了一些FreeRTOS意识的插件可用于以下IDE。 此列表可能并不详尽:

  • Eclipse(StateViewer)
  • Eclipse(ThreadSpy)
  • IAR
  • ARM DS-5
  • Atollic TrueStudio
  • Microchip MPLAB
  • iSYSTEM WinIDEA
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值