/**********************************战舰版实例**************************************/
P80:μc/osⅡ时钟 硬件定时器中断(使用了STM32中的Systick中断)每产生一次,μc/osⅡ时钟就会进入一次系统中断服务程序(OSTickISR()),系统中断服务程序通过调用OSTimeTick()来完成系统每个时钟节拍所要完成的工作(包括遍历每个任务控制块将其延时参数减1等)。 任哲版教材P87钩子函数在战舰的对应源码: 钩子函数的使用(以OSTimerTickHook为例): 需要到os_cfg.h中把这两个宏#define成>0。 P88:OSTimeDly在OSTimeDly中完成OSTCBCur->OSTCBDly(任务延时寄存器)的写入并进行一次任务切换:
关于 μc/osⅡ的疑难: ·μc/osⅡ的时钟OSTimeTick()是怎么与STM32的SysTick关联起来的? 战舰开发板配套程序中在main()中有delay_init()(delay.c下)函数,其原代码如下: void delay_init() { #ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了. u32 reload; #endif SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8 fac_us=SystemCoreClock/8000000; //为系统时钟的1/8 #ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了. reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为K reload*=1000000/OS_TICKS_PER_SEC;//根据OS_TICKS_PER_SEC设定溢出时间 //reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右 fac_ms=1000/OS_TICKS_PER_SEC;//代表ucos可以延时的最少单位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断 SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK #else fac_ms=(u16)fac_us*1000;//非ucos下,代表每个ms需要的systick时钟数 #endif } 可见delay_init()开启了STM32的SYSTICK中断,下面继续找SYSTICK的中断服务程序(同样也在delay.c),代码如下: void SysTick_Handler(void) { OSIntEnter(); //进入中断,其作仅仅是将判断中断层数是否达到255否则OSIntNesting++ OSTimeTick(); //调用ucos的时钟服务程序 OSIntExit(); //触发任务切换软中断 } 发现OSTimeTick(); 在 SYSTICK的中断服务程序被调用,现在μc/osⅡ的时钟OSTimeTick()就与STM32的SysTick关联了起来。 OSIntExit (void)的作用除了执行了OSIntNesting--之外 还进行了一次中断级任务调度OSIntCtxSw() 。 ·OSIntCtxSw()切换任务的原理: Step1:SIntCtxSw()触发了一次软件中断,代码如下 ;/************************************************************************************** ;* 函数名称: OSIntCtxSw ;* 功能描述: 中断级任务切换(其实是进行了一次软件中断) ;* 参 数: None ;* 返 回 值: None ;***************************************************************************************/ OSIntCtxSw PUSH {R4, R5} LDR R4, =NVIC_INT_CTRL ;触发PendSV异常 (causes context switch) ;NVIC_INT_CTRL就是软件中断控制寄存器 LDR R5, =NVIC_PENDSVSET ;NVIC_PENDSVSET是触发软件中断的值. STR R5, [R4] ;将R5中的字数据写入以R4为地址的存储器中就发生了PendSV中断 POP {R4, R5} BX LR NOP Step2:执行完了step后会进入软件中断服务函数,代码(在os_cpu_aasm中)如下 ;/************************************************************************************** ;* 函数名称: OSPendSV ;* ;* 功能描述: 该函数实际上完成了cpu各寄存器的压栈和新任务堆栈向cpu的进栈; ;* 参 数: None ;* ;* 返 回 值: None ;***************************************************************************************/ PendSV_Handler ;软件中断服务函数 CPSID I ; Prevent interruption during context switch MRS R0, PSP ; PSP is process stack pointer 如果在用PSP堆栈,则可以忽略保存寄存器,参考CM3权威中的双堆栈-白菜注 CBZ R0, PendSV_Handler_Nosave ; Skip register save the first time SUBS R0, R0, #0x20 ; Save remaining regs R4-11 on process stack STM R0, {R4-R11} LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP; =OSTCBCur就是取的OSTCBCur ;首地址,即任务控制块的堆栈。 LDR R1, [R1] STR R0, [R1] ; R0 is SP of process being switched out
|