【初入电子坑之stm32篇(六)】定时器基础与Systick

前言

定时器,顾名思义是拿来定时的,但除了定时之外,我们还可以利用定时器的计数特性,演变出其他有意思的功能出来(诸如控制马达转速)。不过在这之前,我们还是先了解了解定时器的工作原理叭。

本文概览

image-20210424160856026

一、定时器基本概念

1.1、计数宽度

这里指计数器的位数。如:CM3内核中Systick定时器的计数位有24位,即最大计数值为 224 -1= 16,777,215。我们就说Systick的计数宽度为24。

1.2、工作模式

定时器是人设计,而设计是自由的。既然你规定了计数的数值范围,那么我们从0数到16,777,215,跟从16,777,215 数到 0 都是可以的不是嘛,所以这就引申出了工作模式的概念:

  • 向上计数:从0数到16,777,215
  • 向下计数:从16,777,215 数到 0

这两种模式,从本质上来说没有任何区别。不过某些定时器可能没有给与我们开发者辣么自由的操作空间,限定了只可以向上计数or只可以向下计数。

1.3、计数周期

既然我们规定了计数的范围,那么从1计数到2,从2计数到3之间的时间周期是多少?我们如何实现计数周期的不变?

这个问题其实很简单。。。利用时钟啊!时钟的脉冲信号变化规律是很容易确定的。那么我们就可以直接使用时钟实现计数周期,即:

TCNT=1fClkSourceT_{CNT} = \frac{1}{f_{ClkSource}}\qquad

1.4、计数时长

啥?刚刚说完计数周期,你现在给我来个计数时长?啥玩意?

哦,这哥们是拿来中断用的。

我们一开始提到了计数宽度,它决定了我们可以取到的最大计数值。但它也只是决定了我们可以取到的最大计数值,实际计数值CNT(count的简写)是多少,这不还是由我们自己定的嘛。根据项目需求,你想取多少就多少。即:

t=CNT×TCNTt = CNT ×T_{CNT}

当向上计数or向下计数达到了CNT次后,计数就会溢出触发一次定时器中断。这个中断有什么用?别急,后面我们我讨论到它。

1.5、重装载计数器

刚刚我们已经说了当向上计数or向下计数达到了CNT次,就会触发一次定时器中断。那么中断结束之后,计数器怎么继续计时?很简单,设计一个专门控制检测计数状态的模块,当每次计数值达到了我们指定的CNT后,就重置计时器,让它重新计数就OK了。

而这个重置操作,一般称为重装载计数器,我们可以通过操作相关寄存器去控制它。

1.6、定时器简易模型

根据以上信息,我们可以构筑出下图的定时器模型:

定时器简易模型

二、Systick

2.1、简介

SysTick—系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。也常称为滴答定时器,因为它只要简单的计数功能。

系统定时器是一个 24bit 的向下递减的计数器(PS:这里只能向下计数),计数器每计数一次的时间为 1/SYSCLK。

因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个

系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

(以上为本人从野火的资料中,截取修改而来。)

2.2、Systick的应用与配置流程

2.2.1、Systick的寄存器组

对于Systick而言,有4个寄存器控制它,分别是:

  • CTRL :SysTick 控制及状态寄存器
  • LOAD :SysTick 重装载数值寄存器
  • VAL :SysTick 当前数值寄存器
  • CALIB :SysTick 校准数值寄存器

有了上一节的内容做铺垫,对于这几个寄存器的出现应该不难理解。而且,如果我们需要直接操作寄存器的话,一般来说只需要操作前3个寄存器即可。当然,我们现在一般都直接用固件库操作啦。

而对于Systick的应用,我们一般都直接用于实现延时函数,做到精确延时。

2.2.1、延时函数实现原理

从前面的介绍我们知道,当计数器的值到了我们指定的CNT后,会产生一次中断。

我们可以定义一个变量Delaytime。每次中断的时候都对Delaytime–处理。在延时函数中,我们只需要传入我们想要延时的时长time给Delaytime,判断Delaytime是否为0即可。

2.2.2、配置

具体操作上,我们只需要调用固件库提供的库函数Systick_Config()即可。

image-20210424150701067

这个函数只需要我们传入计数器的值,大大的简化了操作步骤,结合前面的简易模型看看:

定时器简易模型

那么一般传入参数是多少呢?以stm32f10x系列为例,系统时钟一般为72Mhz,因为单片机执行指令的速度是微秒级的,所以我们一般设定一次中断时间为1ms,即一般传参为72000。

2.3、代码示例

只讲原理不讲应用示例都是耍流氓。。。

这里我使用野火的指南者实现1s的LED闪烁为举例。

完整工程代码:My_Github

2.3.1、主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "stm32f10x.h"
#include "stm32f10x_it.h"
#include "led.h"
u8 state;
int main(void)
{
SysTick_Config(72000);
Led_Init();
Led_Control(Led_All,OFF);
while(1)
{
Led_Control(Led_All,state);
Delay_ms(1000);
state ^=1;
}
}

2.3.2、中断服务函数

1
2
3
4
5
6
7
8
9
10
u32 Delaytime;
void Delay_ms(u32 time)
{
Delaytime = time;
while(Delaytime);
}
void SysTick_Handler(void)
{
Delaytime--;
}

参考资料

《零死角玩转STM32—F103指南者》

《CM3权威指南CnR2》

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2022 逸非安逸
  • Visitors: | Views:

请我喝杯咖啡吧~