当前位置: 首页 > 科技观察

填坑——关于SysTick定时器

时间:2023-03-13 23:45:46 科技观察

这篇文章主要是为了填坑,纠正上一篇文章中的错误。也进一步加深了我对SysTick定时器的理解,希望对大家有所帮助。01坑的由来在之前的推文中介绍《STM32延时的四种方法》使用querytimer精确延时,使用systick定时器,具体代码如下voiddelay_us(uint32_tnus){uint32_ttemp;SysTick->LOAD=RCC_Clocks.HCLK_Frequency/1000000/8*nus;SysTick->VAL=0X00;//清除计数器SysTick->CTRL=0X01;//使能,减为零无动作,使用外部时钟源do{temp=SysTick->CTRL;//读取得到当前倒计时值}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达SysTick->CTRL=0x00;//关闭计数器SysTick->VAL=0X00;//清计数器}voiddelay_ms(uint16_tnms){uint32_ttemp;SysTick->LOAD=RCC_Clocks.HCLK_Frequency/1000/8*nms;SysTick->VAL=0X00;//清计数器SysTick->CTRL=0X01;//启用,递减到零是没有动作,使用外部时钟源do{temp=SysTick->CTRL;//读取当前倒计时值}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达SysTick->CTRL=0x00;//关闭计数器SysTick->VAL=0X00;//清零计数器}For《STM32延时的四种方法》文中提到的内容如下,这就是下面代码中/8的原因。SysTick->LOAD=RCC_Clocks.HCLK_Frequency/1000/8*nms;我对此深信不疑,并在STM32F207参考手册(RM0033)上找到了“证据”。上图中①直接除以8,不像1/2/4/8频率除以②。所以我确定SYSTICK的时钟固定在HCLK时钟的1/8。在学习RTThread的时候,看到配置SysTickcustomizer的代码如下,心里打了一堆问号。STM32官方手册明确指出,SYSTICK时钟固定为HCLK时钟的1/8。我用示波器测量,RTThread的配置没有问题,延时可以正常。02填坑这个坑其实很简单。《STM32延时的四种方法》中也有提到,不过我没注意这个细节。Bit2设置为1,表示时钟频率为AHB,默认为120000000Hz。Bit2清0,表示时钟频率为AHB/8,即120000000/8Hz。RTThread被配置为内部时钟。上一篇文章没有注意配置外部时钟源的细节,导致看RTThread代码的时候有点懵。在这里我更正《STM32延时的四种方法》中错误的描述。准确的描述是:SYSTICK的时钟可以除以HCLK时钟的1分频或8分频。这里我们选择外部时钟源为120M,所以SYSTICK的时钟为(120/8)M。特此修正。关于这点,STM32的标准外设库提供的SysTick_Config函数,也是使用内部时钟的/**\briefSystemTickConfigurationThisfunctioninitialisesthesystemticktimeranditsinterruptandstartthesystemticktimer.Counterisinfreerunningmodetogenerateperiodicalinterrupts.\param[in]ticksNumberofticksbetweentwointerrupts\return0Functionsucceeded\return1Functionfailed*/static__INLINEuint32_tSysTick_Config(uint32_tticks){if(ticks>SysTick_LOAD_RELOAD_Msk)return(1);/*Reloadvalueimpossible*/SysTick->LOAD=(ticks&SysTick_LOAD_RELOAD_Msk)-1;/*setreloadregister*/NVIC_SetPriority(SysTick_IRQn,(1<<__NVIC_PRIO_BITS)-1);/*setPriorityforCortex-M0SystemInterrupts-*/SysTick>VAL=0;/*LoadtheSysTickCounterValue*/SysTick->CTRL=SysTick_CTRL_CLKSOURCE_Msk|SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;/*EnableSysTickIRQandSysTickTimer*/return(0);/*Functionsuccessful*/}调用方法,产生1ms中断调用方法SysTick_Config(SystemCoreClock/1000);Regardingtheselectionoftheclocksource,inadditiontooperatingregisters,therearelibraryfunctionstochoosefrom./***@briefConfigurestheSysTickclocksource.*@paramSysTick_CLKSource:specifiestheSysTickclocksource.*Thisparametercanbeoneofthefollowingvalues:*@argSysTick_CLKSource_HCLK_Div8:AHBclockdividedby8selectedasSysTickclocksource.*@argSysTick_CLKSource_HCLK:AHBclockselectedasSysTickclocksource.*@retvalNone*/voidSysTick_CLKSourceConfig(uint32_tSysTick_CLKSource){/*Checktheparameters*/assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));if(SysTick_CLKSource==SysTick_CLKSource_HCLK){SysTick->CTRL|=SysTick_CLKSource_HCLK;}else{SysTick->CTRL&=SysTick_CLKSource_HCLK_Div8;}}除了上述之外,我还找到了其他证据来说明SYSTICK时钟可以是HCLK时钟分频或8分频。在STM32CubeMx配置软件中,可以选择1分频或者8分频。03修改代码验证修改《STM32延时的四种方法》文章涉及的代码除以1。voiddelay_ms(uint16_tnms){uint32_ttemp;SysTick->LOAD=RCC_Clocks.HCLK_Frequency/1000*nms-1;SysTick->VAL=0X00;//清除计数器SysTick->CTRL=0X01;SysTick_CLKSourceConfig(SysTick_CLKSource_do{HCLK=>)CTRL;//读取当前倒计时值}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达SysTick->CTRL=0x00;//关闭计数器SysTick->VAL=0X00;//清零计数器}然后调用GPIO_SetBits(GPIOE,GPIO_Pin_4);//关闭LED灯delay_ms(500);//延时500msGPIO_ResetBits(GPIOE,GPIO_Pin_4);//点亮LED灯delay_ms(500);//延迟500ms时,又会踩到一个坑,不允许延迟。原因是:此时的SYSTICK时钟频率是一个120MHz的24位倒计时定时器,也就是说一个周期,最大计时是139.810125ms。不能延迟500ms。这里我们纠正一个之前的错误。我们如下图将计数器的值减1,这样更准确。具体需要减1的原因在讲解定时器的文章中已经解释过了。不懂的请看《STM32基础定时器讲解》。04Summary总结:STM32官方手册不一定准确。你必须自己做实验验证。这是老生常谈的问题,谁都知道关键在于实践。