STM32滴答定時器與UCOS時鐘系統,以及心跳和延時函數的實現.

   Systick就是一個定時器而已,只是它放在了NVIC中,主要的目的是爲了給操作系統提供一個硬件上的中斷(號稱滴答中斷)。滴答中斷?這裏來簡單地解釋一下。操作系統進行運轉的時候,也會有“心跳”。它會根據“心跳”的節拍來工作,把整個時間段分成很多小小的時間片,每個任務每次只能運行一個“時間片”的時間長度就得退出給別的任務運行,這樣可以確保任何一個任務都不會霸佔整個系統不放。或者把每個定時器週期的某個時間範圍賜予特定的任務等,還有操作系統提供的各種定時功能,都與這個滴答定時器有關。因此,需要一個定時器來產生週期性的中斷,而且最好還讓用戶程序不能隨意訪問它的寄存器,以維持操作系統“心跳”的節律。 只要不把它在SysTick控制及狀態寄存器中的使能位清除,就永不停息。

     知道systick在系統中的地位後,我們來了解systick的實現。這裏只是舉例說明systick的使用。它有四個寄存器,筆者把它列出來:

    SysTick->CTRL,        --控制和狀態寄存器

    SysTick->LOAD,        --重裝載寄存器

    SysTick->VAL,          --當前值寄存器

   SysTick->CALIB,        --校準值寄存器    

庫裏SysTick相關的函數我們能找到兩個

一個在msic.h中

 

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;
        }
}

 

一個在core_m3.h中


 

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 */
}

我們一般只需要後一個就可以了

 

需要的操作在SysTick_Handler()(庫版本不同可能是SysTickHandler)中添加就好了,意思每到加載到SysTick中的值減到0時就執行SysTick();

 

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,否則可能會被編譯器優化。(以上的過程 在庫函數SysTick_Config() 裏已經配置稍做了解即可);

 

時鐘的選擇 在庫文件 system_stm32f10x.c 裏面

static void SetSysClock(void);

#ifdef SYSCLK_FREQ_HSE
  static void SetSysClockToHSE(void);
#elif defined SYSCLK_FREQ_24MHz
  static void SetSysClockTo24(void);
#elif defined SYSCLK_FREQ_36MHz
  static void SetSysClockTo36(void);
#elif defined SYSCLK_FREQ_48MHz
  static void SetSysClockTo48(void);
#elif defined SYSCLK_FREQ_56MHz
  static void SetSysClockTo56(void);  
#elif defined SYSCLK_FREQ_72MHz
  static void SetSysClockTo72(void);
#endif

以上 引用參考https://blog.csdn.net/yx_l128125/article/details/7884423

SetSysClock() 函數在SystemInit() 裏面被調用 

SystemInit() 在startup_stm32f10x_hd.s 啓動文件中被調用

 

Systick作爲操作系統的心跳:

UCOS 系統的os_cfg.h文件中 #define OS_TICKS_PER_SEC           100u   /* Set the number of ticks in one second                        */

定義了 每秒心跳(中斷的次數);  100次.  假設是72MH的頻率 , 在這裏ucos給systick 的reload值應該爲720000.

100次中斷耗時1s.

心跳()時間片)原理爲 SysTick_Handler() 中斷函數. 裏面增加 OSIntEnter() (OSIntNesting++),然後調用OSTimeTick() ucos的時鐘服務程序,

最後OSIntExit() 任務調度一次.   

OSTimeTick()的功能: 爲OSTime+1 以及爲所有等待任務控制塊的OSTCBDly-1 (如果-1爲0 由後面的OSIntExit啓動調度)

OSTimeDly()與OSTimeDlyHMSM();

OSTimeDly(INT16U ticks)比較簡單就是 任務控制塊 狀態改爲等待 ,OSTCBCur->OSTCBDly賦值延遲心跳次數ticks.並任務調度.

OSTimeDlyHMSM(INT8U hours, INT8U minutes, INT8U seconds, INT16U ms)複雜了一點

小時分鐘秒的延時簡單的計算出對應的ticks 並調用OSTimeDly();毫秒級別的:

注意:最小延時時間爲心跳 (1/OS_TICKS_PER_SEC);  

 ticks= OS_TICKS_PER_SEC * ((INT32U)ms + 500L / OS_TICKS_PER_SEC) / 1000L;

這裏有個500L的取值.我們暫定爲MINTIME變量吧. 

邵貝貝書裏說:這裏的500的取值 目的是爲了(假設OS_TICKS_PER_SEC=100 最小心跳10ms) 延時4ms的時候不延時,5ms以上的時候才延時. 依次14ms延遲一個節拍 15ms兩個節拍.

個人感覺怪怪的....

那麼如果想單純的引發一次任務調度.那麼OSTimeDlyHMSM(0,0,0,5) 至少是5ms才行.

對於單位 10ms以內的精確延時還是要拋棄ucos的函數而自己寫.

 

爲了實現小於(1/OS_TICKS_PER_SEC)的延時函數. 

傳統的做法是定義delay_us()函數.函數功能,對Systicks 定時器的reload 重裝值賦值 需要延時的時間對應的數值.

程序較爲簡單,但是會影響ucos的心跳.不能在操作系統下使用;

 

爲了能在操作系統下實現us級別的延時.

 

參考正點原子教程:可以定義一個delay_us()的延時函數

void delay_us(u32 nus)
{        
    u32 ticks;
    u32 told,tnow,tcnt=0;
    u32 reload=SysTick->LOAD;    //LOAD的值             
    ticks=nus*fac_us;             //需要的節拍數               fac_us=時鐘頻率/1000000(這裏是72)
    tcnt=0;
    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;//時間超過/等於要延遲的時間,則退出.
        }  
    };                                         
}

這段函數功能,就是不斷的讀取SysTick->VAL ; Systick定時器的計數值;並計算差值. 如果大於需要的ticks 嘖延時結束

如果需要的ticks大於重裝載值,  用systick遞減的原理.判斷如果取得計數值大於記錄計數值,嘖更新記錄值,並對cnt計數加reload重裝載值. 實現us延時

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章