STM32 Systick系统定时器知识点记录

一.Systick系统定时器简介

SysTick—系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 72M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

二.Systick系统定时器的功能框图

在这里插入图片描述

三.Systick寄存器介绍

SysTick—系统定时器有 4 个寄存器,简要介绍如下。在使用 SysTick 产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。(详见《Cortex-M3 内核编程手册》 -4.5 章节 SysTick Timer(STK))

表 1 SysTick 寄存器汇总
寄存器名称寄存器描述
CTRLSysTick 控制及状态寄存器
LOADSysTick 重装载数值寄存器
VALSysTick 当前数值寄存器
CALIBSysTick 校准数值寄存器

注释:(个人理解,有错误望指正)
1.VAL寄存器是保存计数器计数的当前值,也就是计数器计数到什么值,VAL寄存器里面的值就是多少(计数器是计数器,只是计数,VAL寄存器只是保持每次计数的数值,两者不是一个概念!)。
2.LOAD寄存器是保存重装载数值的寄存器,用户配置重装载寄存器的值,实际就是写入到该寄存器。
3.当系统定时器的计数器在时钟(STK_CLK 72M或9M)的驱动下向下计数时,重装载寄存器里面的数值,会被加载到计数器里面,也就是计数器初始值就是重装载值。然后计数器向下计数,并且从重装载寄存器的值计数到0时,产生一次中断。同样的VAL寄存器里面的数值也是相同变化。(系统定时器的时钟源是系统时钟SYSCLK = 72MHZ经过8分频或者不分频后产生时钟,所以可以为9M或者72M)。

现根据《Cortex-M3 内核编程》手册总结下寄存器的定义和说明:

表 2 SysTick 控制及状态寄存器
位段名称类型复位值描述
16COUNTFLAGR/W0如果在上次读取本寄存器后, SysTick 已经计到了 0,则该位为 1。
2CLKSOURCER/W0时钟源选择位, 0=AHB/8, 1=处理器时钟 AHB
1TICKINTR/W01=SysTick 倒数计数到 0 时产生 SysTick 异常请求, 0=数到 0 时无动作。也可以通过读取COUNTFLAG 标志位来确定计数器是否递减到0
0ENABLER/W0SysTick 定时器的使能位
表 3 SysTick 重装载数值寄存器
位段名称类型复位值描述
23:0RELOADR/W0当倒数计数至零时,将被重装载的值
表 4 SysTick 当前数值寄存器
位段名称类型复位值描述
23:0CURRENTR/W0读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志

四.Systick中断优先级设置和判断

SysTick 属于内核的外设,有关的寄存器定义和库函数都在内核相关的库文件
core_cm3.h 中 。
介绍一下重要的知识 :
(1)SysTick 配置库函数

// SysTick 配置库函数
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
	// 不可能的重装载值,超出范围
  if (ticks > SysTick_LOAD_RELOAD_Msk) 
   return (1);         
   // 设置重装载寄存器                                         
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;   
  // 设置中断优先级
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); 
  
  // 设置当前数值寄存器
  SysTick->VAL   = 0;                        
   // 设置系统定时器的时钟源为 AHBCLK=72M
   // 使能系统定时器中断
   // 使能定时器                
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   				SysTick_CTRL_TICKINT_Msk | 
                   				SysTick_CTRL_ENABLE_Msk;                    
  return (0);                                         
}
  用固件库编程的时候我们只需要调用库函数 SysTick_Config()即可,
  形参 ticks 用来设置重装载寄存器的值,最大不能超过重装载寄存器的值 2^24,当重装载寄存器的值递减到 0的时候产生中断,
然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。紧随其后设置好中断优先级,
最后配置系统定时器的时钟等于 AHBCLK=72M,使能定时器和定时器中断,这样系统定时器就配置好了,
一个库函数搞定。
SysTick_Config()库函数主要配置了 SysTick 中的三个寄存器: LOAD、 VAL 和 CTRL,
有关具体的部分看代码注释即可。

(2)配置 SysTick 中断优先级
在 SysTick_Config()库函数还调用了固件库函数 NVIC_SetPriority()来配置系统定时器的中断优先级,该库函数也在 core_m3.h 中定义,原型如下:

static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if(IRQn < 0) {
    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
  else {
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
}
  函数首先先判断形参 IRQn 的大小,如果是小于 0,则表示这个是系统异常,系统异常
的优先级由内核外设 SCB 的寄存器 SHPRx 控制,如果大于 0 则是外部中断,外部中断的
优先级由内核外设 NVIC 中的 IPx 寄存器控制。
因为 SysTick 属于内核外设,跟普通外设的中断优先级有些区别,并没有抢占优先级
和子优先级的说法。
  在 STM32F103 中,内核外设的中断优先级由内核 SCB 这个外设的寄
存器: SHPRx(x=1.2.3)来配置。有关 SHPRx 寄存器的详细描述可参考《Cortex-M3 内核
编程手册》 4.4.8 章节。下面我们简单介绍下这个寄存器。
SPRH1-SPRH3 是一个 32 位的寄存器,但是只能通过字节访问,每 8 个字段控制着一
个内核外设的中断优先级的配置。在 STM32F103 中,只有位 7:4 这高四位有效,低四位没
有用到,所以内核外设的中断优先级可编程为: 0~15,只有 16 个可编程优先级,数值越小,
优先级越高。
  如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决
定优先级大小,编号越小,优先级越高。
表 5 系统异常优先级字段
异常字段寄存器描述
Memory management faultPRI_4SHPR1
Bus faultPRI_5SHPR1
Usage faultPRI_6SHPR1
SVCallPRI_11SHPR2
PendSVPRI_14SHPR3
SysTickPRI_15SHPR3

如果要修改内核外设的优先级,只需要修改下面三个寄存器对应的某个字段即可。

表 6 SHPR1 寄存器

在这里插入图片描述

表 7 SHPR2 寄存器

在这里插入图片描述

表 8 SHPR3 寄存器

在这里插入图片描述
从表8知:PRI_15[7:0]控制着SysTick的优先级,但是STM32F103只有高4bit控制着优先级,所以实际PRI_15[7:4]控制着优先级,所以优先级范围是0~15(2^4 即2的4次方)。

   在系统定时器中,配置优先级为(1UL << __NVIC_PRIO_BITS) - 1UL),其中宏
__NVIC_PRIO_BITS 为 4,那计算结果就等于 15,可以看出系统定时器此时设置的优先级
在内核外设中是最低的,如果要修改优先级则修改这个值即可,范围为: 0~15。

1.  // 设置系统定时器中断优先级
2. NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);

2点注意点

(1)片上外设和systick(内核外设)中断优先级怎么比较?

  如果我同时使用了systick 和片上外设呢? 而且片上外设也刚好需要使用中断,
那 systick 的中断优先级跟外设的中断优先级怎么设置? 会不会因为 systick 是内核里面的外设,
所以它的中断优先级就一定比内核之外的外设的优先级高?
外设在设置中断优先级的时候,首先要分组,然后设置抢占优先级和子优先级。
而 systick 这类内核的外设在配置的时候,只需要配置一个寄存器即可,取值范围为 0~15。
既然配置方法不同,那如何区分两者的优先级?
下面举例说明:
  比如配置一个外设的中断优先级分组为 2,抢占优先级为 1,子优先级也为 1,
systick的优先级为固件库默认配置的 15。 当我们比较内核外设和片上外设的中断优先级的时候,
我们只需要抓住 NVIC 的中断优先级分组不仅对片上外设有效,同样对内核的外设也有效。
我们把 systick 的优先级 15 转换成二进制值就是 1111(0b),又因为 NVIC 的优先级分组 2,
那么前两位的 11(0b)就是 3,后两位的 11(0b)也是 3。无论从抢占还是子优先级都比我们设
定的外设的优先级低。 如果当两个的软件优先级都配置成一样,那么就比较他们在中断向
量表中的硬件编号,编号越小,优先级越高。

(2)系统定时器中断优先级怎么修改?
设定模块中断优先级的函数为:
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
将后面那个 粗字体的“1”改为其他值即可改变优先级,然后根据上述的**注意点(1)**进行计算就能知道优先级怎么计算和比较了!

五.SysTick 初始化函数

	/**
 * @brief  启动系统滴答定时器 SysTick
 * @param  无
  * @retval 无
 */
void SysTick_Init(void)
{
	/* SystemFrequency / 1000    1ms中断一次
 	* SystemFrequency / 100000	 10us中断一次
	 * SystemFrequency / 1000000 1us中断一次
	 */
	if (SysTick_Config(SystemCoreClock / 100000))	// ST3.5.0库版本
	{ 
		/* Capture error */ 
		while (1);
	}
}

SysTick 初始化函数由用户编写,里面调用了 SysTick_Config()这个固件库函数,通过设置该固件库函数的形参,就决定了系统定时器经过多少时间就产生一次中断

六.SysTick定时时间计算

(1) SysTick 中断时间的计算

   SysTick 定时器的计数器是向下递减计数的,计数一次的时间 TDEC=1/CLKAHB,
当重装载 寄 存 器 中 的 值 VALUELOAD 减 到 0 的 时 候 , 产 生 中 断 , 
可 知 中 断 一 次 的 时 间Time_INT=VALUELOAD * TDEC= VALUELOAD/CLK_AHB ,
其 中 CLK_AHB =72MHZ 。 如 果 设 置VALUELOAD为 72,那中断一次的时间 Time_INT=72/72M=1us。
不过 1us 的中断没啥意义,整个程序的重心都花在进出中断上了,根本没有时间处理其他的任务。

SysTick_Config(SystemCoreClock / 100000))
SysTick_Config()的形我们配置为 SystemCoreClock / 100000=72M/100000=720,从
刚刚分析我们知道这个形参的值最终是写到重装载寄存器 LOAD 中的,从而可知我们现在
把 SysTick 定时器中断一次的时间 Time_INT=720/72M=10us。

(2) SysTick 定时时间的计算

  当设置好中断时间 Time_INT后,我们可以设置一个变量 t,用来记录进入中断的次数,那
么变量 t 乘以中断的时间 Time_INT就可以计算出需要定时的时间。

(3) SysTick 定时函数

现在我们定义一个微秒级别的延时函数,形参为 nTime,当用这个形参乘以中断时间
Time_INT就得出我们需要的延时时间,其中 Time_INT我们已经设置好为 10us。关于这个函数的具体
调用看注释即可。
/**
  * @brief   us延时程序,10us为一个单位
  * @param  
  *		@arg nTime: Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us
  * @retval  无
  */
void Delay_us(__IO u32 nTime)
{ 
	TimingDelay = nTime;	

	// 使能滴答定时器  
	SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;

	while(TimingDelay != 0);
}
   函数 Delay_us()中我们等待 TimingDelay 为 0,当 TimingDelay 为 0 的时候表示延时时
间到。变量 TimingDelay 在中断函数中递减,即 SysTick 每进一次中断即 10us 的时间
TimingDelay 递减一次。

(4) SysTick 中断服务函数

void SysTick_Handler(void)
{
	TimingDelay_Decrement();	
}
中断复位函数调用了另外一个函数 TimingDelay_Decrement(),原型如下:

	/**
  * @brief  获取节拍程序
  * @param  无
  * @retval 无
  * @attention  在 SysTick 中断函数 SysTick_Handler()调用
  */
void TimingDelay_Decrement(void)
{
	if (TimingDelay != 0x00)
	{ 
		TimingDelay--;
	}
}

  TimingDelay 的值等于延时函数中传进去的 nTime 的值,比如 nTime=100000,则延时
的时间等于 100000*10us=1s。
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值