FreeRTOS内核是高度可定制的,使用配置文件FreeRTOSConfig.h进行定制。每个FreeRTOS应用都必须包含这个头文件,用户根据实际应用来裁剪定制FreeRTOS内核。这个配置文件是针对用户程序的,而非内核,因此配置文件一般放在应用程序目录下,不要放在RTOS内核源码目录下。
在下载的FreeRTOS文件包中,每个演示例程都有一个FreeRTOSConfig.h文件。有些例程的配置文件是比较旧的版本,可能不会包含所有有效选项。如果没有在配置文件中指定某个选项,那么RTOS内核会使用默认值。典型的FreeRTOSConfig.h配置文件定义如下所示,随后会说明里面的每一个参数。
/*
* FreeRTOS Kernel V10.2.1
* Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
#define configUSE_PREEMPTION 1 //是否定义抢占式调度器(还是抢占式调度器)
#define configUSE_IDLE_HOOK 0 //是否定义IDLE空闲任务HOOK函数
#define configUSE_TICK_HOOK 0 //是否定义TICK滴答HOOK函数
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 ) //CPU时钟的频率
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) //时钟Tick的频率
#define configMAX_PRIORITIES ( 5 ) //最大优先级数
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) //最小堆栈大小
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) //堆中可用RAM总量
#define configMAX_TASK_NAME_LEN ( 16 ) //任务名称字符串最大长度
#define configUSE_TRACE_FACILITY 0 //配置使用跟踪
#define configUSE_16_BIT_TICKS 0 //是否使用16位滴答计数值
#define configIDLE_SHOULD_YIELD 1 //是否让空闲任务“放弃”抢占
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0 //是否使用CO_ROUTINES
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) //CO_ROUTINE优先级
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1 //设置任务的优先级
#define INCLUDE_uxTaskPriorityGet 1 //获取某个任务的优先级
#define INCLUDE_vTaskDelete 1 //删除任务
#define INCLUDE_vTaskCleanUpResources 0 //可以回收删除任务后的资源
#define INCLUDE_vTaskSuspend 1 //将指定任务设置为挂起
#define INCLUDE_vTaskDelayUntil 1 //任务会进入阻塞状态
#define INCLUDE_vTaskDelay 1 //可用于固定频率的延时,它用来延时一个绝对时间
/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
(lowest) to 0 (1?) (highest). */
/*这是Cortex-M3 NVIC的原始值。值可以是255(最低)到0(1?)(最高)*/
#define configKERNEL_INTERRUPT_PRIORITY 255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY不能设置为零!!!! */
/*相当于0xb0或优先级11*/
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /*equivalent to 0xb0, or priority 11. */
/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15. This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest
NVIC value of 255. */
/*这是根据ST库使用的值,该库允许16优先级值,0到15。这必须符合配置内核中断优先级设置。
这里15对应于最低值NVIC值为255*/
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
#define vPortSVCHandler SVC_Handler
#endif /* FREERTOS_CONFIG_H */
1、configUSE_PREEMPTION
为1时RTOS使用抢占式调度器,为0时RTOS使用协作式调度器(时间片)。
在多任务管理机制上,操作系统可以分为抢占式和协作式两种。协作式操作系统是任务主动释放CPU后,切换到下一个任务。任务切换的时机完全取决于正在运行的任务。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configUSE_PREEMPTION 12、configUSE_IDLE_HOOK
设置为1使用空闲钩子(Idle Hook类似于回调函数),0忽略空闲钩子。
当RTOS调度器开始工作后,为了保证至少有一个任务在运行,空闲任务被自动创建,占用最低优先级(0优先级)。对于已经删除的RTOS任务,空闲任务可以释放分配给它们的堆栈内存。因此,在应用中应该注意,使用vTaskDelete()函数时要确保空闲任务获得一定的处理器时间。除此之外,空闲任务没有其它特殊功能,因此可以任意的剥夺空闲任务的处理器时间。
应用程序也可能和空闲任务共享同个优先级。
空闲任务钩子是一个函数,这个函数由用户来实现,FreeRTOS规定了函数的名字和参数,这个函数在每个空闲任务周期都会被调用。要创建一个空闲钩子:
1. 设置FreeRTOSConfig.h 文件中的configUSE_IDLE_HOOK为 1;
2.定义一个函数,函数名和参数如下所示:
void vApplicationIdleHook(void );
该钩子函数不可以调用会引起空闲任务阻塞的API函数(例如:vTaskDelay()、带有阻塞时间函数),在钩子函数内部可以使用协程。
使用空闲钩子函数设置CPU进入省电模式是很常见的。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configUSE_IDLE_HOOK 03、 configUSE_TICK_HOOK
设置为1使用时间片钩子(Tick Hook),0忽略时间片钩子。
时间片中断可以周期性的调用一个被称为钩子函数(回调函数)的应用程序。时间片钩子函数可以很方便的实现一个定时器功能。
只有在FreeRTOSConfig.h中的configUSE_TICK_HOOK设置成1时才可以使用时间片钩子。一旦此值设置成1,就要定义钩子函数,函数名和参数如下所示:
void vApplicationTickHook( void );
vApplicationTickHook()函数在中断服务程序中执行,因此这个函数必须非常短小,不能大量使用堆栈,不能调用以”FromISR" 或 "FROM_ISR”结尾的API函数。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configUSE_TICK_HOOK 04、 configCPU_CLOCK_HZ
写入实际的CPU内核时钟频率,也就是CPU指令执行频率,通常称为F c c l k F_{cclk}F
cclk
配置此值是为了正确的配置系统节拍中断周期。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 )stm32f103芯片的晶振为72MHZ。
5、 configTICK_RATE_HZ
RTOS 系统自己的节拍中断的频率。即一秒中断的次数,每次中断RTOS都会进行任务调度。
系统节拍中断用来测量时间,因此,越高的测量频率意味着可测到越高的分辨率时间。但是,高的系统节拍中断频率也意味着RTOS内核占用更多的CPU时间,因此会降低效率。RTOS演示例程都是使用系统节拍中断频率为1000HZ,这是为了测试RTOS内核,比实际使用的要高。(实际使用时不用这么高的系统节拍中断频率)
多个任务可以共享一个优先级,RTOS调度器为相同优先级的任务分享CPU时间,在每一个RTOS 系统节拍中断到来时进行任务切换。高的系统节拍中断频率会降低分配给每一个任务的“时间片”持续时间。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) ---> 表示滴答中断1毫秒中断1次。
#define configTICK_RATE_HZ ( ( TickType_t ) 100 ) ---> 表示滴答中断0.1毫秒中断1次。
6、configMAX_PRIORITIES
配置应用程序可用的优先级数目。任何数量的任务都可以共享一个优先级,使用协程可以单独的给与它们优先权。见configMAX_CO_ROUTINE_PRIORITIES。
在RTOS内核中,每个有效优先级都会消耗一定量的RAM,因此这个值不要超过你的应用实际需要的优先级数目。
每一个任务都会被分配一个优先级,优先级值从0~ (configMAX_PRIORITIES - 1)之间。***低优先级数表示低优先级任务。***空闲任务的优先级为0(tskIDLE_PRIORITY),因此它是最低优先级任务。
FreeRTOS调度器将确保处于就绪状态(Ready)或运行状态(Running)的高优先级任务比同样处于就绪状态的低优先级任务优先获取处理器时间。换句话说,处于运行状态的任务永远是高优先级任务。
处于就绪状态的相同优先级任务使用时间片调度机制共享处理器时间。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configMAX_PRIORITIES ( 5 )警告:
如果 #define configMAX_PRIORITIES ( 5 )
则程序中给任务分配优先级时,可用的优先级为:(0~configMAX_PRIORITIES-1)。
即以下优先级:0,1,2,3,4 共计5个优先级可以分配给任务,作为任务的优先级。
如果configMAX_PRIORITIES优先级或configMAX_PRIORITIES以上的优先级分配给任务(例如:优先级5,优先级6,...,优先级20),则会造成程序崩溃。
7、 configMINIMAL_STACK_SIZE
定义空闲任务使用的堆栈大小。通常此值不应小于对应处理器演示例程文件FreeRTOSConfig.h中定义的数值。
就像xTaskCreate()和xTaskCreateStatic()函数的堆栈大小参数一样,堆栈大小不是以字节为单位而是以字为单位的,比如在32位架构下,栈大小为100表示栈内存占用400字节的空间。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )8、configTOTAL_HEAP_SIZE
FreeRTOS堆中可用的RAM总量。该值仅在configSUPPORT_DYNAMIC_ALLOCATION设置为 1 且应用程序使用FreeRTOS源代码下载中提供的示例内存分配方案之一时该定义才有效。 有关详细信息,请参阅内存配置部分。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) )警告:
FreeRTOS编程模式,动态创建任务时分配的栈区,都是从configTOTAL_HEAP_SIZE占用的空间给任务分配栈空间。
9、configMAX_TASK_NAME_LEN
调用任务函数时,需要设置描述任务信息的字符串,这个宏用来定义该字符串的最大长度。这里定义的长度包括字符串结束符'\0'。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configMAX_TASK_NAME_LEN ( 16 )10、configUSE_TRACE_FACILITY
设置成1表示启动可视化跟踪调试,会激活一些附加的结构体成员和函数。
该配置通常在调试时才会使用,在真正发布程序时必须将其关闭,因为其对于FreeRTOS的性能是有影响的。如下图是使用Percepio Tracealyzer分析的FreeRTOS的运行情况图:
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configUSE_TRACE_FACILITY 011、configUSE_16_BIT_TICKS
时间是以“ticks”来衡量的 - 这是自RTOS内核启动以来tick事件中断已执行的次数。 tick计数保存在TickType_t类型的变量中。
将configUSE_16_BIT_TICKS定义为 1 会使TickType_t为无符号的16位类型定义(typedef’ed)。 将configUSE_16_BIT_TICKS定义为 0 会使TickType_t为无符号32位类型定义(typedef’ed)。
使用16位类型将大大提高8位和16位架构的性能,但将最大可指定时间限制为65535个“ticks”。 因此,假设节拍频率为250Hz,当使用16位计数器时,任务可以延迟或阻塞的最大时间为262秒,而使用32位计数器时为17179869秒。
例如,在STM32F10X中,FreeRTOS Kernel V10.2.1
#define configUSE_16_BIT_TICKS 012、configIDLE_SHOULD_YIELD
设置configIDLE_SHOULD_YIELD为0将阻止空闲任务为用户任务让出CPU,直到空闲任务的时间片结束。这确保所有处在空闲优先级的任务分配到相同多的处理器时间,但是,这是以分配给空闲任务更高比例的处理器时间为代价的。
通过时间片共享同一个优先级的多个任务,如果共享的优先级大于空闲优先级,并假设没有更高优先级任务,这些任务应该获得相同的处理器时间。
但如果共享空闲优先级时,情况会稍微有些不同。当configIDLE_SHOULD_YIELD为1时,其它共享空闲优先级的用户任务就绪时,空闲任务立刻让出CPU,用户任务运行,这样确保了能最快响应用户任务。处于这种模式下也会有不良效果(取决于你的程序需要),描述如下:
图中描述了四个处于空闲优先级的任务,任务A、B和C是用户任务,任务I是空闲任务。上下文切换周期性的发生在T0、T1…T6时刻。当用户任务运行时,空闲任务立刻让出CPU,但是,空闲任务已经消耗了当前时间片中的一定时间。这样的结果就是空闲任务I和用户任务A共享一个时间片。用户任务B和用户任务C因此获得了比用户任务A更多的处理器时间。
可以通过下面方法避免:
(1)、如果合适的话,将处于空闲优先级的各单独的任务放置到空闲钩子函数中;
(2)、创建的用户任务优先级大于空闲优先级;
(3)、设置IDLE_SHOULD_YIELD为0;
13、configKERNEL_INTERRUPT_PRIORITY
configKERNEL_INTERRUPT_PRIORITY:设置FreeRTOS内核本身使用的中断优先级(PendSV中断和SysTick中断),因为FreeRTOS内核中断(PendSV中断和SysTick中断)不允许抢占用户使用的中断,因此这个宏一般定义为硬件最低优先级。对于STM32F103而言,硬件最低优先级为15,因此configKERNEL_INTERRUPT_PRIORITY=255。
/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255
(lowest) to 0 (1?) (highest). */
/*这是Cortex-M3 NVIC的原始值。值可以是255(最低)到0(1?)(最高)*/
#define configKERNEL_INTERRUPT_PRIORITY 255这个255和中断优先级=15怎么联系起来 呢?
STM32F1xx和F4xx只使用了这个8位中的高四位[7:4]作为中断优先级,低四位无任何意义一般填充1。因此255=1111 1111B,高4位的1111B=0x0F=15。
总结:宏configKERNEL_INTERRUPT_PRIORITY设置FreeRTOS内核PendSV中断和SysTick中断的中断优先级。
14、configMAX_SYSCALL_INTERRUPT_PRIORITY
configMAX_SYSCALL_INTERRUPT_PRIORITY:用来设置可以在中断服务程序中调用中断安全的FreeRTOS API函数的最高中断优先级。优先级高于这个值的硬件中断,不受FreeRTOS管控,FreeRTOS无权禁止这些硬件中断,因此也不能在这些硬件中断中调用任何FreeRTOS API函数,否则系统会有崩溃的风险(这些中断中,进入临界区函数因优先级原因失效了)。
(1)、优先级小于等于这个宏所代表的优先级时,程序可以在中断服务程序中安全的调用FreeRTOS API函数;
(2)、优先级大于这个宏所代表的优先级,表示FreeRTOS无法禁止这个中断,在这个中断服务程序中绝不可以调用任何API函数。
对于STM32F103,FreeRTOS默认configMAX_SYSCALL_INTERRUPT_PRIORITY=191,即:可受FreeRTOS管控的中断优先级为11,也就是说只要优先级数值大于11的硬件中断(必须要小于15),都受FreeRTOS管控。即:优先级11,12,13,14都受FreeRTOS管控。
对于STM32F103,FreeRTOS默认configMAX_SYSCALL_INTERRUPT_PRIORITY=191,即:可受FreeRTOS管控的中断优先级为11,也就是说只要优先级数值小于11的硬件中断都不受FreeRTOS管控。即:优先级0,1,2,3,4,5,6,7,8,9,10都不受FreeRTOS管控。
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY不能设置为零!!!! */
/*相当于0xb0或优先级11*/
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /*equivalent to 0xb0, or priority 11. */
这个191和中断优先级=11怎么联系起来 呢?
STM32F1xx和F4xx只使用了这个8位中的高四位[7:4]作为中断优先级,低四位无任何意义一般填充1。因此191=1011 1111B = 0xBF,高4位的1011B=0x0B=11。
因此,如果将此宏设置为优先级11,那么在中断中调用了FreeRTOS API的中断,其优先级小于11,比如10,则系统会有崩溃的风险;如果使能了configASSERT宏,也会触发断言失败。
这里以STM32F1为例,有 16 个优先级, 0 为最高优先级, 15 为最低优先级,配置和结果如下:
configMAX_SYSCALL_INTERRUPT_PRIORITY=95,中断优先级=5

