基於鏈表的軟件定時器實現(轉)


軟件定時器在實際應用比較重要,本文旨在實現一種便於移植,易擴展功能,效率高的軟件定時器。本定時器是基於排序鏈表,將最近將觸發的定時器置於鏈表頭,后續新增定時器將計算出其合適位置插入。

主要數據結構及數據

 


typedef struct m_tm_tcb_struct

{

uint32_t time; //初次觸發時間

uint32_t period; //周期時間,如果是只執行1次,則設為0

void *pdata; //定時器私有參數

m_timeout_handler phandler; //定時器回調函數

struct m_tm_tcb_struct *next;//鏈表

}m_tm_tcb;

static m_tm_tcb *ptm_list_header;

static uint32_t m_timeouts_last_time; //上次觸發的時間

 

例一:我們將依次新增5個定時器,定時周期為3,5,8,8,12;當前時間為n;插入第1個時,如果鏈表空,則設置m_timeouts_last_time = n;設置第一個元素time=3; next=null;插入第2個,設置time2=5-3 = 2(相對與第一個時間),next=NULL;插入第3個,設置time = 8 - 3 - 2 = 3(相對與第二個時間);同理,第4個,time = 0; 第5個 time = 4。

上述是按照排好序的方式添加,如果打亂呢?

例二:依次添加周期為3,5,12,8的定時器。前3個定時器的添加與上個例子相同,插入完成后time1=3,time2=2,time3=7;  插入第4個,time4-time1>0? 成立,time4-=time1=5;與鏈表第2個元素時間對比,time4-time2>0?,成立,time4-=time2 = 3;在與鏈表第3個元素時間對比時,time4-time3>0?不成立,此定時器應該插入在2、3兩個元素之間;但是原來第3個元素的觸發時間就增加了time4的時間,因此將time3 -= time4 = 4。

最后,上述添加是同時添加5個定時器,實際運用時不可能全部同時添加。

例三:添加第1個定時器,間隔5;過了4個時間片后,新增第2個定時器,間隔3;顯然第2個定時器應該在第1個觸發后2個時間片再觸發;雖然time2-time1>0不成立;這里就新增1個變量記錄上次觸發時間:m_timeouts_last_time;添加定時器時將計算出現在距離上次觸發時間diff = now-m_timeouts_last_time;time2+=diff;這個例子中time2=3+(4-0)=7;然后按照例2進行添加。

定時器主處理函數可在中斷中調用,也可在普通任務中(無OS時就是主循環)調用,判斷now-m_timeouts_last_time>ptm_list_header->time?成立,則代表有定時器觸發,需要進行處理;處理后表頭直針后移,看新元素時間是否為0(相對前一個)如果為0,則進行處理,直到不為0;如果時周期性定時器,則可在添加是將定時器結構體內period設置為周期時間,在定時器回調執行完成后重新添加定時器即可。

該定時器使用過程中的注意事項,如果定時器主處理函數如果放在中斷中,則代表超時回調函數也在中斷中處理,此時不應將回調函數寫的太長,例如:擦、寫flash;串口用阻塞的方式發送太長的數據(115200大約在10kB/S,10個字節對應1ms,如果,回調執行超過1ms,明顯會使定時器不准確);如果在普通任務調用則無此問題。比較好的方法其實是使用前后台處理機制,將回調函數必要參數傳回普通任務中,在任務中去執行,這樣想寫多長也不會影響實時性。后續的另一篇文章中,我將實現一種通用的無OS的前后台調度器,降低中斷平面、任務平面的耦合性。

/**
 * @file m_timeouts.h
 * Timer implementations
 */
 #ifndef M_TIMEOUTS_H
 #define M_TIMEOUTS_H
 
 
#include "m_common.h"
//定時器回調函數
typedef void (* m_timeout_handler)(void *arg);
 
//定時器結構體
typedef struct m_tm_tcb_struct
{
    uint32_t time;    //初次觸發時間
    uint32_t period;    //周期時間,如果是只執行1次,則設為0
    void *pdata;        //定時器私有參數
    m_timeout_handler phandler;  //定時器回調函數
    struct m_tm_tcb_struct *next;//鏈表
}m_tm_tcb;
 
//定時器初始化
void m_timeout_init(void);
 
//添加定時器
int8_t m_timeout_add(m_tm_tcb *tm);
 
//刪除定時器
int8_t m_timeout_delete(m_tm_tcb *tm);
 
//定時器處理函數
void m_timeout_process(void);
 
#endif
--------------------- 
/**
 * @file m_timeouts.c
 * Timer implementations
 */
 
#include "m_timeouts.h"
 
static m_tm_tcb *ptm_list_header;
static uint32_t m_timeouts_last_time;    //上次觸發的時間。
 
uint32_t tm_get_now(void)
{
    return HAL_GetTick();
}
 
//定時器初始化
void m_timeout_init(void)
{
    ptm_list_header = NULL;
}
 
 //添加定時器,單次運行;
int8_t m_timeout_add(m_tm_tcb *tm)
{
    uint32_t diff=0,now,msecs;
    m_tm_tcb *p;
    
    now = tm_get_now();
    
    //鏈表為空
    M_ENTER_CRITICAL();
    if(ptm_list_header == NULL)
    {
        m_timeouts_last_time = now;
        ptm_list_header = tm;
        tm->next = NULL;
        M_EXIT_CRITICAL();
        return 0;
    }
    else
    {
        diff = now - m_timeouts_last_time;
        msecs = tm->time;
        tm->time += diff;
    }
 
    if(ptm_list_header->time > tm->time)
    {
        ptm_list_header->time -= tm->time;
        tm->next = ptm_list_header;
        ptm_list_header = tm;
    }
    else
    {
        for(p = ptm_list_header; p!=NULL; p=p->next)
        {
            tm->time -= p->time;
            if(p->next == NULL || p->next->time > tm->time)
            {
                if(p->next != NULL)
                {
                    p->next->time -= tm->time;
                    
                }
                else if(tm->time > msecs)
                {
                    tm->time = msecs+ptm_list_header->time;
                }
                tm->next = p->next;
                p->next = tm;
                break;
            }
        }
    }
    M_EXIT_CRITICAL();
    return 0;
    
}
 
//刪除定時器
int8_t m_timeout_delete(m_tm_tcb *tm)
{
    m_tm_tcb *prev, *t;
    M_ENTER_CRITICAL();
    for(t=ptm_list_header, prev=NULL; t!=NULL; prev=t, t=t->next)
    {
        if(t == tm)
        {
 
            if(t->next)
                t->next->time += tm->time;
            if(prev == NULL)
            {
                ptm_list_header = t->next;
            }
            else
            {
                prev->next = t->next;
            }
            M_EXIT_CRITICAL();
            return 0;
        }
    }
    
    M_EXIT_CRITICAL();
    return -1;
}
 
//定時器處理函數
void m_timeout_process(void)
{
    m_tm_tcb *tmptm = ptm_list_header;
    
    uint32_t diff = tm_get_now() - m_timeouts_last_time;
    
    while(tmptm && (diff >= tmptm->time))
    {
        diff -= tmptm->time;
        
        M_ENTER_CRITICAL();
        m_timeouts_last_time += tmptm->time;
        ptm_list_header = tmptm->next;
        M_EXIT_CRITICAL();
        
        if(tmptm->period)
        {
            tmptm->time = tmptm->period;
            m_timeout_add(tmptm);
        }
    
        if(tmptm->phandler)
            tmptm->phandler(tmptm->pdata);
        
        tmptm = ptm_list_header;
    }
}
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM