1. 程式人生 > >基於連結串列的軟體定時器實現(轉)

基於連結串列的軟體定時器實現(轉)

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

主要資料結構及資料

 


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