零、整体摘要
这篇文章过长了,如果想快速的大体的了解FreeRTOS的创建任务的过程,看这一部分即可。
1、凝练概述
任务调度器通过列表管理任务,列表项作为列表的基本元素与任务控制模块建立联系,任务控制模块则反映了任务中的信息。最终达到任务调度的目的。
2、创建流程概述
- 任务创建的过程就是:
- 初始化任务的栈名字、栈大小、堆栈指针传入(任务栈的初始化)
- 定义任务优先级
- 定义任务控制块指针(列表项的初始化)
- 就绪列表的初始化:
- 初始化列表
- 把创建的任务tcb插入到列表中
- 调度器实现
- 开启调度器
- svc_hander 重命名(arm和freertos的命名不同,需要调整)
- 实现任务切换(现有程序,汇编语言)
3、大体思路概述
静态、动态实现任务启动的大体流程思路
- main
- 初始化硬件(屏幕、adc、串口【用什么初始化什么】)
- 创建开始任务(函数)(调用xtaskcreate、准备好参数:任务相关【栈指针、名字、大小等】,这个函数对列表项和任务栈进行了初始化)
- 创建子任务1(函数)(同上)
- 创建子任务2(函数)(同上)
- 删除开始任务(开始任务只是一个中间过程任务)
- 启动任务调度器【调用函数即可】
- 1、创建了空闲任务和定时器任务
- 2、开启了调度器所用的硬件(中断、systick、定时器)
- 3、任务切换函数开启)
============================================
名词解释
任务调度器:
首先,任务创建的目的是通过freertos的固定格式,对不同的任务进行优先级顺序的管理。为了达到目的我们需要一个核心组件:任务调度器。
任务控制模块:
其次,核心组件:任务调度器管理任务需要任务本身自带属性和信息,通过对信息的判断来执行任务的调度。因此我们需要把输入信息结构化表示,这里就出现了能够通过特定格式来方便任务调度器管理的数据结构:任务控制模块,注意任务控制模块的信息只包含了方便管理的基本信息。但信息仍然很丰富,如优先级、任务状态(就绪、阻塞、挂起)、任务栈指针、堆栈大小。。。(任务控制模块是每个任务的"身份证",帮助调度器了解当前各个任务的信息)
任务栈
每个任务因为要分离管理,其数据需要存储的不同的内存块中,任务栈就是分配好的不同的任务内存对栈地址空间。
列表:
然后,任务调度器的目的是更好的管理任务,它通过任务控制模块来了解每个任务的状态,但是当任务多起来时,任务调度器就会变得很复杂,因此这时引入一个任务调度器的小助手:列表。列表本质上是一种双向链表,方便对不同对象的增删查改和遍历与显示,当这个对象是任务控制模块时,这时的链表就可以被是做一种列表。(列表是任务调度器的助手,帮助任务调度器更有调理的了解各个任务的信息)
列表项
每个列表项即列表中的元素,其和具体的对象关联,指针指向关联对象或存储关联对象。
任务就绪态列表:
一种列表,TCB里面的属性中状态时就绪态,就会被插入到这个列表末尾中。非就绪态任务时无法调用被任务调配器直接调用。
任务的状态【以买咖啡为例子方便理解】:
就绪:任务创建完成所有资源都准备好了,等待被调用(没获得cpu时间片,没执行)
【买咖啡:前准备好了,但是营业员在忙其他事】
运行态:任务正在cpu上执行(单核处理器只能有一个任务运行)
【买咖啡:营业员正在制作你的咖啡订单】
阻塞:参与调度,但某些资源或条件不具备在等待
【买咖啡:没带钱,在等着你的男/女朋友微信转账】
挂起:不参与cpu实际调度,必须回复才能参与任务调度
【买咖啡:昨天睡的好,挺精神的,不买咖啡了,什么时候困了什么时候再买】
删除/终止:任务完成,删除任务;或者任务被操作系统终止
【买咖啡:买到咖啡了,去做其他工作了 ;今天被通知有ddl截止,被强制停止了买咖啡的事情。】
阻塞和挂起的区别
执行状态:
挂起:任务被挂起后,不会参与调度和执行,不会消耗 CPU 时间。
阻塞:任务被阻塞时,仍然参与调度,但是处于等待状态,不会执行任务代码,直到条件满足。等待条件:
挂起:通常是手动挂起任务,等待手动恢复。
阻塞:任务被阻塞通常是因为等待某些事件的发生,例如等待资源可用或者等待超时。恢复方式:
挂起:任务通常需要显式地调用恢复函数来恢复执行。
阻塞:任务在等待的条件满足或者超时时会自动解除阻塞。
一、对任务的定义
通常,裸机编程会将很多功能函数放入到大循环中按顺序执行。
当多任务系统中,将每个功能分割称独立的 、无法返回的函数
任务函数示例:
参数传入的是指针变量
void task_name (void *parg)
{
任务主体,因无限循环,不会推出,所以无返回值。
for(;;)
{
//代码功能实现
}
}
二、任务的实现过程函数
整个过程的函数都放在后面了,可能会有点繁琐,如果想看框架和脉络,直接拉到最后部分。
1、任务栈
全局变量----》存放
子函数调用----》局部变量存放
中断发生—》函数的返回地址放
以上三种情况下,裸机程序会都会存放在栈中。(对应物理世界中的ram芯片)
在多任务系统中,每个任务都需要独立的存储空间,保证互不干扰。
其通常是预定义的全局数组 或 动态分配的内存空间,但都是存放在ram芯片中。
由多少个任务,就定义多少个任务栈
例如屏幕系统和检测系统分别是两个任务,则需要定义两个独立的全局数组
这里的128是字,不是字节 128*4=512字节。
FreetRTOS中会把c中的标准数据类型用typedef来重新定义
#define TASK1_STACK_SIZE_128
StackType_t TaskDisplay[TASK1_STACK_SIZE]
#define TASK2_STACK_SIZE_128
StackType_t TaskDetect[TASK2_STACK_SIZE]
2、定义任务函数
void task2_name (void *parg)
{
任务主体,因无限循环,不会推出,所以无返回值。
for(