填坑-關(guān)于SysTick定時(shí)器
本文主要來(lái)填坑,更正之前文章的錯(cuò)誤。也進(jìn)一步加深了我對(duì)SysTick定時(shí)器的理解,希望對(duì)你有幫助。
01坑的由來(lái)
在之前的推文中《STM32延時(shí)的四種方法》介紹了使用查詢定時(shí)器精確延時(shí),使用的就是systick定時(shí)器,具體代碼如下
- void delay_us(uint32_t nus)
- {
- uint32_t temp;
- SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000000/8*nus;
- SysTick->VAL=0X00;//清空計(jì)數(shù)器
- SysTick->CTRL=0X01;//使能,減到零是無(wú)動(dòng)作,采用外部時(shí)鐘源
- do
- {
- temp=SysTick->CTRL;//讀取當(dāng)前倒計(jì)數(shù)值
- }while((temp&0x01)&&(!(temp&(1<<16))));//等待時(shí)間到達(dá)
- SysTick->CTRL=0x00; //關(guān)閉計(jì)數(shù)器
- SysTick->VAL =0X00; //清空計(jì)數(shù)器
- }
- void delay_ms(uint16_t nms)
- {
- uint32_t temp;
- SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000/8*nms;
- SysTick->VAL=0X00;//清空計(jì)數(shù)器
- SysTick->CTRL=0X01;//使能,減到零是無(wú)動(dòng)作,采用外部時(shí)鐘源
- do
- {
- temp=SysTick->CTRL;//讀取當(dāng)前倒計(jì)數(shù)值
- }while((temp&0x01)&&(!(temp&(1<<16))));//等待時(shí)間到達(dá)
- SysTick->CTRL=0x00; //關(guān)閉計(jì)數(shù)器
- SysTick->VAL =0X00; //清空計(jì)數(shù)器
- }
對(duì)于《STM32延時(shí)的四種方法》文中所說(shuō)的內(nèi)容如下
也就是下面代碼中/8的原因。
- SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000/8*nms;
我對(duì)此深信不疑,并在STM32F207參考手冊(cè)(RM0033)上找到“證據(jù)”。
上圖①處直接是8分頻,而不像②出的1/2/4/8分頻。所以我確信是SYSTICK的時(shí)鐘固定為HCLK時(shí)鐘的1/8。
我在學(xué)習(xí)RTThread的時(shí)候,看到配置SysTick定制器代碼如下
我心里一堆問(wèn)號(hào),STM32官方手冊(cè),明明寫(xiě)了SYSTICK的時(shí)鐘固定為HCLK時(shí)鐘的1/8。我使用示波器測(cè)量,RTThread的配置是沒(méi)有問(wèn)題,可以正常延時(shí)的。
02填坑
這個(gè)坑其實(shí)很簡(jiǎn)單,在《STM32延時(shí)的四種方法》也提到了,只是自己沒(méi)有注意這個(gè)細(xì)節(jié)。
位2置1,表示時(shí)鐘頻率為AHB,也就是默認(rèn)的120000000Hz。
位2清0,表示時(shí)鐘頻率為AHB/8,也就是120000000/8Hz。
RTThread配置為內(nèi)部時(shí)鐘
之前的文章配置為外部時(shí)鐘源
這個(gè)細(xì)節(jié)我沒(méi)有留意,導(dǎo)致我看RTThread代碼時(shí)有點(diǎn)懵逼。在這里我更正《STM32延時(shí)的四種方法》中的錯(cuò)誤描述
準(zhǔn)確的描述是:
SYSTICK的時(shí)鐘可以為HCLK時(shí)鐘的1分頻或8分頻,在這里我們選用外部時(shí)鐘源120M,所以SYSTICK的時(shí)鐘為(120/8)M。
特此更正。
關(guān)于這點(diǎn),STM32的標(biāo)準(zhǔn)外設(shè)庫(kù)提供的SysTick_Config函數(shù),也是使用內(nèi)部時(shí)鐘的
- /** \brief System Tick Configuration
- This function initialises the system tick timer and its interrupt and start the system tick timer.
- Counter is in free running mode to generate periodical interrupts.
- \param [in] ticks Number of ticks between two interrupts
- \return 0 Function succeeded
- \return 1 Function failed
- */
- static __INLINE uint32_t SysTick_Config(uint32_t ticks)
- {
- if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
- SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
- NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
- 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 */
- }
調(diào)用方法,產(chǎn)生1ms中斷調(diào)用方法
- SysTick_Config(SystemCoreClock / 1000);
關(guān)于時(shí)鐘源的選擇,除了操作寄存器外,還有庫(kù)函數(shù)可以選擇。
- /**
- * @brief Configures the SysTick clock source.
- * @param SysTick_CLKSource: specifies the SysTick clock source.
- * This parameter can be one of the following values:
- * @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.
- * @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.
- * @retval None
- */
- void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
- {
- /* Check the parameters */
- 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;
- }
- }
除上外,我找到了其他證據(jù)來(lái)說(shuō)明,SYSTICK的時(shí)鐘可以為HCLK時(shí)鐘的1分頻或8分頻。
在STM32CubeMx配置軟件中,可以選擇1分頻或8分頻。
03修改代碼驗(yàn)證
把《STM32延時(shí)的四種方法》文中涉及的代碼修改成1分頻的。
- void delay_ms(uint16_t nms)
- {
- uint32_t temp;
- SysTick->LOAD = RCC_Clocks.HCLK_Frequency/1000*nms-1;
- SysTick->VAL=0X00;//清空計(jì)數(shù)器
- SysTick->CTRL=0X01;
- SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
- do
- {
- temp=SysTick->CTRL;//讀取當(dāng)前倒計(jì)數(shù)值
- }while((temp&0x01)&&(!(temp&(1<<16))));//等待時(shí)間到達(dá)
- SysTick->CTRL=0x00; //關(guān)閉計(jì)數(shù)器
- SysTick->VAL =0X00; //清空計(jì)數(shù)器
- }
然后調(diào)用
- GPIO_SetBits(GPIOE,GPIO_Pin_4); //熄滅LED燈
- delay_ms(500);//延時(shí)500ms
- GPIO_ResetBits(GPIOE,GPIO_Pin_4);//點(diǎn)亮LED燈
- delay_ms(500);//延時(shí)500ms
就踩到另一個(gè)坑,延時(shí)不準(zhǔn)。
原因是:此時(shí)SYSTICK時(shí)鐘頻率是120MHz的24位的倒計(jì)數(shù)定時(shí)器,也就是說(shuō)一個(gè)周期,最多定時(shí)139.810125ms。不能延時(shí)500ms。
這里再更正之前的一個(gè)錯(cuò)誤,如下圖
這個(gè)計(jì)數(shù)器的值,我們減去了1,這樣才更準(zhǔn)確。需要減1的具體原因在定時(shí)器講解的文章中講解過(guò)了,不明白的同學(xué)請(qǐng)看《STM32基礎(chǔ)定時(shí)器講解》。
04總結(jié)
總結(jié):STM32官方手冊(cè)并不一定是準(zhǔn)確的,要親自做實(shí)驗(yàn),自己動(dòng)手驗(yàn)證。這是個(gè)老生常談的問(wèn)題,大家都知道,關(guān)鍵還在于實(shí)踐。