任务的定义与切换是操作系统的基础,这篇文章总结其运行流程

[TOC]

1. 创建任务

1.1 定义任务堆栈

1.栈是RAM内一段连续内存
2.定义任务栈就是给函数定义一个数组:

1
static     CPU_STK       Task1Stk[TASK1_STK_SIZE] ;

1.2 定义任务函数

每个任务函数必须是一个死循环,其内必须有阻塞延时函数以进行任务调度!!!

1.3 定义任务控制块

定义一个结构体,里面可以存放每个任务的堆栈,名称,形参等等参数,对任务的操作都可以通过这个TCB来实现

1
2
3
4
5
struct os_tcb 
{
CPU_STK *STKPTR
CPU_STK_SZIE StkSize
}

1.4 实现任务创建函数

把任务堆栈、任务实体、任务的TCB联系起来的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void OSTaskCreate
(
OS_TCB *p_tcb,
OS_TASK_PTR p_task,
void *p_arg,
CPU_STK *p_stk_base,
CPU_STK_SIZE stk_size,
OS_ERR *p_err
);
{
CPU_STK *p_sp;
p_sp=OSTaskStkInit(//返回的栈顶指针
p_task,
p_arg,
p_stk_base,
stk_size
);
//把栈顶指针和栈大小给到tcb里面
p_tcb->StkPtr = p_sp;
p_tcb->StkSize= stk_size;
}

参数介绍:
OS_TCB -任务TCB
OS_TASK_PTR -任务指针
*p_arg -参数指针
CPU_STK -堆栈基地址
CPU_STK_SIZE -堆栈大小
OS_ERR -错误枚举

1.5 任务堆栈初始化函数

该函数第一次运行时使用,把堆栈的数据预加载到CPU的寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
CPU_STK *OSTaskStkInit 
(
OS_TASK_PTR p_task,
void *arg,
CPU_STK *p_stk_base,
CPU_STK_SIZE stk_size);
{
CPU_STK *p_stk;
p_stk = &p_stk_base[stk_size];
以下只适合Coterx-M3
/* 异常发生时自动保存的寄存器 */
*--p_stk = (CPU_STK)0x01000000u; /* xPSR的bit24必须置1 */
*--p_stk = (CPU_STK)p_task; /* 任务的入口地址 */
*--p_stk = (CPU_STK)0x14141414u; /* R14 (LR) */
*--p_stk = (CPU_STK)0x12121212u; /* R12 */
*--p_stk = (CPU_STK)0x03030303u; /* R3 */
*--p_stk = (CPU_STK)0x02020202u; /* R2 */
*--p_stk = (CPU_STK)0x01010101u; /* R1 */
*--p_stk = (CPU_STK)p_arg; /* R0 : 任务形参 */
/* 异常发生时需手动保存的寄存器 */
*--p_stk = (CPU_STK)0x11111111u; /* R11 */
*--p_stk = (CPU_STK)0x10101010u; /* R10 */
*--p_stk = (CPU_STK)0x09090909u; /* R9 */
*--p_stk = (CPU_STK)0x08080808u; /* R8 */
*--p_stk = (CPU_STK)0x07070707u; /* R7 */
*--p_stk = (CPU_STK)0x06060606u; /* R6 */
*--p_stk = (CPU_STK)0x05050505u; /* R5 */
*--p_stk = (CPU_STK)0x04040404u; /* R4 */
return (p_stk);
}

1.6 就绪列表

该列表存放已经就绪的列表,表示其中的任务可以进行调度

1
2
3
4
5
struct os_rdy_list
{
OS_TCB *HeadPtr;
OS_TCB *TailPtr;
};

2. OS系统初始化

OSRuning存放系统任务运行状态
OSTCBCurPtr和OSTCBHighRdyPtr是当前任务指针以及最高任务指针

本函数主要初始化清零,初始化就绪列表(清零列表)和空闲任务

1
2
3
4
5
6
7
8
9
10
11
12
void OSInit (OS_ERR *p_err)
{
OSRunning = OS_STATE_OS_STOPPED;

OSTCBCurPtr = (OS_TCB *)0;//当前任务控制块指针
OSTCBHighRdyPtr = (OS_TCB *)0;//最高优先级指针

OS_RdyListInit();//就绪列表初始化,列表清零
//初始化空闲任务
OS_IdleTaskInit(p_err);
*p_err = OS_ERR_NONE;//执行到这无报错
}

3. 启动系统

系统启动,配置最高任务TCB指针,在下一个TICK中切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void OSStart (OS_ERR *p_err)
{
if( OSRunning == OS_STATE_OS_STOPPED )
{
/* 手动配置任务1先运行 */
OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;

/* 启动任务切换,不会返回 --汇编.s文件里面*/
OSStartHighRdy();

/* 不会运行到这里,运行到这里表示发生了致命的错误 */
*p_err = OS_ERR_FATAL_RETURN;
}
else
{
*p_err = OS_STATE_OS_RUNNING;
}
}

4. 任务切换

该函数把就绪列表最高任务赋给下一个最高就绪优先级
(OS_TASK_SW()函数用于把PENDSV异常在中断标志中置位,使之进入PENDSV中断,开启上下文切换)
OS_CRITICAL_ENTER() 开启屏蔽中断—使进入上下文切换不被打断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void OSSched (void)
{
CPU_SR_ALLOC();
OS_CRITICAL_ENTER();

OSPrioHighRdy = OS_PrioGetHighest();
OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;
if(OSTCBCurPtr == OSTCBHighRdyPtr)
{
OS_CRITICAL_EXIT();
return;
}

OS_CRITICAL_EXIT();
//任务切换
OS_TASK_SW();
}

5. MAIN函数

1—初始化相关变量
2—创建任务
3—任务添加到就绪列表
4—启动OS

6. 参考资料教程

野火UCOS-Ⅲ教程
UCOS-Ⅲ内核实现与应用开发实战指南-基于STM32
正点原子开发板