|
SysTick是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。如下图所示为SysTic的相关寄存器:
CM3允许为SysTick提供两个时钟源以供选择。第一个是内核的“自由运行时钟”FCLK。“自由”表现在它不来自系统时钟HCLK,因此在系统时钟停止时FCLK也继续运行。第二个是一个外部的参考时钟。但是使用外部时钟时,因为它在内部是通过FCLK来采样的,因此其周期必须至少是FCLK的两倍(采样定理)。很多情况下芯片厂商都会忽略此外部参考时钟,因此通常不可用。通过检查校准寄存器的位[31](NOREF),可以判定是否有可用的外部时钟源,而芯片厂商则必须把该引线连接至正确的电平。
当SysTick定时器从1计到0时,它将把COUNTFLAG位置位;而下述方法可以清零之:
1、读取SysTick控制及状态寄存器(STCSR)
2、往SysTick当前值寄存器(STCVR)中写任何数据
SysTick的最大使命,就是定期地产生异常请求,作为系统的时基。OS都需要这种“滴答”来推动任务和时间的管理。如欲使能SysTick异常,则把STCSR.TICKINT置位。另外,如果把向量表重定位到了SRAM中,还需要为SysTick异常建立向量,提供其服务例程的入口地址,如下段代码所演示:
MOV R0, #0xF ; 异常号:15
LDR R1, =systick_handler ; 加载服务例程的入口地址
LDR R2, =0xE000ED08 ; 加载向量表偏移量寄存器的地址
LDR R2, [R2] ; 读取向量表的首地址
STR R1, [R2, R0, LSL #2] ; 写入向量
下面的代码演示启用SysTick的基本程序
; 使能SysTick定时器,并且使能SysTick异常
LDR R0, =0xE000E010 ; 加载STCSR的地址
MOV R1, #0
STR R1, [R0] ; 先停止SysTick,以防意外产生异常请求
LDR R1, =0x3FF ; 让SysTick每1024周期计完一次。因为是从1023数到; 0,总共数了1024个周期,所以加载值为0x3FF
STR R1, [R0,#4] ; 写入重装载的值
STR R1, [R0,#8] ; 往STCVR中写任意的数,以确保清除COUNTFLAG标志
MOV R1, #0x7 ; 选择FCLK作为时钟源,并使能SysTick及其异常请求
STR R1, [R0] ; 写入数值,开启定时
除此之外,SysTick定时器还提供了走完10ms所需要的格数(TENMS位段),作为时间校准的参考信息。在CM3处理器的顶层有一个24位的输入,芯片厂商可以写入一个10ms的加载值,写程序时就可以读取STCR寄存器中的TENMS位段来获取此信息。不一定每个芯片都实现了此功能,因此在使用时还需查阅芯片的数据手册。
SysTick定时器还可以用作闹钟,作为启动一个特定任务的时间依据。例如,如果需要在300周期后执行一段代码,就可以在SysTick异常服务例程中设置执行那段代码的软件标志。使用SysTick时,清零CURRENT再编程RELOAD寄存器,以使它在300周期后产生异常,如下述代码所演示:
LDR r0, =15
LDR r1, =SysTickAlarm ; SysTick异常服务例程为SetupExcpHanler
BL SetupExcpHandler ; 调用前面章节讲到的子程来建立向量
LDR R0, =0xE000E010 ; SysTick寄存器组的基地址
MOV R1, #0 ; 编程前先除能SysTick
STR R1, [R0]
STR R1, [R0,#0x8] ; 清零CURRENT
LDR R1, =(300-12) ; 设置装载值。减去12是为了补偿中延迟
STR R1, [R0,#0x4] ; 写入RELOAD
LDR R4, =SysTickFired ; 在RAM中的一个变量,指示是计时到期
MOV R5, #0 ; 初始为0
STR R5, [R4]
MOV R1, #0x7 ; 使用FCLK,使能SysTick,使能SysTick异常
STR R1, [R0] ; 启动计时
LDR R4, =SysTickFired
WaitLoop
LDR R5, [R4] ; 循环查询软件标志
CMP R5, #0
BEQ WaitLoop
... ; SysTickFired在服务例程中被置位,主程序可以继续执行
本例中使用以前讲到的SetupExcpHandler来建立向量表,但注意:必须重定位向量表到RAM中才行。
SetupExcpHandler
; 入口条件:R0 = 异常号
; 入口条件:R1 = 异常服务例程
PUSH {R0, R2, LR}
LDR R2, =NVIC_VECTTBL
LDR R2, [R2] ; 读取向量表的地址
STR.W R1, [R2, R0, LSL #2] ; 表中[R2+R0<<2]的位置就是为该向量的
POP {R0, R2, PC} ; 快速返回
因为计数器是从0开始计数的,所以它会立即把300-12加载入CURRENT。12是中断响应的最短延时,因此减去它用以补偿。但是如果有更高优先级的异常抢占或者阻塞了它,则中断延迟还是会有的。
另外要注意的,减去12只适用于一次性(one shot)的闹钟操作,在这种情况下必须在SysTick服务例程中按停这个SysTick。进一步地,如果其它异常把它延迟得太久,就有可能会使SysTick异常被悬起两次。因此,对于单次处理时,还需要其它一些步骤来消灭二次触发:
SysTickAlarm ; SYSTICK exception handler
PUSH {LR}
LDR R0, =0xE000E010 ; SYSTICK寄存器组的基地址
MOV R1, #0
STR R1, [R0] ; 除能SysTick,因为只使用一次
LDR R0, =0xE000ED04
LDR R1, =0x02000000 ; 手工清除NVIC中的SysTick悬起位
STR R1, [R0]
... ; 执行所需的处理工作
LDR R2, =SysTickFired
LDR R1, [R2]
ORR R1, #1
STR R1, [R2] ; 设置软件标志,与主程序同步,以执行任务
POP {PC} ; 异常返回
在服务例程的末尾处,通过设置SysTickFired标志,通知主程序定时已经到期,可以结束循环等待了。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?现在注册
x
|