STM32F407的系统定时器

系统定时器SysTick

CM4 内核的处理和 CM3 一样,内部都包含了一个SysTick定时器。两者没有区别。其详细介绍,请参阅
《STM32F3与F4系列Cortex M4内核编程手册》第230页。

  • 系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。因为所有的 CM3 芯片都带有这个定时器,软件在不同 CM3 器件间的移植工作得以化简。

  • 系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 72M。

    • 该定时器的时钟源可以是内部时钟,或者是外部时钟。不过,STCLK 的具体来源则由芯片设计者决定
  • 当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。

    • SysTick 设定初值并使能后,每经过 1 个系统时钟周期,计数值就减 1。计数到 0 时,SysTick 计数器自动重装初值并继续计数,同时内部的 COUNT FLAG 标志会置位,触发中断 (如果中断使能情况下)。
  • 只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。
    在这里插入图片描述在M4内核编程手册中这样说道:SysTick是一个24位系统定时器,它将重加载值递减到零。再在下一个时钟沿将该值重新装载到STK_LOAD寄存器中,然后根据随后的时钟进行递减。

滴答定时器寄存器

Systick 部分内容属于 NVIC 控制部分,一共有 4 个寄存器,名称和地址分别是:

STK_CTRL       0xE000E010  --  控制寄存器	
//貌似这个才是控制寄存器
STK_LOAD     0xE000E014  --  重载寄存器	
STK_VAL      0xE000E018  --  当前值寄存器	
STK_CALIB    0xE000E01C  --  校准值寄存器

在这里插入图片描述

STK_CTRL 控制寄存器

在这里插入图片描述
寄存器内有 4 个位具有意义

  • 第 0 位:ENABLE,Systick 使能位(0:关闭 Systick 功能;1:开启 Systick 功能)
  • 第 1 位:TICKINT,Systick 中断使能位(0:关闭 Systick 中断;1:开启 Systick 中断)
  • 第 2 位:CLK SOURCE,Systick 时钟源选择(0:使用 HCLK/8 作为 Systick 时钟;1:使用 HCLK 作为 Systick 时钟)
  • 第 16 位:COUNT FLAG,Systick 计数比较标志,当计数到0时,置1;定时器开始重新计数时(CLK_LOAD重新写入数值)自动清零。

在这里插入图片描述

STK_LOAD 重载寄存器

在这里插入图片描述

Systick 是一个递减的定时器,当定时器递减至0 时,重载寄存器中的值就会被重装载,继续开始递减。STK_LOAD 重载寄存器是个24 位的寄存器最大计数0xFFFFFF。

STK_VAL 当前值寄存器

在这里插入图片描述

24 位的寄存器,读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志。
在这里插入图片描述

STK_CALRB 校准值寄存器

SysTick calibration value register
在这里插入图片描述

  • 位31 NOREF:= 1 没有外部参考时钟(STCLK 不可用),= 0 外部参考时钟可用。
  • 位30 SKEW:= 1 校准值不是准确的1ms,= 0校准值是准确的1ms
  • 不常用

非系统初始化 Systick 定时器

在这里插入图片描述

SysTick_Init

void SysTick_Init(u8 SYSCLK);
SysTick_Init(72);

//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
	fac_us=SYSCLK/8;					
	fac_ms=(u16)fac_us*1000;				   
}								    
 

系统时钟是72/8M,计数一次时间1/9000000秒,换算成us就是1/9us,则计数72/8次也就是9次就是1us。

SysTick_CLKSourceConfig

设置SysTick的时钟源。可选项如下:

#define SysTick_CLKSource_HCLK_Div8    ((uint32_t)0xFFFFFFFB)
#define SysTick_CLKSource_HCLK         ((uint32_t)0x00000004)
#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \
                                       ((SOURCE) == SysTick_CLKSource_HCLK_Div8))

HCLK的介绍

delay_us寄存器

void delay_us(u32 nus)
{	 
	u32 temp;
	SysTick->LOAD = 9*nus;
	SysTick->VAL=0X00;//清空计数器
	SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
	do
	{
		temp=SysTick->CTRL;//读取当前倒计数值
	}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
	//使能且没有计数到0就一直循环
	SysTick->CTRL=0x00; //关闭计数器
	SysTick->VAL =0X00; //清空计数器
}

