1.systick介紹
Systick就是一個定時器而已,只是它放在了NVIC中,主要的目的是爲了給操作系統提供一個硬件上的中斷(號稱滴答中斷)。滴答中斷?這裏來簡單地解釋一下。操作系統進行運轉的時候,也會有“心跳”。它會根據“心跳”的節拍來工作,把整個時間段分成很多小小的時間片,每個任務每次只能運行一個“時間片”的時間長度就得退出給別的任務運行,這樣可以確保任何一個任務都不會霸佔整個系統不放。或者把每個定時器週期的某個時間範圍賜予特定的任務等,還有操作系統提供的各種定時功能,都與這個滴答定時器有關。因此,需要一個定時器來產生週期性的中斷,而且最好還讓用戶程序不能隨意訪問它的寄存器,以維持操作系統“心跳”的節律。 只要不把它在SysTick控制及狀態寄存器中的使能位清除,就永不停息。
知道systick在系統中的地位後,我們來了解systick的實現。這裏只是舉例說明systick的使用。它有四個寄存器,筆者把它列出來:
SysTick->CTRL, --控制和狀態寄存器
SysTick->LOAD, --重裝載寄存器
SysTick->VAL, --當前值寄存器
SysTick->CALIB, --校準值寄存器
下圖有他們的分別描述: 下圖引用地址:http://blog.csdn.net/marike1314/article/details/5673684
2.systick編程
現在我們想通過Systick定時器做一個精確的延遲函數,比如讓LED精確延遲1秒鐘閃亮一次。
思路:利用systick定時器爲遞減計數器,設定初值並使能它後,它會每個1系統時鐘週期計數器減,計數到 0時,SysTick計數器自動重裝初值並繼續計數,同時觸發中斷。
那麼每次計數器減到0,時間經過了:系統時鐘週期 *計數器初值。我們使用72M作爲系統時鐘,那麼每次計數器減1所用的時間是1/72M,計數器的初值如果是72000,那麼每次計數器減到0,時間經過(1/72M)*72000= 0.001,即1ms。(簡單理解:用72M的時鐘頻率,即1s計數72M=72000000次,那1ms計數72000次,所以計數值爲72000)
首先,我們需要有一個72M的systick系統時鐘,那麼,使用下面這個時鐘OK就 !
SystemInit();
這個函數可以讓主頻運行到72M。可以把它作爲systick的時鐘源。
接着開始配置systick,實際上配置systick的嚴格過程如下:
1、調用SysTick_CounterCmd() --失能SysTick計數器
2、調用SysTick_ITConfig() --失能SysTick中斷
3、調用SysTick_CLKSourceConfig() --設置SysTick時鐘源。
4、調用SysTick_SetReload() --設置SysTick重裝載值。
5、調用SysTick_ITConfig() --使能SysTick中斷
6、調用SysTick_CounterCmd() --開啓SysTick計數器
這裏大家一定要注意,必須使得當前寄存器的值VAL等於0!
SysTick->VAL = (0x00);只有當VAL值爲0時,計數器自動重載RELOAD。
接下來就可以直接調用Delay();函數進行延遲了。延遲函數的實現中,要注意的是,全局變量TimingDelay必須使用volatile,否則可能會被編譯器優化。
下面我們來做一下程序分析:
(1)系統時鐘進配置
首先我們對系統時鐘進行了配置並且SetSysClock(void)函數使用72M作爲系統時鐘;
爲了方面看清代碼我選擇截圖:
(2)先來看看主函數
int main(void)
{
unsigned char i=0;
unsigned char a[] = "abncdee";
SystemInit1();//系統初始化
if (SysTick_Config(72000)) //1ms響應一次中斷
{
/* Capture error */
while (1);
}
/*解析:因爲要求是每500ms往中位機發數據一件事,所以放在while語句中,
*送據+延時可以完成相當於中斷的效果;
*若是多任務中,其中一個任務需要中斷,這把這個任務放在中斷函數中調用;
*/
while (1)
{
//測試代碼:測試定時器功能,通過延時來測試
GPIO_SetBits(GPIOC, GPIO_Pin_6); //V6
Delay(50);
GPIO_ResetBits(GPIOC, GPIO_Pin_6); //V6
Delay(50);
//功能1代碼:每500ms發送數據
/*
UART2_TX485_Puts("123450");
Delay(500);
*/
//功能2代碼:上位發特定指令,中位機執行相應操作
// RS485_Test();
}
}
(3)系統滴答定時器的配置--主角登場:
主函數中: SysTick_Config(72000) ;滴答定時器的參數是72000即計數72000
(因爲我們使用72M的時鐘頻率,即1s計數72M=72000000次,那1ms計數72000次,所以計數值爲72000)
在文件Core_cm3.h中
SysTick_Config函數的具體實現如下:
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks>SYSTICK_MAXCOUNT)
return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SYSTICK_MAXCOUNT) - 1;//systick重裝載值寄存器 /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = (0x00); //systick當前值寄存器
/* Load the SysTick Counter Value */
SysTick->CTRL = (1 << SYSTICK_CLKSOURCE) | (1<<SYSTICK_ENABLE) | (1<<SYSTICK_TICKINT);//使能IRQ(普通中斷)和系器
return(0);
/* Function successful */
} <strong> </strong>
我們來看一下這句代碼:SysTick->CTRL = (1 << SYSTICK_CLKSOURCE) | (1<<SYSTICK_ENABLE) | (1<<SYSTICK_TICKINT); 這是使能IRQ(普通中斷)和系統定時器,爲什麼要使能中斷和系統定時器呢?
下面我們來看一下stm32f10x_it.h文件中:
找到滴答定時器中斷函數:SysTickHandler()
void SysTickHandler(void)
{
TimingDelay_Decrement();
}
從上文我們通過裝載的計數值72000知道每1ms發生一次中斷,在中斷函數中調用一個函數TimingDelay_Decrement();-----即每1ms發生中斷時就調用到此函數;
下面我們來看看TimingDelay_Decrement();在幹些什麼?
/*****************************************************************
*函數名稱:TimingDelay_Decrement
*功能描述:中斷裏調用此函數,即沒發生一次中斷,此函數被調用,此函數裏
* 的變量TimingDelay 相當於減法計數器
*
*輸入參數:無
*返回值:無
*其他說明:無
*當前版本:v1.0
*作 者: 樑尹宣
*完成日期:2012年8月3日
*修改日期 版本號 修改人 修改內容
*-----------------------------------------------------------------
*
******************************************************************/
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
我們看了TimingDelay的定義,又看了還有哪些函數調用到這個變量,如下:
/*****************************************************************
* 全局變量
******************************************************************/
static __IO uint32_t TimingDelay=0;
/*****************************************************************
*函數名稱: Delay
*功能描述: 利用系統時鐘計數器遞減達到延時功能
*
*輸入參數:nTime :需要延的時毫秒數
*返回值:無
*其他說明:無
*當前版本:v1.0
*作 者: 樑尹宣
*完成日期:2012年8月3日
*修改日期 版本號 修改人 修改內容
*-----------------------------------------------------------------
*
******************************************************************/
void Delay(__IO uint32_t nTime)//delay被調用時,nTime=500
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
通過上面幾個函數我們知道了,在調用Delay(500)即nTime=500;在後在Delay()函數中TimingDelay =nTime;(即TimingDelay=500是它的初始值),再TimingDelay_Decrement(void)函數的作用就是把TimingDelay- -;每毫秒進行遞減直到減到0爲止;這樣就起到一個延時的作用;
現在我們做出來的Delay(1),就是1毫秒延遲。Delay(1000)就是1秒。
我們來畫個圖,方便這幾個函數間關係的理解:
我們在返回到主函數main()中看這幾條語句:紅色標註
while (1)
{
//測試代碼:測試定時器功能,通過延時來測試
GPIO_SetBits(GPIOC, GPIO_Pin_6); //V6
Delay(500);
GPIO_ResetBits(GPIOC, GPIO_Pin_6); //V6
Delay(500);
//功能1代碼:每500ms發送數據
/*
UART2_TX485_Puts("123450");
Delay(500);
*/
//功能2代碼:上位發特定指令,中位機執行相應操作
//RS485_Test();
}
經過上面系統定時器的分析我們知道Delay(500);是延時500ms ;那麼LED就是每隔500ms閃爍一次;
上面有關係統滴答定時器的應用講解基本完畢!
有關SysTick編譯後的源代碼包,(其實客官細心的話一經發現上面代碼含有485通訊代碼,
只不過被暫時屏蔽掉了,下一節將講到)我放在我的資源裏:http://download.csdn.net/detail/yx_l128125/4511622
下面我們來看看一下參考資料的問題,一邊對上面我寫的博客有更深入的理解:
《Cortex-M3權威指南》
《Cortex-M3 Technical Reference Manual》
Q:什麼是SYSTick定時器?
SysTick 是一個24位的倒計數定時器,當計到0時,將從RELOAD寄存器中自動重裝載定時初值。只要不把它在SysTick控制及狀態寄存器中的使能位清除,就永不停息。
Q:爲什麼要設置SysTick定時器?
(1)產生操作系統的時鐘節拍
SysTick定時器被捆綁在NVIC中,用於產生SYSTICK異常(異常號:15)。在以前,大多操作系統需要一個硬件定時器來產生操作系統需要的滴答中斷,作爲整個系統的時基。因此,需要一個定時器來產生週期性的中斷,而且最好還讓用戶程序不能隨意訪問它的寄存器,以維持操作系統“心跳”的節律。
(2)便於不同處理器之間程序移植。
Cortex‐M3處理器內部包含了一個簡單的定時器。因爲所有的CM3芯片都帶有這個定時器,軟件在不同 CM3器件間的移植工作得以化簡。該定時器的時鐘源可以是內部時鐘(FCLK,CM3上的自由運行時鐘),或者是外部時鐘( CM3處理器上的STCLK信號)。
不過,STCLK的具體來源則由芯片設計者決定,因此不同產品之間的時鐘頻率可能會大不相同,你需要檢視芯片的器件手冊來決定選擇什麼作爲時鐘源。SysTick定時器能產生中斷,CM3爲它專門開出一個異常類型,並且在向量表中有它的一席之地。它使操作系統和其它系統軟件在CM3器件間的移植變得簡單多了,因爲在所有CM3產品間對其處理都是相同的。
(3)作爲一個鬧鈴測量時間。
SysTick定時器除了能服務於操作系統之外,還能用於其它目的:如作爲一個鬧鈴,用於測量時間等。要注意的是,當處理器在調試期間被喊停(halt)時,則SysTick定時器亦將暫停運作。
Q:Systick如何運行?
首先設置計數器時鐘源,CTRL->CLKSOURCE(控制寄存器)。設置重載值(RELOAD寄存器),清空計數寄存器VAL(就是下圖的CURRENT)。置CTRL->ENABLE位開始計時。
如果是中斷則允許Systick中斷,在中斷例程中處理。如採用查詢模式則不斷讀取控制寄存器的COUNTFLAG標誌位,判斷是否計時至零。或者採取下列一種方法
當SysTick定時器從1計到0時,它將把COUNTFLAG位置位;而下述方法可以清零之:
1. 讀取SysTick控制及狀態寄存器(STCSR)
2. 往SysTick當前值寄存器(STCVR)中寫任何數據
只有當VAL值爲0時,計數器自動重載RELOAD。
Q:如何使用SysTicks作爲系統時鐘?
SysTick 的最大使命,就是定期地產生異常請求,作爲系統的時基。OS都需要這種“滴答”來推動任務和時間的管理。如欲使能SysTick異常,則把STCSR.TICKINT置位。另外,如果向量表被重定位到SRAM中,還需要爲SysTick異常建立向量,提供其服務例程的入口地址。