史上最全的FreeRTOS资料_第1页
史上最全的FreeRTOS资料_第2页
史上最全的FreeRTOS资料_第3页
史上最全的FreeRTOS资料_第4页
史上最全的FreeRTOS资料_第5页
已阅读5页,还剩132页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、任务和联合程序FreeRTOS 基础参考 FreeRTOS.org 怎样工作 小节获取多任务的基本概念。任务 和 联合程序 文档提供了怎样判断何时适合以及何时不适合使用联合程序的方法,下面是简单的总结。注意一个系统可以只使用任务、或者只使用联合程序、或混合使用 - 但是任务和联合程序使用不同的 API 函数,因此一个队列(或者信号)不能用于从任务传递数据到联合程序,反之亦然。 '任务'的特性FreeRTOS 低于 V 的版本只允许实时系统的结构以一组任务的方式组成,这是 RTOS 调度的传统模式。 简评In brief: 一个使用 RTOS 的实时应用系统可以由一

2、组独立的任务组成,每个任务执行它自己的内容而不依赖于其他任务或者调度器。在任何时候只有一个任务可以被执行,调度器负责决定哪个任务应当被执行。调度器反复启动和停止任务(切换任务),因为一个任务不清楚调度器的活动,所以在任务切换时保证处理器内容不变(寄存器值、堆栈等)就是实时内核调度器的工作。要做到这一点每个任务都需要使用自己的堆栈,当任务切换时运行的参数保存到堆栈中,任务再次运行时就可以从堆栈中恢复参数。参考 FreeRTOS 怎样工作 小节获得更多内容。 任务综述简单。使用没有限制。支持完全占先。支持优先级。每个任务使用单独的堆栈使得 RAM 占用率较高。使用占先时必须仔细考虑重入问题。

3、9;联合程序'的特性FreeRTOS V4.0.0 开始允许实时系统象使用任务一样选择使用联合程序。联合程序类似于任务,但是又有一些区别(进一步说明在 联合程序文档): 1. 堆栈使用率 系统中的所有联合程序共享一个堆栈,和使用任务相比这极大的减少了 RAM 的需求。 2. 调度和优先级 联合程序之间使用优先级进行调度,但是可以被系统中的任务占先。 3. 宏 联合程序通过一组宏定义执行。 4. 使用限制 减少 RAM 占用率的代价就是使用联合程序上有严格限制。 联合程序综述在联合程序之间共享堆栈将占用少得多的 RAM。联合操作使得重入不再是问题。非常容易移植的架构。相对其他联合程序有完

4、全的优先级关系,但是可以被任务占先如果混合使用的时候。需要仔细考虑堆栈。API 调用时有限制。只能在联合程序之间使用联合操作。任务FreeRTOS 基础任务状态一个任务可以是以下几种状态中的一种: · 运行 正在执行的任务就是处于运行状态,它占用了处理器。 · 就绪 就绪的任务是那些可以执行(没有被阻塞或暂停),但是因为其他相同或更高优先级任务正在运行造成还没有运行的任务。 · 阻塞 当一个任务等待临时事件或外部事件时它就是处于阻塞状态。例如,任务调用 vTaskDelay() ,它将被阻塞(置为阻塞状态)直到超过延时时间 - 一个临时事件。任务也可以阻塞等待队列

5、和信号事件。阻塞状态的任务一般有一个超时时间,超时后任务将解锁。阻塞的任务不会参与调度。 · 暂停 (挂起)暂停状态的任务也不参与调度。任务只有在调用 API 函数 vTaskSuspend() 和 xTaskResume() 时才会进入或者退出暂停状态。它不能指定超时时间。 有效的任务状态转换任务优先级每个任务将分配一个从 0 到 ( configMAX_PRIORITIES - 1 ) 的优先级。configMAX_PRIORITIES 在文件 FreeRTOSConfig.h 中定义,configMAX_PRIORITIES参数值越大,FreeRTOS 占用的 RAM 就越多。

