一種基於有序鏈表的高效定時器的實現

一種基於有序鏈表的高效定時器的實現


軟件定時器應用較普遍,在嵌入式開發時使用OS時,通常可以使用OS提供的軟件定時器,裸機開發時,可能會須要本身造輪子,本文設計了一種基於有序鏈表的軟件定時器。複雜度爲 執行:O(1); 插入:O(n); 刪除:O(1)函數

工做原理

維護1個定時器鏈表,表頭爲最先觸發的定時器,後續每一個定時器均包含距離上一個定時器的間隔時間;執行時只需判斷表頭定時器是否知足執行條件,不須要遍歷全部定時器;添加定時器時,只需從表頭向後遍歷,將定時器插入合適的位置,同時將下1個定時器的間隔時間減去新增定時器間隔時間便可;刪除定時器,則只需將定時器從鏈表移除,同時將下1個定時器間隔時間加上刪除定時器間隔時間。爲實現週期定時功能,須要新增一些指示定時器參數的成員變量。
例如:當定時器鏈表爲空,順序添加5個定時器,定時時間分別爲定時器a:三、定時器b:五、c:十二、d:八、e:8,則鏈表最終表現爲 定時器n:間隔時間 a:3 -> b:2 -> d:3 -> e:0 -> c->4。 可是定時器任務不可能同時添加,可能會在任什麼時候間添加或刪除,所以須要維護一個參數記錄最終觸發時間,與當前時間求差便可得到已逝去時間,將這個差值加在新增定時器間隔時間便可。
定時器主處理函數,可放於中斷中,或者直接放在主循環或者IDLE中便可。放於中斷內,須要注意內部函數執行時間不能過長,不然影響定時精度。ui

代碼

頭文件: m_timeouts.h設計

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

源文件: m_timeouts.ccode

/**
 * @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;
    }
}
相關文章
相關標籤/搜索