SysTick定时器也叫滴答定时器,是属于CM4内核中的一个外设,内嵌在NVIC中。滴答定时器是一个24bit的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK等于 180M。当重装载数值寄存器的值递减到 0的时候,系统定时器就产生一次中断,以此循环往复。
SysTick定时器有4个寄存器,在使用SysTick产生定时的时候,只需要配置CTRL、LOAD、VAL三个寄存器,CALIB校准寄存器不需要配置(出厂时已校准好),寄存器介绍如下:
寄存器名称 | 寄存器描述 |
SYST_CSR | SysTick控制及状态寄存器 |
SYST_RVR | SysTick重装载值寄存器 |
SYST_CVR | SysTick当前数值寄存器 |
SYST_CALIB | SysTick校准数值寄存器 |
1、SYST_CSR控制及状态寄存器
位段 | 名称 | 复位值 | 描述 |
16 | COUNTFLAG | 0 | 如果计时器从上次读取后计数到0,则该位返回1 |
2 | CLKSOURCE | 0 | 时钟源选择位: 0 = AHB/8 1 = 处理器时钟AHB |
1 | TICKINT | 0 | 启用SysTick异常请求: 0 = 计时器数到0时没有异常请求。 1 = 计时器数到0时产生SysTick异常请求 通过读取 COUNTFLAG 位可以确定计数器是否递减到 0 |
0 | ENABLE | 0 | SysTick定时器的使能位 |
2、SYST_RVR重装载值寄存器
位段 | 名称 | 复位值 | 描述 |
23::0 | RELOAD | 0 | 当倒数计数到0时,加载到SYST_CVR寄存器的值 |
RELOAD值可以是0x00000001 - 0x00FFFFFF范围内的任何值。起始值可以为0,但是没有效果,因为SysTick异常请求和COUNTFLAG在从1到0计数时才被激活。
重新装载值是根据其使用情况计算的。例如,要生成周期为N个处理器时钟周期的多次触发定时器,可以配置RELOAD值为N-1。如果每100个时钟脉冲需要SysTick中断,则将RELOAD设置为99。
3、SYST_CVR当前数值寄存器
位段 | 名称 | 复位值 | 描述 |
23::0 | CURRENT | 0 | 读取返回SysTick计数器的当前值。向寄存器写入任何值时都会将该字段清除为0,并将SYST_CSR的COUNTFLAG位清除为0。 |
系统定时器的校准数值寄存器在定时实验中不需要用到。寄存器详细介绍请参考Cortex-M4用户指南《Cortex-M4 Generic User Guide》。
4、HAL库提供了滴答定时器初始化函数,位于“core_cm4.h”文件的2014行,函数主要实现了初始化系统计时器的各个寄存器及其中断,并启动系统计时,函数内容如下:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL);
}
SysTick->LOAD = (uint32_t)(ticks - 1UL);
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
return (0UL);
}
5、在“stm32f4xx_hal.c”文件的269行提供了一个滴答定时器配置函数HAL_InitTick(),该函数根据系统时钟频率SystemCoreClock来重新配置滴答定时器的重装载值,以决定产生每个中断的时间(函数中配置为1ms产生一次中断),函数同时设置了定时器的中断优先级,函数内容如下:
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
{
return HAL_ERROR;
}
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{
return HAL_ERROR;
}
return HAL_OK;
}
6、在“stm32f4xx_hal.c”文件中,HAL库还定义了ms延时函数HAL_Delay(),函数中首先定义了32位全局变量uwTick,在Systick中断服务函数SysTick_Handler(位于“bsp_clock.c”文件)中通过调用HAL_IncTick实现uwTick值不断增加,也就是每隔1ms增加1。而HAL_Delay函数在进入函数之后先记录当前uwTick的值,然后不断在循环中读取uwTick当前值,进行减运算,得出的就是延时的毫秒数。
//下面代码均在文件 stm32f4xx_hal.c 中
static __IO uint32_t uwTick; //定义计数全局变量
__weak void HAL_IncTick(void) //全局变量 uwTick 递增
{
uwTick++;
}
__weak uint32_t HAL_GetTick(void) //获取全局变量 uwTick 的值
{
return uwTick;
}
//开放的 HAL 延时函数,延时 Delay 毫秒
__weak void HAL_Delay(__IO uint32_t Delay)
{
uint32_t tickstart = 0;
tickstart = HAL_GetTick();
while((HAL_GetTick() - tickstart) < Delay)
{
}
}