在實(shí)際的開發(fā)項(xiàng)目中,很多時候我們需要定時的做一些事情,舉例:
①路上的路燈,每天晚上6:00準(zhǔn)時打開,每天早上6:00準(zhǔn)時關(guān)閉;
②定時鬧鐘,起床上班。這些行為其實(shí)都是定時任務(wù)--鬧鐘。
大部分單片機(jī)都提供了rtc alarm硬件鬧鐘,但是實(shí)際很少人使用,就舉個簡單的例子,rt-thread的BSP中也沒有幾個芯片適配了alarm硬件鬧鐘。但是我們要使用怎么辦??
我受到RTOS的調(diào)度的啟發(fā),像M3/M4這種內(nèi)核都是SysTick產(chǎn)生時鐘節(jié)拍,以供系統(tǒng)處理所有和時間有關(guān)的事情,如線程延時,線程的時間片輪轉(zhuǎn),以及定時器超時等。
有了第3點(diǎn)的經(jīng)驗(yàn),那么我們可以寫一個軟件鬧鐘功能就容易多了,只需要提供一個刷新節(jié)拍,定時查看哪一個鬧鐘需要喚醒,就可以解決鬧鐘的管理了。
鬧鐘組件名字:RAlarm(全稱Rice Alarm)。
RAlarm
RAlarm接口說明:
跨平臺
RTOS的種類很多,接口差異性大,所以RAlarm為了解決這個問題,統(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)提供了兩個環(huán)境的適配,如cmsis,rtthread。
接口使用簡單
| 接口 | 說明 |
|---|---|
| ralarm_init | 初始化 |
| ralarm_deinit | 去初始化 |
| ralarm_create | 創(chuàng)建鬧鐘 |
| ralarm_start | 啟動鬧鐘 |
| ralarm_stop | 停止鬧鐘 |
| ralarm_modify | 修改鬧鐘 |
| ralarm_delete | 刪除鬧鐘 |
鬧鐘初始化接口:初始化鬧鐘的鏈表,鬧鐘任務(wù),事件,互斥鎖;去初始化接口:注銷鬧鐘組
/*?鬧鐘初始化?*/ ralarm_err_t?ralarm_init(void); /*?鬧鐘去初始化?*/ void?ralarm_deinit(void);
鬧鐘創(chuàng)建:
參數(shù)說明:
| 參數(shù) | 描述 |
|---|---|
| setup | 鬧鐘的時間和標(biāo)志,flag可為:RALARM_ONESHOT(只設(shè)置一次)和RALARM_DAILY(每天都設(shè)置) |
| cb | 鬧鐘時間到了,喚醒的回調(diào)函數(shù)指針:typedef void (*ralarm_response_cb)(ralarm_t alarm) |
| userData | 設(shè)置鬧鐘時,自帶的用戶數(shù)據(jù)的指針 |
| 返回 | —— |
| ralarm_t | 鬧鐘創(chuàng)建成功,放回鬧鐘句柄 |
| NULL | 鬧鐘創(chuàng)建失敗 |
函數(shù)說明:
①申請鬧鐘控制塊的空間。
②設(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;
}
鬧鐘啟動:將鬧鐘的狀態(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ù) | 描述 |
|---|---|
| alarm | 鬧鐘的句柄 |
| setup | 要修改鬧鐘的時間和標(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;
}
適配簡單
根據(jù)系統(tǒng)能力,提供獲取時間方法,創(chuàng)建ralarm的ops并注冊獲取時間接口。
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接口需要用戶提供一個刷新節(jié)拍,以提供鬧鐘的生命。
refresh根據(jù)鬧鐘鏈表是否存在已設(shè)置的鬧鐘,選擇發(fā)送事件給更新任務(wù),更新檢測鬧鐘的狀態(tài)。
如下圖:當(dāng)檢測鬧鐘鏈表無設(shè)置的鬧鐘,則不會發(fā)送事件給更新任務(wù)

如下圖:
當(dāng)用戶創(chuàng)建了鬧鐘,則會將鬧鐘掛在鬧鐘量表中。
刷新節(jié)拍調(diào)用refresh之后,發(fā)送事件給更新任務(wù),然后調(diào)用wakeup檢測鬧鐘的狀態(tài)。
如果某個鬧鐘時間到,則會調(diào)用對應(yīng)鬧鐘的回調(diào)函數(shù)。

RAlarm的使用
在RT-Thread下使用ralarm組件:
① 鬧鐘的處理函數(shù),當(dāng)鬧鐘時間到了,則會調(diào)用這個函數(shù)。
② 提供給ralarm組件時間接口。
③ 創(chuàng)建ops,提供時間接口。
④ 軟件定時器的處理函數(shù),調(diào)用ralarm的刷新函數(shù),提供刷新節(jié)拍。
⑤ ralarm組件初始化,注冊ops。
⑥ 創(chuàng)建鬧鐘。
⑦ 創(chuàng)建一個軟件定時器,為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é)果:

編輯:黃飛
?
電子發(fā)燒友App







評論