FreeRTOS内核是高度可定制的,使用配置文件FreeRTOSConfig.h进行定制。每一个FreeRTOS应用都必须包含这个头文件,用户根据实际应用来裁剪定制FreeRTOS内核。这个配置文件是针对用户程序的,而非内核,所以配置文件通常放在应用程序目录下,不要放在RTOS内核源码目录下。数组
在下载的FreeRTOS文件包中,每一个演示例程都有一个FreeRTOSConfig.h文件。有些例程的配置文件是比较旧的版本,可能不会包含全部有效选项。若是没有在配置文件中指定某个选项,那么RTOS内核会使用默认值。典型的FreeRTOSConfig.h配置文件定义以下所示,随后会说明里面的每个参数。安全
#ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H /*Here is a good place to include header files that are required across yourapplication. */ #include "something.h" #define configUSE_PREEMPTION 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #define configUSE_TICKLESS_IDLE 0 #define configCPU_CLOCK_HZ 60000000 #define configTICK_RATE_HZ 250 #define configMAX_PRIORITIES 5 #define configMINIMAL_STACK_SIZE 128 #define configTOTAL_HEAP_SIZE 10240 #define configMAX_TASK_NAME_LEN 16 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_TASK_NOTIFICATIONS 1 #define configUSE_MUTEXES 0 #define configUSE_RECURSIVE_MUTEXES 0 #define configUSE_COUNTING_SEMAPHORES 0 #define configUSE_ALTERNATIVE_API 0/* Deprecated! */ #define configQUEUE_REGISTRY_SIZE 10 #define configUSE_QUEUE_SETS 0 #define configUSE_TIME_SLICING 0 #define configUSE_NEWLIB_REENTRANT 0 #define configENABLE_BACKWARD_COMPATIBILITY 0 #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 /*Hook function related definitions. */ #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 0 #define configUSE_MALLOC_FAILED_HOOK 0 /*Run time and task stats gathering related definitions. */ #define configGENERATE_RUN_TIME_STATS 0 #define configUSE_TRACE_FACILITY 0 #define configUSE_STATS_FORMATTING_FUNCTIONS 0 /*Co-routine related definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES 1 /*Software timer related definitions. */ #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY 3 #define configTIMER_QUEUE_LENGTH 10 #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE /*Interrupt nesting behaviour configuration. */ #define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] #define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] /*Define to trap errors during development. */ #define configASSERT( ( x ) ) if( ( x ) == 0) vAssertCalled( __FILE__, __LINE__ ) /*FreeRTOS MPU specific definitions. */ #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 /*Optional functions - most linkers will remove unused functions anyway. */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_xResumeFromISR 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTaskGetCurrentTaskHandle 1 #define INCLUDE_uxTaskGetStackHighWaterMark 0 #define INCLUDE_xTaskGetIdleTaskHandle 0 #define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 #define INCLUDE_pcTaskGetTaskName 0 #define INCLUDE_eTaskGetState 0 #define INCLUDE_xEventGroupSetBitFromISR 1 #define INCLUDE_xTimerPendFunctionCall 0 /* Aheader file that defines trace macro can be included here. */ #end if/* FREERTOS_CONFIG_H*/
1.configUSE_PREEMPTION多线程
为1时RTOS使用抢占式调度器,为0时RTOS使用协做式调度器(时间片)。架构
注:在多任务管理机制上,操做系统能够分为抢占式和协做式两种。协做式操做系统是任务主动释放CPU后,切换到下一个任务。任务切换的时机彻底取决于正在运行的任务。app
2.configUSE_PORT_OPTIMISED_TASK_SELECTIONless
某些运行FreeRTOS的硬件有两种方法选择下一个要执行的任务:通用方法和特定于硬件的方法(如下简称“特殊方法”)。函数
通用方法:性能
特殊方法:测试
3.configUSE_TICKLESS_IDLE优化
设置configUSE_TICKLESS_IDLE为1使能低功耗tickless模式,为0保持系统节拍(tick)中断一直运行。
一般状况下,FreeRTOS回调空闲任务钩子函数(须要设计者本身实现),在空闲任务钩子函数中设置微处理器进入低功耗模式来达到省电的目的。由于系统要响应系统节拍中断事件,所以使用这种方法会周期性的退出、再进入低功耗状态。若是系统节拍中断频率过快,则大部分电能和CPU时间会消耗在进入和退出低功耗状态上。
FreeRTOS的tickless空闲模式会在空闲周期时中止周期性系统节拍中断。中止周期性系统节拍中断可使微控制器长时间处于低功耗模式。移植层须要配置外部唤醒中断,当唤醒事件到来时,将微控制器从低功耗模式唤醒。微控制器唤醒后,会从新使能系统节拍中断。因为微控制器在进入低功耗后,系统节拍计数器是中止的,但咱们又须要知道这段时间能折算成多少次系统节拍中断周期,这就须要有一个不受低功耗影响的外部时钟源,即微处理器处于低功耗模式时它也在计时的,这样在重启系统节拍中断时就能够根据这个外部计时器计算出一个调整值并写入RTOS 系统节拍计数器变量中。
4.configUSE_IDLE_HOOK
设置为1使用空闲钩子(Idle Hook相似于回调函数),0忽略空闲钩子。
当RTOS调度器开始工做后,为了保证至少有一个任务在运行,空闲任务被自动建立,占用最低优先级(0优先级)。对于已经删除的RTOS任务,空闲任务能够释放分配给它们的堆栈内存。所以,在应用中应该注意,使用vTaskDelete()函数时要确保空闲任务得到必定的处理器时间。除此以外,空闲任务没有其它特殊功能,所以能够任意的剥夺空闲任务的处理器时间。
应用程序也可能和空闲任务共享同个优先级。
空闲任务钩子是一个函数,这个函数由用户来实现,RTOS规定了函数的名字和参数,这个函数在每一个空闲任务周期都会被调用。
要建立一个空闲钩子:
void vApplicationIdleHook(void );
这个钩子函数不能够调用会引发空闲任务阻塞的API函数(例如:vTaskDelay()、带有阻塞时间的队列和信号量函数),在钩子函数内部使用协程是被容许的。
使用空闲钩子函数设置CPU进入省电模式是很常见的。
5.configUSE_MALLOC_FAILED_HOOK
每当一个任务、队列、信号量被建立时,内核使用一个名为pvPortMalloc()的函数来从堆中分配内存。官方的下载包中包含5个简单内存分配策略,分别保存在源文件heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c中。 仅当使用这五个简单策略之一时,宏configUSE_MALLOC_FAILED_HOOK才有意义。
若是定义并正确配置malloc()失败钩子函数,则这个函数会在pvPortMalloc()函数返回NULL时被调用。只有FreeRTOS在响应内存分配请求时发现堆内存不足才会返回NULL。
若是宏configUSE_MALLOC_FAILED_HOOK设置为1,那么必须定义一个malloc()失败钩子函数,若是宏configUSE_MALLOC_FAILED_HOOK设置为0,malloc()失败钩子函数不会被调用,即使已经定义了这个函数。malloc()失败钩子函数的函数名和原型必须以下所示:
void vApplicationMallocFailedHook( void);
6.configUSE_TICK_HOOK
设置为1使用时间片钩子(Tick Hook),0忽略时间片钩子。
注:时间片钩子函数(Tick Hook Function)
时间片中断能够周期性的调用一个被称为钩子函数(回调函数)的应用程序。时间片钩子函数能够很方便的实现一个定时器功能。
只有在FreeRTOSConfig.h中的configUSE_TICK_HOOK设置成1时才可使用时间片钩子。一旦此值设置成1,就要定义钩子函数,函数名和参数以下所示:
void vApplicationTickHook( void );
vApplicationTickHook()函数在中断服务程序中执行,所以这个函数必须很是短小,不能大量使用堆栈,不能调用任何不是以”FromISR" 或 "FROM_ISR”结尾的API函数。
在FreeRTOSVx.x.x\FreeRTOS\Demo\Common\Minimal文件夹下的crhook.c文件中有使用时间片钩子函数的例程。
7.configCPU_CLOCK_HZ
写入实际的CPU内核时钟频率,也就是CPU指令执行频率,一般称为Fcclk。配置此值是为了正确的配置系统节拍中断周期。
8.configTICK_RATE_HZ
RTOS 系统节拍中断的频率。即一秒中断的次数,每次中断RTOS都会进行任务调度。
系统节拍中断用来测量时间,所以,越高的测量频率意味着可测到越高的分辨率时间。可是,高的系统节拍中断频率也意味着RTOS内核占用更多的CPU时间,所以会下降效率。RTOS演示例程都是使用系统节拍中断频率为1000HZ,这是为了测试RTOS内核,比实际使用的要高。(实际使用时不用这么高的系统节拍中断频率)
多个任务能够共享一个优先级,RTOS调度器为相同优先级的任务分享CPU时间,在每个RTOS 系统节拍中断到来时进行任务切换。高的系统节拍中断频率会下降分配给每个任务的“时间片”持续时间。
9.configMAX_PRIORITIES
配置应用程序有效的优先级数目。任何数量的任务均可以共享一个优先级,使用协程能够单独的给与它们优先权。见configMAX_CO_ROUTINE_PRIORITIES。
在RTOS内核中,每一个有效优先级都会消耗必定量的RAM,所以这个值不要超过你的应用实际须要的优先级数目。
注:任务优先级
每个任务都会被分配一个优先级,优先级值从0~ (configMAX_PRIORITIES - 1)之间。低优先级数表示低优先级任务。空闲任务的优先级为0(tskIDLE_PRIORITY),所以它是最低优先级任务。
FreeRTOS调度器将确保处于就绪状态(Ready)或运行状态(Running)的高优先级任务比一样处于就绪状态的低优先级任务优先获取处理器时间。换句话说,处于运行状态的任务永远是高优先级任务。
处于就绪状态的相同优先级任务使用时间片调度机制共享处理器时间。
10.configMINIMAL_STACK_SIZE
定义空闲任务使用的堆栈大小。一般此值不该小于对应处理器演示例程文件FreeRTOSConfig.h中定义的数值。
就像xTaskCreate()函数的堆栈大小参数同样,堆栈大小不是以字节为单位而是以字为单位的,好比在32位架构下,栈大小为100表示栈内存占用400字节的空间。
11.configTOTAL_HEAP_SIZE
RTOS内核总计可用的有效的RAM大小。仅在你使用官方下载包中附带的内存分配策略时,才有可能用到此值。每当建立任务、队列、互斥量、软件定时器或信号量时,RTOS内核会为此分配RAM,这里的RAM都属于configTOTAL_HEAP_SIZE指定的内存区。后续的内存配置会详细讲到官方给出的内存分配策略。
12.configMAX_TASK_NAME_LEN
调用任务函数时,须要设置描述任务信息的字符串,这个宏用来定义该字符串的最大长度。这里定义的长度包括字符串结束符’\0’。
13.configUSE_TRACE_FACILITY
设置成1表示启动可视化跟踪调试,会激活一些附加的结构体成员和函数。
14.configUSE_STATS_FORMATTING_FUNCTIONS (V7.5.0新增)
设置宏configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS为1会编译vTaskList()和vTaskGetRunTimeStats()函数。若是将这两个宏任意一个设置为0,上述两个函数不会被编译。
15.configUSE_16_BIT_TICKS
定义系统节拍计数器的变量类型,即定义portTickType是表示16位变量仍是32位变量。
定义configUSE_16_BIT_TICKS为1意味着portTickType表明16位无符号整形,定义configUSE_16_BIT_TICKS为0意味着portTickType表明32位无符号整形。
使用16位类型能够大大提升8位和16位架构微处理器的性能,但这也限制了最大时钟计数为65535个’Tick’。所以,若是Tick频率为250HZ(4MS中断一次),对于任务最大延时或阻塞时间,16位计数器是262秒,而32位是17179869秒。
16.configIDLE_SHOULD_YIELD
这个参数控制任务在空闲优先级中的行为。仅在知足下列条件后,才会起做用。
经过时间片共享同一个优先级的多个任务,若是共享的优先级大于空闲优先级,并假设没有更高优先级任务,这些任务应该得到相同的处理器时间。
但若是共享空闲优先级时,状况会稍微有些不一样。当configIDLE_SHOULD_YIELD为1时,其它共享空闲优先级的用户任务就绪时,空闲任务马上让出CPU,用户任务运行,这样确保了能最快响应用户任务。处于这种模式下也会有不良效果(取决于你的程序须要),描述以下:
图中描述了四个处于空闲优先级的任务,任务A、B和C是用户任务,任务I是空闲任务。上下文切换周期性的发生在T0、T1…T6时刻。当用户任务运行时,空闲任务马上让出CPU,可是,空闲任务已经消耗了当前时间片中的必定时间。这样的结果就是空闲任务I和用户任务A共享一个时间片。用户任务B和用户任务C所以得到了比用户任务A更多的处理器时间。
能够经过下面方法避免:
设置configIDLE_SHOULD_YIELD为0将阻止空闲任务为用户任务让出CPU,直到空闲任务的时间片结束。这确保全部处在空闲优先级的任务分配到相同多的处理器时间,可是,这是以分配给空闲任务更高比例的处理器时间为代价的。
17.configUSE_TASK_NOTIFICATIONS(V8.2.0新增)
设置宏configUSE_TASK_NOTIFICATIONS为1(或不定义宏configUSE_TASK_NOTIFICATIONS)将会开启任务通知功能,有关的API函数也会被编译。设置宏configUSE_TASK_NOTIFICATIONS为0则关闭任务通知功能,相关API函数也不会被编译。默认这个功能是开启的。开启后,每一个任务多增长8字节RAM。
这是个颇有用的特性,一大亮点。
每一个RTOS任务具备一个32位的通知值,RTOS任务通知至关于直接向任务发送一个事件,接收到通知的任务能够解除任务的阻塞状态(因等待任务通知而进入阻塞状态)。相对于之前必须分别建立队列、二进制信号量、计数信号量或事件组的状况,使用任务通知显然更灵活。更好的是,相比于使用信号量解除任务阻塞,使用任务通知能够快45%(使用GCC编译器,-o2优化级别)。
18.configUSE_MUTEXES
设置为1表示使用互斥量,设置成0表示忽略互斥量。读者应该了解在FreeRTOS中互斥量和二进制信号量的区别。
关于互斥量和二进制信号量简单说:
19.configUSE_RECURSIVE_MUTEXES
设置成1表示使用递归互斥量,设置成0表示不使用。
20.configUSE_COUNTING_SEMAPHORES
设置成1表示使用计数信号量,设置成0表示不使用。
21.configUSE_ALTERNATIVE_API
设置成1表示使用“替代”队列函数('alternative' queue functions),设置成0不使用。替代API在queue.h头文件中有详细描述。
注:“替代”队列函数已经被弃用,在新的设计中不要使用它!
22.configCHECK_FOR_STACK_OVERFLOW
每一个任务维护本身的栈空间,任务建立时会自动分配任务须要的占内存,分配内存大小由建立任务函数(xTaskCreate())的一个参数指定。堆栈溢出是设备运行不稳定的最多见缘由,所以FreeeRTOS提供了两个可选机制用来辅助检测和改正堆栈溢出。配置宏configCHECK_FOR_STACK_OVERFLOW为不一样的常量来使用不一样堆栈溢出检测机制。
注意,这个选项仅适用于内存映射未分段的微处理器架构。而且,在RTOS检测到堆栈溢出发生以前,一些处理器可能先产生故障(fault)或异常(exception)来反映堆栈使用的恶化。若是宏configCHECK_FOR_STACK_OVERFLOW没有设置成0,用户必须提供一个栈溢出钩子函数,这个钩子函数的函数名和参数必须以下所示:
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName );
参数xTask和pcTaskName为堆栈溢出任务的句柄和名字。请注意,若是溢出很是严重,这两个参数信息也多是错误的!在这种状况下,能够直接检查pxCurrentTCb变量。
推荐仅在开发或测试阶段使用栈溢出检查,由于堆栈溢出检测会增大上下文切换开销。
任务切换出去后,该任务的上下文环境被保存到本身的堆栈空间,这时极可能堆栈的使用量达到了最大(最深)值。在这个时候,RTOS内核会检测堆栈指针是否还指向有效的堆栈空间。若是堆栈指针指向了有效堆栈空间以外的地方,堆栈溢出钩子函数会被调用。
这个方法速度很快,可是不能检测到全部堆栈溢出状况(好比,堆栈溢出没有发生在上下文切换时)。设置configCHECK_FOR_STACK_OVERFLOW为1会使用这种方法。
当堆栈首次建立时,在它的堆栈区中填充一些已知值(标记)。当任务切换时,RTOS内核会检测堆栈最后的16个字节,确保标记数据没有被覆盖。若是这16个字节有任何一个被改变,则调用堆栈溢出钩子函数。
这个方法比第一种方法要慢,但也至关快了。它能有效捕捉堆栈溢出事件(即便堆栈溢出没有发生在上下文切换时),可是理论上它也不能百分百的捕捉到全部堆栈溢出(好比堆栈溢出的值和标记值相同,固然,这种状况发生的几率极小)。
使用这个方法须要设置configCHECK_FOR_STACK_OVERFLOW为2.
23.configQUEUE_REGISTRY_SIZE
队列记录有两个目的,都涉及到RTOS内核的调试:
除了进行内核调试外,队列记录没有其它任何目的。
configQUEUE_REGISTRY_SIZE定义能够记录的队列和信号量的最大数目。若是你想使用RTOS内核调试器查看队列和信号量信息,则必须先将这些队列和信号量进行注册,只有注册后的队列和信号量才可使用RTOS内核调试器查看。查看API参考手册中的vQueueAddToRegistry() 和vQueueUnregisterQueue()函数获取更多信息。
24.configUSE_QUEUE_SETS
设置成1使能队列集功能(能够阻塞、挂起到多个队列和信号量),设置成0取消队列集功能。
25.configUSE_TIME_SLICING(V7.5.0新增)
默认状况下(宏configUSE_TIME_SLICING未定义或者宏configUSE_TIME_SLICING设置为1),FreeRTOS使用基于时间片的优先级抢占式调度器。这意味着RTOS调度器老是运行处于最高优先级的就绪任务,在每一个RTOS 系统节拍中断时在相同优先级的多个任务间进行任务切换。若是宏configUSE_TIME_SLICING设置为0,RTOS调度器仍然老是运行处于最高优先级的就绪任务,可是当RTOS 系统节拍中断发生时,相同优先级的多个任务之间再也不进行任务切换。
26.configUSE_NEWLIB_REENTRANT(V7.5.0新增)
若是宏configUSE_NEWLIB_REENTRANT设置为1,每个建立的任务会分配一个newlib(一个嵌入式C库)reent结构。
27.configENABLE_BACKWARD_COMPATIBILITY
头文件FreeRTOS.h包含一系列#define宏定义,用来映射版本V8.0.0和V8.0.0以前版本的数据类型名字。这些宏能够确保RTOS内核升级到V8.0.0或以上版本时,以前的应用代码不用作任何修改。在FreeRTOSConfig.h文件中设置宏configENABLE_BACKWARD_COMPATIBILITY为0会去掉这些宏定义,而且须要用户确认升级以前的应用没有用到这些名字。
28.configNUM_THREAD_LOCAL_STORAGE_POINTERS
设置每一个任务的线程本地存储指针数组大小。
线程本地存储容许应用程序在任务的控制块中存储一些值,每一个任务都有本身独立的储存空间,宏configNUM_THREAD_LOCAL_STORAGE_POINTERS指定每一个任务线程本地存储指针数组的大小。API函数vTaskSetThreadLocalStoragePointer()用于向指针数组中写入值,API函数pvTaskGetThreadLocalStoragePointer()用于从指针数组中读取值。
好比,许多库函数都包含一个叫作errno的全局变量。某些库函数使用errno返回库函数错误信息,应用程序检查这个全局变量来肯定发生了那些错误。在单线程程序中,将errno定义成全局变量是能够的,可是在多线程应用中,每一个线程(任务)必须具备本身独有的errno值,不然,一个任务可能会读取到另外一个任务的errno值。
FreeRTOS提供了一个灵活的机制,使得应用程序可使用线程本地存储指针来读写线程本地存储。具体参见后续文章《FreeRTOS系列第12篇---FreeRTOS任务应用函数》。
29.configGENERATE_RUN_TIME_STATS
设置宏configGENERATE_RUN_TIME_STATS为1使能运行时间统计功能。一旦设置为1,则下面两个宏必须被定义:
举一个例子,假如咱们配置了一个定时器,每500us中断一次。在定时器中断服务例程中简单的使长整形变量ulHighFrequencyTimerTicks自增。那么上面提到两个宏定义以下(能够在FreeRTOSConfig.h中添加):
extern volatile unsigned longulHighFrequencyTimerTicks; #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( ulHighFrequencyTimerTicks = 0UL ) #define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerTicks
30.configUSE_CO_ROUTINES
设置成1表示使用协程,0表示不使用协程。若是使用协程,必须在工程中包含croutine.c文件。
注:协程(Co-routines)主要用于资源发很是受限的嵌入式系统(RAM很是少),一般不会用于32位微处理器。
在当前嵌入式硬件环境下,不建议使用协程,FreeRTOS的开发者早已经中止开发协程。
31.configMAX_CO_ROUTINE_PRIORITIES
应用程序协程(Co-routines)的有效优先级数目,任何数目的协程均可以共享一个优先级。使用协程能够单独的分配给任务优先级。见configMAX_PRIORITIES。
32.configUSE_TIMERS
设置成1使用软件定时器,为0不使用软件定时器功能。详细描述见FreeRTOS software timers 。
33.configTIMER_TASK_PRIORITY
设置软件定时器服务/守护进程的优先级。详细描述见FreeRTOS software timers 。
34.configTIMER_QUEUE_LENGTH
设置软件定时器命令队列的长度。详细描述见FreeRTOS software timers。
35.configTIMER_TASK_STACK_DEPTH
设置软件定时器服务/守护进程任务的堆栈深度,详细描述见FreeRTOS software timers 。
36.configKERNEL_INTERRUPT_PRIORITY、configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY
这是移植和应用FreeRTOS出错最多的地方,因此须要打起精神仔细读懂。
Cortex-M三、PIC2四、dsPIC、PIC3二、SuperH和RX600硬件设备须要设置宏configKERNEL_INTERRUPT_PRIORITY;PIC3二、RX600和Cortex-M硬件设备须要设置宏configMAX_SYSCALL_INTERRUPT_PRIORITY。
configMAX_SYSCALL_INTERRUPT_PRIORITY和configMAX_API_CALL_INTERRUPT_PRIORITY,这两个宏是等价的,后者是前者的新名字,用于更新的移植层代码。
注意下面的描述中,在中断服务例程中仅能够调用以“FromISR”结尾的API函数。
经过设置configMAX_SYSCALL_INTERRUPT_PRIORITY的优先级级别高于configKERNEL_INTERRUPT_PRIORITY能够实现完整的中断嵌套模式。这意味着FreeRTOS内核不能彻底禁止中断,即便在临界区。此外,这对于分段内核架构的微处理器是有利的。请注意,当一个新中断发生后,某些微处理器架构会(在硬件上)禁止中断,这意味着从硬件响应中断到FreeRTOS从新使能中断之间的这段短期内,中断是不可避免的被禁止的。
不调用API的中断能够运行在比configMAX_SYSCALL_INTERRUPT_PRIORITY高的优先级,这些级别的中断不会被FreeRTOS禁止,所以不会由于执行RTOS内核而被延时。
例如:假如一个微控制器有8个中断优先级别:0表示最低优先级,7表示最高优先级(Cortex-M3和Cortex-M4内核优先数和优先级别正好与之相反,后续文章会专门介绍它们)。当两个配置选项分别为4和0时,下图描述了每个优先级别能够和不可作的事件:
这些配置参数容许很是灵活的中断处理:
在系统中能够像其它任务同样为中断处理任务分配优先级。这些任务经过一个相应中断唤醒。中断服务例程(ISR)内容应尽量的精简---仅用于更新数据而后唤醒高优先级任务。ISR退出后,直接运行被唤醒的任务,所以中断处理(根据中断获取的数据来进行的相应处理)在时间上是连续的,就像ISR在完成这些工做。这样作的好处是当中断处理任务执行时,全部中断均可以处在使能状态。
中断、中断服务例程(ISR)和中断处理任务是三码事:当中断来临时会进入中断服务例程,中断服务例程作必要的数据收集(更新),以后唤醒高优先级任务。这个高优先级任务在中断服务例程结束后当即执行,它多是其它任务也多是中断处理任务,若是是中断处理任务,那么就能够根据中断服务例程中收集的数据作相应处理。
configMAX_SYSCALL_INTERRUPT_PRIORITY接口有着更深一层的意义:在优先级介于RTOS内核中断优先级(等于configKERNEL_INTERRUPT_PRIORITY)和configMAX_SYSCALL_INTERRUPT_PRIORITY之间的中断容许全嵌套中断模式并容许调用API函数。大于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断优先级毫不会由于执行RTOS内核而延时。
运行在大于configMAX_SYSCALL_INTERRUPT_PRIORITY的优先级中断是不会被RTOS内核所屏蔽的,所以也不受RTOS内核功能影响。这主要用于很是高的实时需求中。好比执行电机转向。可是,这类中断的中断服务例程中毫不能够调用FreeRTOS的API函数。
为了使用这个方案,应用程序要必须符合如下规则:调用FreeRTOS API函数的任何中断,都必须和RTOS内核处于同一优先级(由宏configKERNEL_INTERRUPT_PRIORITY设置),或者小于等于宏configMAX_SYSCALL_INTERRUPT_PRIORITY定义的优先级。
37.configASSERT
断言,调试时能够检查传入的参数是否合法。FreeRTOS内核代码的关键点都会调用configASSERT( x )函数,若是参数x为0,则会抛出一个错误。这个错误极可能是传递给FreeRTOS API函数的无效参数引发的。定义configASSERT()有助于调试时发现错误,可是,定义configASSERT()也会增大应用程序代码量,增大运行时间。推荐在开发阶段使用这个断言宏。
举一个例子,咱们想把非法参数所在的文件名和代码行数打印出来,能够先定义一个函数vAssertCalled,该函数有两个参数,分别接收触发configASSERT宏的文件名和该宏所在行,而后经过显示屏或者串口输出。代码以下:
#define configASSERT( ( x ) ) if( ( x ) == 0 )vAssertCalled( __FILE__, __LINE__ )
这里__FILE__和__LINE__是大多数编译器预约义的宏,分别表示代码所在的文件名(字符串格式)和行数(整形)。
这个例子虽然看起来很简单,但因为要把整形__LINE__转换成字符串再显示,在效率和实现上,都不能让人满意。咱们可使用C标准库assert的实现方法,这样函数vAssertCalled只须要接收一个字符串形式的参数(推荐仔细研读下面的代码并理解其中的技巧):
#define STR(x) VAL(x) #define VAL(x) #x #define configASSERT(x) ((x)?(void) 0 :xAssertCalld(__FILE__ ":" STR(__LINE__) " " #x"\n"))
这里稍微讲解一下,因为内置宏__LINE__是整数型的而不是字符串型,把它转化成字符串须要一个额外的处理层。宏STR和和宏VAL正是用来辅助完成这个转化。宏STR用来把整形行号替换掉__LINE__,宏VAL用来把这个整形行号字符串化。忽略宏STR和VAL中的任何一个,只能获得字符串”__LINE__”,这不是咱们想要的。
这里使用三目运算符’?:’来代替参数判断if语句,这样能够接受任何参数或表达式,代码也更紧凑,更重要的是代码优化度更高,由于若是参数恒为真,在编译阶段就能够去掉没必要要的输出语句。
38.INCLUDE Parameters
以“INCLUDE”起始的宏容许用户不编译那些应用程序不须要的实时内核组件(函数),这能够确保在你的嵌入式系统中RTOS占用最少的ROM和RAM。
每一个宏以这样的形式出现:
INCLUDE_FunctionName
在这里FunctionName表示一个你能够控制是否编译的API函数。若是你想使用该函数,就将这个宏设置成1,若是不想使用,就将这个宏设置成0。好比,对于API函数vTaskDelete():
#define INCLUDE_vTaskDelete 1
表示但愿使用vTaskDelete(),容许编译器编译该函数
#define INCLUDE_vTaskDelete 0
表示禁止编译器编译该函数。