STM32使用SysTick定时器延时
本文最后更新于:2022年8月10日 下午
STM32使用SysTick定时器延时
最近在学习Stm32 HAL库相关教程(之前都是xjb缝合,缺少点原理的理解,在看到TM1640(一个可以方便在数码管上显示内容的模块)驱动编写的过程需要用到us 级别的延时,而HAL 库的 Hal_Dealy()
的函数是ms 级别的,所以需要自己手动编写一个更精准的延时函数。看教程里几行不知所云,在搜索过程中大概了解到了几种写发,但是感觉比较优雅的是使用 SysTick定时器 进行延时。但是几篇教程在原理处比较简略,所以根据个人理解学习了一下。
参考目录
《ARM Cortex-M3 权威指南》第3版
STM32延时函数的四种方法 - Fireflycjd - 博客园 (cnblogs.com)
STM32的SysTick延时方法 - 代码争霸 - 博客园 (cnblogs.com)
STM32学习心得九:Systick滴答定时器和延时函数解读_天亮继续睡的博客-CSDN博客
stm32的systick(滴答定时器)实现精准延时_haha690的博客-CSDN博客_stm32精准定时
STM32延时函数的四种方法:普通延时(2种)、SysTick 定时器延时(2种)_魏波-的博客-CSDN博客_delayms延时函数用法
关于STM32单片机延时微妙(delay_us)函数-hal库_好奇龙猫的博客-CSDN博客_delay_us
几种延时方法
第一种比较粗暴,就是让单片机做一些无关紧要的工作来打发时间,比如写点循环;
第二种是用汇编指令,和第一种大同小异;
第三种是使用SysTick滴答定时器,但是是采取 中断 的方式;
第四种是使用SysTick滴答定时器,但是是采取 查询 的方式,也就是 本次使用的方法 ;
……(前三种可以在参考目录找到具体实现
SysTick 定时器
简介
详细资料可以阅读 《ARM Cortex-M3 权威指南》第3版 9.5 SysTick 定时器。
SysTick 定时器是 Cortex-M 处理器内部集成的一个小型定时器,属于NVIC 的一部分,可以产生SysTick 异常。SysTick 为简单的向下计数的24位计数器,可以使用处理器时钟或者外部时钟。
SysTick 定时器可用作简单的定时器外设,用以产生周期性中断,延时或时间测量。
SyTick 定时器的寄存器
地址 | CMSIS-Core 符号 | 寄存器 |
---|---|---|
0xE000E010 | SysTick -> CTRL |
SysTick 控制和状态寄存器 |
0xE000E014 | SysTick -> LOAD |
SysTick 重装值寄存器 |
0xE000E018 | SysTick -> VAL |
SysTick 当前值寄存器 |
0xE000E01C | SysTick -> CALIB |
SysTick 校准值寄存器 |
SysTick 控制和状态寄存器(SysTick -> CTRL
)
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
16 | COUNTFLAG | RO | 0 | 当SYSTICK 定时器计数到0时,该位变成1,读取寄存器或清除计数器当前值会被清零 |
2 | CLKSOURCE | R/W | 0 | 0 = 外部参考时钟(STCLK); 1 = 使用内核时钟 |
1 | TOCKINT | R/W | 0 | 1 = SYSTICK 定时器计数减至0时产生异常 0 = 不产生异常 |
0 | ENABLE | R/W | 0 | SYSTICK 定时器使能 |
SysTick 重装值寄存器(SysTick -> LOAD
)
位 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
23: 0 | RELOAD | R/W | 未定义 | 定时器为0时的重装载值 |
SysTick 当前值寄存器(SysTick -> VAL
)
位 | 名称 | 类信 | 复位值 | 描述 |
---|---|---|---|---|
23: 0 | CURRENT | R/Wc | 0 | 读出值位SYSTICK 定时器的当前数值。写入任何值都会清除寄存器,SYSTICK 控制和状态寄存器中的 COUNTFLAG 也会清零 |
SysTick 校准值寄存器(SysTick -> CALIB
)
位 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
31 | NOREF | R | - | 1 = 没有外部参考时钟(STCLK 不可用) 0 = 有外部参考时钟可供使用 |
30 | SKEW | R | - | 1= 校准值并非精准的 10ms 0 = 校准值准确 |
23: 0 | TENMS | R/W | 0 | 10毫秒校准值。芯片设计者应通过 Cortex-M3 的输入信号提供该数值,若读出为0,则表示校准值不可用。 |
使用 SysTick 定时器
主要流程
本次使用查询的方式。
- 将0写入
SysTick -> CTRL
禁止 SysTick 定时器,防止之前 SysTick 定时器在之前被使能过; - 将新的重加载值写入
SysTick -> LOAD
,重加载值应为周期数减1(因为是倒数到0); - 将任何数值写入 SysTick 当前值寄存器
SysTick -> VAL
,该寄存器会被清零; - 写入 SysTick 控制和状态寄存器
SysTick -> CTRL
启动寄存器
延时原理
利用SysTick 控制和状态寄存器 SysTick -> CTRL
中的 计数标志位 来确定定时器合适变为0.可以设置 SysTick -> LOAD
的值,然后等待计数标志位变为0,以此实现延时。
示例代码
进行 us 级别延时
关于时钟
本次选用外部时钟。
对于STM32,外部时钟源是HCLK(AHB总线时钟)的1/8,内核时钟是HCLK时钟 ;
)可以翻出来 CubeMX 的时钟树看一眼
以f103 为例,这里HCLK 为72MHz,那么SYSTICK 的时钟为9MHz,即 SYSTICK定时器以9MHz的频率递减 。
计算
如果要获取 Nus的延迟,那么我们需要计算出 SysTick -> LOAD
,即重加载值
容易得到:
$1s = 10^6us$
$$Nus=T\times LOAD=\frac{LOAD}{SYSTICK}\times 10^6$$
$$LOAD=Nus\times SysTick \times 10^{-6}$$
已知有:
$HCLK=72MHz$
$SYSTICK=9MHz$
代入具体数值可以得到:
$LOAD=Nus\times9\times 10^6\times 10^{-6}$
最后得到
$$LOAD=Nus\times9$$
72M主频代码
)博客里面xjb找了一段
us级延时
ms级延时
循环1000次即可
更通用一点的代码
上面那个 SysTick->LOAD=nus*9;
的9是手动算出来的,一般情况下主频是很容易知道的,那直接用主频自动算出这个数字就更方便了
再次回到上面的式子
记系统时钟 $SYSCLK \ Mhz$
$SYSTICK=SYSCLK\div9 \ Mhz$
$$Nus=T\times LOAD=\frac{LOAD}{SYSTICK\times10^6}\times 10^6=\frac{LOAD}{SYSTICK}$$
$$LOAD=Nus\times SYSTICK$$
这样可以在代码中定义一个延时倍乘数 fac_us
$fac_\ us=SYSCLK\div8$
代码
代码分为两步
- 配置
SYSTICK
为SYSCLK
的$\frac{1}{8}$ ,并计算出延时倍乘数fac_us
- 采用SysTick定时器位16标志位读取方式做延时
还要记得在头文件包含 stm32f1xx.h
(以f103为例)
dealy.c
delay.h
函数
HAL_SYSTICK_CLKSourceConfig()
总结
最后看下来就是 设置重装载值,使能,检查标志位 这三个步骤。
还有就是书上讲的是比较清楚详细的。