6、 低优先级任务使用较小的数字,缺省的空闲优先级 tskIDLE_PRIORITY 定义为 0。 调度器保证处于就绪或运行状态的任务分配到处理器时间,高优先级任务先分配。换句话说,处理器时间总是分配给能够运行的最高优先级任务。 执行任务一个任务有下面的结构形式: void vATaskFunction( void *pvParameters ) for( ; ) - Task application code here. - 类型 pdTASK_CODE 定义为返回值是 void 的函数,并使用 void 指针作为唯一的参数。所有的任务函数都是这个类型,可以传递任意类型的参数到任务 - 在 任务

7、系统标准演示 中进行了演示。 任务函数应当从不返回,因此它通常执行一个连续的循环,参考 RTOS 的例子。 任务由函数 xTaskCreate() 创建,使用 vTaskDelete() 删除。 创建任务Task Creation Macros可以使用 portTASK_FUNCTION 和 portTASK_FUNCTION_PROTO 宏定义任务函数。这些宏允许编译器分别添加特定语法到函数定义和声明。这些只有在移植时使用特点声明时使用 (目前只在 PIC18 fedC)。 上面函数声明可以写为下面形式:void vATaskFunction( void *pvParameters );或者

8、,portTASK_FUNCTION_PROTO( vATaskFunction, pvParameters );同样上面的函数可以写为: portTASK_FUNCTION( vATaskFunction, pvParameters ) for( ; ) - Task application code here. - 空闲任务空闲任务在调度器启动时自动创建。 空闲任务负责清理已删除的任务使用的内存,因此调用 vTaskDelete() 函数来保证空闲任务分配到处理时间。 现在能够使用可视化工具检查微控制器分配给空闲任务的时间。 当空闲任务没有使用其他函数,这样在其他任何条件下可以不分配到处理

9、器时间。 系统其他任务可以共享空闲任务的优先级 (tskIDLE_PRIORITY). 空闲任务钩子一个空闲任务钩子就是在每次空闲任务运行时调用的函数。如果你希望一个函数以空闲任务优先级运行,有两个方式: 1. 在空闲任务钩子实现。 必须始终有一个任务准备好运行,在钩子函数中不能调用任何可能引起任务阻塞的 API 函数 (例如vTaskDelay(),它允许联合程序阻塞钩子函数)。 2. 创建空闲优先级任务执行的功能。 这是更灵活的方法但是需要更多的 RAM。 更多内容参考 嵌入式软件应用设计 小节中使用空闲钩子部分。 创建空闲钩子: 1. 在 FreeRTOSConfig.h 中设置 con

10、figUSE_IDLE_HOOK 为 1。 2. 定义下面类型的函数: void vApplicationIdleHook( void ); 通常使用空闲钩子的用法就是简单的将处理器进入节能模式。联合程序FreeRTOS 基础Co-Routine States一个联合程序可以以下面的状态中的一种存在: · 运行 当一个联合程序正在执行时它就是处于运行状态,它就占用了处理器。 · 就绪 就绪状态的联合程序是那些可以执行(没有被阻塞)但是还没有被执行的程序。一个联合程序可能处于就绪状态是因为: 1. 另外一个相同或高优先级的联合程序正处于运行状态,或 2. 一个任务处于运行状态

11、 - 这只会在系统同时使用了任务和联合程序时发生。 · 阻塞 如果一个联合程序正处于暂时等待或等待外部事件,它就是处于阻塞状态。例如,联合程序调用 crDELAY() 它就会被阻塞(放入到阻塞状态)直到达到延时时间 - 一个临时事件。被阻塞的联合程序不会被调度。 目前没有联合程序等价于任务的暂停状态,这个特点将在以后的版本中加入。 有效的联合程序状态转换联合程序属性每个联合程序被分配一个从 0 到 ( configMAX_CO_ROUTINE_PRIORITIES - 1 ) 的优先级。configMAX_CO_ROUTINE_PRIORITIES 在 FreeRTOSConfig.

