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.启动系统

void OSStart (OS_ERR *p_err)
{
    if ( OSRunning == OS_STATE_OS_STOPPED ) { 
    /* 手动配置任务 1 先运行 */
    OSTCBHighRdyPtr = OSRdyList[0].HeadPtr; 

    /* 启动任务切换*/
    OSStartHighRdy(); 

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

/* UCOS-III-Ports/os_cpu_a.asm */
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]

CPSIE I ; 使能总中断,NMI 和 HardFault 除外

OSStartHang
B OSStartHang ; 程序应永远不会运行到这里
/************************************************************************/

4.任务切换

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

/************************   PendSV_Handler()  ****************************/
/
/* UCOS-III-Ports/os_cpu_a.asm */
; 关中断,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 函数
*******************************************************************

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异常
}
/*******************************************/
posted @ 2020-01-13 12:35  D·Kingson.Mo  阅读(270)  评论(0编辑  收藏  举报