RTT小师弟 · 2021年04月21日

RT-Thread 应用笔记 - RTC Alarm 组件的使用

RT-Thread 应用笔记 - 不正确使用LOG也会引发hard fault

RT-Thread 应用笔记 - RTC Alarm 组件的使用

RT-Thread 应用笔记 - freemodbus RTU RS485 从机

RT-Thread 应用笔记 - freemodbus RTU RS485 主机

RT-Thread 应用笔记 - libmodbus RTU RS485 从机

RT-Thread 应用笔记 - libmodbus RTU RS485 主机

RT-Thread 应用笔记 - STM32 CAN 通信双机

RT-Thread USB学习实践系列

背景

  • 调试完rtc,需要实现闹钟的功能,rt-thread已经有了闹钟alarm的组件。
  • 但没找到较详细的使用文档或实现例程。
  • 熟悉MCU平台的rtc alarm功能,把rt-thread alarm组件用起来。

使能 RTC Alarm组件

使用rt-thread ENV工具:menuconfig

[RT-Thread Components] -> [Device Drivers] -> [Using RTC device drivers] -> [Using RTC alarm]


使能alarm


组件包含:

alarm.h  alarm组件头文件
alarm.c  alarm组件功能实现

闹钟的使用方法

什么是闹钟?
  • 闹钟,类似于定时器,如手机上的闹钟,你设置好时间,时间到了,就可以触发闹钟事件,振动或声音提醒。


可以同时设置几个闹钟?
  • 闹钟,对于用户来讲,理论上可以设置无限个闹钟!!,但对于底层硬件,只有一个,也就是你只能设置一个alarm时间。设置多次,只有最后一次的生效!


如何实现多个闹钟?
  • 底层只能设置一个闹钟,但用户需要多个闹钟。所以,需要引入闹钟alarm组件,用于管理多个闹钟。你可以认为每创建一个闹钟,就有一个闹钟的节点,链入到闹钟的链表里。


alarm组件 alarm链表有几个?如何实现其中部分闹钟的使能?
  • alarm组件链表只有一个链表。比如你创建了4个闹钟,只开启2个闹钟。闹钟结构体,有使能的标志,你不使能,那这个闹钟就不工作。


alarm组件基本工作原理

闹钟的创建:rt_alarm_create
  • 例程如下:


static struct rt_alarm * p_alarm_hour = RT_NULL;

/* 闹钟的回调函数,多个闹钟,可以有多个回调函数,用于创建闹钟时注册。闹钟时间到了,会执行此函数 */
static void alarm_hour_cb(rt_alarm_t alarm, time_t timestamp)
{
    LOG_D("alarm_hour_cb ok!\n");
}

static void rtc_alarm_hour_create(void)
{
    static time_t now;
    struct tm *p_tm;
    struct rt_alarm_setup alarm_setup_test;

    if (p_alarm_hour != RT_NULL)
        return;

    now = time(NULL);
    p_tm = localtime(&now);
    alarm_setup_test.flag = RT_ALARM_HOUR;
    alarm_setup_test.wktime.tm_year = p_tm->tm_year;
    alarm_setup_test.wktime.tm_mon = p_tm->tm_mon;
    alarm_setup_test.wktime.tm_mday = p_tm->tm_mday;
    alarm_setup_test.wktime.tm_wday = p_tm->tm_wday;
    alarm_setup_test.wktime.tm_hour = p_tm->tm_hour + 1;
    alarm_setup_test.wktime.tm_min = p_tm->tm_min;
    alarm_setup_test.wktime.tm_sec = p_tm->tm_sec;

    p_alarm_hour = rt_alarm_create(alarm_hour_cb, &alarm_setup_test);
}
  • 闹钟创建后,并不会执行!
  • 闹钟有个flag,用于标识此闹钟是一次性的,还是周期性的。
创建闹钟时的flag参数
  • 介绍如下:
/* alarm flags */
/* 一次性的闹钟,【rtc到了这个时间】,闹钟响应后,就不再工作!只有一次 */
#define RT_ALARM_ONESHOT       0x000 /* only alarm onece */

/* Daily,闹钟每天这个时间,都会响一次 每天一次 */
#define RT_ALARM_DAILY         0x100 /* alarm everyday */

