uC/OS任务创建

1.任务的定义

1)定义任务堆栈

1 #define TASK1_STK_SIZE 128 
2 #define TASK2_STK_SIZE 128
3 
4 static CPU_STK Task1Stk[TASK1_STK_SIZE]; 
5 static CPU_STK Task2Stk[TASK2_STK_SIZE];

2)定义任务函数

3)定义任务控制块

任务控制块是一种数据类型,包含着任务的所有信息(任务堆栈,名字,优先级,链表指针等),任务的执行是通过系统的调度,系统对任务的操作则是通过任务控制块来实现

4)定义任务函数

2.系统初始化

系统初始化一般是在硬件初始化完成之后进行

3.启动系统

1 void OSStart (OS_ERR *p_err)
2 {
3     if ( OSRunning == OS_STATE_OS_STOPPED ) { 
4     /* 手动配置任务 1 先运行 */
5     OSTCBHighRdyPtr = OSRdyList[0].HeadPtr; 
6 
7     /* 启动任务切换*/
8     OSStartHighRdy(); 
9 
10     /* 不会运行到这里,运行到这里表示发生了致命的错误 */
11     *p_err = OS_ERR_FATAL_RETURN;
12     } else {
13     *p_err = OS_STATE_OS_RUNNING;
14     }
15 }
/***********************  OSStartHighRdy()  ***************************/
OSStartHighRdy
LDR R0, = NVIC_SYSPRI14 ; 设置 PendSV 异常优先级为最低(在 uC/OS-III 中,上下文切换是在 PendSV 异常服务程序中执行的,配置 PendSV的优先级为最低,防止在中断服务程序中执行上下文切换)
LDR R1, = NVIC_PENDSV_PRI
STRB R1, [R0]

MOVS R0, #0 ;设置 psp 的值为 0,开始第一次上下文切换
MSR PSP, R0

LDR R0, =NVIC_INT_CTRL ; 触发 PendSV 异常
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
18
19 CPSIE I ; 使能总中断,NMI 和 HardFault 除外
20
21 OSStartHang
22 B OSStartHang ; 程序应永远不会运行到这里
/************************************************************************/

4.任务切换

当调用 OSStartHighRdy()函数时会触发 PendSV 异常,然后在PendSV 异常服务函数里面进行任务切换。
PendSV 异常服务函数名称必须与启动文件里面向量表中 PendSV 的向量名一致,如果不一致则内核是响应
不了用户编写的 PendSV 异常服务函数的,只响应启动文件里面默认的 PendSV 异常服务函数。启动文件里
面为每个异常都编写好默认的异常服务函数,函数体都是一个死循环,当发现代码跳转到这些启动文件里面默
认的异常服务函数的时候,就要检查下异常函数名称是否写错了,是否跟向量表里面的一致。

/************************   PendSV_Handler()  ****************************/
PendSV_Handler
PendSV_Handler
; 关中断,NMI 和 HardFault 除外,防止上下文切换被中断
CPSID I (1)

; 将 psp 的值加载到 R0
MRS R0, PSP (2)

; 判断 R0,如果值为 0 则跳转到 OS_CPU_PendSVHandler_nosave
; 进行第一次任务切换的时候,R0 肯定为 0
CBZ R0, OS_CPU_PendSVHandler_nosave (3)

;-----------------------一、保存上文-----------------------------
; 任务的切换,即把下一个要运行的任务的堆栈内容加载到 CPU 寄存器中
;-------------------------------------------------------------- 
; 在进入 PendSV 异常的时候,当前 CPU 的 xPSR,PC(任务入口地址),
; R14,R12,R3,R2,R1,R0 会自动存储到当前任务堆栈,
; 同时递减 PSP 的值,随便通过 代码:MRS R0, PSP 把 PSP 的值传给 R0

; 手动存储 CPU 寄存器 R4-R11 的值到当前任务的堆栈
STMDB R0!, {R4-R11} 

; 加载 OSTCBCurPtr 指针的地址到 R1,这里 LDR 属于伪指令
LDR R1, = OSTCBCurPtr (16)
; 加载 OSTCBCurPtr 指针到 R1,这里 LDR 属于 ARM 指令
LDR R1, [R1] (17)
;  存储 R0 的值到 OSTCBCurPtr->OSTCBStkPtr,这个时候 R0 存的是任务空闲栈的栈顶
STR R0, [R1] (18)

;-----------------------二、切换下文-----------------------------
; 实现 OSTCBCurPtr = OSTCBHighRdyPtr
; 把下一个要运行的任务的堆栈内容加载到 CPU 寄存器中
;--------------------------------------------------------------
OS_CPU_PendSVHandler_nosave (4)

