1、STM32F4的SYSTick定时器
STM32F4系统定时器SYSTick是属于内核CM4的一个外设,内嵌在NVIC中。所有基于CM4的单片机都具有这个系统定时器,使得软件能够在CM4单片机中可以很容易的移植。
系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。
系统定时器是一个24bit的向下递减的计数器,计数器每计数一次的时间为1/SYSCLK。当重装载数值寄存器的值递减到0时,系统定时器就产生一个中断,以此循环往复。
2、SYSTick的寄存器
SYSTick系统定时器有四个寄存器。在使用SYSTick产生定时时,一般只需要配置前三个寄存器,最后一个校准寄存器不需要使用。
CTRL SYSTick控制及状态寄存器
LOAD SYSTick重装载数值寄存器
VAL SYSTick当前数值寄存器
CALIB SYSTick校准数值寄存器
3、SYSTick配置库函数
(1)库函数解析
SYSTick是属于内核的外设,有关的寄存器定义和库函数都在内核相关的库文件core_cm4.h中。
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks);
使用固件库编程的时候,我们只需要调用库函数SysTick_Config()即可。
形参ticks用来设置重装载数值寄存器的值,最大值为2^24 = 16777216。
返回值 0,设置成功 1,设置失败。
SysTick_Config()库函数主要配置了SYSTick中的三个寄存器:LOAD/VAL/CTRL。其中还调用了NVIC_SetPriority()来配置系统定时器的中断优先级。由于SYSTick属于内核外设,跟普通外设的中断优先级有区别,并没有抢占优先级和子优先级的说法。在STM32F407中,内核外设的中断优先级由内核SCB这个外设的寄存器:SHPPx(x = 1,2,3)来配置。在STM32F407中,只有高四位有效,中断优先级可编程为0~15,只有16个可编程优先级。
标准库的函数原型:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); }
/* Reload value impossible */
SysTick->LOAD = (uint32_t)(ticks - 1UL);
/* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
/* set Priority for Systick Interrupt */
SysTick->VAL = 0UL;
/* 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 (0UL);
/* Function successful */
}
在系统定时器中,配置优先级为 (1UL « __NVIC_PRIO_BITS) - 1UL),其中宏 __NVIC_PRIO_BITS 为 4,那计算结果就等于 15,可以看出系统定时器此时设置的优先级在内核外设中是最低的。
#ifndef __NVIC_PRIO_BITS
#define __NVIC_PRIO_BITS 4
#warning "__NVIC_PRIO_BITS not defined in device header file; using default!"
#endif
SYSTick初始化函数由用户自己编写,里面调用了SysTick_Config()这个固件函数,通过设置该函数的形参,就决定了系统定时器经过多少时间就产生一次中断。
void SysTick_Init(void)
{
if(SysTick_Config(SystemCoreClock/100000)) //10us
{
while(1);
}
}
(2)SYSTick相关计算
SYSTick中断时间计算
SYSTick定时器的计数器是向下递减计数的,计数一次时间 T = 1/AHBCLK
当重载寄存器中的值VALUE减到0的时候,产生中断。
中断一次的时间 T=VALUE/AHBCLK。
如果AHBCLK设置为168MHz,VALUE值设置为168,则中断时间为1us。
VALUE值设置为1680,则中断时间为10us.
VALUE值设置为168000,则中断时间为1ms。
static __IO uint32_t TimingDelay;
void systick_init(void)
{
//初始化SYSTick产生一次中断的时间
while(SysTick_Config(168000)!=0);//1ms产生一次中断
}
//计时程序 nTime*1ms
void Time_us(uint32_t nTime)
{
if(TimingDelay == 0)
{
TimingDelay = nTime;
GPIO_ToggleBits(GPIOF,GPIO_Pin_9);
}
}
//在 SysTick 中断函数 SysTick_Handler()进行计数
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
3、注意事项
中断服务程序中修改的供其它程序检测的变量需要加volatile(即__IO)。
volatile(即__IO) 告诉编译器变量是随时可能发生变化的,每次使用它的时候必须从变量的地址中读取,因而编译器生成的可执行码会重新变量的地址读取数据。而如果没有使用__IO,编译器优化做法是,由于编译器发现两次从i中读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中,而不是重新从i里面读。
确定系统时钟配置的系统频率SYSCLK或HSECLK。