1. 程式人生 > >STM32滴答定時器與UCOS時鐘系統,以及心跳和延時函式的實現.

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

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延時