FreeRTOS内核函数
任务管理:
xTaskCreateStatic(); 静态创建任务
xTaskCreate(); 动态创建任务
vTaskDelete(); 删除任务
vTaskDelay(); 延时
vTaskDelayUntil(); 延时
vTaskStartScheduler(); 开启任务调度器
vTaskSuspend(); 挂起任务调度器
vTaskSuspendAll(); 挂起全部任务调度器
vTaskResume(); 恢复任务调度器
xTaskResumeAll(); 恢复全部任务调度器
xTaskResumeFromISR(); 恢复任务,中断中使用
taskYIELD(); 切换任务
taskENTER_CRITICAL(); 进入临界区
taskEXIT_CRITICAL(); 退出临界区
taskENTER_CRITICAL_FROM_ISR(); 进入临界区,中断中使用
taskEXIT_CRITICAL_FROM_ISR(); 退出临界区,中断中使用
消息队列:
xQueueCreate(); 动态创建消息队列
xQueueCreateStatic(); 静态创建消息队列
vQueueDelete(); 删除消息队列
xQueueSend(); 向队尾发送消息
xQueueSendToBack(); 向队尾发送消息,同上
xQueueSendFromISR(); 向队尾发送消息,中断中使用
xQueueSendToBackFromISR(); 向队尾发送消息,同上
xQueueSendToFront(); 向队头发送消息
xQueueSendToFrontFromISR(); 向队头发送消息,中断中使用
xQueueReceive(); 从队列中接收消息,并把接收的消息从队列中删除
xQueuePeek(); 从队列中接收消息,不把接收的消息从队列中删除,中断中使用
xQueueReceiveFromISR(); 从队列中接收消息,并把接收的消息从队列中删除
xQueuePeekFromISR(); 从队列中接收消息,不把接收的消息从队列中删除,中断中使用
xQueueGenericSend(); 通用消息队列发送函数
xQueueGenericSendFromISR(); 消息队列发送函数,中断中使用
xQueueGenericReceive(); 通用消息队列读取函数
信号量:
xSemaphoreCreateBinary(); 创建二值信号量
xSemaphoreCreateCounting() 创建计数信号量
vSemaphoreDelete(); 信号量删除函数
xSemaphoreGive(); 信号量释放函数
xSemaphoreGiveFromISR(); 信号量释放函数,中断中使用
xSemaphoreTake(); 信号量获取函数
xSemaphoreTakeFromISR(); 信号量获取函数,中断中使用
xSemaphoreCreateMutex(); 互斥量创建函数
vSemaphoreDelete(); 互斥量删除函数
xSemaphoreTake(); 互斥量获取函数
xSemaphoreGive(); 互斥量释放函数
xSemaphoreCreateRecursiveMutex(); 递归互斥量创建函数
xSemaphoreTakeRecursive(); 递归互斥量获取函数
xSemaphoreGiveRecursive(); 递归互斥量释放函数
事件:
xEventGroupCreate(); 事件创建函数
vEventGroupDelete(); 事件删除函数
xEventGroupSetBits(); 事件组置位函数
xEventGroupSetBitsFromISR(); 事件组置位函数
xEventGroupWaitBits(); 等待事件函数
xEventGroupClearBits(); 清除事件组指定的位
xEventGroupClearBitsFromISR(); 清除事件组指定的位
软件定时器:
xTimerCreate(); 软件定时器创建函数
xTimerStart(); 软件定时器启动函数
xTimerStartFromISR(); 软件定时器启动函数,中断中使用
xTimerStop(); 软件定时器停止函数
xTimerStopFromISR(); 软件定时器停止函数
xTaskGetTickCount(); 获取滴答定时器的计数值
1.裸机系统与多任务系统
1.1裸机系统
裸机系统通常分成轮询系统和前后台系统
1.1.1轮询系统
初始化好相关的硬件,让主程序在一个死循环里面不断循环,顺序地做各种事情。只适用于那些只需要顺序执行代码且不需要外部事件来驱动的就能完成的事情。
1 int main(void)
2 {
3 /* 硬件相关初始化 */
4 HardWareInit();
5
6 /* 无限循环 */
7 for (;;) {
8 /* 处理事情 1 */
9 DoSomething1();
10
11 /* 处理事情 2 */
12 DoSomething2();
13
14 /* 处理事情 3 */
15 DoSomething3();
16 }
17 }
1.1.2前后台系统
在轮询系统的基础上加入了中断。外部事件的响应在中断里面完成,事件的处理还是回到轮询系统中完成,中断在这里我们称为前台, main 函数里面的无限循环我们称为后台。
虽然事件的响应和处理是分开了,但是事件的处理还是在后台里面顺序执行的,但相比轮询系统,前后台系统确保了事件不会丢失,再加上中断具有可嵌套的功能,这可以大大的提高程序的实时响应能力。
1 int flag1 = 0;
2 int flag2 = 0;
3 int flag3 = 0;
4
5 int main(void)
6 {
7 /* 硬件相关初始化 */
8 HardWareInit();
9
10 /* 无限循环 */
11 for (;;) {
12 if (flag1) {
13 /* 处理事情 1 */
14 DoSomething1();
15 }
16
17 if (flag2) {
18 /* 处理事情 2 */
19 DoSomething2();
20 }
21
22 if (flag3) {
23 /* 处理事情 3 */
24 DoSomething3();
25 }
26 }
27 }
28
29 void ISR1(void)
30 {
31 /* 置位标志位 */
32 flag1 = 1;
33 /* 如果事件处理时间很短,则在中断里面处理
34 如果事件处理时间比较长,在回到后台处理 */
35 DoSomething1();
36 }
37
38 void ISR2(void)
39 {
40 /* 置位标志位 */
41 flag2 = 1;
42
43 /* 如果事件处理时间很短,则在中断里面处理
44 如果事件处理时间比较长,在回到后台处理 */
45 DoSomething2();
46 }
47
48 void ISR3(void)
49 {
50 /* 置位标志位 */
51 flag3 = 1;
52
53 /* 如果事件处理时间很短,则在中断里面处理
54 如果事件处理时间比较长,在回到后台处理 */
55 DoSomething3();
56 }
1.2多任务系统
相比前后台系统,多任务系统的事件响应也是在中断中完成的,但是事件的处理是在任务中完成的。在多任务系统中, 任务跟中断一样,也具有优先级,优先级高的任务会被优先执行。当一个紧急的事件在中断被标记之后,如果事件对应的任务的优先级足够高,就会立马得到响应。
在多任务系统中,根据程序的功能,我们把这个程序主体分割成一个个独立的,无限循环且不能返回的小程序,这个小程序我们称之为任务。每个任务都是独立的,互不干扰的,且具备自身的优先级,它由操作系统调度管理。
1 int flag1 = 0;
2 int flag2 = 0;
3 int flag3 = 0;
4
5 int main(void)
6 {
7 /* 硬件相关初始化 */
8 HardWareInit();
9
10 /* OS 初始化 */
11 RTOSInit();
12
13 /* OS 启动,开始多任务调度,不再返回 */
14 RTOSStart();
15 }
16
17 void ISR1(void)
18 {
19 /* 置位标志位 */
20 flag1 = 1;
21 }
22
23 void ISR2(void)
24 {
25 /* 置位标志位 */
26 flag2 = 2;
27 }
28
29 void ISR3(void)
30 {
31 /* 置位标志位 */
32 flag3 = 1;
33 }
34
35 void DoSomething1(void)
36 {
37 /* 无限循环,不能返回 */
38 for (;;) {
39 /* 任务实体 */
40 if (flag1) {
41
42 }
43 }
44 }
45
46 void DoSomething2(void)
47 {
48 /* 无限循环,不能返回 */
49 for (;;) {
50 /* 任务实体 */
51 if (flag2) {
52
53 }
54 }
55 }
56
57 void DoSomething3(void)
58 {
59 /* 无限循环,不能返回 */
60 for (;;) {
61 /* 任务实体 */
62 if (flag3) {
63
64 }
65 }
66 }
1.3轮询、前后台和多任务系统软件模型区别
2.任务的定义与任务切换的实现
2.1任务
在裸机系统中, 系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数我们称为任务。
在多任务系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组, 也可以是动态分配的一段内存空间,但它们都存在于 RAM 中。
2.2创建任务
2.2.1定义任务栈
在多任务系统中,有多少个任务就需要定义多少个任务栈。
2.2.2定义任务函数
任务是一个独立的、无限循环且不能返回的函数。
2.2.3定义任务控制块
在裸机系统中,程序的主体是 CPU 按照顺序执行的。而在多任务系统中, 任务的执行是由系统调度的。系统为了顺利的调度任务,为每个任务都额外定义了一个任务控制块,这个任务控制块就相当于任务的身份证,里面存有任务的所有信息,比如任务的栈指针,任务名称, 任务的形参等。有了这个任务控制块之后,以后系统对任务的全部操作都可以通过这个任务控制块来实现。
1 typedef struct tskTaskControlBlock
2 {
3 volatile StackType_t *pxTopOfStack; /* 栈顶 */ (1)
4
5 ListItem_t xStateListItem; /* 任务节点 */ (2)
6
7 StackType_t *pxStack; /* 任务栈起始地址 */ (3)
8 /* 任务名称,字符串形式 */(4)
9 char pcTaskName[ configMAX_TASK_NAME_LEN ];
10 } tskTCB;
11 typedef tskTCB TCB_t; (5)
1 /* 定义任务控制块 */
2 TCB_t Task1TCB;
3 TCB_t Task2TCB;
2.2.4实现任务创建函数
xTaskCreateStatic()
2.3实现就序列表
任务创建好之后,我们需要把任务添加到就绪列表里面, 表示任务已经就绪,系统随时可以调度。
就绪列表实际上就是一个 List_t 类型的数组,数组的大小由决定最 大 任 务 优 先 级 的 宏 configMAX_PRIORITIES 决 定 , configMAX_PRIORITIES 在FreeRTOSConfig.h 中默认定义为 5,最大支持 256 个优先级。 数组的下标对应了任务的优先级,同一优先级的任务统一插入到就绪列表的同一条链表中。
- 定义就序列表
- 就绪列表初始化
- 将任务插入就序列表
2.4实现调度器
调度器是操作系统的核心,主要功能就是实现任务的切换,即从就绪列表里面找到优先级最高的任务,然后去执行该任务。
2.4.1启动调度器
vTaskStartScheduler()
2.4.2任务切换
任务切换就是在就绪列表中寻找优先级最高的就绪任务,然后去执行该任务。
3.临界段的保护
临界段用一句话概括就是一段在执行的时候不能被中断的代码段。在 FreeRTOS 里面,这个临界段最常出现的就是对全局变量的操作。
那么什么情况下临界段会被打断?一个是系统调度,还有一个就是外部中断。在FreeRTOS,系统调度,最终也是产生 PendSV 中断,在 PendSV Handler 里面实现任务的切换,所以还是可以归结为中断。 既然这样, FreeRTOS 对临界段的保护最终还是回到对中断的开和关的控制。
在 FreeRTOS 中,对临界段的保护出现在两种场合,一种是在中断场合一种是在非中断场合。
1 /* 在中断场合,临界段可以嵌套 */
2 {
3 uint32_t ulReturn;
4 /* 进入临界段,临界段可以嵌套 */
5 ulReturn = taskENTER_CRITICAL_FROM_ISR();
6
7 /* 临界段代码 */
8
9 /* 退出临界段 */
10 taskEXIT_CRITICAL_FROM_ISR( ulReturn );
11 }
12
13 /* 在非中断场合,临界段不能嵌套 */
14 {
15 /* 进入临界段 */
16 taskENTER_CRITICAL();
17
18 /* 临界段代码 */
19
20 /* 退出临界段*/
21 taskEXIT_CRITICAL();
22 }
4.空闲任务与阻塞延时的实现
裸机延时一般是while循环实现软件延时,让 CPU 空等来达到延时的效果。
RTOS 中的延时叫阻塞延时,即任务需要延时的时候, 任务会放弃 CPU 的使用权, CPU 可以去干其它的事情,当任务延时时间到,重新获取 CPU 使用权, 任务继续运行,这样就充分地利用了 CPU 的资源,而不是干等着。
当任务需要延时,进入阻塞状态。如果没有其他任务,CPU会运行空闲任务。
阻塞延时:
任务调用该延时函数后, 任务会被剥离 CPU 使用权,然后进入阻塞状态,直到延时结束, 任务重新获取 CPU 使用权才可以继续运行。
5.消息队列
6.信号量
7.互斥量
8.事件
9.软件定时器
10.任务通知