3. 创建任务

在上一章,我们已经基于野火i.MX RT系列开发板创建好了uCOS III的工程模板,这章开始我们将真正进入如何使用uCOS的征程,先从最简单的创建任务开始,点亮一个LED,以慰藉下尔等初学者弱小的心灵。

3.1. 硬件初始化

本章创建的任务需要用到开发板上的LED,所以先要将LED相关的函数初始化好,为了方便以后统一管理板级外设的初始化,我们在bsp.c文件中创建一个BSP_Init()函数, 专门用于存放板级外设初始化函数,具体见 代码清单17-1 的高亮部分。

代码清单‑1 BSP_Init()中添加硬件初始化函数
 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
32
33
34
35
/*
************************************************************************
* 函数名 : BSP_Init
* 描述   : 所有的硬件设备都应该放在这个函数里边初始化
* 形参   : 无
* 返回值 : 无
************************************************************************
*/
void  BSP_Init (void)
{
/* 初始化内存保护单元 */
    BOARD_ConfigMPU();
/* 初始化开发板引脚 */
    BOARD_InitPins();
/* 初始化开发板时钟 */
//BOARD_BootClockRUN();
/* 初始化调试串口 */
    BOARD_InitDebugConsole();

/* 打印系统时钟 */
    PRINTF("\r\n");
    PRINTF("*****欢迎使用野火i.MX RT1052 开发板*****\r\n");
    PRINTF("CPU:             %d Hz\r\n", CLOCK_GetFreq(kCLOCK_CpuClk));
    PRINTF("AHB:             %d Hz\r\n", CLOCK_GetFreq(kCLOCK_AhbClk));
    PRINTF("SEMC:            %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SemcClk));
    PRINTF("SYSPLL:          %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllClk));
    PRINTF("SYSPLLPFD0:      %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllPfd0Clk));
    PRINTF("SYSPLLPFD1:      %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllPfd1Clk));
    PRINTF("SYSPLLPFD2:      %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllPfd2Clk));
    PRINTF("SYSPLLPFD3:      %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllPfd3Clk));
    PRINTF("这是一个[野火]-全系列开发板-uCOS-III  单任务实验!\n\n");
/* 硬件BSP初始化统统放在这里,比如LED,串口,LCD等 */
    LED_GPIO_Config();    //初始化LED

}

执行到BSP_Init()函数的时候,操作系统完全都还没有涉及到,即BSP_Init()函数所做的工作跟我们以前编写的裸机工程里面的硬件初始化工作是一模一样的。运行完BSP_Init ()函数,接下来才慢慢启动操作系统,最后运行创建好的任务。有时候任务创建好,整个系统跑起来了,可想要的实验现象就是出 不来,比如LED不会亮,串口没有输出,LCD没有显示等等。如果是初学者,这个时候就会心急如焚,四处求救,那怎么办?这个时候如何排除是硬件的问题还是系统的问题,这里面有个小小的技巧,即在硬件初始化好之后,顺便测试下硬件,测试方法跟裸机编程一样,具体实现见 代码清单17-2 的高亮部分。

代码清单‑2BSP_Init()中添加硬件测试函数
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//板级驱动
#include"fsl_debug_console.h"
#include"board.h"
#include"pin_mux.h"
#include"clock_config.h"
#include"./led/bsp_led.h"
#include"bsp.h"

/*
************************************************************************
* 函数名 : BSP_Init
* 描述   : 所有的硬件设备都应该放在这个函数里边初始化
* 形参   : 无
* 返回值 : 无
*************************************************************************
*/
void  BSP_Init (void)
{
/* 初始化内存保护单元 */
    BOARD_ConfigMPU();
/* 初始化开发板引脚 */
    BOARD_InitPins();
/* 初始化开发板时钟 */
//BOARD_BootClockRUN();
/* 初始化调试串口 */
    BOARD_InitDebugConsole();(1)

/* 打印系统时钟 */
    PRINTF("\r\n");
    PRINTF("*****欢迎使用野火i.MX RT1052 开发板*****\r\n");
    PRINTF("CPU:             %d Hz\r\n", CLOCK_GetFreq(kCLOCK_CpuClk));
    PRINTF("AHB:             %d Hz\r\n", CLOCK_GetFreq(kCLOCK_AhbClk));
    PRINTF("SEMC:            %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SemcClk));
    PRINTF("SYSPLL:          %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllClk));
    PRINTF("SYSPLLPFD0:      %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllPfd0Clk));
    PRINTF("SYSPLLPFD1:      %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllPfd1Clk));
    PRINTF("SYSPLLPFD2:      %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllPfd2Clk));
    PRINTF("SYSPLLPFD3:      %d Hz\r\n", CLOCK_GetFreq(kCLOCK_SysPllPfd3Clk));
    PRINTF("这是一个[野火]-全系列开发板-uCOS-III  单任务实验!\n\n");
/* 硬件BSP初始化统统放在这里,比如LED,串口,LCD等 */
    LED_GPIO_Config();    //初始化LED(2)
/*让程序停在这里,不再往下执行*/
while (1);(3)

}

代码清单17-2: (1):初始化硬件后,顺便测试硬件,看下硬件是否正常工作。

代码清单17-2: (2):可以继续添加其它的硬件初始化和测试。硬件确认没有问题之后,硬件测试代码可删可不删,因为BSP_Init()函数只执行一遍。

代码清单17-2: (3):方便测试硬件好坏,让程序停在这里,不再继续往下执行,当测试完毕后,这个while(1);必须删除。

注意:以上仅仅是测试代码,以实际工程代码为准。

3.2. 创建单任务

这里,我们创建一个单任务,任务使用的栈和任务控制块都使用静态内存,即预先定义好的全局变量,这些预先定义好的全局变量都存在内部的SRAM中。

3.2.1. 定义任务栈

目前我们只创建了一个任务,当任务进入延时的时候,因为没有另外就绪的用户任务,那么系统就会进入空闲任务,空闲任务是uCOS系统自己创建并且启动的一个任务,优先级最低。当整个系统都没有就绪任务的时候,系统必须保证有一个任务在运行,空闲任务就是为这个设计的。当用户任务延时到期,又会从空闲任务切换回用户任务 。

在uCOS系统中,每一个任务都是独立的,他们的运行环境都单独的保存在他们的栈空间当中。那么在定义好任务函数之后,我们还要为任务定义一个栈,目前我们使用的是静态内存,所以任务栈是一个独立的全局变量,具体见代码清单17‑3。任务的栈占用的是MCU内部的RAM,当任务越多的时候,需要使用的栈空间就越大,即 需要使用的RAM空间就越多。一个MCU能够支持多少任务,就得看你的RAM空间有多少。

代码清单‑3定义任务栈
1
2
3
#define  APP_TASK_START_STK_SIZE                    128u

static  CPU_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];

3.2.2. 定义任务控制块

定义好任务函数和任务栈之后,我们还需要为任务定义一个任务控制块,通常我们称这个任务控制块为任务的身份证。在C代码上,任务控制块就是一个结构体,里面有非常多的成员,这些成员共同描述了任务的全部信息,具体见 代码清单17-4

代码清单‑4定义任务控制块
1
static OS_TCB AppTaskStartTCB;

3.2.3. 定义任务主体函数

任务实际上就是一个无限循环且不带返回值的C函数。目前,我们创建一个这样的任务,让开发板上面的LED灯以500ms的频率闪烁,具体实现见 代码清单17-5

代码清单‑5定义任务函数(此处为伪代码,以工程代码为准)
 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
static  void  AppTaskStart (void *p_arg)
{
    OS_ERR  err;

    (void)p_arg;
    OS_TRACE_INIT();                      //初始化uC / OS-III跟踪记录器

    BSP_OS_TickEnable();                           //启用滴答计时器和中断
    BSP_Init();                                    //板级初始化

#if OS_CFG_STAT_TASK_EN > 0u
    OSStatTaskCPUUsageInit(&err);          //无需任务运行即可计算CPU容量
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN
    CPU_IntDisMeasMaxCurReset();
#endif

while (DEF_TRUE) { (1)//任务体,通常都写成一个死循环
        LED1_TOGGLE;                        // LED灯每隔500ms闪烁一次
        OSTimeDly ( 500,OS_OPT_TIME_DLY,&err);(2)
    }


}

代码清单17-5: (1):任务必须是一个死循环,否则任务将通过LR返回,如果LR指向了非法的内存就会产生HardFault_Handler,而uCOS指向一个任务退出函数OS_TaskReturn(),它如果支持任务删除的话,则进行任务删除操作,否则就进入死循环中,这样子的任务是不安全的,所以避免这种情况 ,任务一般都是死循环并且无返回值的,只执行一次的任务在执行完毕要记得及时删除。

代码清单17-5: (2):任务里面的延时函数必须使用uCOS里面提供的阻塞延时函数,并不能使用我们裸机编程中的那种延时。这两种的延时的区别是uCOS里面的延时是阻塞延时,即调用OSTimeDly()函数的时候,当前任务会被挂起,调度器会切换到其它就绪的任务,从而实现多任务。如果还是使用裸机编程中的那种延时 ,那么整个任务就成为了一个死循环,如果恰好该任务的优先级是最高的,那么系统永远都是在这个任务中运行,比它优先级更低的任务无法运行,根本无法实现多任务,因此任务中必须有能阻塞任务的函数,才能切换到其他任务中。

3.2.4. 创建任务

一个任务的三要素是任务主体函数,任务栈,任务控制块,那么怎么样把这三个要素联合在一起?uCOS里面有一个叫任务创建函数OSTaskCreate(),它就是干这个活的。 它将任务主体函数,任务栈和任务控制块这三者联系在一起,让任务在创建之后可以随时被系统启动与调度,具体见 代码清单17-6

代码清单‑6创建任务
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                        (1)
            (CPU_CHAR   *)"App Task Start",                 (2)
            (OS_TASK_PTR ) AppTaskStart,                    (3)
            (void       *) 0,                                       (4)
            (OS_PRIO     ) APP_TASK_START_PRIO,             (5)
            (CPU_STK    *)&AppTaskStartStk[0],                      (6)
            (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,    (7)
            (CPU_STK_SIZE) APP_TASK_START_STK_SIZE,         (8)
            (OS_MSG_QTY  ) 5u,                              (9)
            (OS_TICK     ) 0u,                              (10)
            (void       *) 0,                                       (11)
(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), (12)
            (OS_ERR     *)&err);                                    (13)

代码清单17-6: (1):任务控制块,由用户自己定义。

代码清单17-6:(2):任务名字,字符串形式,这里任务名字最好要与任务函数入口名字一致,方便进行调试。

代码清单17-6:(3):任务入口函数,即任务函数的名称,需要我们自己定义并且实现。

代码清单17-6:(4):任务入口函数形参,不用的时候配置为0或者NULL即可,p_arg是指向可选数据区域的指针,用于将参数传递给任务,因为任务一旦执行,那必须是在一个死循环中,所以传参只在首次执行时有效。

代码清单17-6:(5):任务的优先级,由用户自己定义。

代码清单17-6:(6):指向堆栈基址的指针(即堆栈的起始地址)。

代码清单17-6:(7):设置堆栈深度的限制位置。这个值表示任务的堆栈满溢之前剩余的堆栈容量。例如,指定stk_size值的10%表示将达到堆栈限制,当堆栈达到90%满就表示任务的堆栈已满。

代码清单17-6:(8):任务堆栈大小,单位由用户决定,如果CPU_STK 被设置为CPU_INT08U,则单位为字节,而如果CPU_STK 被设置为CPU_INT16U,则单位为半字,同理,如果CPU_STK 被设置为CPU_INT32U,单位为字。在32位的处理器下(i.MX RT),一个字等于4个字节,那么任务大小就为APP_TASK_START_STK_SIZE * 4字节。

代码清单17-6:(9):设置可以发送到任务的最大消息数,按需设置即可。

代码清单17-6:(10):在任务之间循环时的时间片的时间量(以滴答为单位)。指定0则使用默认值。

代码清单17-6:(11):是指向用户提供的内存位置的指针,用作TCB扩展。例如,该用户存储器可以保存浮点寄存器的内容在上下文切换期间,每个任务执行的时间,次数、任务已经切换等。

代码清单17-6:(12):用户可选的任务特定选项,具体见 代码清单17-7

代码清单‑7任务特定选项
1
2
3
4
5
#define  OS_OPT_TASK_NONE         (OS_OPT)(0x0000u) (1)
#define  OS_OPT_TASK_STK_CHK      (OS_OPT)(0x0001u) (2)
#define  OS_OPT_TASK_STK_CLR      (OS_OPT)(0x0002u) (3)
#define  OS_OPT_TASK_SAVE_FP      (OS_OPT)(0x0004u) (4)
#define  OS_OPT_TASK_NO_TLS       (OS_OPT)(0x0008u) (5)

代码清单17-7: (1):未选择任何选项。

代码清单17-7: (2):启用任务的堆栈检查。

代码清单17-7: (3):任务创建时清除堆栈。

代码清单17-7: (4):保存任何浮点寄存器的内容,这需要CPU硬件的支持,CPU需要有浮点运算硬件与专门保存浮点类型数据的寄存器。

代码清单17-7: (5):指定任务不需要TLS支持。

代码清单17-6:(13):用于保存返回的错误代码。

3.2.5. 启动任务

当任务创建好后,是处于任务就绪,在就绪态的任务可以参与操作系统的调度。任务调度器只启动一次,之后就不会再次执行了,uCOS中启动任务调度器的函数是OSStart(),并且启动任务调度器的时候就不会返回,从此任务都由uCOS管理,此时才是真正进入实时操作系统中的第一步,具体见。

代码清单‑8启动任务
1
2
3
/\* 启动任务,开启调度 \*/

OSStart(&err);

3.2.6. main.c全貌

现在我们把任务主体,任务栈,任务控制块这三部分代码统一放到main.c中,我们在main.c文件中创建一个AppTaskStart任务,这个任务是仅是用于测试用户任务,以后为了方便管理,我们的所有的任务创建都统一放在这个任务中, 在这个任务中创建成功的任务就可以直接参与任务调度了,具体内容见 代码清单17-9

代码清单‑9main.c全貌
  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
**********************************************************************
* @file    main.c
* @author  fire
* @version V1.0
* @date    2019-xx-xx
* @brief   uCOS-III 系统移植
***********************************************************************
* @attention
*
* 实验平台:野火  i.MXRT1052开发板
* 论坛    :http://www.firebbs.cn
* 淘宝    :http://firestm32.taobao.com
*
***********************************************************************
*/
/*
*************************************************************************
*                         包含的文件
*************************************************************************
*/
//ucosiii系统相关
#include  <cpu.h>
#include  <lib_mem.h>
#include  <os.h>
#include  <bsp_os.h>
#include  <bsp_clk.h>
#include  <bsp_int.h>
#include"os_app_hooks.h"
#include"app_cfg.h"
//板级驱动
#include"bsp.h"

/*
***********************************************************************
*                               任务控制块TCB
***********************************************************************
*/

static  OS_TCB       AppTaskStartTCB;
/*
***********************************************************************
*                                 任务堆栈
************************************************************************
*/
static  CPU_STK      AppTaskStartStk[APP_TASK_START_STK_SIZE];

/*
***********************************************************************
*                                 函数原型
***********************************************************************
*/

static  void  AppTaskStart (void  *p_arg);

/*
***********************************************************************
* 函数名 : main
* 描述   : 标准的C函数入口
* 形参   : 无
* 返回值 : 无
************************************************************************
*/
int  main (void)
{
    OS_ERR  err;


    BSP_ClkInit();                       /* 初始化系统时钟       */
    BSP_IntInit();                       /* 初始化RAM中断向量表. */
    BSP_OS_TickInit();                   /* 初始化内核计时器     */

    Mem_Init();                          /* 初始化内存管理模块   */
    CPU_IntDis();                        /* 禁用所有中断         */
    CPU_Init();                          /* 初始化uC/CPU相关     */

    OSInit(&err);                        /*初始化uC / OS-III     */
if (err != OS_ERR_NONE) {
while (1);
    }

App_OS_SetAllHooks();//设置所有应用程序钩子函数

/* 创建起始任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskStartTCB, //任务控制块地址
                (CPU_CHAR   *)"App Task Start", //任务名称
                (OS_TASK_PTR ) AppTaskStart,    //任务函数
                (void       *) 0,        //传递给任务函数(形参p_arg)的实参
                (OS_PRIO     ) APP_TASK_START_PRIO, //任务的优先级
                (CPU_STK    *)&AppTaskStartStk[0], //任务堆栈的基地址
                (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10, //任务堆栈空间剩下1/10
制其增长
                (CPU_STK_SIZE) APP_TASK_START_STK_SIZE, //任务堆栈空间(单位:
eof(CPU_STK))

             (OS_MSG_QTY  ) 5u, //任务可接收的最大消息数
             (OS_TICK     ) 0u, //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/
             (void       *) 0, //任务扩展(0表不扩展)
             (OS_OPT      )(OS_OPT_TASK_STK_CHK |OS_OPT_TASK_STK_CLR), //任务选

             (OS_ERR     *)&err);
if (err != OS_ERR_NONE) {
while (1);
    }

    OSStart(&err);
启动多任务管理(交由uC/OS-III控制)

while (DEF_ON) {
        ;
    }
}


/*
********************************************************************************
* 函数名:AppTaskStart
* 描述   : 这是一个启动任务,在多任务系统启动后,必须初始化滴答计数器(在 BSP_Init 中实现)。
* 形参   : p_arg   是OSTaskCreate()在创建该任务时传递过来的形参。
* 返回值 : 无
* 注意   : 1) 第一行代码 (void)p_arg; 是为了防止编译器报错,因为形参p_arg并没有用到
*********************************************************************************
*/

static  void  AppTaskStart (void *p_arg)
{
    OS_ERR  err;

    (void)p_arg;

    OS_TRACE_INIT();                               //初始化uC / OS-III跟踪记录器

    BSP_OS_TickEnable();                           //启用滴答计时器和中断
    BSP_Init();                                    //板级初始化

#if OS_CFG_STAT_TASK_EN > 0u
    OSStatTaskCPUUsageInit(&err);                  //无需任务运行即可计算CPU容量
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN
    CPU_IntDisMeasMaxCurReset();
#endif

while (DEF_TRUE) {                             //任务体,通常都写成一个死循环
        LED1_TOGGLE;                 // LED灯每隔500ms闪烁一次
        OSTimeDly ( 500,OS_OPT_TIME_DLY,&err);
    }


}

3.3. 下载验证

将程序编译好,用DAP仿真器把程序下载到野火i.MX RT开发板(具体型号根据你买的板子而定,每个型号的板子都配套有对应的程序),可以看到板子上面的LED灯已经在闪烁,说明我们创建的单任务已经跑起来了。

3.4. 创建多任务

创建多任务只需要按照创建单任务的套路依葫芦画瓢即可,接下来我们创建四个任务,分别是起始任务、 LED1 任务和 LED2 任务。任务1让一个LED灯闪烁,任务2让另外一个LED闪烁,两个LED闪烁的频率不一样,三个任务的优先级不一样。主函数运行时创建起始任务,起始任务运行时进行创建二个LED 灯的任务和删除自身,之后就运行二个 LED 灯的任务。二个 LED 灯的任务优先级不一样,LED1 任务为 LED1 每隔 1 秒切换一次亮灭状态, LED2 任务为 LED2 每隔 5 秒切换一次亮灭状态,首先在“ app_cfg.h”里,增加定义二个 LED 灯任务的优先级和栈空间大小,然后修改main.c的源码,具体见 代码清单17-10 高亮部分。

代码清单‑10main.c全貌
  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
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*
********************************************************************
* @file    main.c
* @author  fire
* @version V1.0
* @date    2019-xx-xx
* @brief   多任务切换2个led
*********************************************************************
* @attention
*
* 实验平台:野火  i.MXRT1052开发板
* 论坛    :http://www.firebbs.cn
* 淘宝    :http://firestm32.taobao.com

********************************************************************
*/
/*
**********************************************************************
*                          包含的文件
**********************************************************************
*/
//ucosiii系统相关
#include  <cpu.h>
#include  <lib_mem.h>
#include  <os.h>
#include  <bsp_os.h>
#include  <bsp_clk.h>
#include  <bsp_int.h>
#include"os_app_hooks.h"
#include"app_cfg.h"
//板级驱动
#include"bsp.h"


/*
***********************************************************************
*                        任务控制块TCB
*************************************************************************
*/

static  OS_TCB       AppTaskStartTCB;
static  OS_TCB       AppTaskLed1TCB;
static  OS_TCB       AppTaskLed2TCB;

/*
*************************************************************************
*                           任务堆栈
*************************************************************************
*/
static  CPU_STK      AppTaskStartStk[APP_TASK_START_STK_SIZE];
static  CPU_STK      AppTaskLed1Stk[APP_TASK_LED1_STK_SIZE];
static  CPU_STK      AppTaskLed2Stk[APP_TASK_LED2_STK_SIZE];

/*
**********************************************************************
*                           函数原型
*********************************************************************
*/

static  void  AppTaskStart (void  *p_arg);
static  void  AppTaskLed1  ( void * p_arg );
static  void  AppTaskLed2  ( void * p_arg );

/*
*****************************************************************
* 函数名 : main
* 描述   : 标准的C函数入口
* 形参   : 无
* 返回值 : 无
*****************************************************************
*/
int  main (void)
{
    OS_ERR  err;


    BSP_ClkInit();                       /* 初始化系统时钟       */
    BSP_IntInit();                       /* 初始化RAM中断向量表. */
    BSP_OS_TickInit();                   /* 初始化内核计时器     */

    Mem_Init();                          /* 初始化内存管理模块   */
    CPU_IntDis();                        /* 禁用所有中断         */
    CPU_Init();                          /* 初始化uC/CPU相关     */

    OSInit(&err);                        /*初始化uC / OS-III     */
if (err != OS_ERR_NONE) {
while (1);
    }

    App_OS_SetAllHooks();  //设置所有应用程序钩子函数

/* 创建起始任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskStartTCB, //任务控制块地址
                (CPU_CHAR   *)"App Task Start",   //任务名称
                (OS_TASK_PTR ) AppTaskStart,    //任务函数
                (void       *) 0,    //传递给任务函数(形参p_arg)的实参
                (OS_PRIO     ) APP_TASK_START_PRIO,    //任务的优先级
                (CPU_STK    *)&AppTaskStartStk[0], //任务堆栈的基地址
                (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10, //任务堆栈空间剩下1/10
制其增长
                (CPU_STK_SIZE) APP_TASK_START_STK_SIZE, //任务堆栈空间(单位:
eof(CPU_STK))
                (OS_MSG_QTY  ) 5u,  //任务可接收的最大消息数
                (OS_TICK     ) 0u, //任务的时间片节拍数(0表默认值
fg_TickRate_Hz/10)
                (void       *) 0, //任务扩展(0表不扩展)
                  (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务

              (OS_ERR     *)&err);
if (err != OS_ERR_NONE) {
while (1);
    }

    OSStart(&err);
启动多任务管理(交由uC/OS-III控制)

while (DEF_ON) {
        ;
    }
}


/*
**********************************************************************
* 函数名:AppTaskStart
* 描述   : 这是一个启动任务,在多任务系统启动后,必须初始化滴答计数器(在 BSP_Init 中实现)。
* 形参   : p_arg   是OSTaskCreate()在创建该任务时传递过来的形参。
* 返回值 : 无
* 注意   : 1) 第一行代码 (void)p_arg; 是为了防止编译器报错,因为形参p_arg并没有用到
***********************************************************************
*/

static  void  AppTaskStart (void *p_arg)
{
    OS_ERR  err;

    (void)p_arg;

    OS_TRACE_INIT();                               //初始化uC / OS-III跟踪记录器

    BSP_OS_TickEnable();                           //启用滴答计时器和中断
    BSP_Init();                                    //板级初始化

#if OS_CFG_STAT_TASK_EN > 0u
    OSStatTaskCPUUsageInit(&err);                  //无需任务运行即可计算CPU容量
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN
    CPU_IntDisMeasMaxCurReset();
#endif

/* 创建Led1任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskLed1TCB, //任务控制块地址
                (CPU_CHAR   *)"App Task Led1", //任务名称
                (OS_TASK_PTR ) AppTaskLed1, //任务函数
                (void       *) 0, //传递给任务函数(形参p_arg)的实参
                (OS_PRIO     ) APP_TASK_LED1_PRIO, //任务的优先级
                (CPU_STK    *)&AppTaskLed1Stk[0], //任务堆栈的基地址
                (CPU_STK_SIZE) APP_TASK_LED1_STK_SIZE / 10,                //任务
空间剩下1/10时限制其增长
                (CPU_STK_SIZE) APP_TASK_LED1_STK_SIZE, //任务堆栈空间(单位:
eof(CPU_STK))
                (OS_MSG_QTY  ) 5u, //任务可接收的最大消息数
                (OS_TICK     ) 0u, //任务的时间片节拍数(0表默认值
fg_TickRate_Hz/10)
                (void       *) 0, //任务扩展(0表不扩展)
                (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务

                (OS_ERR     *)&err); //返回错误类型

/* 创建Led2任务 */
    OSTaskCreate((OS_TCB     *)&AppTaskLed2TCB, //任务控制块地址
                (CPU_CHAR   *)"App Task Led2", //任务名称
                (OS_TASK_PTR ) AppTaskLed2,    //任务函数
                (void       *) 0,     //传递给任务函数(形参p_arg)的实参
                (OS_PRIO     ) APP_TASK_LED2_PRIO,   //任务的优先级
                (CPU_STK    *)&AppTaskLed2Stk[0],  //任务堆栈的基地址
                (CPU_STK_SIZE) APP_TASK_LED2_STK_SIZE / 10, //任务堆栈空间剩下1/10
制其增长
                (CPU_STK_SIZE) APP_TASK_LED2_STK_SIZE,    //任务堆栈空间(单位:
eof(CPU_STK))
                (OS_MSG_QTY  ) 5u,     //任务可接收的最大消息数
                (OS_TICK     ) 0u,  //任务的时间片节拍数(0表默认值
fg_TickRate_Hz/10)
                (void       *) 0,     //任务扩展(0表不扩展)
                (OS_OPT      )(OS_OPT_TASK_STK_CHK | S_OPT_TASK_STK_CLR), //任务选

                (OS_ERR     *)&err);
while (1) {
        OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err );  //延时1s
    }

}
/*
***********************************************************************
*                        LED1 任务
***********************************************************************
*/

static  void  AppTaskLed1 ( void * p_arg )
{
    OS_ERR      err;


    (void)p_arg;                                      //没有用到形参,防止编译器报错

    LED_RGBOFF;                                      //关闭RGB LED

while (DEF_TRUE) {                               //任务体,通常都写成一个死循环
        LED1_TOGGLE;                                 //LED1闪烁
        OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err );  //延时1s
    }


}


/*
************************************************************************
*                             LED2 任务
************************************************************************
*/

static  void  AppTaskLed2 ( void * p_arg )
{
    OS_ERR      err;


    (void)p_arg;                                      //没有用到形参,防止编译器报错

    LED_RGBOFF;                                      //关闭RGB LED

while (DEF_TRUE) {                               //任务体,通常都写成一个死循环
        LED2_TOGGLE;                                 //LED2闪烁
        OSTimeDly ( 5000, OS_OPT_TIME_DLY, & err );  //延时5s
    }

}

3.5. 下载验证

将程序编译好,用DAP仿真器把程序下载到野火i.MX RT系列开发板(具体型号根据你买的板子而定,每个型号的板子都配套有对应的程序),可以看到板子上面的三个LED灯以不同的频率在闪烁,说明我们创建的多任务已经跑起来了。