12、h 中定义并在基本系统中设置。configMAX_CO_ROUTINE_PRIORITIES 的数值越大 FreeRTOS 消耗的 RAM 越多。 低优先级联合程序使用小数值。 联合程序优先级只针对其他联合程序,任务的优先级总是高于联合程序。 执行联合程序 联合程序使用下面的结构: void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex ) crSTART( xHandle ); for( ; ) - Co-routine application code here. - crEND(

13、); 类型 crCOROUTINE_CODE 定义为返回值为 void 的函数,使用 xCoRoutineHandle 和一个索引值作为参数。所有执行一个联合程序的函数都是这个类型(如上面所示)。 调用 xCoRoutineCreate() 创建联合程序。 要点说明: · 所有联合程序函数 必须 以调用 crSTART() 开始。 · 所以联合程序函数 必须 以调用 crEND() 结束。 · 联合程序函数从不返回,通常是执行一个不断的循环。 · 许多联合程序可以从一个单一联合程序函数创建。uxIndex 参数提供区分这些联合函数的方法下面给出一个例子。

14、 调度联合程序联合程序由反复调用 vCoRoutineSchedule() 进行调度,调用 vCoRoutineSchedule() 的最佳位置是在空闲任务钩子中。这是因为即使你的系统只使用联合程序,当调度开始后仍将自动创建空闲任务。参考下面的例子。 混合任务和联合程序从空闲任务调度联合程序就很容易在同一个系统中混合使用任务和联合程序。这样只有在没有比空闲任务优先级更高的任务运行时联合程序才会执行,查看下面的例子。此外 ARM Cortex-M3 和 PC 演示例子中演示了这个方式。 限制和约束联合程序和任务相比来说,优点是 RAM 占用率更低,但是联合程序相比任务来说更加复杂和有更多限制。

15、· 共享堆栈 当联合程序阻塞时,它的堆栈不再继续保持。这意味着变量的数值很可能会丢失。为了解决这个问题,一个需要保持参数的变量必须声明为静态变量,例如: void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex ) static char c = 'a' / Co-routines must start with a call to crSTART(). crSTART( xHandle ); for( ; ) / If we set c to equal &#

16、39;b' here . c = 'b' / . then make a blocking call . crDELAY( xHandle, 10 ); / . c will only be guaranteed to still / equal 'b' here if it is declared static / (as it is here). / Co-routines must end with a call to crEND(). crEND(); 共享堆栈的另外一个后果是调用 API 函数引起联合程序阻塞只可能是联合程序本身 - 而不是从联

17、合程序中调用函数。例如: void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex ) / Co-routines must start with a call to crSTART(). crSTART( xHandle ); for( ; ) / It is fine to make a blocking call here, crDELAY( xHandle, 10 ); / but a blocking call cannot be made from within / vACa

18、lledFunction(). vACalledFunction(); / Co-routines must end with a call to crEND(). crEND(); void vACalledFunction( void ) / Cannot make a blocking call here!   · 使用 switch 状态 包括在 FreeRTOS 下载的缺省的联合程序不允许在 switch 状态中执行阻塞调用。例如: void vACoRoutineFunction( xCoRoutineHandle xHandle, unsigned portB

19、ASE_TYPE uxIndex ) / Co-routines must start with a call to crSTART(). crSTART( xHandle ); for( ; ) / It is fine to make a blocking call here, crDELAY( xHandle, 10 ); switch( aVariable ) case 1 : / Cannot make a blocking call here! break; default: / Or here! / Co-routines must end with a call to crEN

