1. 程式人生 > >stm32系統滴答定時器使用

stm32系統滴答定時器使用

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

首先,我們需要有一個72Msystick系統時鐘,那麼,使用下面這個時鐘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)先來看看主函式

[plain] view plain copy print?
  1. int main(void)  
  2. {            unsigned char i=0;  
  3.         unsigned char a[] = "abncdee";  
  4.         SystemInit1();//系統初始化  
  5.        if (SysTick_Config(72000))  //1ms響應一次中斷  
  6.         {   
  7.             /* Capture error */  
  8.                  while (1);  
  9.         }   
  10.         /*解析:因為要求是每500ms往中位機發資料一件事,所以放在while語句中,  
  11. *送據+延時可以完成相當於中斷的效果;  
  12.                *若是多工中,其中一個任務需要中斷,這把這個任務放在中斷函式中呼叫;  
  13.                */  
  14.         while (1)  
  15.         {  
  16.              //測試程式碼:測試定時器功能,通過延時來測試  
  17.              GPIO_SetBits(GPIOC, GPIO_Pin_6);      //V6  
  18.              Delay(50);  
  19.              GPIO_ResetBits(GPIOC, GPIO_Pin_6);         //V6  
  20.              Delay(50);  
  21.             //功能1程式碼:每500ms傳送資料  
  22.                /*  
  23.                       UART2_TX485_Puts("123450");  
  24.                       Delay(500);  
  25.            */  
  26.             //功能2程式碼:上位發特定指令,中位機執行相應操作  
  27.               //     RS485_Test();  
  28.               }       
  29. }  
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函式的具體實現如下:

[html] view plain copy print?
  1. static __INLINE uint32_t SysTick_Config(uint32_t ticks)  
  2. {   
  3.     if (ticks>SYSTICK_MAXCOUNT)    
  4.      return (1);      /* Reload value impossible */  
  5.     SysTick->LOAD = (ticks & SYSTICK_MAXCOUNT) - 1;//systick重灌載值暫存器   /* set reload register */  
  6.     NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */  
  7.     SysTick->VAL = (0x00);  //systick當前值暫存器                                
  8.    /* Load the SysTick Counter Value */  
  9.    SysTick->CTRL = (1 <<SYSTICK_CLKSOURCE) | (1<<SYSTICK_ENABLE) | (1<<SYSTICK_TICKINT);//使能IRQ(普通中斷)和系器         return(0);      /* Function successful */  
  10. }                                         
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 */

}                                       

我們來看一下這句程式碼: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();在幹些什麼?

[html] view plain copy print?
  1. /*****************************************************************  
  2. *函式名稱:TimingDelay_Decrement  
  3. *功能描述:中斷裡呼叫此函式,即沒發生一次中斷,此函式被呼叫,此函式裡       
  4. *          的變數TimingDelay 相當於減法計數器  
  5. *   
  6. *輸入引數:無  
  7. *返回值:無  
  8. *其他說明:無  
  9. *當前版本:v1.0  
  10. *作    者: 樑尹宣  
  11. *完成日期:2012年8月3日  
  12. *修改日期      版本號      修改人      修改內容  
  13. *-----------------------------------------------------------------  
  14. *  
  15. ******************************************************************/  
  16. void TimingDelay_Decrement(void)    
  17. {    
  18.   if (TimingDelay != 0x00)    
  19.   {     
  20.     TimingDelay--;    
  21.   }  
  22. }    
  23. 我們看了TimingDelay的定義,又看了還有哪些函式呼叫到這個變數,如下:  
  24. /*****************************************************************  
  25. *                                        全域性變數  
  26. ******************************************************************/  
  27. static __IO uint32_t TimingDelay=0;  
  28. /*****************************************************************  
  29. *函式名稱:    Delay  
  30. *功能描述:    利用系統時鐘計數器遞減達到延時功能  
  31. *   
  32. *輸入引數:nTime :需要延的時毫秒數  
  33. *返回值:無  
  34. *其他說明:無  
  35. *當前版本:v1.0  
  36. *作    者: 樑尹宣  
  37. *完成日期:2012年8月3日  
  38. *修改日期      版本號      修改人      修改內容  
  39. *-----------------------------------------------------------------  
  40. *  
  41. ******************************************************************/  
  42. void Delay(__IO uint32_t nTime)//delay被呼叫時,nTime=500
  43. {   
  44.   TimingDelay = nTime;  
  45.   while(TimingDelay != 0);  
  46. }  
/*****************************************************************

*函式名稱: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()中看這幾條語句:紅色標註de

[html] view plain copy print?
  1. while (1)  
  2.         {  
  3.              //測試程式碼:測試定時器功能,通過延時來測試  
  4.              GPIO_SetBits(GPIOC, GPIO_Pin_6);      //V6   
  5.              Delay(500);  
  6.              GPIO_ResetBits(GPIOC, GPIO_Pin_6);         //V6   
  7.              Delay(500);  
  8.             //功能1程式碼:每500ms傳送資料  
  9.                /*  
  10.                       UART2_TX485_Puts("123450");  
  11.                       Delay(500);  
  12.            */  
  13.             //功能2程式碼:上位發特定指令,中位機執行相應操作  
  14.               //     RS485_Test();  
  15.               }       
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通訊程式碼,

下面我們來看看一下參考資料的問題,一邊對上面我寫的部落格有更深入的理解:

Cortex-M3權威指南》

Cortex-M3 Technical Reference Manual

Q:什麼是SYSTick定時器?

SysTick 是一個24位的倒計數定時器,當計到0時,將從RELOAD暫存器中自動重灌載定時初值。只要不把它在SysTick控制及狀態暫存器中的使能位清除,就永不停息。

Q:為什麼要設定SysTick定時器?

1)產生作業系統的時鐘節拍

SysTick定時器被捆綁在NVIC用於產生SYSTICK異常(異常號:15)。在以前,大多作業系統需要一個硬體定時器來產生作業系統需要的滴答中斷,作為整個系統的時基。因此,需要一個定時器來產生週期性的中斷,而且最好還讓使用者程式不能隨意訪問它的暫存器,以維持作業系統心跳的節律。

2)便於不同處理器之間程式移植。

CortexM3處理器內部包含了一個簡單的定時器。因為所有的CM3晶片都帶有這個定時器,軟體在不同 CM3器件間的移植工作得以化簡。該定時器的時鐘源可以是內部時鐘(FCLKCM3上的自由執行時鐘),或者是外部時鐘( CM3處理器上的STCLK訊號)。

不過,STCLK的具體來源則由晶片設計者決定,因此不同產品之間的時鐘頻率可能會大不相同,你需要檢視晶片的器件手冊來決定選擇什麼作為時鐘源。SysTick定時器能產生中斷CM3為它專門開出一個異常型別,並且在向量表中有它的一席之地。它使作業系統和其它系統軟體在CM3器件間的移植變得簡單多了,因為在所有CM3產品間對其處理都是相同的

3)作為一個鬧鈴測量時間。

SysTick定時器除了能服務於作業系統之外,還能用於其它目的:如作為一個鬧鈴,用於測量時間等。要注意的是,當處理器在除錯期間被喊停(halt)時,則SysTick定時器亦將暫停運作。

QSystick如何執行?

首先設定計數器時鐘源,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異常建立向量,提供其服務例程的入口地址。