欣欣学习网,老工程师带你学习单片机技术,欢迎来坐坐。
首  页 | 学习NIOSII | 学习C51 | 学习CPLD | 51+CPLD实验板 | | | MY-RTOS

 系统简介
 系统启动
 创建任务
 等待与休眠
 使用互斥量
 使用信号量
 信号量组
 使用事件
 使用事件组
 消息队列
 动态内存管理
 只读内存管理
 临界段与内核锁
 任务复位与终止


玛雅实时操作系统(MY-RTOS)

创 建 任 务


版权声明:玛雅实时操作系统(MY-RTOS)软件及其说明文档由 欣欣学习网 版主 Stoneway QI 创作,作者保留其版权。任何人可以免费在其产品设计或著作中使用或引用该软件及其说明文档,但要求保留原作品当中的版权声明,并注明引用段落的出处。

-- Stoneway QI 主页: http://www.xxworks.com 邮箱: Stonewayqi@hotmail.com

玛雅实时操作系统的任务有两种:“常规任务”和“紧急任务”。这两种任务在优先级控制及创建方式上匀有不同之处。

在玛雅操作系统中,任务的优先级被分为“常规优先级”和“紧急优先级”,其中“紧急优先级”的级别要高于“常规优先级”。而“紧急优先级”又进一步被分为16个或32个优先级,从“紧急0优先级”到“紧急15优先级”或者从“紧急0优先级”到“紧急31优先级”。其中“紧急0优先级”的级别最高,其后的各个优先级的级别依次递减。

“常规任务”只能拥有“常规优先级”,而“紧急任务”可以处于“常规优先级”上,也可以处于与之相对应的“紧急优先级”上,这取决于是否有紧急信号传递给该任务。例如:“紧急任务0”在没有紧急信号时处于“常规优先级”,而在有紧急信号时则处于“紧急0优先级”上,同理,“紧急任务3”在没有紧急信号时处于“常规优先级”,而在有紧急信号时处于“紧急3优先级 ”上。

系统内核在做任务调度时,会优先调度高优先级的任务,从而做到紧急事务优先处理。对于处于“常规优先级”上的任务,由于它们的优先级相同,采用“分时轮番调度”的方式分享CPU。

创建1个“常规任务”是通过调用系统函数OSCteateTask()实现的,如下面代码所示:

void main(void)
{
    BSP();                            // 硬件初化
    OSInit(72000);                    //玛雅系统初始化
    OSCreateTask(&TaskTcb, Task, &StackTask[STACK_SIZE_TASK], pData); 
    ...
    while(1)
    {
        ...
    }
}                       (1)

系统函数OSCteateTask()有4个入口参数,取值依次是:任务控制块地址、任务的过程函数地址、任务的堆栈起始地址、任务的入口参数地址。

“任务控制块”是由用户程序定义的变量,用于对任务的管理。玛雅系统要求该变量要定义成全局变量,或静态局部变量,如以下代码所示:

OS_TCB TaskTcb;                       //定义1个全局类型的“任务控制块”
void main(void)
{
    BSP();                            // 硬件初化
    OSInit(72000);                    //玛雅系统初始化
    OSCreateTask(&TaskTcb, Task, &StackTask[STACK_SIZE_TASK], pData); 
    ...
    while(1)
    {
        ...
    }
}                       (2)

void main(void)
{
    static OS_TCB TaskTcb;            //定义1个静态局部类型的“任务控制块”
    BSP();                            // 硬件初化
    OSInit(72000);                    //玛雅系统初始化
    OSCreateTask(&TaskTcb, Task, &StackTask[STACK_SIZE_TASK], pData); 
    ...
    while(1)
    {
        ...
    }
}                       (3)

任务的“过程函数”是任务的执行代码,要定义成如下的函数形式:

void Task(void)
{
    ...
    ...
    while(1)
    {
        ...
    }
}                       (4)

任务的堆栈是一个OS_STK型的数组,而它的起始地址则取决于所使用的硬件平台和编译器。在有些硬件平台上,堆栈是向上生长的,而另外一些硬件平台上,堆栈则是向下生长。也有一些硬件平台不限定堆栈的生长方向,这种情况下则是由编译器决定其生长方向。同样,有些硬件平台上要求堆栈是满栈,即:堆栈指针所指向的位置已保存数据;而另外一些硬件平台上要求堆栈是空栈,即:堆栈指针所指向的位置尚未保存数据。在硬件平台没有规定满栈还是空栈时,由编译器决定使用哪种方式。

假如我们定义的数组为:

OS_STK Stack[MAX];

则根据硬件平台及编译器对堆栈生长方向的要求及满栈还是空栈的要求,确定堆栈的起始地址如下表所示:

  向上生长 向下生长
满栈 &Stack[0]-1 &Stack[MAX]
空栈 &Stack[0] &Stack[MAX]-1

