目录
1. 裸机系统和多任务系统
2. 任务的定任务切换的实现
2.1 什么是任务?
2.2 调度器
2.3 临界段
3. 空闲任务和阻塞延迟
4. 时间片
1. 裸机系统和多任务系统
裸机系统:
裸机系统分为轮询系统和前后台系统;(51单片机就属于裸机系统)
轮询系统是在裸机编程的时候,先初始化好相关的硬件,然后让主程序在死循环里面不断的循环,顺序的做各种事情。(也就是我们写51单片机程序时,初始化好相关的外设,然后在主程序 int main() 中的 while(1) 不断的循环我们想要通过外设来实现的功能)
相比于轮询系统,前后台系统是在轮询系统的基础之上加入了中断。
前台:中断服务程序接收异步中断,来通知后台,后台收到中断请求后进行处理。
void XXX_ISR(void)
{
Clear interrupt;
Read data;
}
后台:应用程序通常是一个无限的循环,在循环中,通过调用相应的处理函数,完成相应的操作,这部分可以看做是后台行为。
void main(void)
{
init();
while(1)
{
Task(1);
Task(2);
Task(3);
Task(4);
……
}
}
多任务系统:
在多任务系统中,每个任务都是独立的,任务跟中断一样,也具有优先级,优先级高的任务会被优先执行。
void XXX_ISR(void)
{
Clear interrupt;
Read data;
}
void Task1()
{
while(1)
{
/*无限循环*/
do_xxxx();
}
}
void Task2()
{
while(1)
{
/*无限循环*/
do_xxxx();
}
}
多任务系统就是:首先后台会分布着多个任务,假设是 A B C D ,这个时候会通过调度器分配哪个任务开始运行,任务中也会存在优先级高的任务,优先级高的任务先执行,执行完以后会返回后台接着执行优先级低的任务,如果有中断,那么会优先执行中断;
2. 任务的定任务切换的实现
2.1 什么是任务?
根据功能的不同,把整个系统分割成一个个独立的且无法返回(void)的函数,这个函数我们称之为任务。
比方说,我们在裸机系统中,LED0和LED1呈现流水灯,那么程序应该是LED0点亮,延迟,LED1熄灭,延迟,LED0熄灭,延迟,LED1点亮,延迟;而在操作系统中,LED0和LED1是同时进行的;
在裸机系统中,局部变量统统放在一个叫栈的地方(调用子函数或者发生中断时,它们会将这些变量压入栈中;其他的全局变量统统存放在 RAM 中),栈是单片机 RAM 里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里面指定,最后由C库函数_main进行初始化;
在操作系统中,每个任务都是独立的,互不干扰的,所以要为每个任务都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间,但它们都存在于 RAM 中。
任务函数:
任务是一个独立的函数,函数主体无限循环且不能返回。
任务控制块:
任务控制块就相当于任务的身份证,里面存有任务的所有信息,比如任务的栈指针,任务名称,任务的形参等。
定义一个任务控制块需要一个新的数据类型,该数据类型在 task.c 这C头文件中声明。
任务创建函数:
任务创建函数 xTaskCreateStatic() 将任务栈、任务函数实体等与任务控制块联系起来,最终统一管理任务控制块。
任务创建函数在 Task.c 中定义,在 Task.h 中声明,所有跟任务相关的函数都在这个文件定义。
2.2 调度器
调度器是操作系统的核心,其主要功能就是实现任务的切换,即从就绪列表里面找到优先级最高的任务,然后去执行该任务。
调度器的启动由 vTaskStartScheduler() 函数来完成,该函数在 task.c 中。
指定运行的任务,然后调用 xPortStartScheduler() 函数。
2.3 临界段
临界段用一句话概括就是一段在执行的时候不能被中断的代码段。
那么什么情况下临界段会被打断?
一个是系统调度,还有一个就是外部中断。
因此 FreeRTOS 对临界段的保护最终还是回到对中断的开和关的控制。
3. 空闲任务和阻塞延迟
任务体内的延时使用的是软件延时,也就是说让 CPU 空等(空等的意思就是说任务中一旦运行到延时时,CPU 停下来等待延时时间过去,在延时的这块时间内 CPU 什么都不干)来达到延时的效果。
而使用 RTOS 的很大优势就是榨干 CPU 的性能,永远不能让它闲着。
RTOS 中的延时叫做阻塞延迟,即任务需要延时的时候,任务会放弃 CPU 的使用权,CPU 可以去干其他的事情,当任务延时时间到以后,任务重新获得 CPU 的使用权,任务继续运行,这样可以充分的利用 CPU 的资源,而不是让 CPU 干等着。
如果没有其他任务可以运行,这个时候 CPU 就运行空闲任务(空闲任务是主函数在进行任务调度的时候创建的,它的任务优先级是最低的)。因为系统保证必须每时每刻都有一个可以运行的任务。
实现空闲任务:
1. 定义空闲任务栈,空闲任务的栈在 main.c 中定义。
2. 定义空闲任务的任务控制块。
3. 定义空闲任务函数主体。
4. 创建空闲任务。
实现阻塞延时:
阻塞延时的阻塞是指任务调用该延时函数后,任务会被剥离 CPU 的使用权,然后进入阻塞状态,直到延时结束,任务重新获取 CPU 使用权才可以继续运行。
需要在任务控制块中添加成员变量 xTicksToDelay。
4. 时间片
所谓时间片就是同一优先级下可以有多个任务,每个任务轮流地享有 CPU 的使用权,享有 CPU 的时间我们叫做时间片。
RT-Thread 和 uC/OS 可以指定时间片的大小为多个 tick,但是 FreeRTOS 时间片只能是一个 tick。