一、滴答定时器SysTick
滴答定时器是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD 寄存器中自动重装载定时器初值,只要不把它在 SysTick 控制以及状态寄存器中的使能位清零,就将永久不息。SysTick 的最大使命,就是定期地产生异常请求作为系统的时基。 OS 都需要这种“滴答”来推动任务和时间的管理。
我们在移植 UCOSII 的过程中就要使用滴答定时器来作为系统时钟, 首先就是对滴答定时器的设置,主要是设置它的定时周期,我们是在 delay_init()函数中完成滴答定时器设置的,delay_init()函数代码如下。
其 中 红 色 代 码 部 分 就 是 在 使 用 UCOSII 时 配 置 SysTick 的 代 码 , 如 果
SYSTEM_SUPPORT_OS 被定义了就说明使用了 UCOS,那么我们就需要配置 SysTick。 首先要
根据 UCOSII 中的定义的 OS_TICKS_PER_SEC 来计算出 SysTick 的装载值 reload,开启 SysTick中断,将 reload 值写进 SysTick 的 LOAD 寄存器中,最后开启 SysTick。开启 SysTick 后还要编写 SysTick 的中断服务函数 SysTick_Handler(),函数代码如下,同样也采用了条件编译。
//systick 中断服务函数,使用 OS 时用到
void SysTick_Handler(void)
{HAL_IncTick();//HAL库用此函数获取时间,这里一定要注意
if(delay_osrunning == 1)//OS开始跑了,才执行正常的调度处理
{
OSIntEnter();//进入中断
OSTimeTick();//调用ucos的时钟服务程序
OSIntExit();//触发任务切换中断
}
}
注意!!!! 滴答定时器中断服务函数 SysTick_Handler()中一定要调用 HAL_IncTick(), 因为
HAL 库使用此函数来获得系统时间(非 UCOS 系统时间)供 HAL 库中延时函数使用!!! 如果没
有调用的话 HAL 库会工作不正常。
二、os_cpu_a.asm 文件
为了方便起见, os_cpu_a.asm 文件分段来介绍。
上面代码分为两部分,上半部分使用 IMPORT 来定义,下半部分使用 EXPORT 来定义。IMPORT 定义表示这是一个外部变量的标号,不是在本程序定义的; EXPORT 定义表示这些函数是在本文件中定义的,供其它文件调用。
EQU 和 C 语言中的#define 一样, 定义一个宏。 NVIC_INT_CTRL 为中断控制寄存器,地址为 0xE000ED04; NVIC_SYSPRI14 为 PendSV 中断优先级寄存器,地址为 0xE000ED22;
NVIC_PENDSV_PRI 为 PendSV 和 Systick 的中断优先级,这里为 0xFFFF, 都为最低优先级;
OS_CPU_FP_Reg_Push 和 OS_CPU_FP_Reg_Pop 是对 FPU 寄存器进行入栈和出栈操作的,对于带有 FPU 的 MCU 来说非常重要!
OS_CPU_SR_Save 和 OS_CPU_SR_Restore 是开关中断的汇编代码, 通过给 PRIMASK 写 1
来关中断,写 0 来打开中断。 这里也可是使用 CPS 指令来快速的开关中断,OS_CPU_SR_Save 中就使用了 CPSID I 来关中断。
OSStartHighRdy是由OSStart()调用,用来开启多任务的,如果多任务开启失败的话就会进入OSStartHang。
OSCtxSw 和 OSIntCtxSw 这两个是用来做任务切换的, 这两个看起来都是一样的, 其实它们都只是触发一个 PendSV 中断,具体的切换过程在 PendSV 中断服务函数里面进行。 这两个函数看起来是一样的,但是他们的意义是不同的, OSCtxSw 是任务级切换,比如从任务 A 切换到任务B, OSIntCtxSw 是中断级切换,是从中断退出时切换到一个任务中, 从中断切换到任务时,CPU 的寄存器入栈工作已经完成,无需做第二次。
上面的汇编代码才是真正的任务切换程序, 在每行代码后都有详细的注释,为了更好的理解,我对代码中打标号的地方重点讲解一下。
(1)如果 PSP 为 0 的话说明是第一次做任务切换,而任务创建的时候会调用堆栈初始化函数 OSTaskStkInit()来初始化堆栈,在初始化的过程中已经做了入栈处理,所以这里就不需要在做入栈处理,直接跳转到 PendSV_Handler_Nosave。
(2)此时 SP 指向的就是要运行的最高优先级的任务。
(3)因为进入中断使用的是 MSP,而退出中断的时候使用的是 PSP,因此这里需要将 LR 的位 2 置 1。