9*nus :假设外设频率为 9M,也就是经过 8 分频,那么计数 9 次是 1us,乘以 9 的意义就是参数的时间对应的次数,也就是重装载值

  • 这里看一下判断条件,第一位是使能,16位是计数到0。
    第 16 位:COUNT FLAG,Systick 计数比较标志,当计数到0时,置1;定时器开始重新计数时(CLK_LOAD重新写入数值)自动清零。

总结一下过程

  • 先把要延时的 us 数换算成 SysTick 的时钟数
  • 写入 LOAD 寄存器
  • 清空当前寄存器 VAL 的内容
  • 开启倒数功能。
  • 等到倒数结束,即延时了 nus。
  • 最后关闭 SysTick,清空 VAL 的值

注意:

  • nus 的值,不能太大,必须保证 nus<=(2^24)/fac_us,否则将导致延时时间不准确
  • temp&0x01,这一句是用来判断 systick 定时器是否还处于开启状态,可以防止 systick 被意外关闭导致的死循环。

delay_us库函数

//延时nus
//注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 				//时间加载	  		 
	SysTick->VAL=0x00;        				//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 	 
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
	SysTick->VAL =0X00;       				//清空计数器 
}

delay_xms短时


//延时nms
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对168M条件下,nms<=798ms 
void delay_xms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;			//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;           			//清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //开始倒数 
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //关闭计数器
	SysTick->VAL =0X00;     		  		//清空计数器	  	    
} 

delay_ms长时


//延时nms 
//nms:0~65535
void delay_ms(u16 nms)
{	 	 
	u8 repeat=nms/540;						//这里用540,是考虑到某些客户可能超频使用,
											//比如超频到248M的时候,delay_xms最大只能延时541ms左右了
	u16 remain=nms%540;
	while(repeat)
	{
		delay_xms(540);
		repeat--;
	}
	if(remain)delay_xms(remain);
} 

SysTick_Config

设置重装载值。该函数的内部就是个:SysTick->LOAD = 9*nus;
SysTick_Config 的参数是一个时钟次数,意思就是我要多少个1/fosc 时间后中断一下。

static void BSP_CoreClockInit(void)
{ 
	SysTick_Config(SystemCoreClock / 100);      //10ms
	//SysTick_Config(720000);    				//10ms
}

SystemCoreClock / 100 代表的是时钟次数,即放入重装载寄存器中的值,每计时一个数需要 1/72000000s 的时间,那么计SystemCoreClock / 100 (SystemCoreClock 为 72M)个数的时间就是 10ms,通过在中断中设置标志位达到 systick 定时中断的功能。
函数实现

__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 */
}

在外设的中断优先级设置里,外设的硬件编号都是大于等于零的,内核外设的编号都小于零,相当于一个IP。

  • __NVIC_PRIO_BITS=4,因为F4使用了四位。
  • 中断处理函数IP:
