提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
笔者在去年曾经开始过rtos的学习(虽然后面一直没更新),最近趁着有时间更新一下这部分内容,本节主要是对以前的文章进行优化和整理,已经看过的朋友可以跳过了。
一、什么是RTOS
1.1 引入
实时操作系统(Real Time Operating System,简称RTOS)是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应,并控制所有实时任务协调一致运行的操作系统。提供及时响应和高可靠性是其主要特点。这里我们借用韦东山老师的例子进行说明:一个妈妈需要同时要给孩子喂饭和给同事回信息,下面是二者的代码。
// 裸机(无rtos)
void main(){
while(1){
喂饭();
回消息();
}
}
// 有rtos
void main(){
creat_task(喂饭);
creat_task(回消息);
start_scheduler();
while(1){
sleep();
}
}
从这段代码中,可以明显看到对于不同的对象,前者妈妈在同一时间只能做一件事情(长时间一件事是而让另一个人等待),而后者妈妈将时间碎片化(可能是喂饭喂了一班转头去回一半消息),这样让两个人都感觉好像自己同时获得的“回应”。
这里要注意,RTOS 不是指某一个确定的系统,而是指一类系统。比如 UCOS,FreeRTOS,RTX,RT-Thread 等这些都是 RTOS 类操作系统。本次所学的FreeROTS 就是一个免费的 RTOS 类系统,他十分小巧可以在资源有限的MCU中运行当然它也可以被扩展到其他的控制器中。
1.2 和Linux操作系统的区别
Linux 和RTOS 都是操作系统,但它们的设计目标、应用场景和内部架构有很大的区别。总体而言,Linux 适合功能复杂、资源充足的系统,而RTOS 则更适合资源有限、需要高实时性的嵌入式系统。我们以所用的FreeRTOS 为例,以下是二者的区别:
二、Free RTOS的基本内容
FreeRTOS 是一个轻量级的实时操作系统(RTOS),广泛应用于嵌入式系统中,尤其是在资源有限的微控制器和小型嵌入式设备上。它由Richard Barry开发,并以开放源代码的方式提供,支持多种架构和平台。FreeRTOS 通常由以下基本组件构成:
- 任务(Task):独立的执行单元,每个任务有自己的堆栈和控制块。
- 调度器(Scheduler):管理任务的执行顺序和时间,支持优先级调度。
- 队列(Queue):用于任务间的数据传递,支持多生产者多消费者模型。
- 信号量(Semaphore):实现任务间的同步和互斥,防止资源竞争。
- 事件组(Event Group):允许任务等待多个事件的发生,提供位标志的方式进行同步。
- 软件定时器(Software Timer):实现基于时间的回调函数,方便定时任务的执行。
2.1 FreeRTOS的特点
FreeRTOS是一个可裁剪的小型RTOS系统,其特点包括:
- 轻量级和高效性:代码体积小,运行效率高,通常只占用4k~9k的字节空间,非常适合微控制器等资源有限的设备。
- 多平台支持:兼容多种处理器架构,如ARM Cortex-M系列、AVR、PIC等,具有良好的可移植性。
- 丰富的功能模块:
- 任务管理:支持多任务,允许创建优先级不同的任务。
- 调度器:提供抢占式和协作式调度机制,确保关键任务的实时性。
- 同步机制:提供信号量、互斥量、队列等,方便任务间通信与同步。
- 内存管理:支持多种内存分配策略,满足不同应用需求。
2.2 Free RTOS编程风格
- 数据类型
新定义的数据类型 | 对应C标准类型 | 备注 |
---|---|---|
portCHAR | char | |
portSHORT | short | 16bit |
portLONG | long | 32bit |
portTickType | unsigned (short) int | 用于定义系统时其计数器的值和阻塞时间的值,在头文件Free RTOSConfig.h头文件中的宏configUSE_16BIT_TICKS为1时,则为16bit,为0时,则为32bit |
portBASE_TYPE | long | 根据处理器的架构来决定多少位,如果是32/16/8bit的处理器则对应32/16/8bit的数据类型,一般用于定义函数的返回值或者布尔类型 |
- 变量前缀
例如char型变量的前缀是c,short型的变量前缀是s, long型变量的前缀是l, portBASE_TYPE类型的前缀是x,还有其他的数据类型例如数据结构、任务句柄、队列句柄等定义的变量名前缀也是x。如果是一个无符号型的前面还会有一个u的前缀,如果是一个指针型变量型则会有一个前缀p。 - 函数前缀
如果是一个私有的函数,则会加一个prv(private)的前缀。如果在函数前加上一个v的前缀,则表示该函数的返回值为void的类型;若函数前缀为x,则返回值为上文所提到的portBASE_TYPE类型。 - 宏
前缀 | 所在宏定义文件 |
---|---|
port | portable.h |
task | task.h |
pd | projdefsh |
config | FreeRTOSConfig.h |
err | projdefsh |
2.3 Free RTOS的配置文件
configASSERT(x):断言,类似 C 标准库中的 assert()函数,调试代码的时候可以检查传入的参数是否合理,FreeRTOS 内核中的关键点都会调用 configASSERT(x),当 x 为 0 的时候说明有错误发生。
configUSE_PREEMPTION:使能抢占式调度器,为 1 时使用抢占式调度器,为 0 时使用协程。如果使用抢占式调度器的话内核会在每个时钟节拍中断中进行任务切换,在任务结束后CPU不会等待低优先级任务,直接由高优先级占取,而使用协程的话会在如下地方进行任务切换:
- 一个任务调用了函数 taskYIELD( );
- 一个任务调用了可以使任务进入阻塞态的 API 函数;
- 应用程序明确定义了在中断中执行上下文切换。
configUSE_TIME_SLICING:使能时间片调度器,默认情况下为高电平,此时FreeRTOS 使用抢占式调度器,这意味着调度器永远都在执行已经就绪了的最高优先级任务,优先级相同的任务在时钟节拍中断中进行切换,当该宏为 0 的时候不会在时钟节拍中断中执行相同优先级任务的任务切换。值得一提的是某些运行FreeRTOS的硬件有以下两种方法:通用方法和特殊方法(硬件)。
configUSE_PORT_OPTIMISED_TASK_SELECTION: 是否启用特殊方法来执行下一个任务,为高电平时使能tickless低功耗模式;低电平时保持系统节拍中断一直进行 。与此同时,若该宏为1,则采用上述的通用方式,若该宏为0,则采用特殊方法。
configUSE_TICKLESS_IDLE:是否启动低功耗模式。置1:使能低功耗模式;置0:关闭低功耗模式。
configCPU_CLOCK_HZ (SystemCoreClock):配置CPU时钟频率。
configTICK_RATE_HZ (1000):配置系统节拍中断的频率,即一秒钟中断的次数,每次中断RTOS都会进行任务调动。此频率也是滴答定时器的中断频率,需要使用此宏来配置滴答定时器的中断。
configMAX_PRIORITIES (32):配置优先级数字越大优先级越高,数字越小优先级越低。
configMINIMAL_STACK_SIZE((unsigned short)130):设置空闲任务的最小任务堆栈大小,以字为单位,不是字节。比如在STM32 上设置为100的话,那么真正的堆栈大小就是 100*4=400 字节
configMAX_TASK_NAME_LEN:设置任务名最大长度。
configUSE_16_BIT_TICKS: 设置系统节拍计数器变量数据类型,系统节拍计数器变量类型为 TickType_t,当该宏为1的时候 TickType_t 就是 16 位的,当 configUSE_16_BIT_TICKS为 0 的话 TickType_t 就是 32 位。
configIDLE_SHOULD_YIELD :默认为高电平,此时空闲任务放弃CPU的使用权给其他同优先级的任务。
configUSE_QUEUE_SETS:默认为低电平,启动队列。
configUSE_TASK_NOTIFICATIONS:默认为高电平,开启任务通知功能。
configUSE_MUTEXES :默认为高电平,使用互斥信号。
configUSE_COUNTING_SEMAPHORES:设置为 1 的时候启用计数型信号量,相关的 API 函数会被编译。
configQUEUE_REGISTRY_SIZE:设置可以注册的队列和信号量的最大数量,在使用内核调试器查看信号量和队列的时候需要设置此宏,而且要先将消息队列和信号量进行注册,只有注册了的队列和信号量才会再内核 调试器中看到,如果不使用内核调试器的话此宏设置为 0 即可。
configSUPPORT_DYNAMIC_ALLOCATION:默认为高电平,此时在创建 FreeRTOS 的内核对象的时候所需要的 RAM 就会从 FreeRTOS 的堆中动态的获取内存,如果定义为 0 的话所需的 RAM 就需要用户自行提供,默认情况下宏configSUPPORT_DYNAMIC_ALLOCATION 为 1。
configTOTAL_HEAP_SIZE ((size_t)(20*1024)) :设置系统所有堆大小,如果使用了动态内存管理的话,FreeRTOS 在创建任务、信号量、队列等的时候 就 会 使 用 heap_x.c(x 为 1~5) 中 的 内 存 申 请 函 数 来 申 请 内 存 。 这 些 内 存 就 是 从 堆 ucHeap[configTOTAL_HEAP_SIZE]中申请的,堆的大小由 configTOTAL_HEAP_SIZE 来定义。
configUSE_IDLE_HOOK:默认为低电平,使用空闲任务钩子函数,用户需要实现空闲任务钩子函数,函数的原型为:void vApplicationIdleHook( void )。这个函数在每个空闲任务周期都会被调用,对已经删除的任务空闲任务可以释放给他们堆栈内存,因此需要保证空闲任务可以被CPU所执行,不可调用的任务会引起空闲任务阻塞的API函数。
configUSE_TICK_HOOK:默认为低电平,也是一个函数void vApplicationTickHook( void )。时间片中断可以周期调用,但必须十分短小,不可使用大量的堆栈,并且不能以调用"FromISR"或者"FROM_ISR"结尾的API函数。
configGENERATE_RUN_TIME_STATS: 默认为低电平,设置为 1 开启时间统计功能,相应的 API 函数会被编译,为 0 时关闭时间统计功能。如果宏 configGENERATE_RUN_TIME_STATS 为 1 的话还需要定义下表中的宏。
免责声明:本文参考了网上公开资料,仅用于学习交流,若有错误或侵权请联系笔者。