目录
概述
1 SysTick 定时器
1.1 SysTick 定时器功能介绍
1.2 SysTick 定时器功能实现
1.3 SysTick在系统中的作用
2 SysTick应用的实例
2.1 建立异常服务例程
2.2 使能异常
2.3 闹钟功能
2.4 重定位向量表
2.5 消灭二次触发
3 SysTick在FreeRTOS中的应用
3.1 STM32Cube配置SysTick
3.2 STM32HAL中sysTick接口
概述
本文主要介绍Cortex-M3的SysTick 定时器的相关知识,还介绍了一个使用汇编语言编写的Cortex-M3的SysTick的应用实例。还介绍基于Cortex-M3内核的经典MCU STM32F103中SysTick在实际项目应用的方法。
1 SysTick 定时器
1.1 SysTick 定时器功能介绍
SysTick 是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD 寄存器中自动重装载定时初值。只要SysTick 控制及状态寄存器中的使能位被置位, 就永不停息。下图是SysTick的相关寄存器:
CM3 允许为 SysTick 提供两个时钟源以供选择:
1)是内核的“自由运行时钟” FCLK。“自由” 表现在它不来自系统时钟 HCLK, 因此在系统时钟停止时 FCLK 也继续运行。
2)一个外部的参考时钟。但是使用外部时钟时,因为它在内部是通过 FCLK 来采样的,因此其周期必须至少是 FCLK 的两倍(采样定理)。
很多情况下芯片厂商都会忽略此外部参考时钟,因此通常不可用。通过检查校准寄存器的位[31](NOREF), 可以判定是否有可用的外部时钟源,而芯片厂商则必须把该引线连接至正确的电平。
1.2 SysTick 定时器功能实现
当 SysTick 定时器从 1 计到 0 时,它将把 COUNTFLAG 位置位;
下述方法可以清零之:
1)读取 SysTick 控制及状态寄存器(STCSR)
2)往 SysTick 当前值寄存器(STCVR)中写任何数据
1.3 SysTick在系统中的作用
1)SysTick 的最大使命,就是定期地产生异常请求,作为系统的时基。 OS 都需要这种“滴答” 来推动任务和时间的管理。
2)如欲使能 SysTick 异常, 则把 STCSR.TICKINT 置位。 另外, 如果向量表被重定位到 SRAM 中,还需要为 SysTick 异常建立向量,提供其服务例程的入口地址。
2 SysTick应用的实例
2.1 建立异常服务例程
建立SysTick异常服务例程
MOV R0, #0xF ; 异常号: 15
LDR R1, =systick_handler ; 加载服务例程的入口地址
LDR R2, =0xE000ED08 ; 加载向量表偏移量寄存器的地址
LDR R2, [R2] ; 读取向量表的首地址
STR R1, [R2, R0, LSL #2] ; 写入向量
2.2 使能异常
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 位段来获取此信息。不一定每个芯片都实现了此功能,因此在使用时还需查阅芯片的数据手册。
2.3 闹钟功能
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在服务例程中被置位,主程序可以继续执行
2.4 重定位向量表
因为计数器是从 0 开始计数的,所以它会立即把 300‐12 加载入 CURRENT。 12 是中断响应的最短延时,因此减去它用以补偿。但是如果有更高优先级的异常抢占或者阻塞了它,则中断延迟还是会有的。
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} ; 快速返回
2.5 消灭二次触发
在2.4节 重定位向量表时,减去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 标志,通知主程序定时已经到期,可以结束循环等待了。
3 SysTick在FreeRTOS中的应用
3.1 STM32Cube配置SysTick
笔者以STM32103RTB芯片作为平台配置SysTick和FreeRTOS
1) 配置FreeRTOS的系统时钟为SysTick
2)使能FreeRTOS
3.2 STM32HAL中sysTick接口
在STM32HAL库中定义在core_cm3.h中,这部分代码在实际项目应用中,不建议修改,用户可以重新stm32f1xx_hal.c中和sysTick相关的应用接口。在这个文件中,和sysTick相关的应用接口被定义为__weak类型,意味着其可以被重写。