SysTick
——系统时钟都是属于CM3内核的一个外设,内核在NVIC中。系统定时器的时钟是一个24位向下递减的计数器,计数器每计数一次的时间为1/SYSTICK,一般我们设置系统时钟的等于72M,当重装载数值寄存器计数到0时,系统定时器就会产生一次中断,以此重复循环。
SysTick寄存器汇总
CTRL: SysTick控制及状态寄存器
LOAD: SysTick重装载数值寄存器
VAL: SysTick当前值寄存器
CALIB: SysTick校准数值寄存器
编程的要点:
1.配置重装载寄存器的值
2.清除当前值寄存器的值
3.配置控制及状态寄存器
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ //不可能的装载值,超出范围
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
//设置重装载寄存器
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
//设置中断优先级
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
//设置当前值寄存器
SysTick->VAL = 0; /* Load the SysTick Counter Value */
//设置定时器的时钟源为AHBCLK=72MHz
//使能定时器中断
//使能定时器
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
形参ticks用来设置重装载寄存器的值,最大不能超过重装载寄存器的2的24次方。当重装载的值递减到0时产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。随后配置好中断优先级,最后配置系统定时器的时钟等于AHBCLK=72MHz,使能定时器和定时器中断,这样系统定时器的时钟就配置好了。
因为SysTick属于内核的外设,与普通外设的中断优先级有些区别,并没有抢占优先级和子优先级的说法。内核外设的中断优先级由内核SCB这个外设的寄存器SHPRx(x=1~3)来配置。
SPRH1是一个32位的寄存器,但是只能通过字节来访问,每8个字段控制一个内核外设的中断优先级设置。在STM32F103中,只有位7~位3这高四位有效,低四位没有用到,所以内核外设的中断优先级可编程为0到15,只有16个可编程优先级,数值越小,优先级越高。
但是,问题来了,如果同时使用了片上外设和SysTick呢?而且片上外设也刚好需要使用中断,那SysTick的中断优先级跟外设的中断优先级怎么设置?会不会因为SysTick是内核里面的外设,所以它的中断优先级就比内核外的中断优先级高呢?
外设在设置中断优先级的时候,首先要分组,然后还要设置抢占优先级和子优先级,而SysTick这类内核外设在配置的时候,只需要配置一个寄存器即可,取值范围为0~15,既然配置的方式不同那么如何区分两者的优先级,下面举例说明。
比如设置一个中断优先级分组为2,抢占优先级为1,子优先级也为1,SysTick的优先级为固件库默认配置的15.当我们比较内核外设和片上外设的中断优先级时,我们只需抓住NVIC中断优先级分组不仅对片上外设有效,对内核外设也有效这一点,把SysTick的优先级15转化成二进制的数1111,又因为NVIC的优先级分组为2,那么前两位的11(0b)就是3,后面两位的11(0b)也是3,。无论是抢占优先级还是子优先级,都比我们设定的片上外设的优先级低。如果当两个软件的优先级都配置成一样,那么就比较它们在中断向量表中的硬件编号,编号越小,优先级越高。
SysTick中断时间的计算
SysTick定时器的计数器是向下递减计数的,计数一次的时间为TDEC=1/CLKAHB ,当重装载寄存器中的值为VALUELOAD减到0的时候,产生中断,可知中断一次的时间为TINT=VALUELOAD*TDEC=VALUELOAD/CLKAHB,其中CLKAHB=72MHz。如果设置VALUELOAD为72,那中断一次的时间TINT=72/72M=1us。不过1us的中断没啥意义,整个程序的重心都花在进出中断上了,根本没有时间处理其他任务。
SysTick定时时间的计算
当设置好中断时间TINT后,我们可以设置一个变量t,用来记录进入中断的次数,那么变量t乘以中断的时间TINT就可以计算定时的时间了。
一种更加简洁的定时器编程
我们知道,SysTick的counter从reload值往下递减到0的时候,CTRL寄存器的为16:countflag会置1,且读取该位的值可以清0,所以我们可以使用软件查询的方式来实现延时。我敢肯定这样的写法初学者肯定喜欢,因为它直接,更容易了解。
void SysTick_Delay_Us(__IO uint32_t us)
{
uint32_t i;
SysTick _Config(SystemCoreClock/1000000);
for(i=0; i<us; i++)
{ /* 当计数器的值减到0时,CTRL寄存器的位16会置1 */
while(!((SysTick->CTRL)&(1<<16)));
}
SysTick->CTRL&=~ SysTick_CTRL_ENABLE_Msk;
}
若有概念上的错误,欢迎在下方评论区改正!