前面我們實(shí)現(xiàn)了鏈表這個(gè)非?;A(chǔ)的”輪子”,《https://mp.weixin.qq.com/s/8auensNh3q0T0dOayM0d2A》,基于此我們就可以實(shí)現(xiàn)更多的通用組件。這一篇分享比較常見的軟件定時(shí)器的實(shí)現(xiàn)。
主要思想如下:通過鏈表管理定時(shí)器。
定義定時(shí)器結(jié)構(gòu)
/*** \struct timer_info* 定時(shí)器信息結(jié)構(gòu)體.** 通過next_item_pst連接后續(xù)節(jié)點(diǎn).*/struct timer_info{uint8_t mode; /**< bit[0] 0:停止 1:使能 bit[1] 0:單次模式 1:持續(xù)模式 */timer_callback_pf func; /**< 回調(diào)函數(shù) */uint32_t period; /**< 周期 */uint32_t time; /**< 當(dāng)前值 */};
鏈表節(jié)點(diǎn)itemobj_p指向上述定時(shí)器結(jié)構(gòu),則可以通過鏈表管理定時(shí)器。
按照以下需求設(shè)計(jì):
1.為了減少依賴,通用,具備可移植性,不使用動(dòng)態(tài)內(nèi)存分配,實(shí)例由調(diào)用者提供。
2.定時(shí)器精度由調(diào)用者決定,調(diào)用loop函數(shù)間隔不一樣,精度就不一樣,非常靈活。
3.實(shí)際應(yīng)用可以在硬件中斷服務(wù)函數(shù)中調(diào)用loop提高實(shí)時(shí)性,也可以在其他線程循環(huán)中調(diào)用, 可以實(shí)現(xiàn)任意組定時(shí)器。
4.定時(shí)器組數(shù),每組的定時(shí)器個(gè)數(shù)無限制。
5.定時(shí)器不使用絕對(duì)時(shí)間戳的方式,絕對(duì)時(shí)間戳?xí)新闊┖蛺廊说囊绯鰡栴},而是使用一個(gè)人周期設(shè)置值和實(shí)時(shí)計(jì)數(shù)值,通過實(shí)時(shí)計(jì)數(shù)值減少到0來確定定時(shí)到,定時(shí)到了重新加載周期設(shè)置值到實(shí)時(shí)計(jì)數(shù)值,重新開始遞減。雖然多了一個(gè)變量多占空了空間,但是這是值得的。
6.模塊本身只提供純粹的定時(shí)器框架,至于實(shí)際應(yīng)用的可重入,比如多線程調(diào)用等問題,由具體環(huán)境再進(jìn)行一層封裝即可,也可再封裝使用動(dòng)態(tài)分配實(shí)例。
其他數(shù)據(jù)結(jié)構(gòu)如下
/*** \enum timer_res_e* 返回結(jié)果枚舉.*/typedef enum{TIMER_OK = 0,TIMER_PARAM_ERR = 1,TIMER_ENQUEUE_ERR = 2,TIMER_DEQUEUE_ERR = 3,}timer_res_e;typedef void(*timer_callback_pf)(void*); /**< 定時(shí)器回調(diào)函數(shù) *//*** \struct timer_info* 定時(shí)器信息結(jié)構(gòu)體.** 通過next_item_pst連接后續(xù)節(jié)點(diǎn).*/struct timer_info{uint8_t mode; /**< bit[0] 0:停止 1:使能 bit[1] 0:單次模式 1:持續(xù)模式 */timer_callback_pf func; /**< 回調(diào)函數(shù) */uint32_t period; /**< 周期 */uint32_t time; /**< 當(dāng)前值 */};typedef struct timer_info timer_info_st; /**< 重定義節(jié)點(diǎn)類型 *//*** \enum TIMER_MODE_e* 定時(shí)器模式枚舉.**/typedef enum{TIMER_MODE_S = 0x00, /**< 單次模式 */TIMER_MODE_C = 0x02, /**< 持續(xù)模式 */}TIMER_MODE_e;/*** \enum TIMER_STATE_e* 定時(shí)器狀態(tài)枚舉.**/typedef enum{TIMER_STATE_STOP = 0x00, /**< 停止 */TIMER_STATE_START = 0x01, /**< 運(yùn)行 */}TIMER_STATE_e;
定義如下接口
/*** \fn timer_list_init* 初始化定時(shí)器鏈表* 初始化鏈表為空* \param[in] t_list \ref list_st 指向定時(shí)器鏈表的指針,用戶提供實(shí)例* \retval TIMER_OK* \retval TIMER_PARAM_ERR*/timer_res_e timer_list_init(list_st* t_list);/*** \fn timer_init* \brief 定時(shí)器初始化.* \param[in] t_list \ref list_st 鏈表,用戶提供實(shí)例* \param[in] t_item \ref list_item_st 鏈表節(jié)點(diǎn),用戶提供實(shí)例.* \param[in] t_info \ref timer_info_st 定時(shí)器信息,用戶提供實(shí)例.* \retval TIMER_OK* \retval TIMER_PARAM_ERR*/timer_res_e timer_init(list_st* t_list, list_item_st* t_item, timer_info_st* t_info);/*** \fn timer_start* \brief 啟動(dòng)定時(shí)器.* \param[in] t_item \ref list_item_st 定時(shí)器節(jié)點(diǎn).* \retval TIMER_OK* \retval TIMER_PARAM_ERR*/timer_res_e timer_start(list_item_st* t_item);/*** \fn timer_stop* \brief 停止定時(shí)器.* \param[in] t_item \ref list_item_st 定時(shí)器節(jié)點(diǎn).* \retval TIMER_OK* \retval TIMER_PARAM_ERR*/timer_res_e timer_stop(list_item_st* t_item);/*** \fn timer_set_period* \brief 設(shè)置定時(shí)器周期.* \param[in] t_item \ref list_item_st 定時(shí)器節(jié)點(diǎn).* \param[in] period 定時(shí)器周期,單位為timer_loop調(diào)用間隔.* \retval TIMER_OK* \retval TIMER_PARAM_ERR*/timer_res_e timer_set_period(list_item_st* t_item, uint32_t period);/*** \fn timer_restart* \brief 重啟定時(shí)器.* 與start的區(qū)別是除了設(shè)置啟動(dòng)標(biāo)志還會(huì)重置定時(shí)器計(jì)數(shù)值* \param[in] t_item \ref list_item_st 定時(shí)器節(jié)點(diǎn).* \retval TIMER_OK* \retval TIMER_PARAM_ERR*/timer_res_e timer_restart(list_item_st* t_item);/*** \fn timer_remove* \brief 移除定時(shí)器.* \param[in] t_list \ref list_st 定時(shí)器鏈表.* \param[in] t_item \ref list_item_st 定時(shí)器節(jié)點(diǎn).* \retval TIMER_OK* \retval TIMER_PARAM_ERR*/timer_res_e timer_remove(list_st* t_list, list_item_st* t_item);/*** \fn timer_loop* \brief 定時(shí)器周期處理函數(shù),定時(shí)器單位即該函數(shù)的調(diào)用周期.* \param[in] t_list \ref list_st 定時(shí)器鏈表.* \retval TIMER_OK* \retval TIMER_PARAM_ERR*/timer_res_e timer_loop(list_st* t_list);
初始化鏈表,用戶提供鏈表實(shí)例
timer_res_e timer_list_init(list_st* t_list){/* 參數(shù)檢查 */if(t_list == 0){return TIMER_PARAM_ERR;}/* 初始化鏈表為空 */t_list->next_item_pst = 0;}
初始化定時(shí)器,即將定時(shí)器節(jié)點(diǎn)添加到鏈表中
timer_res_e timer_init(list_st* t_list, list_item_st* t_item, timer_info_st* t_info){/* 參數(shù)檢查 */if((t_list == 0) || (t_item == 0) || (t_info == 0)){return TIMER_PARAM_ERR;}/* 初始化節(jié)點(diǎn) */t_item->itemobj_p = t_info;t_item->next_item_pst = 0;t_item->itemvalue_t = 0;/* 添加節(jié)點(diǎn)到鏈表中 */if(LIST_OK == list_enqueuetail(t_list, t_item)){return TIMER_ENQUEUE_ERR;}else{return TIMER_OK;}}
啟動(dòng)定時(shí)器
timer_res_e timer_start(list_item_st* t_item){/* 參數(shù)檢查 */if(t_item == 0){return TIMER_PARAM_ERR;}/* 啟動(dòng)定時(shí)器 */timer_info_st *timer_item = (timer_info_st *)(t_item->itemobj_p);timer_item->mode |= (1<return TIMER_OK;}
停止定時(shí)器
timer_res_e timer_stop(list_item_st* t_item){/* 參數(shù)檢查 */if(t_item == 0){return TIMER_PARAM_ERR;}/* 停止定時(shí)器 */timer_info_st *timer_item = (timer_info_st *)(t_item->itemobj_p);timer_item->mode &= ~(1<return TIMER_OK;}
設(shè)置定時(shí)器周期
timer_res_e timer_set_period(list_item_st* t_item, uint32_t period){/* 參數(shù)檢查 */if(t_item == 0){return TIMER_PARAM_ERR;}/** 設(shè)置周期 */timer_info_st *timer_item = (timer_info_st *)(t_item->itemobj_p);timer_item->period = period;return TIMER_OK;}
重啟定時(shí)器,會(huì)重新加載定時(shí)器計(jì)數(shù)值
timer_res_e timer_restart(list_item_st* t_item){/* 參數(shù)檢查 */if(t_item == 0){return TIMER_PARAM_ERR;}/* 重啟定時(shí)器即重新設(shè)置定時(shí)器的周期,并設(shè)置啟動(dòng)位 */timer_info_st *timer_item = (timer_info_st *)(t_item->itemobj_p);timer_item ->time = timer_item->period;timer_item->mode |= (1<return TIMER_OK;}
從鏈表移除定時(shí)器
timer_res_e timer_remove(list_st* t_list, list_item_st* t_item){/* 參數(shù)檢查 */if((t_list == 0) || (t_item == 0)){return TIMER_PARAM_ERR;}/* 鏈表中移除定時(shí)器節(jié)點(diǎn) */if(LIST_OK != list_dequeueitem(t_list, t_item)){return TIMER_DEQUEUE_ERR;}return TIMER_OK;}
用戶周期調(diào)用loop處理
timer_res_e timer_loop(list_st* t_list){list_st *list = t_list; /* 待遍歷的鏈表 */list_item_st **curr = 0; /* curr用于遍歷節(jié)點(diǎn)的next_item_pst成員的地址 其值為next_item_pst */list_item_st *item = 0; /* 遍歷節(jié)點(diǎn)指針 */timer_info_st *timer = 0;/* 參數(shù)檢查 */if(t_list == 0){return TIMER_PARAM_ERR;}/* 遍歷鏈表 */curr = &(list -> next_item_pst);while(*curr != 0){item = *curr;timer = (timer_info_st *)(item->itemobj_p);if((timer->mode & (1<0 ){/*定時(shí)器是使能的*/if(timer->time > 1){timer->time--;}else{/* 進(jìn)行回調(diào) */if(timer->func){timer->func(item);}if((timer->mode & (1<0 ){/* 如果是持續(xù)模式繼續(xù)設(shè)置周期 */timer->time = timer->period;}else{/* 單次模式 停止定時(shí)器 */timer->mode &= ~(1<}}}/* 繼續(xù)遍歷 */curr = &(item -> next_item_pst);}return TIMER_OK;}
timer_main.c
void t1_cb(void* t){printf("t1:%ld\r\n",time(0));}void t2_cb(void* t){printf("t2:%ld\r\n",time(0));}int main(void){/* 分配鏈表實(shí)例并初始化 */list_st t_list;timer_list_init(&t_list);/* 分配節(jié)點(diǎn)實(shí)例* 分配定時(shí)器實(shí)例,并初始化* 一個(gè)鏈表添加兩個(gè)定時(shí)器*/list_item_st t_item1;timer_info_st t_info1={.func = t1_cb,.mode = TIMER_MODE_S,.period = 5,.time = 0,};timer_init(&t_list, &t_item1, &t_info1);list_item_st t_item2;timer_info_st t_info2={.func = t2_cb,.mode = TIMER_MODE_C,.period = 3,.time = 0,};timer_init(&t_list, &t_item2, &t_info2);/* 啟動(dòng) */timer_start(&t_item1);timer_start(&t_item2);while(1){/* 周期調(diào)用lopp處理 */sleep(1);timer_loop(&t_list);}}
gcc list.c timer_main.c timer.c -o timer
./timer
可以看到定時(shí)器1,3秒后執(zhí)行一次停止,單次模式。
定時(shí)器2,3秒執(zhí)行一次,連續(xù)模式。
./timert1:1703947253t2:1703947253t2:1703947256t2:1703947259t2:1703947262t2:1703947265
可以看到有了好的鏈表的”輪子”,基于此可以實(shí)現(xiàn)很多常用的組件,基于鏈表實(shí)現(xiàn)的軟件定時(shí)器,幾乎是水到渠成的事。
