SysTick—系统定时器

SysTick 简介

SysTick—系统定时器是属于 CM4 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit
的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK
等于 180M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环
往复。
因为 SysTick 是属于 CM4 内核的外设,所以所有基于 CM4 内核的单片机都具有这个系统定时器,
使得软件在 CM4 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维
持操作系统的心跳。

SysTick 定时实验

1、设置重装载寄存器的值
2、清除当前数值寄存器的值
3、配置控制与状态寄存器

SysTick 初始化函数

1
2
3
4
5
6
7
void SysTick_Init(void)
{
if (SysTick_Config(SystemCoreClock / 100000))
{
while (1);
}
}

SysTick 中断时间的计算

SysTick 定时器的计数器是向下递减计数的,计数一次的时间 TDEC=1/CLKAHB,当重装载寄存器
中的值 VALUELOAD 减到 0 的时候,产生中断,可知中断一次的时间 TINT=VALUELOAD* TDEC 中断
= VALUELOAD/CLKAHB,其中 CLKAHB =168MHZ。如果 VALUELOAD 设置为 180,那中断一次的时
间 TINT=168/168M=1us。不过 1us 的中断没啥意义,整个程序的重心都花在进出中断上了,根本
没有时间处理其他的任务。

1
SysTick_Config(SystemCoreClock / 100000))

SysTick_Config()的形我们配置为 SystemCoreClock / 100000=180M/100000=1680,从刚刚分析我们知道这个形参的值最终是写到重装载寄存器 LOAD 中的,从而可知我们现在把 SysTick 定时器
中断一次的时间 TINT=1680/168M=10us。

SysTick 定时时间的计算

当设置好中断时间 TINT 后,我们可以设置一个变量 t,用来记录进入中断的次数,那么变量 t 乘
以中断的时间 TINT 就可以计算出需要定时的时间。

SysTick 定时函数

1
2
3
4
5
6
void Delay_us(__IO u32 nTime)
{
TimingDelay = nTime;

while(TimingDelay != 0);
}

函数 Delay_us() 中我们等待 TimingDelay 为 0,当 TimingDelay 为 0 的时候表示延时时间到。变量
TimingDelay 在中断函数中递减,即 SysTick 每进一次中断即 10us 的时间 TimingDelay 递减一次。

SysTick 中断服务函数

1
2
3
4
void SysTick_Handler(void)
{
TimingDelay_Decrement();
}

中断复位函数调用了另外一个函数 TimingDelay_Decrement(),原型如下:

1
2
3
4
5
6
7
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}

TimingDelay 的值等于延时函数中传进去的 nTime 的值,比如 nTime=100000,则延时的时间等于
100000*10us=1s。

另外一种更简洁的定时编程

上面的实验,我们是使用了中断,而且经过多个函数的调用,还使用了全局变量,理解起来挺费
劲的,其实还有另外一种更简洁的写法。我们知道, systick 的 counter 从 reload 值往下递减到 0 的
时候, CTRL 寄存器的位 16:countflag 会置 1,且读取该位的值可清 0,所有我们可以使用软件查
询的方法来实现延时。

1
2
3
4
5
6
7
8
9
10
11
void SysTick_Delay_Us( __IO uint32_t us)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000000);
for (i=0; i<us; i++) {
// 当计数器的值减小到 0 的时候, CRTL 寄存器的位 16 会置 1
while ( !((SysTick->CTRL)&(1<<16)) );
}
// 关闭 SysTick 定时器
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
void SysTick_Delay_Ms( __IO uint32_t ms)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000);

for (i=0; i<ms; i++) {
// 当计数器的值减小到 0 的时候, CRTL 寄存器的位 16 会置 1
// 当置 1 时,读取该位会清 0
while ( !((SysTick->CTRL)&(1<<16)) );
}
// 关闭 SysTick 定时器
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}

在这两个微秒和毫秒级别的延时函数中,我们还是调用了 SysTick_Config 这个固件库函数 ,其中 SystemCoreClock 是一个宏,大小为 168000000,如果不想使用这个宏,也可以直接改成数字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 这个 固件库函数 在 core_cm3.h 中
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
// reload 寄存器为 24bit,最大值为 2^24
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);

// 配置 reload 寄存器的初始值
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;

// 配置中断优先级为 1<<4 -1 = 15,优先级为最低
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);

// 配置 counter 计数器的值
SysTick->VAL = 0;
// 配置 systick 的时钟为 72M
// 使能中断
// 使能 systick
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
return (0);
}