一 SysTick 简介
SysTick
—系统定时器是属于
CM3
内核中的一个外设,内嵌在
NVIC
中。系统定时器
是一个
24bit
的向下递减的计数器,计数器每计数一次的时间为
1/SYSCLK
,一般我们设置
系统时钟
SYSCLK
等于
72M
。当重装载数值寄存器的值递减到
0
的时候,系统定时器就产
生一次中断,以此循环往复。
SysTick
寄存器介绍
SysTick
—系统定时器有
4
个寄存器,简要介绍如下。在使用
SysTick
产生定时的时候,
只需要配置前三个寄存器,最后一个校准寄存器不需要使用。
CTRL SysTick 控制及状态寄存器
LOAD SysTick 重装载数值寄存器
VAL SysTick 当前数值寄存器
CALIB SysTick 校准数值寄存器
SysTick
属于内核的外设,有关的寄存器定义和库函数都在内核相关的库文件
core_cm3.h
中。
SysTick
配置库函数
1
__STATIC_INLINE
uint32_t
SysTick_Config(
uint32_t
ticks)
2
{
3
//
不可能的重装载值,超出范围
4
if
((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) {
5
return
(1UL);
6
}
7
8
//
设置重装载寄存器
9
SysTick->LOAD = (
uint32_t
)(ticks - 1UL);
10
11
//
设置中断优先级
12
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
13
14
//
设置当前数值寄存器
15
SysTick->VAL = 0UL;
16
17
//
设置系统定时器的时钟源为
AHBCLK=72M
18
//
使能系统定时器中断
19
//
使能定时器
20
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
21
SysTick_CTRL_TICKINT_Msk |
22
SysTick_CTRL_ENABLE_Msk;
23
return
(0UL);
24
}
用固件库编程的时候我们只需要调用库函数
SysTick_Config()
即可,形参
ticks
用来设
置重装载寄存器的值,最大不能超过重装载寄存器的值
,当重装载寄存器的值递减到
0
的时候产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。紧随
其后设置好中断优先级,最后配置系统定时器的时钟等于
AHBCLK=72M
,使能定时器和
定时器中断,这样系统定时器就配置好了,一个库函数搞定。
SysTick_Config()
库函数主要配置了
SysTick
中的三个寄存器:
LOAD
、
VAL
和
CTRL
,
其中
在
SysTick_Config()
库函数还调用了固件库函数
NVIC_SetPriority()
来配置系统定时器
的中断优先级,该库函数也在
core_m3.h
中定义,原型如下:
1
__STATIC_INLINE
void
NVIC_SetPriority(IRQn_Type IRQn,
uint32_t
priority)
2
{
3
if
((
int32_t
)IRQn < 0) {
4
SCB->SHP[(((
uint32_t
)(
int32_t
)IRQn) & 0xFUL)-4UL] =
5
(
uint8_t
)((priority << (8 - __NVIC_PRIO_BITS)) & (
uint32_t
)0xFFUL);
6
}
else
{
7
NVIC->IP[((
uint32_t
)(
int32_t
)IRQn)] =
8
(
uint8_t
)((priority << (8 - __NVIC_PRIO_BITS)) & (
uint32_t
)0xFFUL);
9
}
10
}
函数首先先判断形参
IRQn
的大小,如果是小于
0
,则表示这个是系统异常,系统异常
的优先级由内核外设
SCB
的寄存器
SHPRx
控制,如果大于
0
则是外部中断,外部中断的
优先级由内核外设
NVIC
中的
IPx
寄存器控制。
因为
SysTick
属于内核外设,跟普通外设的中断优先级有些区别,并没有抢占优先级
和子优先级的说法。在
STM32F103
中,内核外设的中断优先级由内核
SCB
这个外设的寄
存器:
SHPRx
(
x=1.2.3
)来配置。有关
SHPRx
寄存器的详细描述可参考《
Cortex-M3
内核
编程手册》
4.4.8
章节。下面我们简单介绍下这个寄存器。
SPRH1-SPRH3
是一个
32
位的寄存器,但是只能通过字节访问,每
8
个字段控制着一
个内核外设的中断优先级的配置。在
STM32F103
中,只有位
7:3
这高四位有效,低四位没
有用到,所以内核外设的中断优先级可编程为:
0~15
,只有
16
个可编程优先级,数值越小,
优先级越高。如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决
定优先级大小,编号越小,优先级越高。
在系统定时器中,配置优先级为
(1UL << __NVIC_PRIO_BITS) - 1UL)
,其中宏
__NVIC_PRIO_BITS
为
4
,那计算结果就等于
15
,可以看出系统定时器此时设置的优先级
在内核外设中是最低的,如果要修改优先级则修改这个值即可,范围为:
0~15
。
1
//
设置系统定时器中断优先级
2
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
但是,问题来了,刚刚我们只是学习了内核的外设的优先级配置。如果我同时使用了
systick
和片上外设呢?而且片上外设也刚好需要使用中断,那
systick
的中断优先级跟外设
的中断优先级怎么设置?会不会因为
systick
是内核里面的外设,所以它的中断优先级就一
定比内核之外的外设的优先级高?
从《
STM32
中断应用概览》这章我们知道,外设在设置中断优先级的时候,首先要分
组,然后设置抢占优先级和子优先级。而
systick
这类内核的外设在配置的时候,只需要配
置一个寄存器即可,取值范围为
0~15
。既然配置方法不同,那如何区分两者的优先级?下
面举例说明。
比如配置一个外设的中断优先级分组为
2
,抢占优先级为
1
,子优先级也为
1
,
systick
的优先级为固件库默认配置的
15
。当我们比较内核外设和片上外设的中断优先级的时候,
我们只需要抓住
NVIC
的中断优先级分组不仅对片上外设有效,同样对内核的外设也有效。
我们把
systick
的优先级
15
转换成二进制值就是
1111(0b)
,又因为
NVIC
的优先级分组
2
,
那么前两位的
11(0b)
就是
3
,后两位的
11(0b)
也是
3
。无论从抢占还是子优先级都比我们设
定的外设的优先级低。如果当两个的软件优先级都配置成一样,那么就比较他们在中断向
量表中的硬件编号,编号越小,优先级越高。
二 编程实现
一种更简洁的定时编程
如果我们是使用了中断,而且经过多个函数的调用,还使用了全局变量,理
解起来挺费劲的,其实还有另外一种更简洁的写法。我们知道,
systick
的
counter
从
reload
值往下递减到
0
的时候,
CTRL
寄存器的位
16:countflag
会置
1
,且读取该位的值可清
0
,
所有我们可以使用软件查询的方法来实现延时。
#include "systick.h"
//通过查询标志位来完成定时功能,没有采用中断
void Systick_Delay_us(uint32_t us){
uint32_t i = 0;
SysTick_Config(72);//重载值72,1us计数到72
for(i = 0;i<us;i++){
// 当计数器的值减小到 0 的时候,CRTL 寄存器的位 16 会置 1 同时清零操作
while(!((SysTick->CTRL) & (1<<16)));//计数满标志位
}
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;//定时器使能位:让失能 SysTick_CTRL_ENABLE_Msk == 1<<0
}
void Systick_Delay_ms(uint32_t ms){
uint32_t i = 0;
SysTick_Config(72000);
for(i = 0;i<ms;i++){
while(!((SysTick->CTRL) & (1<<16)));
}
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "rcc.h"
#include "exti.h"
#include "systick.h"
//systick系统定时器定时1s,灯反转一次。
void delay(uint32_t count){
for(;count!=0;count--);
}
int main(void)
{
LED0_GPIO_Config();
while (1){
Systick_Delay_ms(1000);
LED0_TOGGLE;
}
}
在这两个微秒和毫秒级别的延时函数中,我们还是调用了
SysTick_Config
这个固件库
函 数 , 有 关 这 个 函 数 的 说 明 具 体 见 代 码
//
这个 固件库函数 在
core_cm3.h
中
2
static
__INLINE
uint32_t
SysTick_Config(
uint32_t
ticks)
3
{
4
// reload
寄存器为
24bit
,最大值为
2^24
5
if
(ticks > SysTick_LOAD_RELOAD_Msk)
return
(1);
6
7
//
配置
reload
寄存器的初始值
8
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
9
10
//
配置中断优先级为
1<<4 -1 = 15
,优先级为最低
11
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
12
13
//
配置
counter
计数器的值
14
SysTick->VAL = 0;
15
16
//
配置
systick
的时钟为
72M
17
//
使能中断
18
//
使能
systick
19
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
20
SysTick_CTRL_TICKINT_Msk |
21
SysTick_CTRL_ENABLE_Msk;
22
return
(0);
23
}