20、D(). crEND();   快速例子这个快速例子演示了联合程序的使用。 1. 创建一个简单的联合程序闪动一个 LED 下面代码定义了一个简单的联合程序,除了周期性改变 LED 外什么也不做。 void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex ) / Co-routines must start with a call to crSTART(). crSTART( xHandle ); for( ; ) / Delay for a fixed period. crDELAY(

21、 xHandle, 10 ); / Flash an LED. vParTestToggleLED( 0 ); / Co-routines must end with a call to crEND(). crEND(); 就是这样! 2. 调度联合程序 联合程序由反复调用 vCoRoutineSchedule() 进行调度。最佳的位置是在空闲任务中放置空闲任务钩子。首先保证 FreeRTOSConfig.h 中的宏 configUSE_IDLE_HOOK 设置为 1 ,然后在空闲任务钩子中写入: void vApplicationIdleHook( void ) vCoRoutineSche

22、dule( void ); 或者,如果空闲任务没有执行其他任何函数,更有效率的做法是在循环中调用 vCoRoutineSchedule() : void vApplicationIdleHook( void ) for( ; ) vCoRoutineSchedule( void ); 3. 创建联合程序并启动调度 联合程序可以在 main() 中创建: #include "task.h" #include "croutine.h" #define PRIORITY_0 0 void main( void ) / In this case the inde

23、x is not used and is passed / in as 0. xCoRoutineCreate( vFlashCoRoutine, PRIORITY_0, 0 ); / NOTE: Tasks can also be created here! / Start the scheduler. vTaskStartScheduler();   4. 扩展例子:使用索引参数 现在假定我们希望从同一个函数中创建 8 个这样的联合程序。每个联合程序将以不同速率闪动一个不同的 LED。索引参数可以用于在联合程序之间进行区分。 这次我们将创建 8 个联合程序并给每个指定不同的索引值

24、。 #include "task.h" #include "croutine.h" #define PRIORITY_0 0 #define NUM_COROUTINES 8 void main( void ) int i; for( i = 0; i < NUM_COROUTINES; i+ ) / This time i is passed in as the index. xCoRoutineCreate( vFlashCoRoutine, PRIORITY_0, i ); / NOTE: Tasks can also be created

25、here! / Start the scheduler. vTaskStartScheduler(); 同时扩展联合程序函数,使得每个使用不同的速率显示 LED。 const int iFlashRates NUM_COROUTINES = 10, 20, 30, 40, 50, 60, 70, 80 ; const int iLEDToFlash NUM_COROUTINES = 0, 1, 2, 3, 4, 5, 6, 7 void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex ) / C

26、o-routines must start with a call to crSTART(). crSTART( xHandle ); for( ; ) / Delay for a fixed period. uxIndex is used to index into / the iFlashRates. As each co-routine was created with / a different index value each will delay for a different / period. crDELAY( xHandle, iFlashRate uxIndex ); /

27、Flash an LED. Again uxIndex is used as an array index, / this time to locate the LED that should be toggled. vParTestToggleLED( iLEDToFlash uxIndex ); / Co-routines must end with a call to crEND(). crEND();   例子演示程序下载的演示程序中包括两个使用联合程序的文件: 1. crflash.c 这个文件功能上等同于标准的演示文件 flash.c,但是使用了联合程序取代了任务。此外,

28、因为只是为了用于演示目的,没有在联合程序中直接改变 LED(如同上面的快速例子那样),需要改变 LED 的数量放入到高优先级的联合程序队列中。 2. crhook.c 演示了从中断到联合程序交换数据。使用一个节拍钩子函数作为数据源。 PC 和 ARM Cortex-M3 的演示程序已经预先配置好使用这些相同的联合程序文件,可以作为参考。其他的演示程序只使用了任务,但是可以很容易的按照下面步骤转换为联合程序。主要是使用 crflash.c 替换 flash.c : 1. 在 FreeRTOSConfig.h 中设置 configUSE_CO_ROUTINES 和 configUSE_IDLE_H

