任务调度
1.
抢占式调度
针对优先级不同的任务,高优先级可以抢占低优先级的任务
2.
时间片调度
针对优先级相同的任务,多个任务优先级相同且就绪时,任务调度器会根据用户设置的时间片轮
询的运行这些任务。
时间片是以一次系统时钟节拍为单位,系统时钟就是(
SysCLK
滴答定时器)。
UCOS-III
默认设置任务时间片
为
100
,
UCOS-III
会在当前任务运行
100
次系统时钟节拍后切换到另一个同优先级的任务中运行。
抢占式调度:
如果有三个任务分别优先级是
3
,
2
,
1
,那么任务一在执行中,任务二就绪之后,立刻执行任
务二,任务三就绪后,立刻执行任务三。
如果任务三被挂起,会让出
CPU
的使用,那么优先级最高的任务二执行。
在
FreeRTOS
中优先级数字越小,优先级越低
在
UCOSIII
和
STM32
中优先级数字越小,优先级越高
时间片调度:
三个任务相同的优先级,在时间片用完之后会切换到下一个任务执行
同等优先级任务轮流执行,时间片轮转。
一个时间片大小,取决于滴答定时器中断频率
每个任务都可以定义自身的一个时间片长度。
没有用完的时间片不会再使用,如果有任务在执行时被中途挂起了,那么下次执行这个任务的时候还是
按照原来设置的时间片的时钟节拍运行。
任务状态
1.
运行态:正在执行的任务,但是在
STM32
中,同一时间仅仅只有一个任务处于运行态。
2.
就绪态:任务能够被执行,但还没被执行,那么该任务处于就绪态。
3.
挂起态:一个运行态的任务因延时或等待某一事件发生时被挂起,这个任务处于挂起态。
4.
休眠态:任务已经在
CPU
内存中(任务被删除),但是还没有交给
UCOSIII
内核管理。
5.
中断态:处于运行态的任务被中断打断,
CPU
跳转去执行中断服务函数,原本属于运行态会被切换到中断态,直到中断结束,再切回运行态继续运行。
![](https://img-blog.csdnimg.cn/direct/65b08d113acd4033b46040df41ae5b00.png)
1.
被创建的任务,初始状态为就绪态。
2.
被删除的任务,转为休眠态。
3.
仅就绪态和中断态可转变为运行态。
4.
其他状态的任务想运行,必须转为就绪态。
UCOS-III
主要有三大类列表用来跟踪任务状态:
1.
就绪列表:
准备运行的任务就放在就绪列表:
OSRdyList[x]
,其中
x
代表任务优先级数值。
例如三个任务优先级分别是
1
,
2
,
3
:
OSRdyList[1] -> task1
,
OSRdyList[2] -> task2
,
OSRdyList[3] -> task3
如果优先级都是
1
,那么三个任务都会被挂载在
OSRdyList[1] -> task1 task2 task3
调度器总是再所有处于就绪列表的任务中,选择具有最高优先级的任务来执行。
2. Tick
列表:
1. 正在等待延时超时或挂起的对象超时的任务,将放在
OSTickList
3.
挂起列表:
1. 当任务等待信号量、事件时,任务将放在挂起列表
PendList
任务堆栈的创建
先进先出
FIFO
的原则,每个任务都有自己的堆栈
堆栈的创建
#define START_STK_SIZE 512 //
堆栈的大小
512 x 4 = 2048
字节
CPU_STK START_TASK_STK[START_STK_SIZE]; //
定义一个数组来作为任务堆栈
CPU_STK ---> uint32_t
任务堆栈初始化
CPU_STK OSTaskStkInit()
参数:
OS_TASK_PTR p_task, void *p_arg
CPU_STK *p_stk_base
CPU_STK *p_stk_limit
CPU_STK_SIZE stk_size
OS_OPT opt
用户一般不会直接操作堆栈初始化函数,任务堆栈初始化函数由任务创建函数
OSTaskStkInit()
调用。
不同
CPU
对于寄存器和堆栈的操作方式不同,因此在移植
UCOSIII
的时候需要用户根据自身所选的
CPU
来编
创建任务堆栈 OSTaskCreate()
void OSTaskCreate (OS_TCB *p_tcb,
CPU_CHAR *p_name,
OS_TASK_PTR p_task,
void *p_arg,
OS_PRIO prio,
CPU_STK *p_stk_base, //
任务堆栈基地址
CPU_STK_SIZE stk_limit, //
任务堆栈浅深
当任务堆栈小于这个设定的深度的时候,说
明快用完了。
CPU_STK_SIZE stk_size, //
任务堆栈大小
OS_MSG_QTY q_size,
OS_TICK time_quanta,
void *p_ext,
OS_OPT opt,
OS_ERR *p_err
)
任务控制块结构
任务控制块是用来记录与任务相关的信息的数据结构,每个任务都要有自己的任务控制块。任务控制块由用 户自行创建。
OS_TCB StartTaskTCB; //
创建一个任务控制块
OS_TCB
是一个结构体,描述任务控制块,任务控制块的成员比哪里用户不能直接访问,更不能改变他们。
OS_TCB
,有些成员采用了条件编译的方式来确定。
优先级
UCOSSIII
任务优先级由宏
OS_CFG_PRIO_MAX
来配置,
UCOSIII
中数值越小,优先级越高,最低可用优先级 就是OS_CFG_PRIO_MAX-1,数值越小,优先级越高。
就绪表
优先级位映射表
OSPrioTbl[]
用来记录哪个优先级下有任务就绪
就绪任务列表
OSRdyList[]
用来记录每个优先级下所有就绪的任务。
CPU_DATA OSPrioTbl[OS_PRIO_TBL_SIZE]; // CPU_DATA
也是无符号
32
位
OS_PRIO_TBL_SIZE = ((OS_CFG_PRIO_MAX - 1u) / (DEF_INT_CPU_NBR_BITS) + 1u)
OS_CFG_PRIO_MAX
由用户自行定义,默认
32
DEF_INT_CPU_NBR_BITS = CPU_CFG_DATA_SIZE * DEF_OCTET_NBR_BITS
CPU_CFG_DATA_SIZE = CPU_WORD_SIZE_32 = 4
DEF_OCTET_NBR_BITS = 8
所以当系统有
32
个优先级的时候
OS_PRIO_TBL_SIZE = ((32 - 1) / (4 * 8) + 1) = 1
可剥夺型任务调度
任务调度就是中止当前正在运行的任务转去执行其他任务。
UCOSIII
是可剥夺型内核,因此当一个高优先级的任务准备就绪,并且此时发生了任务调度,那么这个高优
先级的任务就会获得
CPU
的使用权。
UCOSIII
的任务调度由任务调度器来完成,任务调度器有
2
种,任务级调度器和中断级调度器。
任务级调度器为函数
OSSched()
中断级调度器
OSIntExit()
,当退出外部中断服务函数的时候使用中断级任务调度。
任务调度点
释放信号或者发送信息,可以通过配置相应参数不发生任务调度
使用延时函数
OSTimeDly()
或者
OSTimeDlyHMSM()
任务等待的事情还没发生(等待信号量,消息队列等)
任务取消等待
创建任务
删除任务
删除一个内核对象
任务改变自身的优先级或者其他任务的优先级
任务通过调用
OSTaskSpuspend()
将自身挂起
任务解挂某个挂起的任务
退出所有的嵌套中断
通过
OSSchedUnlock()
给调度器解锁
任务调用
OSSchedRoundRobinYield()
放弃其执行时间片
用户调用
OSSched()
调度器的上锁和解锁
有时候不希望发生任务调度,因为始终有些代码的执行不能被打断,此时可以使用
OSSchedLock()
对调度器
加锁,当想恢复时使用
OSSchedUnLock()
给上锁的任务调度器解锁。
时间片轮转调度
UCOSIII
允许一个优先级下多个任务,每个任务可以执行指定时间(时间片),然后轮到下一个任务,这个
过程就是时间片轮转掉段。
时间片轮转调度器:
OS_SchedRoundRobin()
任务切换
当
UCOSIII
需要切换到另外一个任务时,它将保存当前任务当前状态到任务堆栈种,主要时
CPU
寄存器,然
后恢复新的线程并且执行新的任务
这个过程就是任务切换
任务切换分两种,任务级切换和中断级切换
任务级切换:
OSCtxSw()
中断级切换:
OSIntCtxSw()
UCOSIII
系统初始化
OSInit()完成初始化,而且OSInit()必须优先于其他UCOSIII函数调用,包括OSStart()
int main(void)
{
OS_ERR err;
// 其他函数,一般为外设初始化函数
OSInit(&err);
// 其他函数,一般为创建任务函数
OSStart(&err)
}
任务创建与停止
#include "led.h"
#include "delay.h"
#include "Sys.h"
#include "Usart.h"
#include "includes.h"
//UCOSIII中以下优先级用户程序不能使用,ALIENTEK
//将这些优先级分配给了UCOSIII的5个系统内部任务
//优先级0:中断服务服务管理任务 OS_IntQTask()
//优先级1:时钟节拍任务 OS_TickTask()
//优先级2:定时任务 OS_TmrTask()
//优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask()
//优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()
// 任务优先级
#define START_TASK_PRIO 3
//任务队列大小
#define START_STK_SIZE 128
// 任务控制块
OS_TCB StartTaskTCB;
// 任务堆栈
CPU_STK START_TASK_STK[START_STK_SIZE];
// 任务函数
void start_task(void *p_arg);
// 任务优先级
#define TASK1_TASK_PRIO 4
//任务队列大小
#define TASK1_STK_SIZE 128
// 任务控制块
OS_TCB Task1_TaskTCB;
// 任务堆栈
CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
// 任务函数
void task1_task(void *p_arg);
// 任务优先级
#define TASK2_TASK_PRIO 5
//任务队列大小
#define TASK2_STK_SIZE 128
// 任务控制块
OS_TCB Task2_TaskTCB;
// 任务堆栈
CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE];
// 任务函数
void task2_task(void *p_arg);
int main(void)
{
OS_ERR err;
CPU_SR_ALLOC();
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200);
LED_Init();
OSInit(&err); // ucosiii初始化
OS_CRITICAL_ENTER(); // 进入临界区
OSTaskCreate(
(OS_TCB * )&StartTaskTCB,
(CPU_CHAR * ) "start task",
(OS_TASK_PTR ) start_task,
(void * ) 0,
(OS_PRIO ) START_TASK_PRIO,
(CPU_STK * ) &START_TASK_STK[0],
(CPU_STK_SIZE) START_STK_SIZE / 10,
(CPU_STK_SIZE) START_STK_SIZE,
(OS_MSG_QTY ) 0,
(OS_TICK ) 0,
(void * ) 0,
(OS_OPT ) OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
&err
);
OS_CRITICAL_EXIT(); // 退出临界区
OSStart(&err);
while(1);
}
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //进入临界区
//创建TASK1任务
OSTaskCreate((OS_TCB * )&Task1_TaskTCB,
(CPU_CHAR * )"Task1 task",
(OS_TASK_PTR )task1_task,
(void * )0,
(OS_PRIO )TASK1_TASK_PRIO,
(CPU_STK * )&TASK1_TASK_STK[0],
(CPU_STK_SIZE)TASK1_STK_SIZE/10,
(CPU_STK_SIZE)TASK1_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
//创建TASK2任务
OSTaskCreate((OS_TCB * )&Task2_TaskTCB,
(CPU_CHAR * )"task2 task",
(OS_TASK_PTR )task2_task,
(void * )0,
(OS_PRIO )TASK2_TASK_PRIO,
(CPU_STK * )&TASK2_TASK_STK[0],
(CPU_STK_SIZE)TASK2_STK_SIZE/10,
(CPU_STK_SIZE)TASK2_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
OS_CRITICAL_EXIT(); //退出临界区
OSTaskDel((OS_TCB*)0,&err); //删除start_task任务自身
}
void task1_task(void * p_arg)
{
u8 task1_num=0;
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
while(1)
{
task1_num++; //任务执1行次数加1 注意task1_num1加到255的时候会清零!!
LED0= ~LED0;
printf("任务1已经执行:%d次\r\n",task1_num);
if(task1_num==5)
{
// OSTaskDel((OS_TCB*)&Task2_TaskTCB,&err); //任务1执行5此后删除掉任务2
OSTaskSuspend((OS_TCB *)&Task2_TaskTCB, &err);
printf("任务1挂起了任务2!\r\n");
}
if(task1_num == 10)
{
OSTaskResume((OS_TCB *)&Task2_TaskTCB, &err);
printf("任务1恢复了任务2!\r\n");
}
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
}
}
void task2_task(void * p_arg)
{
u8 task2_num=0;
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
// POINT_COLOR = BLACK;
//OS_CRITICAL_ENTER();
while(1)
{
task2_num++; //任务2执行次数加1 注意task1_num2加到255的时候会清零!!
LED1=~LED1;
printf("任务2已经执行:%d次\r\n",task2_num);
OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err); //延时1s
}
}