SysTick:系统定时器,由四个寄存器控制,存在于内核,嵌套在NVIC中,所有的Cortex-M3内核的单片机都具有这个定时器。
①SysTick控制及状态寄存器(地址:0xE000_E010)
只有上面红色线框柱的位有效,其他位都是保留。
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
16 | COUNTFLAG | R | 0 | 如果上次读取本寄存器后,SysTick 已经数到了0,则该位为 1。如果读取该位,该位将自动清零 |
2 | CLKSOURCE | R/W | 0 | 0=外部时钟源(STCLK),即=AHB/8。1=内核时钟(FCLK),即=AHB,可结合下面框图部分看。 |
1 | TICKINT | R/W | 0 | 1=SysTick 倒数到 0 时产生 SysTick 异常请求。0=数到 0 时无动作。(为0产生中断,为1不产生中断) |
0 | ENABLE | R/W | 0 | SysTick 定时器的使能位 |
②SysTick重装载数值寄存器(地址:0xE000_E014)
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
23:0 | RELOAD | R/W | 0 | 当倒数至零时,将被重装载的值 |
③SysTick当前数值寄存器(地址:0xE000_E018)
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
23:0 | CURRENT | R/Wc | 0 | 读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的COUNTFLAG 标志 |
图中STK_CLK对应的上面的CLKSOURCE,结合RCC时钟树:
当CLKSOURCE位为0时,时钟是AHB/8=(72/8)M=9M,当CLKSOURCE位为1时,时钟是AHB=72M。
图中STK_LOAD对应上面的RELOAD,STK_VAL对应上面的CURRENT。
递减计数器(值为STK_VAL)在时钟的驱动下,从RELOAD初值开始往下递减计数到0,产生中断和置位COUNTFLAG标志。然后又从RELOAD值开始重新递减计数,如此循环。
①t:一个计数循环的时间,跟RELOAD和CLK有关。
②CLK:72M或者9M,由CTRL寄存器配置。
③RELOAD:24位,用户自己配置。
t=RELOAD*(1/CLK)。
例:更改RELOAD的值。
当CLK=72M时,t=(72) * (1/72M)=1us。
当CLK=72M时,t=(72000) * (1/72M)=1ms。
1s = 1000 ms = 1000 000 us = 1000 000 000 ns。
如果要实现一个1ms的延时,那么将始终配置成72M,然后计数72000次。这就实现了一个1ms的延时。一般我们的程序都是毫秒级别的。
1.SysTick结构体
//在core_cm3.h中
__IO uint32_t CTRL; //控制及状态寄存器
__IO uint32_t LOAD; //重装载数值寄存器
__IO uint32_t VAL; //当前数值寄存器
__IO uint32_t CALIB; //校准寄存器
2.SysTick配置库函数
//在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);
//配置递减计数器的值
SysTick->VAL = 0;
//配置systick的时钟为72M
//使能中断
//使能systick
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
return (0);
}
其中SysTick_LOAD_RELOAD_Msk的定义如下,也就是2^24。
#define SysTick_LOAD_RELOAD_Pos 0
#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)
__NVIC_PRIO_BITS的定义如下。
#define __NVIC_PRIO_BITS 4
__NVIC_PRIO_BITS通过改变这个宏定义和1<<__NVIC_PRIO_BITS中"<<"左边的数(0或1),可以实现对systick中断(内核中断)的分组。
NVIC_SetPriority固件库函数如下。
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
//设置优先级for Cortex-M3 系统中断
if(IRQn < 0) {
SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); }
//设置优先级for外设中断
else {
NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); }
}
在STM32的中断中,有内核中断和外设中断,内核中断并不一定比外设中断的优先级高,这得看具体的设计。
STM32里无论是内核还是外设都是使用4个二进制位来表示中断优先级。内核中断设置的是System handler priority registers (SHPRx)寄存器:
SysTick中断是设置System handler priority register 3 ( SCB_ SHPR3)寄存器:
SysTick用的是上面红色线框住的的四个位(给SysTick中断分了8位,但是STM32只用高四位)。
中断优先级的分组对内核和外设同样适用,当比较的时候,只需把内核外设的中断优先级的四个位按照外设的中断优先级来解析即可,即认为的分出抢占优先级和子优先级。