STM32之系統(tǒng)滴答定時器
一、SysTick(系統(tǒng)滴答定時器)概述
本文引用地址:http://m.butianyuan.cn/article/201611/318497.htm操作系統(tǒng)需要一個滴答定時器周期性產(chǎn)生中斷,以產(chǎn)生系統(tǒng)運行的節(jié)拍。在中斷服務(wù)程序里,基于優(yōu)先級調(diào)度的操作系統(tǒng)會根據(jù)進(jìn)程優(yōu)先級切換任務(wù),基于時間片輪轉(zhuǎn)系統(tǒng)會根據(jù)時間片切換任務(wù)??傊未鸲〞r器是一個操作系統(tǒng)的“心跳”。
Cortex-M3在內(nèi)核部分封裝了一個滴答定時器--SysTick,在之前的ARM內(nèi)核通常是不會把定時器做進(jìn)內(nèi)核,定時器都是SOC廠商自己制作的外設(shè)。顯然,Cortex-M3封裝了這么一個定時器,對于將操作系統(tǒng)移植到不同SOC廠商生產(chǎn)的Cortex-M3系類MCU上,帶來了極大的方便。Cortex-M3內(nèi)核統(tǒng)一了這樣的一個系統(tǒng)滴答定時器,移植操作系統(tǒng)的時候可以使用內(nèi)核的定時器,而忽略掉不同廠商生產(chǎn)定時器帶來的分歧。
二、SysTick control and status register(STK_CTRL)
SysTick的控制是極其簡單的,它的控制和狀態(tài)都匯聚在同一個寄存器STK_CTRL上。
STK_CTRL的每一位的含義英文解釋都是很清晰的,不必多說。需要額外討論的是COUNTFLAG標(biāo)志位,這個標(biāo)志位代表的含義是:當(dāng)計數(shù)為0時,也即STK_VAL計數(shù)至0時,此標(biāo)志位置1。
經(jīng)過筆者一番摸索,對此位有更多的看法。
COUNTFLAG:
1、此位與SysTick的中斷無關(guān),不是中斷標(biāo)志位,可以算作事件標(biāo)志位(計數(shù)至0事件)。
2、此位可以用作軟件查詢
3、讀寫此寄存器都會硬件自動更新COUNTFLAG的值,當(dāng)然此位的值軟件也是可以改寫的。也就是說,倘若我們輪訓(xùn)查看COUNTFLAG是否置1(也就是計數(shù)是否結(jié)束)。當(dāng)SysTick硬件上計數(shù)為0,COUNTFLAG因此硬件自動置1。在我們軟件讀取STK_CTRL的時候,其實SysTick的STK_VAL的值已經(jīng)不是0(因為自動重裝,并且可能計時幾個CLK了)。這個時候我們讀取到了COUNTFLAG的標(biāo)志位的1,同時也將COUNTFLAG自動清零。
三、滴答定時器應(yīng)用之精準(zhǔn)延時函數(shù)
1、函數(shù)實現(xiàn)思路
函數(shù)實現(xiàn)使用“輪詢狀態(tài)位COUNTFLAG”實現(xiàn)精準(zhǔn)延時節(jié)拍10us。
在使用的時候,首先調(diào)用函數(shù)SysTick_Init配置SysTick的定時周期為10us。在延時函數(shù)中,當(dāng)啟動定時器后,就調(diào)用函數(shù)SysTick_GetFlagStatus輪詢是否定時10us結(jié)束,如果結(jié)束就更新一下延時節(jié)拍變量nTime。
由于SysTick定時器自動重裝計數(shù)器初值,而且SysTick_GetFlagStatus在檢測到SET的時候,COUNTFLAG也自動清理。所以軟件不必裝定時器初值,也不必手動清除標(biāo)志位COUNTFLAG。
2、函數(shù)實現(xiàn)代碼
#include "bsp_sysTick.h"/*** @brief 讀取SysTick的狀態(tài)位COUNTFLAG* @param 無* @retval The new state of USART_FLAG (SET or RESET).*/static FlagStatus SysTick_GetFlagStatus(void) {if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) {return SET;}else{return RESET;}}/*** @brief 清除SysTick的狀態(tài)位COUNTFLAG* @param 無* @retval 無*/static void SysTick_ClearFlag(void){ SysTick->CTRL &= ~ SysTick_CTRL_COUNTFLAG_Msk;}/*** @brief 配置系統(tǒng)滴答定時器 SysTick* @param 無* @retval 1 = failed, 0 = successful*/uint32_t SysTick_Init(void){/* SystemFrequency / 1 1ms中斷一次* SystemFrequency / 100 10us中斷一次* SystemFrequency / 1 1us中斷一次*//* 設(shè)置定時周期為10us */if (SysTick_Config(SystemCoreClock / 100)) { /* Capture error */ return (1);}/* 關(guān)閉滴答定時器且禁止中斷 */SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk SysTick_CTRL_TICKINT_Msk); return (0);}/*** @brief us延時程序,10us為一個單位* @param * @arg nTime: Delay_us( 1 ) 則實現(xiàn)的延時為 1 * 10us = 10us* @retval 無*/void Delay_us(__IO uint32_t nTime){ /* 清零計數(shù)器并使能滴答定時器 */ SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; for( ; nTime > 0 ; nTime--){/* 等待一個延時單位的結(jié)束 */while(SysTick_GetFlagStatus() != SET);}/* 關(guān)閉滴答定時器 */SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;}
3、函數(shù)的優(yōu)點和缺陷
優(yōu)點:
使用輪詢法實現(xiàn)精準(zhǔn)延時是可靠的,因為硬件自動重裝定時器初值,只要我們在下一次計數(shù)結(jié)束(為0)之前,將節(jié)拍計數(shù)變量nTime更新,那么這個延時函數(shù)就是可靠的。
使用輪詢法的另一個好處是沒有用到全局變量,完全是局部變量搞定了所需功能。倘若使用中斷延時,必須利用全局變量給精準(zhǔn)延時函數(shù)傳遞參數(shù)。
缺陷:
由于使用的是輪詢法,有可能被其他的中斷打斷,假設(shè)其他的中斷的服務(wù)時間有很長,使得“在下一次計數(shù)結(jié)束(為0)之前,沒有將節(jié)拍計數(shù)變量nTime更新”,那么延時的時間將要增長。
4、注意
此延時函數(shù)的最小分辨率不能設(shè)置為1us,最好設(shè)置為>=10us,這是因為輪訓(xùn)的周期和1us相比具有可比性,時間誤差太大。
四、滴答定時器應(yīng)用之程序段計時
1、函數(shù)實現(xiàn)思路
首先對滴答定時器初始化,計時節(jié)拍數(shù)是計數(shù)器的最大值。在感興趣的程序段開始處,啟動定時器,在程序段的結(jié)束處關(guān)閉定時器。倘若這段時間很長,超過了計數(shù)器的計數(shù)最大值,就會在中斷函數(shù)中對溢出次數(shù)進(jìn)行計數(shù)。最終的程序段時間決定于計數(shù)器的數(shù)據(jù)寄存器SysTick->VAL中的剩余值和中斷溢出次數(shù)。
另外為了使程序能夠?qū)Σ煌某绦蚨位蛘卟煌闆r下的程序段進(jìn)行計時,使用了一個結(jié)構(gòu)體定義保存計時數(shù)據(jù)的結(jié)構(gòu)體類型。在對程序段進(jìn)行計時的時候,通過一個運行指針指向所要保存的變量中。
2、函數(shù)代碼
① User_SysTick.c
/*********************************************************************************計時最小單位:1/72M s*計時最大長度:2^32/72M = 59.65 s*使用方法:*(1) 定義一個保存計時數(shù)據(jù)的TimingVarTypeDef類型變量Time*(2) 初始化* SysTick_Time_Init(&Time);*(3) 在while循環(huán)中放置啟動/停止函數(shù)* while(1){* SysTick_Time_Start();* 測試運行時間的代碼* SysTick_Time_Stop();* }*******************************************************************************//* 定義保存未使用DMA時測試程序段運行時間的變量 */TimingVarTypeDef Time;/* 指針指向當(dāng)前保存時間的變量 */TimingVarTypeDef * CurrentTimingVar; /* 系統(tǒng)滴答定時器的中斷次數(shù) */uint32_t TimeupTimes;/*** @brief 配置系統(tǒng)滴答定時器 SysTick* @param 無* @retval 1 = failed, 0 = successful*/uint32_t SysTick_Init(void){/* 設(shè)置定時周期為最大定時數(shù)SysTick_LOAD_RELOAD_Msk */if (SysTick_Config(SysTick_LOAD_RELOAD_Msk)) { /* Capture error */ return (1);}/* 關(guān)閉滴答定時器且禁止中斷 */SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk; return (0);} /*** @brief 滴答定時器 SysTick 計時初始化* @param 初始化計時變量的成員--計時次數(shù)* @retval 無*/void SysTick_Time_Init(TimingVarTypeDef * TimingVar){/* 指針指向當(dāng)前保存時間的變量 */CurrentTimingVar = TimingVar;/* 計時次數(shù)初始化 */ CurrentTimingVar->SetSaveTimesNum = SaveTimesBufNum - 2;}/*** @brief 滴答定時器 SysTick 計時啟動* @param 無* @retval 無*/void SysTick_Time_Start(void){/* 判斷已經(jīng)計時次數(shù)是否達(dá)到設(shè)置的計時次數(shù) */if(CurrentTimingVar->SaveTimesTemp < CurrentTimingVar->SetSaveTimesNum){/* 滴答定時器的數(shù)據(jù)寄存器清零 */SysTick->VAL = 0;/* 滴答定時器中斷次數(shù)清零 */TimeupTimes = 0;/* 啟動滴答定時器 */SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; }}/*** @brief 滴答定時器 SysTick 計時停止并保存處理數(shù)據(jù)* @param 無* @retval 無*/void SysTick_Time_Stop(void){/* 保存已經(jīng)計時次數(shù) */ uint32_t TimesTemp = CurrentTimingVar->SaveTimesTemp;/* 保存設(shè)置計時總次數(shù) */uint32_t SetSaveTimesNum = CurrentTimingVar->SetSaveTimesNum;uint32_t i,TimeWidthAverageTemp = 0; /* 保存設(shè)置計時總次數(shù) */ if(SysTick->CTRL & SysTick_CTRL_ENABLE_Msk){/* 關(guān)閉滴答定時器 */SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;/* 計算計時總時間 */CurrentTimingVar->TimeWidth[TimesTemp] = SysTick_LOAD_RELOAD_Msk * TimeupTimes + (SysTick_LOAD_RELOAD_Msk - SysTick->VAL + 1);/* 判斷計時次數(shù)是否滿 */if((++TimesTemp) == SetSaveTimesNum){/* 計算平均值 */for(i = 0;i < SetSaveTimesNum; i++){TimeWidthAverageTemp += CurrentTimingVar->TimeWidth[i];}CurrentTimingVar->TimeWidthAvrage = TimeWidthAverageTemp/SetSaveTimesNum; }/* 已經(jīng)計時次數(shù)變量加1 */CurrentTimingVar->SaveTimesTemp++; }}
② User_SysTick.h
#define SaveTimesBufNum 4 /* 計時存儲區(qū)的大小 */typedef struct {uint32_t SetSaveTimesNum; /* 設(shè)置計時總次數(shù) */uint32_t SaveTimesTemp; /* 已經(jīng)計時的次數(shù) */uint32_t TimeWidth[SaveTimesBufNum]; /* 計時存儲區(qū) */uint32_t TimeWidthAvrage; /* 平均計時長度 */} TimingVarTypeDef; /* 計時變量類型 */extern TimingVarTypeDef Time;extern uint32_t TimeupTimes;extern uint32_t SysTick_Init(void);extern void SysTick_Time_Init(TimingVarTypeDef * TimingVar);extern void SysTick_Time_Start(void);extern void SysTick_Time_Stop(void);
③ stm32f10x_it.c
/*** @brief This function handles SysTick Handler.* @param None* @retval None*/void SysTick_Handler(void){TimeupTimes++;}
參考資料:《STM32F10xxx Cortex-M3 programming manual.pdf》
《STM32庫開發(fā)實戰(zhàn)指南》
評論