1、什么是Systick定时器?
---》Systick定时器是一个简单的滴答定时器,对于ST的M3、M4、M7内核芯片,都有滴答定时器。
---》Systick滴答定时器常常用来做延迟,或者时时操作系统的心跳时钟。这样可以节省MCU的资源,不用另外浪费一个定时器。比如UCOS系统中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick滴答定时器做UCOS的心跳时钟。
---》Systick定时器就是一个系统滴答定时器,一个24位的倒计数定时器,记到0时,将从RELOAD寄存器中自动装载定时器初值。只要不把它在Systick控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。
---》Systick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。
---》Systick中断的优先级也可以设置。
2、Systick相关寄存器
4个Systick寄存器在HAL库中(core_cm7.h//core_cm4.h)定义位:
1 typedef struct 2 { 3 __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ 4 __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ 5 __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ 6 __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ 7 } SysTick_Type;
CTRL-------------Systick控制和状态寄存器
LOAD-------------Systick自动重装载除值寄存器
VAL----------------Systick当前值寄存器
CALIB-------------Systick校准值寄存器
对于STM32,外部时钟源是HCLK(AHB总线时钟)的1/8,内核时钟是HCLK时钟。
3、Systick相关配置函数
配置函数为:
1 HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource);//Sysick时钟源选择
1 Systick_Config(uint32_t ticks);//初始化Systick时钟为HCLK并开启中断
Systick中断服务函数为:
1 void SysTick_Handler(void);
SysTick_Config函数:
1 __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) 2 { 3 if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) 4 { 5 return (1UL); /* Reload value impossible */ 6 } 7 8 SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ 9 NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ 10 SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ 11 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | 12 SysTick_CTRL_TICKINT_Msk | 13 SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ 14 return (0UL); /* Function successful */ 15 }
SysTick_Config(uint32_t ticks)
HAL_SYSTICK_CLKSourceConfig函数:
1 void HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource) 2 { 3 /* Check the parameters */ 4 assert_param(IS_SYSTICK_CLK_SOURCE(CLKSource)); 5 if (CLKSource == SYSTICK_CLKSOURCE_HCLK) 6 { 7 SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; 8 } 9 else 10 { 11 SysTick->CTRL &= ~SYSTICK_CLKSOURCE_HCLK; 12 } 13 }
HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource)
用滴答定时器制作的系统延迟函数(无需支持OS系统):
1 void delay_init(u8 SYSCLK) 2 { 3 HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK 4 fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用 5 }
delay_init(u8 SYSCLK)
1 2 void delay_us(u32 nus) 3 { 4 u32 ticks; 5 u32 told,tnow,tcnt=0; 6 u32 reload=SysTick->LOAD; //LOAD的值 7 ticks=nus*fac_us; //需要的节拍数 8 told=SysTick->VAL; //刚进入时的计数器值 9 while(1) 10 { 11 tnow=SysTick->VAL; 12 if(tnow!=told) 13 { 14 if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了. 15 else tcnt+=reload-tnow+told; 16 told=tnow; 17 if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出. 18 } 19 }; 20 }
delay_us(u32 nus)
1 void delay_ms(u16 nms) 2 { 3 u32 i; 4 for(i=0;i<nms;i++) delay_us(1000); 5 }
delay_ms(u16 nms)
当使用OS系统时:
1 //初始化延迟函数 2 //当使用ucos的时候,此函数会初始化ucos的时钟节拍 3 //SYSTICK的时钟固定为AHB时钟的1/8 4 //SYSCLK:系统时钟频率 5 void delay_init(u8 SYSCLK) 6 { 7 #if SYSTEM_SUPPORT_OS //如果需要支持OS. 8 u32 reload; 9 #endif 10 HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK 11 fac_us=SYSCLK; //不论是否使用OS,fac_us都需要使用 12 #if SYSTEM_SUPPORT_OS //如果需要支持OS. 13 reload=SYSCLK; //每秒钟的计数次数 单位为K 14 reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间 15 //reload为24位寄存器,最大值:16777216,在216M下,约合77.7ms左右 16 fac_ms=1000/delay_ostickspersec; //代表OS可以延时的最少单位 17 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//开启SYSTICK中断 18 SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次 19 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK 20 #endif 21 }
delay_init(u8 SYSCLK)
1 //延时nus 2 //nus:要延时的us数. 3 //nus:0~204522252(最大值即2^32/fac_us@fac_us=21) 4 void delay_us(u32 nus) 5 { 6 u32 ticks; 7 u32 told,tnow,tcnt=0; 8 u32 reload=SysTick->LOAD; //LOAD的值 9 ticks=nus*fac_us; //需要的节拍数 10 delay_osschedlock(); //阻止OS调度,防止打断us延时 11 told=SysTick->VAL; //刚进入时的计数器值 12 while(1) 13 { 14 tnow=SysTick->VAL; 15 if(tnow!=told) 16 { 17 if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了. 18 else tcnt+=reload-tnow+told; 19 told=tnow; 20 if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出. 21 } 22 }; 23 delay_osschedunlock(); //恢复OS调度 24 }
delay_us(u32 nus)
1 //延时nms 2 //nms:要延时的ms数 3 //nms:0~65535 4 void delay_ms(u16 nms) 5 { 6 if(delay_osrunning&&delay_osintnesting==0)//如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度) 7 { 8 if(nms>=fac_ms) //延时的时间大于OS的最少时间周期 9 { 10 delay_ostimedly(nms/fac_ms); //OS延时 11 } 12 nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时 13 } 14 delay_us((u32)(nms*1000)); //普通方式延时 15 }
delay_ms(u16 nms)
根据滴答定时器的递减技术计数特性,就可以很容易的理解编程的思路了。对于Contex-M系统中,Systick代码可以通用,如果使用时发现延时不一致,问题一般都是因为不同内核时钟不一样而已。修改ticks值即可。
原文地址:https://www.cnblogs.com/vcan123/p/10428760.html