/* weekly,闹钟【每周的这一天】【rtc到了这个时间】,会响一次 每周一次 */
#define RT_ALARM_WEEKLY        0x200 /* alarm weekly at Monday or Friday etc. */

/* monthly,闹钟【每月的这一天】【rtc到了这个时间】,会响一次 ,每月一次 */
#define RT_ALARM_MONTHLY       0x400 /* alarm monthly at someday */

/* yearly,闹钟【每年,并且这个月的这一天】【rtc到了这个时间】,会响一次,每年一次*/
#define RT_ALARM_YAERLY        0x800 /* alarm yearly at a certain date */

/* each hourly,闹钟【每小时】【这个时间:分钟:秒】,会响一次,每天24次!!*/
#define RT_ALARM_HOUR          0x1000 /* alarm each hour at a certain min:second */

/* each minute,闹钟【每分钟】【这个时间:秒】,会响一次,每小时60次!!*/
#define RT_ALARM_MINUTE        0x2000 /* alarm each minute at a certain second */

/* each second ,闹钟【每秒】【这个时间:不关心!】,会响一次,每分钟60次!!*/
#define RT_ALARM_SECOND        0x4000 /* alarm each second */
  • 闹钟设置,一般MCU只会设置hh:mm:ss。MCU的alarm,一般可以设置按天、按周等周期性的重复响应,也可以设置一次性的。
  • 按天周期性的闹钟,一天一次,设置时,只关心hh:mm:ss
  • 按小时周期性的闹钟,一天24次,设置时,只关心mm:ss,也就是分钟:秒钟,每个小时都会触发一次。
  • 按分钟周期性的闹钟,一分钟一次,只关心秒。下一分钟的那个秒,就会触发。
  • 按秒周期性的闹钟,每一秒触发一次!! 设置的hh:mm:ss,不关心!!
alarm 开启:rt_alarm_start
  • 类似于定时器的开启
static void rtc_alarm_hour_start(void)
{
    if (p_alarm_hour != RT_NULL)
        rt_alarm_start(p_alarm_hour);
}
alarm 停止:rt_alarm_stop
  • 类似于定时器的停止
static void rtc_alarm_hour_stop(void)
{
    if (p_alarm_hour != RT_NULL)
        rt_alarm_stop(p_alarm_hour);
}
alarm 删除:rt_alarm_delete
  • 闹钟的删除,删除后,再次使用,需要重新创建闹钟
static void rtc_alarm_hour_delete(void)
{
    if (p_alarm_hour != RT_NULL)
    {
        if (rt_alarm_delete(p_alarm_hour) == RT_EOK)
            p_alarm_hour = RT_NULL;
    }
}
alarm组件的工作原理
  • 闹钟创建一个,就会新增一个闹钟的节点在闹钟的链表上。注意,无排序。
  • 闹钟启动:每次遍历一遍闹钟的链表,找到最先超时的闹钟,并设置闹钟。
  • 闹钟的停止:更改闹钟的flag为不使能。
  • 闹钟的删除:闹钟的链表上,删除这个闹钟节点,目前,闹钟节点句柄是全局的。
  • 闹钟中断,需要调用:rt_alarm_update
  • rt_alarm_update 会根据闹钟的周期性标志位,判断是否超时,并执行注册的闹钟回调函数。并重新遍历闹钟链表,获取最先超时的闹钟。并设置闹钟。
alarm组件的实现
  • 首先需要实现rtc的功能
  • 配置MCU alarm,默认使能 [按天]周期性响应的闹钟。
  • 完善rtc的平台适配:drv_rtc.c control函数
  • 示例如下:
static struct rt_rtc_device rtc_device;

#ifdef RT_USING_ALARM
static rt_err_t rtc_alarm_time_set(struct rt_rtc_device* p_dev);
static int rt_rtc_alarm_init(void);
#endif


