在實(shí)際的開發(fā)項(xiàng)目中,很多時(shí)候我們需要定時(shí)的做一些事情,舉例:
①路上的路燈,每天晚上6:00準(zhǔn)時(shí)打開,每天早上6:00準(zhǔn)時(shí)關(guān)閉;
②定時(shí)鬧鐘,起床上班。這些行為其實(shí)都是定時(shí)任務(wù)--鬧鐘。
大部分單片機(jī)都提供了rtc alarm硬件鬧鐘,但是實(shí)際很少人使用,就舉個(gè)簡(jiǎn)單的例子,rt-thread的BSP中也沒有幾個(gè)芯片適配了alarm硬件鬧鐘。但是我們要使用怎么辦??
我受到RTOS的調(diào)度的啟發(fā),像M3/M4這種內(nèi)核都是SysTick產(chǎn)生時(shí)鐘節(jié)拍,以供系統(tǒng)處理所有和時(shí)間有關(guān)的事情,如線程延時(shí),線程的時(shí)間片輪轉(zhuǎn),以及定時(shí)器超時(shí)等。
有了第3點(diǎn)的經(jīng)驗(yàn),那么我們可以寫一個(gè)軟件鬧鐘功能就容易多了,只需要提供一個(gè)刷新節(jié)拍,定時(shí)查看哪一個(gè)鬧鐘需要喚醒,就可以解決鬧鐘的管理了。
鬧鐘組件名字:RAlarm(全稱Rice Alarm)。
RAlarm
RAlarm接口說明:
跨平臺(tái)
RTOS的種類很多,接口差異性大,所以RAlarm為了解決這個(gè)問題,統(tǒng)一為上層提供一整套接口。
線程接口。
typedef?void?*ralarm_task_id; struct?ralarm_task_attr{ ????const?char?*name;???????//?name?of?the?task ????uint32_t?stack_size;?????//?size?of?stack ????uint8_t?priority;???????//?initial?task?priority }; typedef?void(*ralarm_task_func)(void?*arg); ralarm_task_id?ralarm_task_create(ralarm_task_func?func,?void?*arg,?const?struct?ralarm_task_attr?*attr); void?ralarm_task_delete(ralarm_task_id?thread);
互斥量接口。
typedef?void?*ralarm_mutex_id; ralarm_mutex_id?ralarm_mutex_create(void); ralarm_err_t?ralarm_mutex_lock(ralarm_mutex_id?mutex); ralarm_err_t?ralarm_mutex_unlock(ralarm_mutex_id?mutex); void?ralarm_mutex_delete(ralarm_mutex_id?mutex);
事件接口。
typedef?void?*ralarm_event_id; ralarm_event_id?ralarm_event_create(void); uint32_t?ralarm_event_recv(ralarm_event_id?event,?uint32_t?flags); ralarm_err_t?ralarm_event_send(ralarm_event_id?event,?uint32_t?flags); void?ralarm_event_delete(ralarm_event_id?event);
RAlarm目前已經(jīng)提供了兩個(gè)環(huán)境的適配,如cmsis,rtthread。
接口使用簡(jiǎn)單
接口 | 說明 |
---|---|
ralarm_init | 初始化 |
ralarm_deinit | 去初始化 |
ralarm_create | 創(chuàng)建鬧鐘 |
ralarm_start | 啟動(dòng)鬧鐘 |
ralarm_stop | 停止鬧鐘 |
ralarm_modify | 修改鬧鐘 |
ralarm_delete | 刪除鬧鐘 |
鬧鐘初始化接口:初始化鬧鐘的鏈表,鬧鐘任務(wù),事件,互斥鎖;去初始化接口:注銷鬧鐘組
/*?鬧鐘初始化?*/ ralarm_err_t?ralarm_init(void); /*?鬧鐘去初始化?*/ void?ralarm_deinit(void);
鬧鐘創(chuàng)建:
參數(shù)說明:
參數(shù) | 描述 |
---|---|
setup | 鬧鐘的時(shí)間和標(biāo)志,flag可為:RALARM_ONESHOT(只設(shè)置一次)和RALARM_DAILY(每天都設(shè)置) |
cb | 鬧鐘時(shí)間到了,喚醒的回調(diào)函數(shù)指針:typedef void (*ralarm_response_cb)(ralarm_t alarm) |
userData | 設(shè)置鬧鐘時(shí),自帶的用戶數(shù)據(jù)的指針 |
返回 | —— |
ralarm_t | 鬧鐘創(chuàng)建成功,放回鬧鐘句柄 |
NULL | 鬧鐘創(chuàng)建失敗 |
函數(shù)說明:
①申請(qǐng)鬧鐘控制塊的空間。
②設(shè)置鬧鐘參數(shù)到控制塊中。
③將鬧鐘加入到鬧鐘鏈表中。
struct?ralarm_setup?{ ????ralarm_flag?flag; ????struct?ralarm_time?time; }; typedef?struct?ralarm_setup?*ralarm_setup_t; struct?ralarm?{ ????ralarm_state?state; ????struct?ralarm_setup?setup; ????ralarm_response_cb?cb; ????void?*userData; ????ralarm_list_t?list; }; typedef?struct?ralarm?*ralarm_t; ralarm_t?ralarm_create(ralarm_setup_t?setup,?ralarm_response_cb?cb,?void?*userData) { ????ralarm_t?alarm?=?NULL; ???? ????if(setup?==?NULL)?{ ????????RALARM_LOGE("Create?alarm?failed,?Setup?param?is?NULL"); ????????return?NULL; ????} ????alarm?=?RALARM_MALLOC(sizeof(struct?ralarm));???????????????????????????//?----① ????if(alarm?==?NULL)?{ ????????RALARM_LOGE("Malloc?alarm?memory?failed"); ????????return?NULL; ????} ????ralarm_list_init(&alarm->list);?????????????????????????????????????????//?----② ????memset((void?*)alarm,?0,?sizeof(struct?ralarm)); ????memcpy((void?*)&alarm->setup,?setup,?sizeof(struct?ralarm_setup)); ????alarm->cb?=?cb; ????alarm->userData?=?userData; ????ralarm_mutex_lock(g_container.mutex); ????ralarm_list_insert_after(&g_container.list,?&alarm->list);??????????????//?----③ ????ralarm_mutex_unlock(g_container.mutex); ????return?alarm; }
鬧鐘啟動(dòng):將鬧鐘的狀態(tài)的start bit置為1。
ralarm_err_t?ralarm_start(ralarm_t?alarm) { ????if(alarm?==?NULL)?{ ????????return?RALARM_ERROR; ????} ????ralarm_mutex_lock(g_container.mutex); ????alarm->state?|=?RALARM_STATE_START; ????ralarm_mutex_unlock(g_container.mutex); ????return?RALARM_EOK; }
鬧鐘停止:將鬧鐘的狀態(tài)的start bit置為0。
ralarm_err_t?ralarm_stop(ralarm_t?alarm) { ????if(alarm?==?NULL)?{ ????????return?RALARM_ERROR; ????} ????ralarm_mutex_lock(g_container.mutex); ????alarm->state?&=?~RALARM_STATE_START; ????ralarm_mutex_unlock(g_container.mutex); ????return?RALARM_EOK; }
鬧鐘修改:修改鬧鐘的標(biāo)志和鬧鐘的時(shí)間
參數(shù)說明:
參數(shù) | 描述 |
---|---|
alarm | 鬧鐘的句柄 |
setup | 要修改鬧鐘的時(shí)間和標(biāo)志參數(shù) |
返回 | —— |
RALARM_EOK | 修改成功 |
RALARM_ERROR | 修改失敗 |
ralarm_err_t?ralarm_modify(ralarm_t?alarm,?ralarm_setup_t?setup)
{ ????if(alarm?==?NULL)?{ ????????return?RALARM_ERROR; ????} ????ralarm_mutex_lock(g_container.mutex); ????memcpy((void?*)&alarm->setup,?setup,?sizeof(struct?ralarm_setup)); ????ralarm_mutex_unlock(g_container.mutex); ????return?RALARM_EOK; }
刪除鬧鐘:
函數(shù)說明:
①將鬧鐘的狀態(tài)的start bit置為0。
②將鬧鐘從鬧鐘鏈表中移除。
③釋放鬧鐘的內(nèi)存。
ralarm_err_t?ralarm_delete(ralarm_t?alarm)
{ ????if(alarm?==?NULL)?{ ????????return?RALARM_ERROR; ????} ????ralarm_mutex_lock(g_container.mutex); ????alarm->state?&=?~RALARM_STATE_START;????????//?---① ????ralarm_list_remove(&alarm->list);???????????//?---② ????RALARM_FREE(alarm);?????????????????????????//?---③ ????alarm?=?NULL; ????ralarm_mutex_unlock(g_container.mutex); ????return?RALARM_EOK; }
適配簡(jiǎn)單
根據(jù)系統(tǒng)能力,提供獲取時(shí)間方法,創(chuàng)建ralarm的ops并注冊(cè)獲取時(shí)間接口。
struct?ralarm_ops{ ????ralarm_err_t?(*time_get)(ralarm_time_t?time); }; ralarm_err_t?ralarm_register_ops(struct?ralarm_ops?*ops);
提供刷新節(jié)拍,然后調(diào)用刷新接口。
void?ralarm_refresh(void);
RAlarm運(yùn)行邏輯:
鬧鐘的refresh接口需要用戶提供一個(gè)刷新節(jié)拍,以提供鬧鐘的生命。
refresh根據(jù)鬧鐘鏈表是否存在已設(shè)置的鬧鐘,選擇發(fā)送事件給更新任務(wù),更新檢測(cè)鬧鐘的狀態(tài)。
如下圖:當(dāng)檢測(cè)鬧鐘鏈表無設(shè)置的鬧鐘,則不會(huì)發(fā)送事件給更新任務(wù)
如下圖:
當(dāng)用戶創(chuàng)建了鬧鐘,則會(huì)將鬧鐘掛在鬧鐘量表中。
刷新節(jié)拍調(diào)用refresh之后,發(fā)送事件給更新任務(wù),然后調(diào)用wakeup檢測(cè)鬧鐘的狀態(tài)。
如果某個(gè)鬧鐘時(shí)間到,則會(huì)調(diào)用對(duì)應(yīng)鬧鐘的回調(diào)函數(shù)。
RAlarm的使用
在RT-Thread下使用ralarm組件:
① 鬧鐘的處理函數(shù),當(dāng)鬧鐘時(shí)間到了,則會(huì)調(diào)用這個(gè)函數(shù)。
② 提供給ralarm組件時(shí)間接口。
③ 創(chuàng)建ops,提供時(shí)間接口。
④ 軟件定時(shí)器的處理函數(shù),調(diào)用ralarm的刷新函數(shù),提供刷新節(jié)拍。
⑤ ralarm組件初始化,注冊(cè)ops。
⑥ 創(chuàng)建鬧鐘。
⑦ 創(chuàng)建一個(gè)軟件定時(shí)器,為ralarm組件提供刷新節(jié)拍。
static?rt_timer_t?timer; ralarm_t?alarm_test?=?NULL; static?void?alarm_handler(ralarm_t?alarm)???????????????????????????????//?---① { ????rt_kprintf("Time:?%02d:%02d:%02d ",?alarm->setup.time.hour,?????? ????????????????alarm->setup.time.minute,?alarm->setup.time.second); ????ralarm_stop(alarm); ????ralarm_dump(); } static?ralarm_err_t?alarm_time_get(ralarm_time_t?timer)?????????????????//?---② { ????time_t?current; ????struct?tm?*local; ???? ????time(¤t); ????local?=?localtime(¤t); ????timer->hour?=?local->tm_hour; ????timer->minute?=?local->tm_min; ????timer->second?=?local->tm_sec; ????return?RALARM_EOK; } static?struct?ralarm_ops?ops?=?{????????????????????????????????????????//?---③ ????.time_get?=?alarm_time_get, }; static?void?time_handler(void?*param)???????????????????????????????????//?---④ { ????ralarm_refresh(); } int?main(void) { ????ralarm_init();??????????????????????????????????????????????????????//?---⑤ ????ralarm_register_ops(&ops); ????struct?ralarm_setup?setup; ????setup.flag?=?RALARM_DAILY; ????setup.time.hour?=?15; ????setup.time.minute?=?0; ????setup.time.second?=?0; ????alarm_test?=?ralarm_create(&setup,?alarm_handler,?NULL);????????????//?---⑥ ????ralarm_start(alarm_test); ????ralarm_dump(); ????timer?=?rt_timer_create("timer",?time_handler,??????????????????????//?---⑦ ?????????????????????????????RT_NULL,?800, ?????????????????????????????RT_TIMER_FLAG_PERIODIC); ????if?(timer?!=?RT_NULL)? ????????rt_timer_start(timer); }
驗(yàn)證結(jié)果:
編輯:黃飛
?
評(píng)論