29、OOK 为 1. 2. 在 IDE 项目或项目的 makefile (与使用的演示程序有关): i. 替换文件 FreeRTOS/Demo/Common/Minimal/flash.c 为 FreeRTOS/Demo/Common/Minimal/crflash.c. ii. 添加文件 FreeRTOS/Source/croutine.c. 3. 在 main.c: i. 包含头文件 croutine.h,它定义了联合程序的宏和函数声明。 ii. 替换包含的头文件 flash.h 为 crflash.h. iii. 删除创建 flash 任务的函数调用 vStartLEDFlashTasks(

30、) . iv. . 并替换为创建 flash 联合程序 vStartFlashCoRoutines( n ), 这里 n 是需要创建联合程序的数目。每个联合程序以不同的速率控制一个不同的 LED。 v. 添加空闲钩子函数调度联合程序: vi. void vApplicationIdleHook( void )vii. viii. vCoRoutineSchedule( void );ix. 如果 main() 已经包含了一个空闲钩子,那么简单的添加 vCoRoutineSchedule() 函数调用即可。 4. 用 flash 联合程序替换 flash 任务意味着至少减少分配了两个堆栈以及更少

31、用于调度器的堆空间。如果你的项目的 RAM 不足以包含 croutine.c 那么在 FreeRTOSConfig.h 中减少 portTOTAL_HEAP_SPACE 为 ( 2 * portMINIMAL_STACK_SIZE ) .任务间通讯 Inter-task CommunicationFreeRTOS 基础 FreeRTOS.org 提供: · 队列 · 二进制信号灯 · 计数信号灯 · 互斥 · 递归互斥, 以及更多 队列队列是内部通信的主要形式。它可以用于在任务和任务之间以及任务和中断之间发送消息。在大多数情况下使用线程安全 F

32、IFO(先进先出)缓存,新数据放在队列的最后,虽然数据也可以放在前面。 队列可以包含固定大小的 '项目' - 每个项目的大小和队列可以保存项目的最大数量在创建队列时就已经定义了。 项目以复制而不是引用的方式放入队列,因此最好使放入队列项目的大小成为最小。以复制的方式放入队列可以使你的系统设计极大的简化,因为两个任务不会同时访问数据。队列帮助你管理所有的互斥问题。如果你希望在队列中使用大的项目,可能最好用插入队列指针 - 但是这样做必须注意要确保你的系统明确定义任务和/或中断是数据的"所以者"。 队列 API 函数可以指定阻塞的时间。阻塞时间代表任务进入阻塞状

33、态或者等待队列中数据时(当任务读取队列但是队列是空的时候)的最大'节拍'数,或者等待队列空间变为可以使用(当任务需要写数据到队列,但是队列已满时)。当一个以上任务在同一个队列中被阻塞时,高优先级的任务先解除阻塞。 查看用户文档中 队列管理 小节中与队列相关的 API 函数列表。搜索 FreeRTOS/Demo/Common/Minimal 文件夹下的文件可以发现多个这种用法的例子。注意中断里不能使用不是用 "FromISR" 结束的 API 函数。   写入和读取队列。在这个例子中队列保存5个项目,并且从不变满。二进制信号灯二进制信号灯同时用于互斥

34、和同步的目的。 二进制信号灯和互斥非常相似,但是有一些细微的不同:互斥包括优先级继承机制,而二级制信号没有。这使得二进制信号灯用于同步更方便 (在任务之间或任务与中断之间),而互斥用在简单的互相排斥更好。在怎样用互斥作为互相排斥的机制中 说明 二进制信号灯用法,这个子章节只说明使用二进制信号灯进行同步。 信号灯 API 函数可以指定阻塞的时间。阻塞时间代表任务因为等待获取信号灯而进入阻塞状态的最大'节拍'数。如果超过一个以上的任务因为同一个信号灯被阻塞,那么在信号灯可以使用时高优先级的任务先解除阻塞。 将一个二进制信号灯看作为队列,它只能保存一个项目,这个队列只能是空的或者是满