/* rtc 与 alrm,共用 control函数 */
static rt_err_t rt_rtc_control(rt_device_t dev, int cmd, void *args)
{
    rt_err_t result = RT_EOK;
#ifdef RT_USING_ALARM
    struct rt_rtc_wkalarm *p_wkalarm = RT_NULL;
#endif
    RT_ASSERT(dev != RT_NULL);

    switch (cmd)
    {
    case RT_DEVICE_CTRL_RTC_GET_TIME:
        *(rt_uint32_t *)args = get_rtc_timestamp();
        LOG_D("RTC: get rtc_time %x\n", *(rt_uint32_t *)args);
        break;

    case RT_DEVICE_CTRL_RTC_SET_TIME:
        if (set_rtc_timestamp(*(rt_uint32_t *)args))
        {
            result = -RT_ERROR;
        }
#ifdef RT_USING_ALARM
        rt_alarm_update(&rtc_device.device, 1);
#endif
        LOG_D("RTC: set rtc_time %x\n", *(rt_uint32_t *)args);
        break;

#ifdef RT_USING_ALARM
    case RT_DEVICE_CTRL_RTC_GET_ALARM:
        args = &rtc_device.wkalarm;
        break;

    case RT_DEVICE_CTRL_RTC_SET_ALARM:
        p_wkalarm = (struct rt_rtc_wkalarm *)args;
        if (p_wkalarm != RT_NULL)
        {
            rtc_device.wkalarm.enable = p_wkalarm->enable;
            rtc_device.wkalarm.tm_hour = p_wkalarm->tm_hour;
            rtc_device.wkalarm.tm_min = p_wkalarm->tm_min;
            rtc_device.wkalarm.tm_sec = p_wkalarm->tm_sec;
            rtc_alarm_time_set(&rtc_device);
        }
        else
        {
            result = -RT_ERROR;
        }
        break;
#endif
    }

    return result;
}

/* 设置闹钟的MCU适配函数,不同的MCU,会有区别 */
static rt_err_t rtc_alarm_time_set(struct rt_rtc_device* p_dev)
{
    am_hal_rtc_time_t rtc_Time = {0};
    time_t timestamp = 0;
    struct tm *p_tm;

    if (p_dev->wkalarm.enable)
    {
        timestamp = get_rtc_timestamp();
        p_tm = localtime(&timestamp);

        if (p_tm->tm_year < 100)
        {
            return -RT_ERROR;
        }

        rtc_Time.ui32Second = p_dev->wkalarm.tm_sec ;
        rtc_Time.ui32Minute = p_dev->wkalarm.tm_min ;
        rtc_Time.ui32Hour   = p_dev->wkalarm.tm_hour;
        LOG_D("%s-%d:%d:%d", __func__, p_dev->wkalarm.tm_hour,
            p_dev->wkalarm.tm_min,p_dev->wkalarm.tm_sec);
        rtc_Time.ui32DayOfMonth    = p_tm->tm_mday;
        rtc_Time.ui32Month   = p_tm->tm_mon + 1 ;
        rtc_Time.ui32Year    = p_tm->tm_year - 100;
        rtc_Time.ui32Weekday = p_tm->tm_wday + 1;

        am_hal_rtc_alarm_set(&rtc_Time, RTC_ALM_RPT_INTERVAL);
        rt_rtc_alarm_enable();
    }

    return RT_EOK;
}

alarm组件功能验证


创建了alarm,怎么知道是否成功?


  • 除了LOG的打印,等事件是很难的,如设置了一年后,需要一年后再回来看结果?使用date更改系统的时间!!
  • alarm组件提供alarm查询功能,如下:
msh />alarm_dump
| alarm_id | YYYY-MM-DD hh:mm:ss | weekday |  flags  |
+----------+---------------------+---------+---------+
| No     0 | 2000-01-01 00:00:20 |       6 |  0x4003 |
+----------+---------------------+---------+---------+
msh />
  • 前期可以使用MSH CMD命令,进行闹钟的创建、开启、停止、删除操作。


原文链接:https://club.rt-thread.org/as...

推荐阅读
关注数
8062
内容数
181
小而美的物联网操作系统,经过14年的累积发展,RT-Thread 已经拥有一个国内最大的嵌入式开源社区,同时被广泛应用于能源、车载、医疗、消费电子等多个行业,累积装机量超过4亿台,成为国人自主开发、国内最成熟稳定和装机量最大的开源 RTOS。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息