-
在实际的开发项目中,很多时候我们需要定时的做一些事情,举例:
- ①路上的路灯,每天晚上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;//nameofthetask
uint32_tstack_size;//sizeofstack
uint8_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);
}
- 验证结果:
审核编辑黄宇
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
接口
+关注
关注
33文章
8639浏览量
151385 -
RTOS
+关注
关注
22文章
817浏览量
119715
发布评论请先 登录
相关推荐
SILABS CP2103芯片是否不依赖于我错过的微/ picoblaze处理器?
/TechnicalDocs/CP2103.pdf几乎没有说明这个芯片的使用情况(除了告诉我RTS和CTS之外)活跃的低)。这个芯片的vhdl或verilog示例代码是否不依赖于我错过的微/ picoblaze处理器
发表于 07-23 13:00
一种不依赖于棋盘格等辅助标定物体实现像素级相机和激光雷达自动标定的方法
主要内容本文提出了一种不依赖于棋盘格等辅助标定物体,实现像素级相机和激光雷达自动标定的方法。方法直接从点云中提取3D边特征,一避免遮挡问题,并且使用了精确度更高的深度连续边。文中首先指出:以下四种
发表于 09-01 07:42
是否可以在主内核处于唤醒状态时在ESP32-S3上使用ULP?
我认为 ULP 协处理器是 ESP32 系列中的一个独立硬件单元,不依赖于主内核。如果这是真的,那么它可以在主内核运行时用作硬件看门狗(比如计算滴答)。不确定这是否可行,是否值得尝
发表于 03-02 07:51
据调查64%的人表示:日常生活中不依赖物联网设备
大多数人(64%)表示,他们不依赖连网设备来完成日常活动,这一比例是36%的人表示他们依靠设备来度过日常生活的两倍。领先的B2B研究、评级和评论公司Clutch一项新调查发现,67%拥有连网设备的人拥有智能家用电器,如智能冰箱、烤箱或电视。
发表于 10-27 10:13
•1850次阅读
量子技术革GPS的命:不依赖卫星就可以进行导航
导航卫星系统(GNSS),这类系统可以发送和接收来自绕地球运行的卫星的信号。量子加速度计是一个独立的系统,不依赖任何外部信号。 这一点尤其重要,因为卫星信号可能因高层建筑物等阻碍因素而无法使用,或者可能被堵塞、模仿或拒绝,因而无法进行
发表于 11-19 16:22
•397次阅读
PHP简单实现不依赖于Unix系统Cron的定时任务程序资料说明
本文档的主要内容详细介绍的是PHP简单实现不依赖于Unix系统Cron的定时任务程序资料说明。
发表于 03-01 16:52
•2次下载
INS是一种不依赖于外部信息的自主式导航系统
惯性导航系统(INS)也称作惯性参考系统,是一种不依赖于外部信息、也不向外部辐射能量(如无线电导航那样)的自主式导航系统。其工作环境不仅包括空中、地面,还可以在水下。 惯性导航的基本工作原理是以牛顿
发表于 06-08 15:29
•2744次阅读
一个种不依赖昂贵检测设备的偏置电流测试方法
本篇介绍一个种不依赖昂贵检测设备的偏置电流测试方法,同时配合LTspice仿真增强理解。工程师可以在普通实验室环境中,根据该方法调整放大器局部电路实现偏置电流的准确测量。 如图2.36为
以色列成立新研究中心,开发不依赖GPS的导航系统
以色列开发不依赖GPS的导航技术 据C4ISR网站2021年3月18日报道,以色列国防部和以色列航空工业公司(IAI)成立了一个新的研究中心,开发不依赖于易中断的GPS的导航系统。全球军事力量都在
智行者发布国内首款不依赖高精地图的高级别自动驾驶解决方案
与市场上其他高速领航系统不同,智行者的H-INP采用了“重感知 轻地图”的技术方案,成为国内首款不依赖高精地图的高级别自动驾驶解决方案。
一个种不依赖昂贵检测设备的偏置电流测试方法
本篇介绍一个种不依赖昂贵检测设备的偏置电流测试方法,同时配合LTspice仿真增强理解。工程师可以在普通实验室环境中,根据该方法调整放大器局部电路实现偏置电流的准确测量。
发表于 02-22 14:17
•841次阅读
原生鸿蒙系统正式发布,余承东宣布不依赖国外核心技术
’,标志着华为在移动操作系统领域迈出了坚实的一步。” 这款原生鸿蒙系统作为中国自主研发的移动操作系统,其最大的亮点在于不依赖于国外的编程语言和操作系统内核等核心技术,实现了真正的自主可控。这一突破对于提升我国在全球科技领域的竞争力具有重要意义。 然而,对于
评论