typedef enum IRQn
{
/******  Cortex-M4 Processor Exceptions Numbers ****************************************************************/
  NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                                          */
  MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M4 Memory Management Interrupt                           */
  BusFault_IRQn               = -11,    /*!< 5 Cortex-M4 Bus Fault Interrupt                                   */
  UsageFault_IRQn             = -10,    /*!< 6 Cortex-M4 Usage Fault Interrupt                                 */
  SVCall_IRQn                 = -5,     /*!< 11 Cortex-M4 SV Call Interrupt                                    */
  DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M4 Debug Monitor Interrupt                              */
  PendSV_IRQn                 = -2,     /*!< 14 Cortex-M4 Pend SV Interrupt                                    */
  SysTick_IRQn                = -1,     /*!< 15 Cortex-M4 System Tick Interrupt                                */
/******  STM32 specific Interrupt Numbers **********************************************************************/
  WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                                         */
  PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt                         */
  TAMP_STAMP_IRQn             = 2,      /*!< Tamper and TimeStamp interrupts through the EXTI line             */
  RTC_WKUP_IRQn               = 3,      /*!< RTC Wakeup interrupt through the EXTI line                        */
  FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                                            */
  RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                              */
  EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                              */
  EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                              */
  EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                              */
  EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                              */
  EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                              */
  DMA1_Stream0_IRQn           = 11,     /*!< DMA1 Stream 0 global Interrupt                                    */
  DMA1_Stream1_IRQn           = 12,     /*!< DMA1 Stream 1 global Interrupt                                    */
  DMA1_Stream2_IRQn           = 13,     /*!< DMA1 Stream 2 global Interrupt                                    */
  DMA1_Stream3_IRQn           = 14,     /*!< DMA1 Stream 3 global Interrupt                                    */
  DMA1_Stream4_IRQn           = 15,     /*!< DMA1 Stream 4 global Interrupt                                    */
  DMA1_Stream5_IRQn           = 16,     /*!< DMA1 Stream 5 global Interrupt                                    */
  DMA1_Stream6_IRQn           = 17,     /*!< DMA1 Stream 6 global Interrupt                                    */
  ADC_IRQn                    = 18,     /*!< ADC1, ADC2 and ADC3 global Interrupts   

内核外设和普通外设的中断优先级比较,虽然二者不是在同一个寄存器中,但是在比较时,也是把内核外设的优先级寄存器配置按照普通的解读。

系统RTOS延时

//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
	u32 reload;
 	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);  //168M,8分频 21M
	fac_us=SYSCLK/8;						//不论是否使用OS,fac_us都需要使用21M
	//168M也就是168次/us,对于滴答就是21次/us,所以计时21次就是1us,单位就是fac_us
	reload=SYSCLK/8;						//每秒钟的计数次数 单位为M	   
	reload*=1000000/delay_ostickspersec;	//根据delay_ostickspersec设定溢出时间
											//reload为24位寄存器,最大值:16777216,在168M下,约合0.7989s左右	
	fac_ms=1000/delay_ostickspersec;		//代表OS可以延时的最少单位	   
	SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;   	//开启SYSTICK中断
	SysTick->LOAD=reload; 					//每1/delay_ostickspersec秒中断一次	
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; 	//开启SYSTICK    

}	

这里需要注意的是:SysTick 的时钟源自 HCLK 的 8 分频,假设我们外部晶振为 8M,
然后倍频到 168M,那么 SysTick 的时钟即为 21Mhz,也就是 SysTick 的计数器 VAL 每减 1,就
代 表 时 间 过 了 1/21us 。

fac_us=SYSCLK/8; ; 这 句 话 就 是 计 算 在SystemCoreClock 时 钟 频 率 下 延 时 1us 需 要 多 少 个 SysTick 时 钟 周 期。21M的时钟,21次是1us,1次就是1/21us。sysClk就是168。fac_us就是168/8次。

Systick 的时钟来自系统时钟 8 分频,正因为如此,系统时钟如果不是 8 的倍数(不能被 8 整除),则会导致延时函数不准确,这也是我们推荐外部时钟选择 8M 的原因

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
STM32F407是一款由STMicroelectronics开发的32位ARM Cortex-M4微控制器。它具有丰富的外设和强大的处理能力,适用于各种应用场景。其中,滴答定时器(SysTick)是STM32F407内置的一个定时器,用于系统时基的生成和延时操作。 滴答定时器是一个24位的倒计时计数器,它可以与系统时钟进行配置,并且可以设置为不同的工作模式。主要用途包括: 1. 生成系统时基:可以通过配置滴答定时器的时钟源和计数值,实现系统定时中断的触发。这样可以方便地实现一些周期性任务的处理,如任务调度、时间戳记录等。 2. 延时操作:通过滴答定时器,可以实现精确的延时操作。可以根据需要配置定时器的计数值,并通过轮询或中断方式进行延时。 要在STM32F407上使用滴答定时器,需要进行以下步骤: 1. 配置时钟源和计数值:可以选择使用内部时钟源(HCLK)或外部时钟源(HSE/HSI等),并设置计数值。 2. 启动定时器:使能滴答定时器,并开启中断(如果需要)。 3. 编写中断服务程序(如果需要):根据需要,在中断服务程序中处理定时器中断触发的任务。 4. 使用系统时基:可以根据自己的需要,在代码中使用系统时基进行周期性任务的处理,或者使用滴答定时器进行延时操作。 需要注意的是,具体的配置和使用方法可以根据不同的开发环境和编程语言来实现。在使用滴答定时器之前,建议查阅相关的技术文档和参考资料,以便正确地配置和使用该定时器

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万码无虫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值