目录
一、基础概念
1、FreeRTOS
2、单片机编程的系统概念
a、裸机系统,包括轮询系统(不包括中断)和前后台系统(中断为前台,轮询为后台)
b、多任务系统
3、FreeRTOS编程风格
a、数据类型
b、变量名的定义
c、函数名
d、宏定义
二、Cubemx创建工程
1、创建任务
2、创建队列
3、信号量
4、互斥量
5、中断
三、FreeRTOS基础知识
1、任务创建流程
a、任务的概念
b、任务的栈
c、任务的函数主体
d、任务控制块
e、任务创建函数(与任务函数主体区分,任务函数主体是任务创建函数的一个参数)
f、实现就绪列表
g、实现调度器
2、FreeRTOS任务执行原则
a、合作式任务调度
b、抢占式任务调度
3、FreeRTOS任务的状态
a、freeRTOS任务的状态有四种:运行、就绪、阻塞、挂起
b、任务状态的转换
c、俩个任务延时函数的区别
4、消息队列
a、基本概念
b、队列的操作编辑
5、信号量
a、信号量概念
b、信号量分类
c、二值信号量的基本操作编辑
d、计数信号量的基本操作
6、互斥量
a、互斥量的理解
b、互斥量的基本操作
7、事件组
(只看了概念)
8、任务通知
9、软件计时器
10、中断
RTOS是一类操作系统,µC/OS,FreeRTOS,RTX,RT-Thread 等这些都是RTOS 类的操作系统
FreeRTOS 是众多RTOS 类操作系统中的一种,FreeRTOS 十分的小巧,可以在资源有限的
微控制器中运行,FreeRTOS 也不仅仅局限于在微控制器中使用。就单从文件数量上来看FreeRTOS 要比µC/OS 少得多。
RTOS是实时操作系统,例如汽车的操控:必须要在规定时间内完成响应。我们日常使用的Windows、IOS、Android等都是非实时操作系统,这些系统对任务响应时间没有严格要求,例如网页打开时可能会很久或者直接闪退等。
RTOS的最大特征就是“实时性”。所有实时操作系统中都要包含一个实时任务调度器,这个任务调度器与其它操作系统的最大不同是强调严格按照优先级来分配CPU时间
FreeRTOS 操作系统是一个功能强大的RTOS 操作系统,并且能够根据需求进行功能裁剪,
以满足各种环境的要求、高效、开源、免费
在顺序执行后台程序的时候,如果有中断来临,那么中断会打断后台程序的正常执行
流,转而去执行中断服务程序,在中断服务程序里面标记事件,如果事件要处理的事情很
简短,则可在中断服务程序里面处理,如果事件要处理的事情比较多,则返回到后台程序
里面处理。虽然事件的响应和处理是分开了,但是事件的处理还是在后台里面顺序执行的,
但相比轮询系统,前后台系统确保了事件不会丢失,再加上中断具有可嵌套的功能,这可
以大大的提高程序的实时响应能力。在大多数的中小型项目中,前后台系统运用的好,堪
称有操作系统的效果
相比前后台系统,多任务系统的事件响应也是在中断中完成的,但是事件的处理是在任务中完成的。在多任务系统中, 任务跟中断一样,也具有优先级,优先级高的任务会被优先执行。当一个紧急的事件在中断被标记之后,如果事件对应的任务的优先级足够高,就会立马得到响应。相比前后台系统,多任务系统的实时性又被提高了。
在裸机系统中,程序的主体是 CPU 按照顺序执行的。而在多任务系统中, 任务的执行
是由系统调度的。
在 FreeRTOS 中, 使用的数据类型虽然都是标准 C 里面的数据类型,但是针对不同的处理器,对标准 C 的数据类型又进行了重定义,给它们取了一个新的名字, 比如 char 重新定义了一个名字 portCHAR, 这里面的 port 表示接口的意思,就是 FreeRTOS 要移植到这些处理器上需要这些接口文件来把它们连接在一起。但是用户在写程序的时候并非一定要遵循 FreeRTOS 的风格, 我们还是可以直接用 C 语言的标准类型。在 FreeRTOS 中, int 型从不使用, 只使用 short 和 long型。 在 Cortex-M 内核的 MCU 中, short 为 16 位, long 为 32位。
FreeRTOS 中详细的数据类型重定义在 portmacro.h 这个头文件中实现
在 FreeRTOS 中,定义变量的时候往往会把变量的类型当作前缀加在变量上, 这样的好处是让用户一看到这个变量就知道该变量的类型。 比如 char 型变量的前缀是 c, short 型变量的前缀是 s, long 型变量的前缀是 l, portBASE_TYPE 类型变量的前缀是 x。 还有其他的数据类型,比如数据结构,任务句柄, 队列句柄等定义的变量名的前缀也是 x。如果一个变量是无符号型的那么会有一个前缀 u, 如果是一个指针变量则会有一个前缀 p。因此,当我们定义一个无符号的 char 型变量的时候会加一个 uc 前缀, 当定义一个char 型的指针变量的时候会有一个 pc 前缀
函数名包含了函数返回值的类型、 函数所在的文件名和函数的功能,如果是私有的函数则会加一个 prv(private) 的前缀。特别的, 在函数名中加入了函数所在的文件名, 这大大的帮助了用户提高寻找函数定义的效率和了解函数作用的目的, 具体的举例如下:
1. vTaskPrioritySet()函数的返回值为 void 型, 在task.c 这个文件中定义。
2. xQueueReceive()函数的返回值为 portBASE_TYPE 型, 在 queue.c 这个文件中定义。
3. vSemaphoreCreateBinary()函数的返回值为 void 型, 在 semphr.h 这个文件中定义。
宏均是由大写字母表示,并配有小写字母的前缀, 前缀用于表示该宏在哪个头文件定
义
Queue Size是队列长度
Item Size是队列每个数据的大小
任务是一个独立的函数,函数主体无限循环且不能返回。
在裸机系统中, 系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务。
在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组, 也可以是动态分配的一段内存空间,但它们都存在于 RAM 中。在多任务系统中,有多少个任务就需要定义多少个任务栈。
任务栈其实就是一个预先定义好的全局数据,数据类型为StackType_t,大小由 TASK1_STACK_SIZE 这个宏来定义, 默认为 128,单位为字,即 512字节,这也是 FreeRTOS 推荐的最小的任务栈。
注意:在 FreeRTOS 中,凡是涉及到数据类型的地方, FreeRTOS 都会将标准的 C 数据类型用 typedef 重新取一个类型名。这些经过重定义的数据类型放在 portmacro.h中
任务的栈, 任务的函数实体, 任务的控制块最终需要联系起来才能由系统进行统一调度。那么这个联系的工作就由任务创建函数 xTaskCreateStatic()来实现,该函数在 task.c(task.c 第一次使用需要自行在文件夹 freertos 中新建并添加到工程的 freertos/source 组)中定义, 在 task.h 中声明, 所有跟任务相关的函数都在这个文件定义。
任务创建好之后,我们需要把任务添加到就绪列表里面, 表示任务已经就绪,系统随
时可以调度。 就绪列表在 task.c 中定义
调度器是操作系统的核心,其主要功能就是实现任务的切换,即从就绪列表里面找到优先级最高的任务,然后去执行该任务。从代码上来看,调度器无非也就是由几个全局变量和一些可以实现任务切换的函数组成,全部都在 task.c 文件中实现。
默认情况下:使用时间片抢占式任务调度
但是还有一种合作式任务调度,但是不太用了
注意:永远优先执行优先级最高的任务
注意:任务创建后,默认是最高优先级的先执行,如果俩个任务同优先级,后创建的任务先执行
vTaskDelay()让函数阻塞固定的时间(n Tick的时间相同)
vTaskDelayUntil()让函数周期性的执行,给函数一个周期,在这个周期里函数只执行一次,剩下的时间就让函数阻塞(t1、2、3....之间的间隔相同)
队列是为了任务与任务、任务与中断之间的通信而准备的,可以在任务与任务、任务与中 断之间传递消息,队列中可以存储有限的、大小固定的数据项目。任务与任务、任务与中断之 间要交流的数据保存在队列中,叫做队列项目。
队列所能保存的最大数据项目数量叫做队列的 长度,创建队列的时候会指定数据项目的大小和队列的长度。由于队列用来传递消息的,所以 也称为消息队列。FreeRTOS 中的信号量的也是依据队列实现的!所以有必要深入的了解 FreeRTOS 的队列。
写队列和读队列都是采用复制的方式,队列的操作都是采用先进先出的的方式(FIFO)
写的队列操作分为在任务中使用和在中断中使用
在中断中使用
读队列(被读取得队列中的数据会被去除,所以只能读一次,但是还有队列偷窥操作,可以多次读队列中的数据)
队列偷窥(多次读队列中的地址)
低优先级的任务执行后高优先级任务才能执行,但是中优先级会插低优先级任务的队,等于是插了高优先级任务的队,把高优先级任务和低优先级任务绑定起来,让中优先级的任务等低优先级的任务,不会插到高优先级任务的队。
(只看了概念)