35、的 (二进制)。使用队列的任务和中断不关心队列保存了什么 - 它们只关系队列是空的还是满的。这个机制可以用于同步任务和中断。 考虑这样一个情况,任务使用一个外设,轮询外设将浪费 CPU 资源,并阻止了其他任务的运行。因此最好是任务将大部分时间用于阻塞状态 (允许其他任务运行),只有真正需要操作时才去轮询。这就是在 '试图' 获得信号时使用二进制信号灯进行任务阻塞,而一个中断服务程序用于外设,当需要使用外设时 '给出' 信号。任务总是 '获取' 信号 (从队列读取直到队列为空),但是从不 '给出'。中断服务程序总是 '给出&

36、#39; 信号 (写入队列直到队列变满) 而从不获取。 xSemaphoreGiveFromISR() 源代码的文档中清楚的解释了这个方法。 可以使用任务的优先级保证及时的外设服务 - 有效的产生 '不同的中断' 方式。另外一种方法是使用队列代替信号,在中断服务程序中捕捉外设的数据并通过队列发送给任务。当队列的数据可以使用后任务解除阻塞,从队列读取数据后,进行数据处理。第二种方法允许中断程序尽可能的短,通过 post 处理而不是发生在一个任务中。 参考用户文档的 信号灯/互斥 小节中关于信号灯的 API 函数。搜索 FreeRTOS/Demo/Common/Minimal 文件

37、夹下的文件可以获得很多这种用法的例子。注意中断里不能使用不是以 "FromISR" 结束的 API 函数。   使用信号灯同步任务和中断。中断只 '给出' 信号,而任务只 '获取' 信号。计数信号灯正如二进制信号灯可以认为长度是 1 的队列,计数信号灯可以看成是长度大于 1 的队列。同样,用户的信号不是对队列数据的数值感兴趣 - 只需要看队列是不是空的。 计数信号灯典型用于两个方面: 1. 计数事件。 在这个情况下使用一个事件处理程序将在每次事件发生时 '给出' 信号 (增加信号计数值), 同时每次处理事件时处理任务

38、将 '获得' 信号 (减少信号计数值)。因此计数值是事件发生次数和处理次数的差,信号创建时,这个计数值是0。 2. 资源管理。 在这个情况下,计数值代表可用的资源数。为了获得一个资源的控制,任务必须先获得信号 - 减少信号的计数值。当计数值达到 0,代表没有剩余的自由资源。当任务使用完资源后,需要 '返还' 信号 - 增加信号计数值。在这种方式下信号创建时的计数值等于最大计数值。 参考 信号灯/互斥 小节查看相关的 API 函数。搜索 FreeRTOS/Demo/Common/Minimal 文件夹下的文件可以查看关于这种用法的例子。注意中断里不能使用不以 &q

39、uot;FromISR" 结束的 API 函数。 互斥互斥是包含优先级继承机制的 二进制信号灯。对于同步(在任务之间或者任务与中断之间)来说二进制信号灯是更好的选择,互斥对于简单的互相排斥更方便 (mutex 就是 'MUT'ual 'EX'clusion). 当用于互相排斥时,互斥就像是资源保护。当一个任务需要访问资源,它必须先获得 ('take') 令牌;当访问结束后,它必须释放令牌 - 允许其他任务能够访问这个资源。 互斥使用了和信号灯相同的 API 函数,所以也可以指定阻塞时间。阻塞时间代表了任务因为试图获取一个不可马上使用的信

40、号而进入阻塞状态的最大 '节拍' 数。和二进制信号灯不同 - 互斥采用了优先级继承关系。这意味着如果高优先级任务因为试图获取被低优先级任务保持的互斥信号(令牌)而阻塞,那么低优先级任务的优先级将临时上升到被阻塞的任务。这个机制保证了高优先级任务被阻塞的时间最短,并减少了已经发生的 '优先级反转'。 优先级继承并不能解决优先级反转的问题!它只是在某些条件下降低了它的影响。硬实时系统要把防止优先级反转放在第一位来考虑。 使用互斥来保护共享的资源递归互斥一个使用了递归的互斥可以反复被所有者 '获取'。互斥变为不可用直到所有者为每次 xSemaphore

