μC/OS-III内核实现与应用开发实战指南:基于STM32
上QQ阅读APP看书,第一时间看更新

3.6 main()函数

main()函数在文件app.c中编写,app.c文件的完整代码参见代码清单3-29。

代码清单3-29 app.c文件

1 /*
 2 *******************************************************************
 3 *                          包含的头文件
 4 *******************************************************************
 5 */
 6 #include "os.h"
 7 #include "ARMCM3.h"
 8 
 9 /*
10 *******************************************************************
11 *                            宏定义
12 *******************************************************************
13 */
14 
15 
16 /*
17 *******************************************************************
18 *                          全局变量
19 *******************************************************************
20 */
21 
22 uint32_t flag1;
23 uint32_t flag2;
24 
25 /*
26 *******************************************************************
27 *                        TCB & STACK &任务声明
28 *******************************************************************
29 */
30 #define  TASK1_STK_SIZE       20
31 #define  TASK2_STK_SIZE       20
32 
33 static   CPU_STK   Task1Stk[TASK1_STK_SIZE];
34 static   CPU_STK   Task2Stk[TASK2_STK_SIZE];
35 
36 static   OS_TCB    Task1TCB;
37 static   OS_TCB    Task2TCB;
38 
39 void     Task1( void *p_arg );
40 void     Task2( void *p_arg );
41 
42 /*
43 *******************************************************************
44 *                            函数声明
45 *******************************************************************
46 */
47 void delay(uint32_t count);
48 
49 /*
50 *******************************************************************
51 *                            main()函数
52 *******************************************************************
53 */
54 /*
55 * 注意事项:1)该工程使用软件仿真,debug需要选择为Ude Simulator
56 *         2)在Target选项卡中把晶振Xtal(MHz)的值改为25,默认是12,
57 *              改成25是为了与system_ARMCM3.c中定义的__SYSTEM_CLOCK相同,
58 *         3)确保仿真时时钟一致
59 */
60 int main(void)
61 {
62     OS_ERR err;
63 
64 
65 
66     /* 初始化相关的全局变量 */
67     OSInit(&err);
68 
69     /* 创建任务 */
70     OSTaskCreate ((OS_TCB*)      &Task1TCB,
71                   (OS_TASK_PTR ) Task1,
72                   (void *)       0,
73                   (CPU_STK*)     &Task1Stk[0],
74                   (CPU_STK_SIZE) TASK1_STK_SIZE,
75                   (OS_ERR *)     &err);
76 
77     OSTaskCreate ((OS_TCB*)      &Task2TCB,
78                   (OS_TASK_PTR ) Task2,
79                   (void *)       0,
80                   (CPU_STK*)     &Task2Stk[0],
81                   (CPU_STK_SIZE) TASK2_STK_SIZE,
82                   (OS_ERR *)     &err);
83 
84     /* 将任务加入就绪列表 */
85     OSRdyList[0].HeadPtr = &Task1TCB;
86     OSRdyList[1].HeadPtr = &Task2TCB;
87 
88     /* 启动操作系统,将不再返回 */
89     OSStart(&err);
90 }
91 
92 /*
93 *******************************************************************
94 *                           函数实现
95 *******************************************************************
96 */
97 /* 软件延时 */
98 void delay (uint32_t count)
99 {
100 for (; count!=0; count--);
101 }
102 
103 
104 
105 /* 任务1 */
106 void Task1( void *p_arg )
107 {
108     for ( ;; ) {
109         flag1 = 1;
110         delay( 100 );
111         flag1 = 0;
112         delay( 100 );
113 
114         /* 任务切换,这里是手动切换 */
115         OSSched();
116     }
117 }
118 
119 /* 任务2 */
120 void Task2( void *p_arg )
121 {
122     for ( ;; ) {
123         flag2 = 1;
124         delay( 100 );
125         flag2 = 0;
126         delay( 100 );
127 
128         /* 任务切换,这里是手动切换 */
129         OSSched();
130     }
131 }

代码清单3-29中的所有代码在本小节之前都有循序渐进的讲解,这里只是融合在一起放在main()函数中。Task1和Task2并不会真正自动切换,而是在各自的函数体中加入了OSSched()函数来实现手动切换。OSSched()函数的实现具体参见代码清单3-30。

代码清单3-30 OSSched()函数

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

OSSched()函数的调度算法很简单,即如果当前任务是任务1,那么下一个任务就是任务2,如果当前任务是任务2,那么下一个任务就是任务1,然后调用OS_TASK_SW()函数触发PendSV异常,再在PendSV异常中实现任务的切换。在此后的章节中,我们将继续完善,加入SysTick中断,从而实现系统调度的自动切换。OS_TASK_SW()函数其实是一个宏定义,具体是往中断及状态控制寄存器SCB_ICSR的位28(PendSV异常启用位)写入1,从而触发PendSV异常。OS_TASK_SW()函数在os_cpu.h文件中实现(第一次使用os_cpu.h时需要自行在文件夹C-CPU中新建并添加到工程的C/CPU组),文件的内容具体参见代码清单3-31。

代码清单3-31 os_cpu.h文件

1 #ifndef  OS_CPU_H
 2 #define  OS_CPU_H
 3 
 4 /*
 5 *******************************************************************
 6 *                              宏定义
 7 *******************************************************************
 8 */
 9 
10 #ifndef  NVIC_INT_CTRL
11 /* 中断控制及状态寄存器 SCB_ICSR */
12 #define  NVIC_INT_CTRL                 *((CPU_REG32 *)0xE000ED04)
13 #endif
14 
15 #ifndef  NVIC_PENDSVSET
16 /* 触发PendSV异常的值 Bit28:PENDSVSET */
17 #define  NVIC_PENDSVSET                0x10000000
18 #endif
19 
20 /* 触发PendSV异常 */
21 #define  OS_TASK_SW()               NVIC_INT_CTRL = NVIC_PENDSVSET
22 /* 触发PendSV异常 */
23 #define  OSIntCtxSw()               NVIC_INT_CTRL = NVIC_PENDSVSET
24 /*
25 *******************************************************************
26 *                              函数声明
27 *******************************************************************
28 */
29 void OSStartHighRdy(void);/* 在os_cpu_a.s中实现 */
30 void PendSV_Handler(void);/* 在os_cpu_a.s中实现 */
31 
32 
33 #endif/* OS_CPU_H */