以STM32F103硬件平台和IAR Embedded Workbench编译器为例。它的堆栈是向下生长的满栈,所以传给函数OSCreateTask()的堆栈起始地址,如下列代码所示:

#define STACK_SIZE_TASK  100
...
OS_STK StackTask[STACK_SIZE_TASK];    //定义1个OS_STK型的数组
...
void main(void)
{
    BSP();                            // 硬件初化
    OSInit(72000);                    //玛雅系统初始化
    OSCreateTask(&TaskTcb, Task, &StackTask[STACK_SIZE_TASK], pData); 
    ...
    while(1)
    {
        ...
    }
}                       (5)

传给函数OSCreateTask()的第4个参数是1个可选参数,它的取值是任务的入口参数地址。任务的入口参数的形式和用途由用户程序自行定义,与系统无关。在任务不需要入口参数的情况下,可向该参数传递1个NULL值。任务可以通过调用系统函数OSGetTaskArg()来获得其入口参数的地址。

以一个U8类型的数组作为任务的入口参数,示例如下:

...
U8 Data[100];                         //要传给任务的入口参数
void main(void)
{
    BSP();                            // 硬件初化
    OSInit(72000);                    //玛雅系统初始化
    //在创建任务时,将任务的入口参数地址(Data)传给任务
    OSCreateTask(&TaskTcb, Task, &StackTask[STACK_SIZE_TASK], Data); 
    ...
    while(1)
    {
        ...
    }
}

void Task(void)
{
    U8 * pData;
    ...
    pData = OSGetTaskArg(); //任务通过系统函数调用来获得其入口参数的地址
    ...
    while(1)
    {
        ...
    }
}                       (6)

函数OSCreateTask()的返回值为OS_STATUS_OK时,表示任务被成功的创建,其它值则分别代表各种错误。

与创建常规任务不同,创建紧急任务需使用另外1个系统函数:OSAttachEmTask()。该函数也有4个入口参数,第1个参数的取值是紧急任务的代码,表明要创建哪个紧急任务。它的取值范围是从OS_EM_TASK1到OS_EM_TASK15或者从OS_EM_TASK1到OS_EM_TASK31。如果试图创建1个已存在的紧急任务,或创建1个非法代码的紧急任务,该函数都会返回1个错误代码,而不创建任何的任务。该函数的其它3个入口参数与函OSCreateTask()的后3个参数的含义与用法完全相同。当该函数的返回值为OS_STATUS_OK时,表示创成功,其它值则分别代表各种错误。以创建紧急任务2为例,代码如下:

#define STACK_SIZE_TASK  100
...
OS_STK StackTask[STACK_SIZE_TASK];    //定义1个OS_STK型的数组
...
void main(void)
{
    BSP();                            // 硬件初化
    OSInit(72000);                    //玛雅系统初始化
    OSAttachEmTask(OS_EM_TASK2, Task, &StackTask[STACK_SIZE_TASK], NULL); 
    ...
    while(1)
    {
        ...
    }
}                       (7)

新创建的紧急任务的紧急信号是被屏蔽的,因此它的优先级为常规优先级。可以通过调用系统函数OSEnableEmergency()来解除对紧急信号的屏蔽,也可以通过调用系统函数OSDisableEmergency()来恢复对紧急信号的屏蔽。仍以紧急任务2为例,代码如下:

#define STACK_SIZE_TASK  100
...
OS_STK StackTask[STACK_SIZE_TASK];    //定义1个OS_STK型的数组
...
void main(void)
{
    BSP();                            // 硬件初化
    OSInit(72000);                    //玛雅系统初始化
    OSAttachEmTask(OS_EM_TASK2, Task, &StackTask[STACK_SIZE_TASK], NULL); 
    ...
    while(1)
    {
        ...
        ...
    }
}

void Task(void)
{
    ...
    while(1)
    {
        ...
        OSEnableEmergency();          //解除对紧急信号的屏蔽
        ...
        OSDisableEmergency();         //开启对紧急信号的屏蔽
        ...
    }
}                       (8)

紧急任务可以触发自己的紧急信号,也可以清除自己的紧急信号。相关的系统函数为:OSEnterEmergency()和OSExitEmergency()。如果此时任务的紧急信号是未被屏蔽的,触发紧急信号后,任务将进入紧急优先级运行。清除紧急信号,则使任务退出紧急优先级,回到常规优先级上。

void Task(void)
{
    ...
    OSEnableEmergency();              //解除对紧急信号的屏蔽
    while(1)
    {
        ...
        OSEnterEmergency();           //触发紧急信号
        ...                           //以紧急优先级运行
        ...
        OSExitEmergency();            //清除紧急信号
        ...
    }
}                       (9)


管理员信箱: stonewayqi@hotmail.com

欣 欣 学 习 网

粤ICP备2023138008号