41、TakeRecursive() 请求调用 xSemaphoreGiveRecursive()。例如,如果任务成功 '获取' 相同的互斥 5 次,然后互斥将对其他任务变为不可用,直到它正好 '返还' 互斥 5 次。 这个类型的信号灯使用了优先级继承机制,所以任务 '获取' 信号后必须总是在不再需要信号后 '返还'。 互斥类型信号不能用于中断服务程序。 钩子函数FreeRTOS 基础 空闲钩子函数空闲任务可以选择调用程序定义的钩子(或回调)函数 - 空闲钩子。空闲任务以最低的优先级运行,这样空闲钩子函数只有在没有其他高优先级任务运行时

42、执行。这使得空闲钩子函数是一个理想的位置使处理器进入低功耗状态 - 没有需要处理的任务时自动节能。 空闲钩子只有在 FreeRTOSConfig.h 的宏 configUSE_IDLE_HOOK 被设置为 1 时在调用,此时用户程序必须提供下面形式的钩子函数: void vApplicationIdleHook( void ); 空闲钩子在空闲任务运行时将反复调用。至关重要的是空闲钩子函数不调用任何可能引起阻塞的 API 函数。节拍钩子函数节拍中断可以选择调用程序定义的钩子(或回调)函数 - 节拍钩子。节拍钩子提供了方便的计时功能,它只有在文件 FreeRTOSC

43、onfig.h 中的宏  configUSE_TICK_HOOK 设置为 1 时才会调用。当它被设置后,用户程序必须提供下面形式的钩子函数: void vApplicationTickHook( void ); vApplicationTickHook() 从 ISR 中执行,所以它必须非常短,同时也不使用太多堆栈。参考 演示程序的文件 crhook.c ,查看怎样使用节拍钩子。跟踪钩子宏FreeRTOS 基础说明引入到 FreeRTOS.org V5.0.0 的跟踪钩子宏功能非常强大,允许你搜集你的嵌入式系统程序行为的数据。 兴趣要点在 FreeRTOS.org 源代码中

44、包含一些程序可以重定义的空白宏,目的是用于验证指定的跟踪设备。系统只需要执行感兴趣的那些宏 - 而没有用到的宏仍然保留为空,不会占用系统时间。 例子下面是使用这些宏的一些例子: · 设置数字输出来指示哪个任务正在运行 - 允许使用逻辑分析仪观察和记录任务运行次序和时间。 · 同样 - 设置模拟电压输出代表正在运行的任务 - 允许使用示波器观察和记录任务运行的次序和时间。 · 记录任务运行次序、任务时间、内核事件和 API 调用供脱机分析。 · 将内核事件集成到第三方的调试器。 例子 1FreeRTOS.org 任务标签功能提供了一种简单的机制通过数字或者

45、模拟输出建立记录。例如,标签值可以设置为对于任务是唯一的电压。traceSWITCHED_IN() 宏可以简单定义成与任务切换相关的模拟输出。例如: */ First task sets its tag value to 1. */ void vTask1( void *pvParameters ) */ This task is going to be represented by a voltage scale of 1. */ vTaskSetApplicationTaskTag( NULL, ( void * ) 1 ); for( ; ) */ Task code goes here

46、. */ /*/ */ Second task sets its tag value to 2. */ void vTask1( void *pvParameters ) */ This task is going to be represented by a voltage scale of 2. */ vTaskSetApplicationTaskTag( NULL, ( void * ) 2 ); for( ; ) */ Task code goes here. */ /*/ */ Define the traceTASK_SWITCHED_IN() macro to output th

