在實際的開發項目中,很多時候我們需要定時的做一些事情,舉例:
①路上的路燈,每天晚上6:00準時打開,每天早上6:00準時關閉;
②定時鬧鐘,起床上班。這些行為其實都是定時任務--鬧鐘。
大部分單片機都提供了rtc alarm硬件鬧鐘,但是實際很少人使用,就舉個簡單的例子,rt-thread的BSP中也沒有幾個芯片適配了alarm硬件鬧鐘。但是我們要使用怎么辦??
我受到RTOS的調度的啟發,像M3/M4這種內核都是SysTick產生時鐘節拍,以供系統處理所有和時間有關的事情,如線程延時,線程的時間片輪轉,以及定時器超時等。
有了第3點的經驗,那么我們可以寫一個軟件鬧鐘功能就容易多了,只需要提供一個刷新節拍,定時查看哪一個鬧鐘需要喚醒,就可以解決鬧鐘的管理了。
鬧鐘組件名字:RAlarm(全稱Rice Alarm)。
RAlarm
RAlarm接口說明:
跨平臺
RTOS的種類很多,接口差異性大,所以RAlarm為了解決這個問題,統一為上層提供一整套接口。
線程接口。
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目前已經提供了兩個環境的適配,如cmsis,rtthread。
接口使用簡單
接口 | 說明 |
---|---|
ralarm_init | 初始化 |
ralarm_deinit | 去初始化 |
ralarm_create | 創建鬧鐘 |
ralarm_start | 啟動鬧鐘 |
ralarm_stop | 停止鬧鐘 |
ralarm_modify | 修改鬧鐘 |
ralarm_delete | 刪除鬧鐘 |
鬧鐘初始化接口:初始化鬧鐘的鏈表,鬧鐘任務,事件,互斥鎖;去初始化接口:注銷鬧鐘組
/*?鬧鐘初始化?*/ ralarm_err_t?ralarm_init(void); /*?鬧鐘去初始化?*/ void?ralarm_deinit(void);
鬧鐘創建:
參數說明:
參數 | 描述 |
---|---|
setup | 鬧鐘的時間和標志,flag可為:RALARM_ONESHOT(只設置一次)和RALARM_DAILY(每天都設置) |
cb | 鬧鐘時間到了,喚醒的回調函數指針:typedef void (*ralarm_response_cb)(ralarm_t alarm) |
userData | 設置鬧鐘時,自帶的用戶數據的指針 |
返回 | —— |
ralarm_t | 鬧鐘創建成功,放回鬧鐘句柄 |
NULL | 鬧鐘創建失敗 |
函數說明:
①申請鬧鐘控制塊的空間。
②設置鬧鐘參數到控制塊中。
③將鬧鐘加入到鬧鐘鏈表中。
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; }
鬧鐘啟動:將鬧鐘的狀態的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; }
鬧鐘停止:將鬧鐘的狀態的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; }
鬧鐘修改:修改鬧鐘的標志和鬧鐘的時間
參數說明:
參數 | 描述 |
---|---|
alarm | 鬧鐘的句柄 |
setup | 要修改鬧鐘的時間和標志參數 |
返回 | —— |
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; }
刪除鬧鐘:
函數說明:
①將鬧鐘的狀態的start bit置為0。
②將鬧鐘從鬧鐘鏈表中移除。
③釋放鬧鐘的內存。
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; }
適配簡單
根據系統能力,提供獲取時間方法,創建ralarm的ops并注冊獲取時間接口。
struct?ralarm_ops{ ????ralarm_err_t?(*time_get)(ralarm_time_t?time); }; ralarm_err_t?ralarm_register_ops(struct?ralarm_ops?*ops);
提供刷新節拍,然后調用刷新接口。
void?ralarm_refresh(void);
RAlarm運行邏輯:
鬧鐘的refresh接口需要用戶提供一個刷新節拍,以提供鬧鐘的生命。
refresh根據鬧鐘鏈表是否存在已設置的鬧鐘,選擇發送事件給更新任務,更新檢測鬧鐘的狀態。
如下圖:當檢測鬧鐘鏈表無設置的鬧鐘,則不會發送事件給更新任務
如下圖:
當用戶創建了鬧鐘,則會將鬧鐘掛在鬧鐘量表中。
刷新節拍調用refresh之后,發送事件給更新任務,然后調用wakeup檢測鬧鐘的狀態。
如果某個鬧鐘時間到,則會調用對應鬧鐘的回調函數。
RAlarm的使用
在RT-Thread下使用ralarm組件:
① 鬧鐘的處理函數,當鬧鐘時間到了,則會調用這個函數。
② 提供給ralarm組件時間接口。
③ 創建ops,提供時間接口。
④ 軟件定時器的處理函數,調用ralarm的刷新函數,提供刷新節拍。
⑤ ralarm組件初始化,注冊ops。
⑥ 創建鬧鐘。
⑦ 創建一個軟件定時器,為ralarm組件提供刷新節拍。
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); }
驗證結果:
編輯:黃飛
?
評論