SysTick详解

SysTick定时器简介

SysTick定时器也叫SysTick滴答定时器,它是Cortex内核的一个外设,被嵌入在NVIC中。它是一个24位向下递减的定时器,每计数一次所需时间为1/SYSTICK,SYSTICK是系统定时器时钟,它可以直接取自系统时钟,还可以通过系统时钟8分频后获取。当定时器计数到0时,将从LOAD 寄存器中自动重装定时器初值,重新向下递减计数,如此循环往复。如果开启SysTick中断的话,当定时器计数到0,将产生一个中断信号。因此只要知道计数的次数就可以准确得到它的延时时间。

SysTick定时器作为HAL_Delay的基准

在cube生成的代码中,main函数中HAL_RCC_ClockConfig();初始化系统时钟的时候Systick被初始化。
初始化时,Cube默认设置Systick中断优先级为最高优先级,中断周期由函数HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)确定,默认值为1ms。
中断频率可以通过修改HAL_TICK_FREQ_DEFAULT参数修改,hal.h中给出10Hz,100Hz,1KHz,三种选项。

修改后HAL_Delay函数延时的时间也会改变

如何修改SysTick中断频率?

1
2
3
4
5
6
7
8
9
10
11
12
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */

SysTick->LOAD = ticks - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}

Systick的中断频率是通过该函数设置的,SystemCoreClock是系统的主频。

“SysTick->LOAD = ticks – 1;”知道,ticks就是LOAD值,即重载值,表示两次中断的计数。

我们知道了时钟,就知道定时器每递减一个值需要的时间了,即:1/SystemCoreClock 秒,即每递减一个值,耗时1/SystemCoreClock秒。所以如果要使得10ms定时,即0.01/(1/SystemCoreClock)=SystemCoreClock/100,回头看看前面定时10ms的参数,是不是这个值呢。以此类推,需要定时多长时间,你可以自己算一个参数带进去了,需要注意的是,LOAD值是个24位数,带进去的数不要超过24位数的最大值。还有一个需要注意的地方,就是LOAD值最小255,当你给LOAD值带进去小于255值,LOAD会自动变成255。

因此,如果要把HAL_Delay改成1us一次中断

HAL_SYSTICK_Config(==SystemCoreClock / (10U / uwTickFreq)==);即修改重载值为==SystemCoreClock/10^6==,则中断一次需要(1/SystemCoreClock)X SystemCoreClock/10^6 = 1us。

但实际上中断设为1us是不行的,因为SystemCoreClock/10^6<255了,所以可以改成10us,即装载设成==SystemCoreClock /10^5==

使用时的注意事项

1.需要注意的是,调高Systick的中断频率会导致系统频繁中断,实际应用中不建议将Systick设置的过高
如需快于1ms的时间基准,建议再开一个定时器,通过读取TIMx->CNT来获取。
2.在用户的其他中断中使用HAL_Delay(),如果遇到延时跳不出的情况,检查用户中断优先级,建议用户中断优先级设置尽可能的低

非修改SysTick中断频率得到1us延时的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include "Delay.h"

static uint32_t fac_us=0; //1us计数fac_us个数

/*******************************************************************************
* 函 数 名: SysTick_Init
* 函数功能: SysTick初始化函数
* 输 入: SYSCLK:系统时钟频率
* 输 出: 无
*******************************************************************************/
void SysTick_Init(uint8_t SYSCLK)
{
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
/*这句就是设置SysTick的时钟源,用Cube配置了的话就不需要*/

fac_us=SYSCLK; //保存1us所需的计数次数
}

/*************************************************
*函数名: Delay_us
*函数功能: 微秒级延时函数
*输入: nus:延时nus微秒
注意:nus的取值为0~190887435(最大值即2^32/fac_us@fac_us=22.5)
*返回值: 无
**************************************************/
void Delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
}
};
}

/*************************************************
*函数名: Delay_ms
*函数功能: 毫秒级延时函数
*输入: nus:延时nus毫秒
*返回值: 无
**************************************************/
void Delay_ms(uint16_t nms)
{
uint32_t i;
for(i=0;i<nms;i++) Delay_us(1000);
}

SystemCoreClock/1000000 = (1/1000000) / (1/SystemCoreClock)=1 us / SysTick中断周期