1、就是说,如果配置configMAX_SYSCALL_INTERRUPT_PRIORITY = 95(0x5F也即优先级5),那么,中断优先级为0~4 的中断向量不受FreeRTOS控制,用户不能在中断优先级为0~4 的中断向量中调用以FromISR结尾的API函数,会造成系统崩溃。
2、就是说,如果配置configMAX_SYSCALL_INTERRUPT_PRIORITY = 95(0x5F也即优先级5),那么,中断优先级为5~15 的中断向量可以接受FreeRTOS控制,用户可以在中断优先级为5~15 的中断向量中调用以FromISR结尾的API函数。
3、如果用户在任务中调用关中断API函数taskENTER_CRITICAL(); 如果中断0~4 中断触发,用户关中断起不到任何作用,程序仍然会跳转到中断0~4 的中断服务程序中执行。因此中断0~4的中断被称之为不可屏蔽中断。
因此设置宏configMAX_SYSCALL_INTERRUPT_PRIORITY一定要小心。
4、如果用户在任务中调用关中断API函数taskENTER_CRITICAL(); 如果中断5~15 的中断触发,用户关中断起作用,程序不会跳转到中断5~15 的中断服务程序中,仍然会执行任务中的程序,只有当任务执行开中断API函数taskEXIT_CRITICAL();,系统才会去响应中断5~15 的中断,跳转到中断5~15 的中断服务程序中去执行。
FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS ()和 portDISABLE_INTERRUPTS()。这两个函数其实是宏定义,在 portmacro.h 中有定义,如下:
#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to lower the BASEPRI value. */
msr basepri, ulBASEPRI
}
}
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical section. */
msr basepri, ulNewBASEPRI
dsb
isb
}
}函数 vPortSetBASEPRI()是向寄存器 BASEPRI 写入一个值,此值作为参数 ulBASEPRI 传递进来,portENABLE_INTERRUPTS()是开中断,它传递了个 0 给 vPortSetBASEPRI(),根据我们前面讲解 BASEPRI 寄存器可知,结果就是开中断。
函 数 vPortRaiseBASEPRI() 是 向 寄 存 器 BASEPRI 写 入 宏configMAX_SYSCALL_INTERRUPT_PRIORITY , 那 么 优 先 级 低 于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断就会被屏蔽。
临界区具体宏定义参见如下链接:FreeRTOS之03-中断配置与临界段(ING) (copyfuture.com)
警告: 宏configMAX_SYSCALL_INTERRUPT_PRIORITY的设置范围为1~15,千万不能将宏configMAX_SYSCALL_INTERRUPT_PRIORITY设置为0。
总结:宏configMAX_SYSCALL_INTERRUPT_PRIORITY用于设置临界区中断的中断优先级。
15、configLIBRARY_KERNEL_INTERRUPT_PRIORITY
FreeRTOS内核调度需要pendSV中断,这个硬件中断的优先级一般设置到最低(不允许抢占用户使用的中断)。宏configLIBRARY_KERNEL_INTERRUPT_PRIORITY=15就是设置这个中断(pendSV中断)的优先级。
/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15. This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest
NVIC value of 255. */
/*这是根据ST库使用的值,该库允许16优先级值,0到15。这必须符合配置内核中断优先级设置。
这里15对应于最低值NVIC值为255*/
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15