3.4 启动系统
任务创建好且系统初始化完毕之后,就可以启动系统了。系统启动函数OSStart()在os_core.c中定义,具体实现参见代码清单3-23。
代码清单3-23 OSStart()函数
1 void OSStart (OS_ERR *p_err) 2 { 3 if ( OSRunning == OS_STATE_OS_STOPPED ) {(1) 4 /* 手动配置任务1先运行 */ 5 OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;(2) 6 7 /* 启动任务切换,不会返回 */ 8 OSStartHighRdy();(3) 9 10 /* 不会运行到这里,如果运行到这里,则表示发生了错误 */ 11 *p_err = OS_ERR_FATAL_RETURN; 12 } else { 13 *p_err = OS_STATE_OS_RUNNING; 14 } 15 }
代码清单3-23(1):如果系统是第一次启动,则if为真,继续往下运行。
代码清单3-23(2):OSTCBHighRdyPtr指向第一个要运行的任务的TCB。因为暂时不支持优先级,所以系统启动时先手动指定第一个要运行的任务。
代码清单3-23(3):OSStartHighRdy()用于启动任务切换,即配置PendSV的优先级为最低,然后触发PendSV异常,在PendSV异常服务函数中进行任务切换。该函数不再返回,在文件os_cpu_a.s中定义(第一次使用os_cpu_a.s时需要自行在文件夹μC/OS-III\Ports中新建并添加到工程的μC/OS-III Ports组),用汇编语言编写,具体实现参见代码清单3-24。os_cpu_a.s文件中涉及的ARM汇编指令的用法如表3-1所示。
表3-1 常用的ARM汇编指令
代码清单3-24 OSStartHighRdy()函数
1 ;******************************************************************* 2 ; 开始第一次上下文切换 3 ; 1)配置PendSV异常的优先级为最低 4 ; 2)在开始第一次上下文切换之前,设置psp=0 5 ; 3)触发PendSV异常,开始上下文切换 6 ;******************************************************************* 7 OSStartHighRdy 8 LDR R0, = NVIC_SYSPRI14 ; 设置PendSV 异常优先级为最低(1) 9 LDR R1, = NVIC_PENDSV_PRI 10 STRB R1, [R0] 11 12 MOVS R0, #0 ;设置PSP的值为0,开始第一次上下文切换(2) 13 MSR PSP, R0 14 15 LDR R0, =NVIC_INT_CTRL ; 触发PendSV异常(3) 16 LDR R1, =NVIC_PENDSVSET 17 STR R1, [R0] 18 19 CPSIE I ; 启用总中断,NMI和HardFault除外(4) 20 21 OSStartHang 22 B OSStartHang ; 程序应永远不会运行到这里
代码清单3-24中涉及的NVIC_INT_CTRL、NVIC_SYSPRI14、NVIC_PENDSV_PRI和NVIC_PENDSVSET这4个常量在os_cpu_a.s的开头定义,具体参见代码清单3-25,有关这4个常量的含义参见代码注释即可。
代码清单3-25 NVIC_INT_CTRL、NVIC_SYSPRI14、NVIC_PENDSV_PRI和NVIC_PENDSVSET常量定义
1 ;******************************************************************** 2 ; 常量 3 ;******************************************************************** 4 ;-------------------------------------------------------------------- 5 ;有关内核外设寄存器定义可参考官方文档:STM32F10xxx Cortex-M3 programming manual 6 ;系统控制块外设地址范围:0xE000ED00~0xE000ED3F 7 ;-------------------------------------------------------------------- 8 NVIC_INT_CTRL EQU 0xE000ED04 ; 中断控制及状态寄存器 SCB_ICSR 9 NVIC_SYSPRI14 EQU 0xE000ED22 ; 系统优先级寄存器 SCB_SHPR3 10 ; 位16~23 11 NVIC_PENDSV_PRI EQU 0xFF ; PendSV 优先级的值(最低) 12 NVIC_PENDSVSET EQU 0x10000000 ; 触发PendSV异常的值位28:PENDSVSET
代码清单3-24(1):配置PendSV的优先级为0XFF,即最低。在μC/OS-III中,上下文切换是在PendSV异常服务程序中执行的,配置PendSV的优先级为最低,从而排除了在中断服务程序中执行上下文切换的可能。
代码清单3-24(2):设置PSP的值为0,开始第一个任务切换。在任务中,使用的栈指针都是PSP,后面如果判断出PSP为0,则表示第一次任务切换。
代码清单3-24(3):触发PendSV异常,如果中断启用且编写了PendSV异常服务函数,则内核会响应PendSV异常,去执行PendSV异常服务函数。
代码清单3-24(4):开中断,因为有些用户在main()函数中会先关掉中断,等全部初始化完成后,在启动操作系统时才开中断。为了快速地开关中断,ARM CM3专门设置了一条CPS指令,有4种用法,具体参见代码清单3-26。
代码清单3-26 CPS指令用法
1 CPSID I ;PRIMASK=1 ;关中断 2 CPSIE I ;PRIMASK=0 ;开中断 3 CPSID F ;FAULTMASK=1 ;关异常 4 CPSIE F ;FAULTMASK=0 ;开异常
代码清单3-26中,PRIMASK和FAULTMASK是ARM CM3中3个中断屏蔽寄存器中的两个,还有一个是BASEPRI,有关这3个寄存器的详细用法如表3-2所示。
表3-2 ARM CM3中断屏蔽寄存器