47、e voltage associated with the task being selected to run on port 0. */ #define traceTASK_SWICTHED_IN() vSetAnalogueOutput( 0, ( int ) pxCurrentTCB->pxTaskTag )例子 2API 调用记录可以用于记录发生切换的原因。内核调用记录可以用于记录任务执行的顺序。例如:/* traceBLOCKING_ON_QUEUE_RECEIVE() is just one of the macros that can be used to record

48、whya context switch is about to occur. */#define traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue) ulSwitchReason = reasonBLOCKING_ON_QUEUE_READ;/* log_event() is an application defined function that logs which tasks ran when, and why. */#define traceTASK_SWITCHED_OUT() log_event( pxCurrentTCB, ulSwitchReason

49、 );宏定义宏被称为内中断,也特别称作节拍中断,必须被快速执行,而不是使用很多堆栈空间。设置变量,写入跟踪寄存器,或者输出到端口。试图用 fprintf() 记录数据到低速磁盘是无法工作的! 宏定义之前必须包含 FreeRTOS.h 头文件。最早定义跟踪宏的位置是在 FreeRTOSConfig.h 底部,或者在 FreeRTOSConfig.h 底部包含单独一个头文件。 下面的表格描述了可用的宏,宏的参数说明记录关联事件的任务、队列、信号灯或互斥。宏定义说明traceTASK_INCREMENT_TICK()在节拍中断调用。traceTASK_SWITCHED_OUT()在选择新任务运行前调

50、用。这时 pxCurrentTCB 包含了将脱离运行状态任务的句柄。traceTASK_SWITCHED_IN()在选择运行任务后运行。这时 pxCurrentTCB 包含了即将进入运行状态任务的局部。traceBLOCKING_ON_QUEUE_RECEIVE(pxQueue)代表当前运行任务被阻塞因为试图读取空队列,或者试图'获取'空信号或互斥。traceBLOCKING_ON_QUEUE_SEND(pxQueue)代表当前任务被阻塞因为试图写入一个已经满的队列。traceGIVE_MUTEX_RECURSIVE(pxMutex)如果队列创建成功在 xSemaphoreGi

51、veRecursive() 中调用。traceQUEUE_CREATE()在 xQueueCreate() 中调用。traceQUEUE_CREATE_FAILED()如果队列因为堆内存不足而创建不成功在 xQueueCreate() 中调用。traceCREATE_MUTEX()如果互斥创建成功在 xSemaphoreCreateMutex() 中调用。traceCREATE_MUTEX_FAILED()如果互斥因为堆内存不足而创建不成功在 xSemaphoreCreateMutex() 中调用。traceGIVE_MUTEX_RECURSIVE(pxMutex)如果'获取'

52、互斥成功在 xSemaphoreGiveRecursive() 中调用。traceGIVE_MUTEX_RECURSIVE_FAILED(pxMutex)如果调用者不是互斥所有者在 xSemaphoreGiveRecursive() 中调用。traceTAKE_MUTEX_RECURSIVE(pxMutex)在 xQueueTakeMutexRecursive() 中调用。traceCREATE_COUNTING_SEMAPHORE()如果信号灯创建成功在 xSemaphoreCreateCounting() 中调用。traceCREATE_COUNTING_SEMAPHORE_FAILED(

53、)如果信号灯因为堆内存不足创建不成功时在 xSemaphoreCreateCounting() 中调用。traceQUEUE_SEND(pxQueue)当队列发送成功时,在 xQueueSend(), xQueueSendToFront(), xQueueSendToBack(), 或任何信号灯 '给出'函数中调用。traceQUEUE_SEND_FAILED(pxQueue)当因为队列满(在达到指定阻塞时间)造成发送失败后在 xQueueSend(), xQueueSendToFront(), xQueueSendToBack(), 或其他信号灯 '给出' 函数中调用。traceQUEUE_RECEIVE(pxQueu

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论