stm32滴答定时器SysTick笔记

SysTick 是属于 CM3 内核的外设,有关寄存器的定义和部分库函数都在 core_CM3.h 这个头文件中实现。
系统定时器是一个 24bit的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK等于 72M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。
SysTick—系统定时器有 4 个寄存器(CTRL、LOAD、VAL、CALIB)。在使用 SysTick 产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。

SysTick控制及状态寄存器(地址:0xE000_E010)
在这里插入图片描述SysTick重装载数值寄存器(地址:0xE000_E014)
在这里插入图片描述SysTick当前数值寄存器(地址:0xE000_E018)
在这里插入图片描述
SysTick校准数值寄存器(地址:0xE000_E01C)
在这里插入图片描述设置重装载寄存器的值,最大不能超过重装载寄存器的值 2^24.。当重装载寄存器的值递减到 0 的时候产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。

SysTick 配置库函数
在这里插入图片描述
配置 SysTick 中断优先级
在这里插入图片描述

SysTick 属于内核外设,跟普通外设的中断优先级有些区别,并没有抢占优先级和子优先级的说法。在 STM32F103 中,内核外设的中断优先级由内核 SCB 这个外设的寄存器:SHPRx(x=1.2.3)来配置。有关 SHPRx 寄存器的详细描述可参考《Cortex-M3 内核编程手册》4.4.8 章节。
SPRH1-SPRH3 是一个 32 位的寄存器,但是只能通过字节访问,每 8 个字段控制着一个内核外设的中断优先级的配置。在 STM32F103 中,只有位 7:4 这高四位有效,低四位没有用到,所以内核外设的中断优先级可编程为:0~15,只有 16 个可编程优先级,数值越小,优先级越高。如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决定优先级大小,编号越小,优先级越高。

系统异常优先级字段
在这里插入图片描述
如果要修改内核外设的优先级,只需要修改SPRH1-SPRH3三个寄存器对应的某个字段即可。
在系统定时器中,配置优先级为 (1UL « __NVIC_PRIO_BITS) - 1UL),其中宏 __NVIC_PRIO_BITS为 4,那计算结果就等于 15,可以看出系统定时器此时设置的优先级在内核外设中是最低的,如果要修改优先级则修改这个值即可,范围为:0~15。
在这里插入图片描述

如果同时使用了 systick 和片上外设呢,而且片上外设也刚好需要使用中断,那 systick 的中断优先级跟外设的中断优先级怎么设置?会不会因为 systick 是内核里面的外设,所以它的中断优先级就一定比内核之外的外设的优先级高?
外设在设置中断优先级的时候,首先要分组,然后设置抢占优先级和子优先级。而 systick 这类内核的外设在配置的时候,只需要配置一个寄存器即可,取值范围为 0~15。既然配置方法不同,那如何区分两者的优先级?

举例:配置一个外设的中断优先级分组为 2,抢占优先级为 1,子优先级也为 1,systick 的优先级为固件库默认配置的 15。当我们比较内核外设和片上外设的中断优先级的时候,我们只需要抓住NVIC 的中断优先级分组不仅对片上外设有效,同样对内核的外设也有效。我们把 systick 的优先级 15 转换成二进制值就是 1111(0b),又因为 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_Config(ticks)的形参我们配置为 SystemCoreClock / 100000=72M/100000=720,从刚刚分析我们知道这个形参的值最终是写到重装载寄存器 LOAD 中的,从而可知我们现在把 SysTick 定时器中断一次的时间 TINT=720/72M=10us。

原子哥的延时函数

#include "delay.h"

static u8  fac_us=0;							//us延时倍乘数		   
static u16 fac_ms=0;							//ms延时倍乘数
		   
//初始化延迟函数
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8
	fac_us=SystemCoreClock/8000000;				//为系统时钟的1/8  
	fac_ms=(u16)fac_us*1000;					//代表每个ms需要的systick时钟数   
}								    
    								
//延时nus
//nus为要延时的us数.		    								   
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;      					 //清空计数器	 
}

//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(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;       					//清空计数器	  	    
} 



野火:

void SysTick_Delay_Us( __IO uint32_t us)
{
	uint32_t i;
	SysTick_Config(SystemCoreClock/1000000);
	for (i=0; i<us; i++) {
		// 当计数器的值减小到 0 的时候,CRTL 寄存器的位 16 会置 1  
		while ( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭 SysTick 定时器
	SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}


void SysTick_Delay_Ms( __IO uint32_t ms)
{ 
	uint32_t i;
	SysTick_Config(SystemCoreClock/1000);
	for (i=0; i<ms; i++) {
		// 当计数器的值减小到 0 的时候,CRTL 寄存器的位 16 会置 1
		// 当置 1 时,读取该位会清 0 
		while ( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭 SysTick 定时器
	SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}

以上两种方法比较好理解,没有使用中断,其核心是一样的。

还有一种利用systick中断的方法,稍微绕了一点,其实也一样。

void Delay_us(__IO u32 nTime)
{ 
	TimingDelay = nTime;	
	// 使能滴答定时器  
	SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;

	while(TimingDelay != 0);
}
void TimingDelay_Decrement(void)
{
	if (TimingDelay != 0x00)
	{ 
		TimingDelay--;
	}
}
// 这个 固件库函数 在 core_cm3.h中
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  // reload 寄存器为24bit,最大值为2^24
	if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);
  
  // 配置 reload 寄存器的初始值	
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
	
	// 配置中断优先级为 1<<4-1 = 15,优先级为最低
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); 
	
	// 配置 counter 计数器的值
  SysTick->VAL   = 0;
	
	// 配置systick 的时钟为 72M
	// 使能中断
	// 使能systick
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    
  return (0); 
}

在中断里面调用TimingDelay_Decrement()函数。

///内核中断不用NVIC_IRQChannel,因为SysTick_IRQn是-1(在stm32f10x.h可查),属于异常中断,外部中断(如定时器中断)属于正常中断
在这里插入图片描述

使用systick中断
示例
void Systick_Init()
{
SysTick->LOAD = 9000;

NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);  //SCB->SHP[11] = ((u8)15<<4);
  
SysTick->VAL = 0;

SysTick->CTRL &=~ ((u32)0x01<<2);   //SysTick_CTRL_CLKSOURCE_Pos
SysTick->CTRL |= ((u32)0x01<<1);    //SysTick_CTRL_TICKINT_Pos
SysTick->CTRL |= ((u32)0x01<<0);    //SysTick_CTRL_ENABLE_Msk

}
上面的程序对8M晶振来说,可实现1ms中断一次。
在单片机中,delay()相当于空跑,会停止其他的正在进行的任务。可以使用systick进行中断,在中断里设置变量++,这样可以实现达到一段时间后执行所需要的任务的目的。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值