1.STM32-Systick滴答定時器
-
Systick定時器,是一個簡單的定時器,對於ST的CM3,CM4,CM7內核芯片,都有Systick定時器。
-
Systick定時器常用來做延時,或者實時系統的心跳時鍾。這樣可以節省MCU資源,不用浪費一個定時器。比如UCOS中,分時復用,需要一個最小的時間戳,一般在STM32+UCOS系統中,都采用Systick做UCOS心跳時鍾。
-
Systick定時器就是系統滴答定時器,一個24 位的倒計數定時器,計到0 時,將從RELOAD 寄存器中自動重裝載定時初值。只要不把它在SysTick 控制及狀態寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。
-
SysTick定時器被捆綁在NVIC中,用於產生SYSTICK異常(異常號:15)。
-
Systick中斷的優先級也可以設置。
-
4個Systick寄存器
CTRL SysTick 控制和狀態寄存器 LOAD SysTick 自動重裝載除值寄存器 VAL SysTick 當前值寄存器 CALIB SysTick 校准值寄存器
可在core-core_cm7.h文件中找到
typedef struct
{
__IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
__IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
__IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
__IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
SysTick 控制和狀態寄存器- CTRL:
對於STM32,外部時鍾源是 HCLK(AHB總線時鍾)的1/8。內核時鍾是 HCLK時鍾。配置函數:HAL_SYSTICK_CLKSourceConfig();
SysTick重裝載數值寄存器-LOAD
SysTick當前數值寄存器-VAL
HAL庫中的Systick相關函數:
stm32f7xx_hal_cortex.c文件中:HAL_SYSTICK_CLKSourceConfig () ; //Systick時鍾源選擇
如果SysTick的時鍾源自HCLK,假設外部晶振為25M,倍頻到216MHZ,那么SysTick的時鍾即為216MHZ,也就是SysTick的計數器VAL每減1,就代表時間過了1/216us。
void HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource) { /* Check the parameters */ assert_param(IS_SYSTICK_CLK_SOURCE(CLKSource)); if (CLKSource == SYSTICK_CLKSOURCE_HCLK) { SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; } else { SysTick->CTRL &= ~SYSTICK_CLKSOURCE_HCLK; } }
可以看一下CLKSource可以有哪幾種:找到IS_SYSTICK_CLK_SOURCE的定義,發現可以為SYSTICK_CLKSOURCE_HCLK和SYSTICK_CLKSOURCE_HCLK_DIV8,也就是不分頻或者8分頻。
#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SYSTICK_CLKSOURCE_HCLK) || \ ((SOURCE) == SYSTICK_CLKSOURCE_HCLK_DIV8))
core_cm7.h文件中:SysTick_Config (uint32_t ticks) //初始化systick,時鍾為HCLK,並開啟中斷。
ticks:經過多少個systick周期發生一次中斷。用來配置SysTick定時器經過多少個ticks發生一次中斷。
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) { if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); /* Reload value impossible */ } SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ SysTick->VAL = 0UL; /* 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 (0UL); /* Function successful */ }
首先看到SysTick_LOAD_RELOAD_Msk,由於Systick是一個24 位的倒計數定時器,所以值不能太大。
#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/)
Systick中斷服務函數:void SysTick_Handler (void);
//初始化延遲函數 //當使用ucos的時候,此函數會初始化ucos的時鍾節拍 //SYSTICK的時鍾固定為AHB時鍾的1/8 //SYSCLK:系統時鍾頻率 void delay_init(u8 SYSCLK) { #if SYSTEM_SUPPORT_OS //如果需要支持OS. u32 reload; #endif HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick頻率為HCLK fac_us=SYSCLK; //不論是否使用OS,fac_us都需要使用 #if SYSTEM_SUPPORT_OS //如果需要支持OS. reload=SYSCLK; //每秒鍾的計數次數 單位為K reload*=1000000/delay_ostickspersec; //根據delay_ostickspersec設定溢出時間 //reload為24位寄存器,最大值:16777216,在216M下,約合77.7ms左右 fac_ms=1000/delay_ostickspersec; //代表OS可以延時的最少單位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;//開啟SYSTICK中斷 SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中斷一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //開啟SYSTICK #endif }
在main中調用delay_init(216);//延時初始化。1微秒等於10的負6次方秒,1MHZ等於10^6赫茲。
fac_us=SYSCLK;系統時鍾是216MHZ,fac_us=216意思是:systick運行1us,需要多少個systick周期。由於systick是216MHZ,所以需要216個周期。后面如果需要延時n個微秒,只需要n*fac_us即可。由於設置的SysTick頻率為HCLK,所以調用delay_init函數中的參數SYSCLK就是216 。
delay_us函數思路:
在循環里檢測當前值,如果當前值小於前一次的值,說明沒有減到0,通過told-tnow就可以知道當前跑了幾個周期。否則,說明已經溢出了,也就是說當前跑了reload-tnow+told個周期。最后如果時間超過/等於要延遲的時間,則退出。
//延時nus //nus:要延時的us數. //nus:0~204522252(最大值即2^32/fac_us@fac_us=21) void delay_us(u32 nus) { u32 ticks; u32 told,tnow,tcnt=0; u32 reload=SysTick->LOAD; //LOAD的值 ticks=nus*fac_us; //需要的節拍數 delay_osschedlock(); //阻止OS調度,防止打斷us延時 told=SysTick->VAL; //剛進入時的計數器值 while(1) { tnow=SysTick->VAL; if(tnow!=told) { if(tnow<told)tcnt+=told-tnow; //這里注意一下SYSTICK是一個遞減的計數器就可以了. else tcnt+=reload-tnow+told; told=tnow; if(tcnt>=ticks)break; //時間超過/等於要延遲的時間,則退出. } }; delay_osschedunlock(); //恢復OS調度 }