1. 程式人生 > >毫秒,微妙級別軟體定時器

毫秒,微妙級別軟體定時器

微控制器開發中,軟體定時器是常用的工具。定時執行特定任務和延時功能,都可以用軟體定時器實現。

  常見的延時函式的實現做法有:

  1. 使用空指令進行延時,通過控制空指令的執行次數,進行延時。優點:不需要佔用系統外設。缺點:系統執行指定個空指令的時間不穩定,中途出現的中斷處理會嚴重影響計時的精確性。

  2.使用微控制器的定時器外設,設定特定的時間產生中斷,進行計時。優點:計時準確,不受其他中斷影響計時。缺點:浪費微控制器外設資源,並且延時處理不能巢狀呼叫,靈活性不夠。

  這裡要介紹的是利用微控制器內部的sysTicket 定時器實現的軟體定時器。sysTicket timer每毫秒產生一次中斷,微控制器內有一個無符號型別的32位全域性變數msTicket對中斷次數進行計數,我們可以認為msTicket

為當前“系統時間”。

  先介紹相對簡單的ms定時器,ms定時器的結構定義如下:

typedef struct
{
    uint16_t start;
    uint32_t value;
}MsSoftTimer;

  start欄位用來表示定時器的開關狀態,考慮到位元組對齊的問題,用了十六位的型別。如果微控制器儲存資源緊張,可以不用這個欄位。value欄位用來儲存開始計時時刻系統的時間,也就是msTicket的值。

ms定時器的介面函式如下:

複製程式碼 複製程式碼
 1 #define        def_ms_tm(tm)    MsSoftTimer tm;
 2 #define        declare_ms_tm(tm)    extern MsSoftTimer tm;
 3
#define get_ms_tm_val(tm) _get_ms_tm_val(tm.value) 4 5 #define start_ms_tm(tm) do \ 6 { \ 7 tm.start = 1; \ 8 tm.value = get_msTicks(); \ 9 }while(0) 10 11 #define init_ms_tm(tm) do \ 12 { \ 13 tm.start = 0
; \ 14 tm.value = 0; \ 15 }while(0) 16 17 #define is_ms_tm_on(tm) ( tm.start) 18 #define stop_ms_tm(tm) tm.start = 0
複製程式碼 複製程式碼

定義定時器,本質是就是定義一個定時器型別的變數。可以巢狀呼叫,如果要在中斷處理函式中使用軟體定時器,要先將msTicket 中斷的優先順序設定為最高級別的,並且可以搶佔。獲取當前的計時時間,就是將當前的“系統時間”,減去定時器開始計時時刻的時間。具體實現如下:

複製程式碼
 1 uint32_t _get_ms_tm_val(uint32_t pre_timer_val)
 2 {
 3     uint32_t curr_timer_val = msTicks;
 4     uint32_t ret_timer_val = 0;
 5     
 6     if ( curr_timer_val >= pre_timer_val)
 7     {
 8         ret_timer_val  = curr_timer_val - pre_timer_val;
 9     }
10     else
11     {
12         ret_timer_val = 0xFFFFFFFF - pre_timer_val + curr_timer_val;
13     }
14 
15     return ret_timer_val;
16 }
複製程式碼

第12行程式碼中,對msTicket 變數溢位做了判斷和處理。

利於ms軟體定時器實現的ms延時函式如下:

複製程式碼
void delay_ms(uint32_t ms)
{
    if ( ms == 0) return ;
    def_ms_tm(tm_ms_count);
    start_ms_tm(tm_ms_count);
    while ( get_ms_tm_val(tm_ms_count) < ms)
    ;
    stop_ms_tm(tm_ms_count);
}
複製程式碼

us 定時器實現原理跟ms定時器類似,但會稍微複雜一些。us定時利用系統sysTicket 定時器內部的計數值(SysTick Current Value)進行計時。如果系統時鐘為20M,每隔1us,SysTick Current Value減少20。

如果系統時鐘為48M,每隔1us,SysTick Current Value 減少48。系統sysTicket 定時器結構如下:

複製程式碼
1 typedef struct
2 {
3   __IO uint32_t CTRL;                    /*!< Offset: 0x000 (R/W)  SysTick Control and Status Register */
4   __IO uint32_t LOAD;                    /*!< Offset: 0x004 (R/W)  SysTick Reload Value Register       */
5   __IO uint32_t VAL;                     /*!< Offset: 0x008 (R/W)  SysTick Current Value Register      */
6   __I  uint32_t CALIB;                   /*!< Offset: 0x00C (R/ )  SysTick Calibration Register        */
7 } SysTick_Type;
複製程式碼

us 定時器的結構體如下:

複製程式碼
1 typedef struct 
2 {
3     uint16_t start;
4     uint32_t init_ticket_val;
5     uint32_t init_ms_val;
6 }UsSoftTimer;
複製程式碼

init_ticket_val 記錄的是開始計時時刻SysTick Current Value的值。init_ms_val 記錄的是開始計時時刻msTicket 的值。

us 定時器介面函式實現如下:

複製程式碼
 1 void _start_us_sw(USSoftTimer* pTM)
 2 {
 3     pTM->init_ms_val =  msTicks;
 4     pTM->init_ticket_val = SysTick->VAL;
 5     pTM->start = 1;
 6 }
 7 
 8 #pragma O0
 9 uint32_t _get_us_tm_val(USSoftTimer* pTM)
10 {
11     volatile uint32_t curr_ms = msTicks;    
12     volatile uint32_t curr_ticket_val = SysTick->VAL;
13     volatile uint32_t ms_interval = 0;
14     volatile uint32_t sys_clock = SysTick -> LOAD / 1000;
15     volatile uint32_t us_interval = 0;
16     
17     
18     if ( curr_ticket_val > pTM->init_ticket_val)
19     us_interval = ( SysTick->LOAD  - (curr_ticket_val - pTM->init_ticket_val)) / sys_clock;
20     else
21     us_interval = (pTM->init_ticket_val - curr_ticket_val) / sys_clock;
22     
23     if ( curr_ms != pTM->init_ms_val)
24     {
25 
26     if ( curr_ms >= pTM->init_ms_val)
27         ms_interval = curr_ms - pTM->init_ms_val;
28     else
29         ms_interval = 0xFFFFFFFF - pTM->init_ms_val+ curr_ms;
30     
31     if ( curr_ticket_val > pTM->init_ticket_val)
32         ms_interval -= 1;
33 
34     us_interval += ms_interval * 1000;
35 
36     }
37 
38     return us_interval;
39 }
40 
41 #define       def_us_tm(tm)       UsSoftTimer tm 
42 #define       declare_us_tm(tm)   extern    UsSoftTimer tm        
43 #define       get_us_tm_val(tm)    _get_us_tm_val(&tm)
44 #define is_us_tm_on(tm)    (1== tm.start)
45 #define stop_us_tm(tm)    tm.start = 0
46 #define start_us_tm(tm)    _start_us_sw(&tm)
複製程式碼

us延時函式的實現如下:

複製程式碼
1 void delay_us(uint32_t us)
2 {
3     if ( us <= 1) return ;
4     def_us_tm(tm_us_count);
5     start_us_tm(tm_us_count);
6     while ( get_us_tm_val(tm_us_count) < us)
7     ;
8     stop_us_tm(tm_us_count);
9 }
複製程式碼