这个问题说简单也简单,说难也难,因为每个人掌握的基础不同,自然,从哪里开始学起也各有不同。
我认为,首先你要去了解 RTOS 相关的一些基础知识,然后再下载源码实践运行,跑起来!接下来真正入门的第一步,我觉得还得从“配置”文件开始,这里的配置,可以理解为大家说的“裁剪系统”及相关的一些配置。
比如 FreeRTOS 中“FreeRTOSConfig.h”配置文件:

当然,配置文件是你已经具备一定基础知识,上手源码第一步要掌握的内容。不仅仅是 FreeRTOS,其他μCOS、RT-Thread,甚至Linux都是从配置开启第一步的。
可能会有老铁说,使用STM32CubeMX配置FreeRTOS,就不用修改(配置)FreeRTOSConfig.h文件?
这么说吧,第三方工具图形化配置,也会牵涉到配置文件,而且,仅仅是基础的配置,你要做项目,还是会修改配置文件。
通过第三方配置工具,你不会真正掌握RTOS底层原理,后期做项目你会发现很困难。
FreeRTOS配置文件常见内容
本文结合流程的FreeRTOS给大家讲讲其中配置文件的内容。
FreeRTOS配置文件看起来有点多,但它都有分类,多了解一下,你会发现理解起来也不是很难。
现在官网做了一定的“汉化”,相比以前更适合国人学习了。
1通用配置
通用配置基本配置,就是需要我们定义的一些配置,也是比较重要的配置。
1.configUSE_PREEMPTION
调度模式配置
配置为0:合作式调度,即时间片轮流执行;
配置为1:抢占式调度,即优先级高的任务抢先执行;
由于我们要求实时响应,就配置为1,使用抢占式调度方式。否则就发挥不到实时操作系统的作用。
2.configCPU_CLOCK_HZ
CPU时钟,就是我们常说的主频。注意:单位是Hz。
如:STM32F407主频为168M:
#define configCPU_CLOCK_HZ (168000000)
3.configTICK_RATE_HZ
系统滴答,即系统每秒钟滴答的次数,可以说是系统的心跳,但需要和主频区分开来。系统滴答的值要根据CPU主频来看,一般主频越高,取值相对越大,一般在100至1000之间。
简单举例:系统滴答决定vTaskDelay。
比如:
#define configTICK_RATE_HZ (1000)
则:
vTaskDelay(1000),表示延时1S。
4.configMAX_PRIORITIES
系统最大优先级值
我们创建任务是,配置的优先级值不能超过这个最大值。
xTaskCreate(vAppTask1, "Task1", TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, NULL);
提示:
a.系统优先级和中断优先级原理类似,高优先级的会抢在低优先级的前面,但需要区分系统和中断优先级的应用场景。
b.FreeRTOS中优先级数值越大,优先级越高。而UCOS则相反。
5.configMINIMAL_STACK_SIZE
最小堆栈值
在系统中,一般用于空闲、定时等一些系统任务中,当然,我们有些地方也可以使用这个定义的堆栈值。
注意数值的单位,一般在ARM中为4个字节。
6.configTOTAL_HEAP_SIZE
系统总共堆(栈)大小
我们需要根据需要使用的情况定义这个值。不能定义太小,太小内存容易溢出;也不能定义太大,有些芯片RAM本身就不大(有些就只有几K),如果太大我们就没法定义太多全局变量,或分配其他堆栈空间。
7.configMAX_TASK_NAME_LEN
任务名称最大长度
也就是创建任务定义任务名称的字符串长度
xTaskCreate(vAppTask1, "Task1", TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, NULL);
提示:结束符 '\0'也包含在内。
8.configUSE_16_BIT_TICKS
是否使用16位滴答计数值
配置为0:则使用32位的滴答计数值,一般在32位处理器中都是配置为0;
配置为1:则使用16位的滴答计数值,一般8位或者16位处理器中配置为1。
9.configIDLE_SHOULD_YIELD
是否让空闲任务“放弃”抢占
也就是说在执行与空闲任务相同优先级的任务过程中,空闲任务是否具有抢占的机会。
配置为0:不放弃抢占;
配置为1:放弃抢占;
10.configUSE_MUTEXES
是否使用互斥锁
配置为0:不使用
配置为1:使用
提示:互斥锁也叫互斥信号量,也就是说对资源“加锁”。它的作用是实现多任务间共享资源的独占式处理。简单的说,就是某个资源在某一时刻只允许一个任务处理,处理完之后才允许其他任务处理该资源。
比如:A任务优先级高,B任务优先级低;AB任务都会使用一个串口发送指令数据,(即每次必须发送完成,不能发送到一半就被打断)。
当B任务正在发送数据时,A任务处于就绪状态(要打断B任务)。那么B任务就需要使用互斥锁占有该串口(加锁,占有该资源),等发送完指令,就释放该串口(开锁,释放该资源)。一旦释放了该资源,A任务就可以使用该串口(资源)了。
11.configUSE_RECURSIVE_MUTEXES
是否使用递归互斥锁
配置为0:不使用
配置为1:使用
13.configQUEUE_REGISTRY_SIZE(*)
可添加(或登记)队列名的数量
这个配置信息不好翻译,它主要结合vQueueAddToRegistry与vQueueUnregisterQueue这两个函数使用。
直接上函数接口:
void vQueueAddToRegistry(QueueHandle_t xQueue, const char *pcQueueName);
void vQueueUnregisterQueue(QueueHandle_t xQueue);
提示:很多初学者理解为“可创建队列的最大数”,这个配置参数与其完全不一样的概念。
14.configUSE_QUEUE_SETS(*)
是否使用消息队列“SET”功能
配置为0:不使用
配置为1:使用
这个配置信息也相对较难理解。
提示:网上很多解释为:使能/禁止消息队列。这个理解太笼统,与消息队列配置相关的信息比较多,我个人觉得不正确。
15.configUSE_TIME_SLICING
是否使用时间片进行调度
这个参数结合上面第1各配置参数configUSE_PREEMPTION一起使用。
这个配置参数是在后面新版本增加的,好像在V7版本之前是没有这个配置参数。所以,在FreeRTOSConfig.h配置文件中默认是没有的,而是定义在FreeRTOS.h中。
#ifndef configUSE_TIME_SLICING
#define configUSE_TIME_SLICING 1
#endif
2HOOK配置
HOOK也叫钩子,初学者可以暂时只做了解。
1.configUSE_IDLE_HOOK是否定义IDLE空闲任务HOOK函数
配置为0:不定义配置为1:定义
configUSE_IDLE_HOOK是系统设计之初就有的,必须在“FreeRTOSConfig.h”中宏定义。不像有些宏定义可以不在“FreeRTOSConfig.h”中定义,因为它们在“FreeRTOS.h”有判断是否定义了,如果没有定义,会有一个默认的定义。
比如:上一篇文章说的“configUSE_MUTEXES”,可以不在“FreeRTOSConfig.h”中定义,而在“FreeRTOS.h”中可以看到如下一段代码:
#ifndef configUSE_MUTEXES
#define configUSE_MUTEXES 0
#endif
也就是说,如果没有定义,它会默认给你定义。回来说configUSE_IDLE_HOOK,在task.c文件中,有如下一段代码:
#if (configUSE_IDLE_HOOK == 1)
{
extern void vApplicationIdleHook(void);
vApplicationIdleHook();
}
#endif
意思是说:如果你配置configUSE_IDLE_HOOK为1,那么你就必须要实现“vApplicationIdleHook()”这个函数,否则编译会出错。初学者默认不定义该函数。2.configUSE_TICK_HOOK是否定义TICK滴答HOOK函数
配置为0:不定义配置为1:定义
在task.c文件中的xTaskIncrementTick函数下可以看见如下代码:
#if (configUSE_TICK_HOOK == 1)
{
if(uxPendedTicks == (UBaseType_t) 0U)
{
vApplicationTickHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
提示:xTaskIncrementTick函数是在PendSV_Handler中断函数中被调用的。因此,vApplicationTickHook()函数执行的时间必须很短才行。
3.configCHECK_FOR_STACK_OVERFLOW是否定义栈溢出HOOK函数
配置为0:不定义配置为1:定义
这个配置比较关键和重要,特别对于复杂的系统设计,代码量比较大那种工程,使用该功能,可以帮你分析是否有内存越界的情况。
4.configUSE_MALLOC_FAILED_HOOK是否定义内存分配失败HOOK函数
配置为0:不定义配置为1:定义
我们创建任务、信号量、队列等都需要耗费系统堆栈,如果我们对系统总共分配堆栈不够多,在创建多个任务或队列时容易分配失败,这个时候就起到一个提示作用。
5.configUSE_DAEMON_TASK_STARTUP_HOOK是否定义守护进程HOOK函数
配置为0:不定义配置为1:定义
通过分析软件源代码可以发现,这个HOOK函数是在TIMER任务下面实现的,所以需要配置configUSE_TIMERS为1。
3TIMERS配置
TIMER即定时器,在RTOS中的TIMER属于软件定时。FreeRTOS的定时器精度不高,会随着定时的增加而改变,特别是TIMER任务优先级较低,高优先级占用资源的情况下。
若要使用高精度的定时,还是最后使用硬件的定时器(现在处理器一般都有多个硬件TIMER)。
1.configUSE_TIMERS
是否使用软件定时器
配置为0:不使用
配置为1:使用
其他许多相关的功能都需要结合该配置才能使用,使用时需要注意是否关联。
2.configTIMER_TASK_PRIORITY
软件定时器任务优先级
软件定时器其实也是需要创建一个任务,创建方式和我们常规的一样,只是它是有系统内核完成,不用我们自己写创建任务代码。
这里的这个优先级就是定时器任务的优先级。
3.configTIMER_QUEUE_LENGTH
软件定时器命令队列长度
4.configTIMER_TASK_STACK_DEPTH
分配给软件定时器的堆栈空间
4CO_ROUTINES配置
CO_ROUTINES这个不好翻译,网上都叫协同程序,或者合作程序,理解为协同一起使用的程序,后期结合应用讲述。
1.configUSE_CO_ROUTINES是否使用CO_ROUTINES
配置为0:不使用配置为1:使用
2.configMAX_CO_ROUTINE_PRIORITIESCO_ROUTINE优先级
5MEMORY配置
内存分配相关的配置,这里的配置与heap_x.c有关,后面会再次进行讲述。
1.configSUPPORT_STATIC_ALLOCATION
是否支持静态分配
配置为0:不支持
配置为1:支持
2.configSUPPORT_DYNAMIC_ALLOCATION
是否支持动态分配
配置为0:不支持
配置为1:支持
3.configTOTAL_HEAP_SIZE
分配给系统的堆栈
创建任务,堆栈,静态、动态都分配的内存都来自这里。
4.configAPPLICATION_ALLOCATED_HEAP
APP使用哪里分配的堆
配置为0:使用系统分配的堆
配置为1:使用外部分配的堆
默认使用系统分配的堆,见下面定义:
#if(configAPPLICATION_ALLOCATED_HEAP == 1 )
extern uint8_t ucHeap[configTOTAL_HEAP_SIZE];
#else
static uint8_t ucHeap[configTOTAL_HEAP_SIZE];
#endif
6RUN_TIME_STATS配置
运行时信息统计配置
1.configGENERATE_RUN_TIME_STATS
是否生成统计信息
配置为0:否
配置为1:是
2.configUSE_TRACE_FACILITY
是否协助执行可视化和跟踪
配置为0:否
配置为1:是
这里会添加额外的结构体来实现。
3.configUSE_STATS_FORMATTING_FUNCTIONS
是否统计相关的功能
配置为0:否
配置为1:是
设置宏configUSE_TRACE_FACILITY和configUSE_STATS_FORMATTING_FUNCTIONS为1会编译vTaskList()和vTaskGetRunTimeStats()函数。如果将这两个宏任意一个设置为0,上述两个函数不会被编译。
7其他配置
这里简单综合讲述一下各项配置
1.configASSERT
断言配置
2.Interrupt相关
configKERNEL_INTERRUPT_PRIORITY:内核中断优先级
configMAX_SYSCALL_INTERRUPT_PRIORITY:系统调用最大的优先级
configMAX_API_CALL_INTERRUPT_PRIORITY:API调用的最大优先级
这一节与(Cortex)内核硬件中断有关。
3.INCLUDE配置
这里给大家分享了常见的一些配置内容,要深入理解并掌握,还是需要自己多动手修改代码验证才行。
关注公众号,加星标,回复1024获取学习资料,每天进步一点点。
声明:
本号原创、转载的文章、图片等版权归原作者所有,如有侵权,请联系删除。

