不依赖硬件,可以无限扩展的闹钟组件

2023-05-18 11:16:33 来源:Rice 嵌入式开发技术分享


(资料图片)

在实际的开发项目中,很多时候我们需要定时的做一些事情,举例:

①路上的路灯,每天晚上6:00准时打开,每天早上6:00准时关闭;②定时闹钟,起床上班。这些行为其实都是定时任务--闹钟。

大部分单片机都提供了rtc alarm硬件闹钟,但是实际很少人使用,就举个简单的例子,rt-thread的BSP中也没有几个芯片适配了alarm硬件闹钟。但是我们要使用怎么办??

我受到RTOS的调度的启发,像M3/M4这种内核都是SysTick产生时钟节拍,以供系统处理所有和时间有关的事情,如线程延时,线程的时间片轮转,以及定时器超时等。

有了第3点的经验,那么我们可以写一个软件闹钟功能就容易多了,只需要提供一个刷新节拍,定时查看哪一个闹钟需要唤醒,就可以解决闹钟的管理了。

闹钟组件名字:RAlarm(全称Rice Alarm),源码连接:https://gitee.com/RiceChen0/ralarm

RAlarm

RAlarm接口说明:

跨平台

RTOS的种类很多,接口差异性打,所以RAlarm为了解决这个问题,统一为上层提供一整套接口。线程接口。
typedefvoid*ralarm_task_id;structralarm_task_attr{constchar*name;//nameofthetaskuint32_tstack_size;//sizeofstackuint8_tpriority;//initialtaskpriority};typedefvoid(*ralarm_task_func)(void*arg);ralarm_task_idralarm_task_create(ralarm_task_funcfunc,void*arg,conststructralarm_task_attr*attr);voidralarm_task_delete(ralarm_task_idthread);
互斥量接口。
typedefvoid*ralarm_mutex_id;ralarm_mutex_idralarm_mutex_create(void);ralarm_err_tralarm_mutex_lock(ralarm_mutex_idmutex);ralarm_err_tralarm_mutex_unlock(ralarm_mutex_idmutex);voidralarm_mutex_delete(ralarm_mutex_idmutex);
事件接口。
typedefvoid*ralarm_event_id;ralarm_event_idralarm_event_create(void);uint32_tralarm_event_recv(ralarm_event_idevent,uint32_tflags);ralarm_err_tralarm_event_send(ralarm_event_idevent,uint32_tflags);voidralarm_event_delete(ralarm_event_idevent);
RAlarm目前已经提供了两个环境的适配,如cmsis,rtthread

接口使用简单

接口说明
ralarm_init初始化
ralarm_deinit去初始化
ralarm_create创建闹钟
ralarm_start启动闹钟
ralarm_stop停止闹钟
ralarm_modify修改闹钟
ralarm_delete删除闹钟
闹钟初始化接口:初始化闹钟的链表,闹钟任务,事件,互斥锁;去初始化接口:注销闹钟组件
/*闹钟初始化*/ralarm_err_tralarm_init(void);/*闹钟去初始化*/voidralarm_deinit(void);
闹钟创建:参数说明:
「参数」「描述」
setup闹钟的时间和标志,flag可为:RALARM_ONESHOT(只设置一次)和RALARM_DAILY(每天都设置)
cb闹钟时间到了,唤醒的回调函数指针:typedef void (*ralarm_response_cb)(ralarm_t alarm)
userData设置闹钟时,自带的用户数据的指针
「返回」——
ralarm_t闹钟创建成功,放回闹钟句柄
NULL闹钟创建失败
函数说明:①申请闹钟控制块的空间。②设置闹钟参数到控制块中。③将闹钟加入到闹钟链表中。
structralarm_setup{ralarm_flagflag;structralarm_timetime;};typedefstructralarm_setup*ralarm_setup_t;structralarm{ralarm_statestate;structralarm_setupsetup;ralarm_response_cbcb;void*userData;ralarm_list_tlist;};typedefstructralarm*ralarm_t;ralarm_tralarm_create(ralarm_setup_tsetup,ralarm_response_cbcb,void*userData){ralarm_talarm=NULL;if(setup==NULL){RALARM_LOGE("Createalarmfailed,SetupparamisNULL");returnNULL;}alarm=RALARM_MALLOC(sizeof(structralarm));//----①if(alarm==NULL){RALARM_LOGE("Mallocalarmmemoryfailed");returnNULL;}ralarm_list_init(&alarm->list);//----②memset((void*)alarm,0,sizeof(structralarm));memcpy((void*)&alarm->setup,setup,sizeof(structralarm_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);returnalarm;}
闹钟启动:将闹钟的状态的start bit置为1。
ralarm_err_tralarm_start(ralarm_talarm){if(alarm==NULL){returnRALARM_ERROR;}ralarm_mutex_lock(g_container.mutex);alarm->state|=RALARM_STATE_START;ralarm_mutex_unlock(g_container.mutex);returnRALARM_EOK;}
闹钟停止:将闹钟的状态的start bit置为0。
ralarm_err_tralarm_stop(ralarm_talarm){if(alarm==NULL){returnRALARM_ERROR;}ralarm_mutex_lock(g_container.mutex);alarm->state&=~RALARM_STATE_START;ralarm_mutex_unlock(g_container.mutex);returnRALARM_EOK;}
闹钟修改:修改闹钟的标志和闹钟的时间参数说明:
「参数」「描述」
alarm闹钟的句柄
setup要修改闹钟的时间和标志参数
「返回」——
RALARM_EOK修改成功
RALARM_ERROR修改失败
ralarm_err_tralarm_modify(ralarm_talarm,ralarm_setup_tsetup){if(alarm==NULL){returnRALARM_ERROR;}ralarm_mutex_lock(g_container.mutex);memcpy((void*)&alarm->setup,setup,sizeof(structralarm_setup));ralarm_mutex_unlock(g_container.mutex);returnRALARM_EOK;}
删除闹钟:函数说明:①将闹钟的状态的start bit置为0。②将闹钟从闹钟链表中移除。③释放闹钟的内存。
ralarm_err_tralarm_delete(ralarm_talarm){if(alarm==NULL){returnRALARM_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);returnRALARM_EOK;}

适配简单

根据系统能力,提供获取时间方法,创建ralarm的ops并注册获取时间接口。
structralarm_ops{ralarm_err_t(*time_get)(ralarm_time_ttime);};ralarm_err_tralarm_register_ops(structralarm_ops*ops);
提供刷新节拍,然后调用刷新接口。
voidralarm_refresh(void);

RAlarm运行逻辑:

闹钟的refresh接口需要用户提供一个刷新节拍,以提供闹钟的生命。refresh皆苦根据闹钟链表是否存在已设置的闹钟,选择发送事件给更新任务,更新检测闹钟的状态。如下图:当检测闹钟链表无设置的闹钟,则不会发送事件给更新任务如下图:当用户创建了闹钟,则会将闹钟挂在闹钟量表中。刷新节拍调用refresh之后,发送事件给更新任务,然后调用wakeup检测闹钟的状态。如果某个闹钟时间到,则会调用对应闹钟的回调函数。

RAlarm的使用

在RT-Thread下使用ralarm组件:① 闹钟的处理函数,当闹钟时间到了,则会调用这个函数。② 提供给ralarm组件时间接口。③ 创建ops,提供时间接口。④ 软件定时器的处理函数,调用ralarm的刷新函数,提供刷新节拍。⑤ ralarm组件初始化,注册ops。⑥ 创建闹钟。⑦ 创建一个软件定时器,为ralarm组件提供刷新节拍。
staticrt_timer_ttimer;ralarm_talarm_test=NULL;staticvoidalarm_handler(ralarm_talarm)//---①{rt_kprintf("Time:%02d:%02d:%02drn",alarm->setup.time.hour,alarm->setup.time.minute,alarm->setup.time.second);ralarm_stop(alarm);ralarm_dump();}staticralarm_err_talarm_time_get(ralarm_time_ttimer)//---②{time_tcurrent;structtm*local;time(¤t);local=localtime(¤t);timer->hour=local->tm_hour;timer->minute=local->tm_min;timer->second=local->tm_sec;returnRALARM_EOK;}staticstructralarm_opsops={//---③.time_get=alarm_time_get,};staticvoidtime_handler(void*param)//---④{ralarm_refresh();}intmain(void){ralarm_init();//---⑤ralarm_register_ops(&ops);structralarm_setupsetup;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);}
验证结果:

审核编辑黄宇

标签:

最近更新