; 加载 OSTCBCurPtr 指针的地址到 R0,这里 LDR 属于伪指令
LDR R0, = OSTCBCurPtr (5)
; 加载 OSTCBHighRdyPtr 指针的地址到 R1,这里 LDR 属于伪指令
LDR R1, = OSTCBHighRdyPtr (6)
; 加载 OSTCBHighRdyPtr 指针到 R2,这里 LDR 属于 ARM 指令 
LDR R2, [R1] (7)
; 存储 OSTCBHighRdyPtr 到 OSTCBCurPtr
STR R2, [R0] (8)

; 加载 OSTCBHighRdyPtr 到 R0 
LDR R0, [R2] (9)
; 加载需要手动保存的信息到 CPU 寄存器 R4-R11
LDMIA R0!, {R4-R11} (10)

; 更新 PSP 的值,这个时候 PSP 指向下一个要执行的任务的堆栈的栈底
;(这个栈底已经加上刚刚手动加载到 CPU 寄存器 R4-R11 的偏移) 
MSR PSP, R0 (11)

; 确保异常返回使用的堆栈指针是 PSP,即 LR 寄存器的位 2 要为 1
ORR LR, LR, #0x04 (12) 

; 开中断
CPSIE I (13)

; 异常返回,这个时候任务堆栈中的剩下内容将会自动加载到 xPSR,
; PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
; 同时 PSP 的值也将更新,即指向任务堆栈的栈顶。
; 在 STM32 中,堆栈是由高地址向低地址生长的。
BX LR 
/*************************************************************************/

5.程序示例

/*
*******************************************************************
* 包含的头文件
*******************************************************************
*/
#include "os.h"
#include "stm32f4xx_hal.h"

/*
*******************************************************************
* 宏定义
*******************************************************************
*/


/*
*******************************************************************
* 全局变量
*******************************************************************
*/



/*
*******************************************************************
* TCB & STACK & 任务声明
*******************************************************************
*/
#define TASK1_STK_SIZE 20
#define TASK2_STK_SIZE 20

static CPU_STK Task1Stk[TASK1_STK_SIZE];
static CPU_STK Task2Stk[TASK2_STK_SIZE];

static OS_TCB Task1TCB;
static OS_TCB Task2TCB;

void Task1( void *p_arg );
void Task2( void *p_arg );

/*
*******************************************************************
* 函数声明
*******************************************************************
*/


/*
*******************************************************************
* main 函数
*******************************************************************
*/
/*
* 注意事项:1、该工程使用软件仿真,debug 需选择 Ude Simulator
* 2、在 Target 选项卡里面把晶振 Xtal(Mhz)的值改为 25,默认是 12,
* 改成 25 是为了跟 system_ARMCM3.c 中定义的__SYSTEM_CLOCK 相同,
* 确保仿真的时候时钟一致
*/
int main(void)
{
    OS_ERR err;

    /* 初始化相关的全局变量 */
    OSInit(&err);

    /* 创建任务 */
    OSTaskCreate ((OS_TCB*) &Task1TCB,
        (OS_TASK_PTR ) Task1,
        (void *) 0,
        (CPU_STK*) &Task1Stk[0],
        (CPU_STK_SIZE) TASK1_STK_SIZE,
        (OS_ERR *) &err);

        OSTaskCreate ((OS_TCB*) &Task2TCB,
        (OS_TASK_PTR ) Task2,
        (void *) 0,
        (CPU_STK*) &Task2Stk[0],
        (CPU_STK_SIZE) TASK2_STK_SIZE,
        (OS_ERR *) &err);

        /* 将任务加入到就绪列表 */
        OSRdyList[0].HeadPtr = &Task1TCB;
        OSRdyList[1].HeadPtr = &Task2TCB;

        /* 启动 OS,将不再返回 */
        OSStart(&err);
}
/*
*******************************************************************
* 函数实现
*******************************************************************
*/

/* 任务 1 */
void Task1( void *p_arg )
{
    while(1)
    {
    /*******  功能实现  *********/
    .....
    /***************************/
    
    /* 手动切换任务 */
        OSSched();
    }

}

/* 任务 2 */
void Task2( void *p_arg )
{
    while(1)
    {
    /*******  功能实现  *********/
    .....
    /***************************/
    
    /* 手动切换任务 */
        OSSched();
    }

}

/*************  OSSched()  *****************/
/* 任务切换,实际就是触发 PendSV 异常,然后在 PendSV 异常中进行上下文切换 */
void OSSched (void)
{
    if ( OSTCBCurPtr == OSRdyList[0].HeadPtr ) {
        OSTCBHighRdyPtr = OSRdyList[1].HeadPtr;
        } else {
        OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;
    }

    OS_TASK_SW();   //触发 PendSV异常
}
/*******************************************/
上一篇:思科 利用静态路由实现路由器互通


下一篇:微软 Build 2016