RT-THREAD RTC组件中Alarm功能驱动完善

使用Rt-Thread的目的为了更快的搭载工程,使用Rt-Thread丰富的组件和第三方包资源,解耦硬件,在更换芯片时可以移植应用层代码。你是要RTT的目的什么呢?

文章项目背景

以STM32L475RCT6为例

RTC使用的为LSE外部低速32 .756k Hz 的晶体振荡器。

使用RT-Thread Studio作为IDE进行项目开发,内核版本使用的为4.1.1。

参考:

RTC设备

RT-Thread-drv_rtc.c alarm 的实现

shell指令:

date:查看日期,(查询的为北京时间,不是UTC时间,北京时间比UTC时间快8h)

list_alarm:查看rtc闹钟

1.创建项目打开RTC组件

1.1配置RTC外设

打开drivers/board.h,按照注释进行对应操作,

打开RT-Thread Settings rtc driver support

board.h宏定义#define BSP_USING_ONCHIP_RTC

stm32xxxx_hal_config.h宏定义#define HAL_RTC_MODULE_ENABLED

1.2 编译报错

正常步骤来说进行上述操作后就可以了,但是编译报错

drivers/drv_rtc.c驱动报错

conflicting types for 'rt_hw_rtc_register'

出现这个错误的原因是rt_hw_rtc_register函数在两个地方被定义了,所以出现了类型定义冲突。这里只需要屏蔽其中一个定义就可以了。RTC初始化的操作是在BSP中的驱动中执行的,因此这里建议屏蔽内核中的rt_hw_rtc_register定义,只需要屏蔽rt- thread\components\drivers\include\drivers下的rtc.h文件中的函数声明就行了。

修改后编译成功,可以跑一下RTC的例程,RTC 设备使用示例

1.3 使用Alarm功能

查看 drivers/drv_rtc.c驱动,在rtt中,对RTC设备操作,最终还是通过函数指针使用static rt_err_t rt_rtc_control(rt_device_t dev, int cmd, void *args)函数进行的操作。可以发现该函数驱动并没有完善的功能。当然这部分功能在不同的BSP中驱动也会不同,所以需要手动完善。

因此,RTC的alarm功能需要完善,因此无法直接使用。

2.Alarm功能驱动开发

首先要明白Alarm的原理框架,在步骤1.2中虽然屏蔽了内核中的rtc设备注册函数,但是Alarm的功能框架仍然需要依赖内核中的rRTC组件中的Alarm功能。

查看项目中rt-thread\components\drivers\rtc目录下的alarm.c文件,在该文件中Alarm使用RTT的自动初始化机制INIT_PREV_EXPORT(rt_alarm_system_init);创建了一个线程用来监测RTC的闹钟。

通过参考官方Alarm使用示例:RTC设备

cpp 复制代码
/*
** 程序清单:这是一个 RTC 设备使用例程
** 例程导出了 alarm_sample 命令到控制终端
** 命令调用格式:alarm_sample
** 程序功能:设置RTC时间,创建闹钟,模式:每秒触发,启动闹钟
**/

void user_alarm_callback(rt_alarm_t alarm, time_t timestamp)
{
    rt_kprintf("user alarm callback function.\n");
}

void alarm_sample(void)
{  
    rt_device_t dev = rt_device_find("rtc");
    struct rt_alarm_setup setup;
    struct rt_alarm * alarm = RT_NULL;
    static time_t now;
    struct tm p_tm;

    if (alarm != RT_NULL)
        return;

    /* 获取当前时间戳,并把下一秒时间设置为闹钟时间 */
    now = time(NULL) + 1;
    gmtime_r(&now,&p_tm);

    setup.flag = RT_ALARM_SECOND;            
    setup.wktime.tm_year = p_tm.tm_year;
    setup.wktime.tm_mon = p_tm.tm_mon;
    setup.wktime.tm_mday = p_tm.tm_mday;
    setup.wktime.tm_wday = p_tm.tm_wday;
    setup.wktime.tm_hour = p_tm.tm_hour;
    setup.wktime.tm_min = p_tm.tm_min;
    setup.wktime.tm_sec = p_tm.tm_sec;   

    alarm = rt_alarm_create(user_alarm_callback, &setup);    
    if(RT_NULL != alarm)
    {
        rt_alarm_start(alarm);
    }
}
/* export msh cmd */
MSH_CMD_EXPORT(alarm_sample,alarm sample);

Alarm功能主要是通过 rt_alarm_create函数创建一个Alarm闹钟,该函数返回一个rt_alarm类型的结构体来承载闹钟信息。

然后再通过rt_alarm_start来启动闹钟。

在该函数中需要注意,rt_alarm_start 函数下的 alarm_setup函数会根据闹钟标志位对

alarm->wktime 闹钟的时间信息重新赋值,alarm_setup 中重新赋值 使用 get_timestamp 函数重新获取时间,rtc.c文件中的get_timestamp 本质上也是通过 rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_GET_TIME, timestamp);来获取rtc时间,最终还是通过调用drivers/drv_rtc.c驱动中的get_rtc_timestamp函数 。

在梳理整个的RTC驱动时,要注意区分UTC时间和本地时间(北京时间)的函数接口

cpp 复制代码
gmtime和localtime的线程安全版本:
gmtime_r(&now, &timeinfo);//UTC时间若为00 :00:00
localtime_r(&now, &timeinfo);//本地时间即为08 :00:00

mktime(&tm_new); 和 timegm(&tm_new);

在移植驱动的过程中,可能会因为时间原因导致闹钟配置错误。

Alarm简介

rtt的官方文档中是这样介绍Alarm功能的:

alarm 闹钟功能是基于 RTC 设备实现的,根据用户设定的闹钟时间,当时间到时触发 alarm 中断,执行闹钟事件,在硬件上 RTC 提供的 Alarm 是有限的,RT-Thread 将 Alarm 在软件层次上封装成了一个组件,原理上可以实现无限个闹钟,但每个闹钟只有最后一次设定有效

我们知道STM32 的 RTC 模块通常支持设置两个闹钟 ,分别为闹钟 A(Alarm A)闹钟 B(Alarm B)。但是在rtt中通过软件确实实现了多个闹钟的功能。

(移植完驱动代码后)通过在drv_rtc.c文件中的下列代码中添加打印,来判断RTC硬件闹钟和RTT软件闹钟实现的区别。

该回调函数为硬件RTC闹钟的回调函数

复制代码
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    rt_alarm_update(0,0);
    rt_kprintf("HAL RTC Alarm Handle CallBack\r\n");
}

通过list_alarm指令查询闹钟

这里可以发现,RTT是通过for循环遍历_container链表来管理的软件闹钟。虽然理论上可以实现无限个闹钟,但是如果需要RTC闹钟唤醒休眠状态下的MCU,那么软件闹钟是行不通的。

如果说进行低功耗方案设计,每次休眠状态都要进入待机模式:

在该模式下,除了 RTC 和备份域(包括备份寄存器和 RTC 时钟源),芯片的其他所有电源都被切断,内部电压调节器被关闭,SRAM 和寄存器内容丢失。

因此软件RTC闹钟数据会丢失,硬件RTC闹钟数据会被保留,在进行低功耗设计时使用RTT提供的RTC Alarm组件需要注意这一点。

复制代码
void rt_alarm_dump(void)
{
    rt_list_t *next;
    rt_alarm_t alarm;

    rt_kprintf("| hh:mm:ss | week | flag | en |\n");
    rt_kprintf("+----------+------+------+----+\n");
    for (next = _container.head.next; next != &_container.head; next = next->next)
    {
        alarm = rt_list_entry(next, struct rt_alarm, list);
        rt_uint8_t flag_index = get_alarm_flag_index(alarm->flag);
        rt_kprintf("| %02d:%02d:%02d |  %2d  |  %2s  | %2d |\n",
            alarm->wktime.tm_hour, alarm->wktime.tm_min, alarm->wktime.tm_sec,
            alarm->wktime.tm_wday, _alarm_flag_tbl[flag_index].name, alarm->flag & RT_ALARM_STATE_START);
    }
    rt_kprintf("+----------+------+------+----+\n");
}

MSH_CMD_EXPORT_ALIAS(rt_alarm_dump, list_alarm, list alarm info);

Alarm回调函数的实现

下列函数为drv_rtc.c中RTC Alarm的事件回调函数。当RTC触发闹钟,程序便会触发该回调函数,执行rt_alarm_update

cpp 复制代码
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    rt_alarm_update(0,0);
}

alarm.c文件中的rt_alarm_update函数,rt_event_send是Rt-Thread中的线程同步机制:事件集。

事件集

在此处释放特定事件唤醒 alarmsvc 线程,最终通过该线程的alarm_update(recv)执行注册好的闹钟回调函数。

cpp 复制代码
void rt_alarm_update(rt_device_t dev, rt_uint32_t event)
{
    rt_event_send(&_container.event, 1);
}

2.1drv_rtc.c驱动代码

附上drv_rtc.c完整代码,仅供参考

cpp 复制代码
/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date         Author        Notes
 * 2018-12-04   balanceTWK    first version
 */

#include "board.h"
#include<rtthread.h>
#include<rtdevice.h>

#ifdef BSP_USING_ONCHIP_RTC


#ifndef HAL_RTCEx_BKUPRead
#define HAL_RTCEx_BKUPRead(x1, x2) (~BKUP_REG_DATA)
#endif
#ifndef HAL_RTCEx_BKUPWrite
#define HAL_RTCEx_BKUPWrite(x1, x2, x3)
#endif
#ifndef RTC_BKP_DR1
#define RTC_BKP_DR1 RT_NULL
#endif

//#define DRV_DEBUG
#define LOG_TAG             "drv.rtc"
#include <drv_log.h>

#define BKUP_REG_DATA 0xA5A5

static struct rt_device rtc;
static RTC_HandleTypeDef RTC_Handler;

static time_t get_rtc_timestamp(void)
{
    RTC_TimeTypeDef RTC_TimeStruct = {0};
    RTC_DateTypeDef RTC_DateStruct = {0};
    struct tm tm_new;

    HAL_RTC_GetTime(&RTC_Handler, &RTC_TimeStruct, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&RTC_Handler, &RTC_DateStruct, RTC_FORMAT_BIN);

    tm_new.tm_sec  = RTC_TimeStruct.Seconds;
    tm_new.tm_min  = RTC_TimeStruct.Minutes;
    tm_new.tm_hour = RTC_TimeStruct.Hours;
    tm_new.tm_mday = RTC_DateStruct.Date;
    tm_new.tm_mon  = RTC_DateStruct.Month - 1;
    tm_new.tm_year = RTC_DateStruct.Year + 100;

    LOG_D("get rtc time.");
//    return mktime(&tm_new);
    return timegm(&tm_new);
}

static rt_err_t set_rtc_time_stamp(time_t time_stamp)
{
    RTC_TimeTypeDef RTC_TimeStruct = {0};
    RTC_DateTypeDef RTC_DateStruct = {0};
    struct tm *p_tm;

    p_tm = localtime(&time_stamp);
    if (p_tm->tm_year < 100)
    {
        return -RT_ERROR;
    }

    RTC_TimeStruct.Seconds = p_tm->tm_sec ;
    RTC_TimeStruct.Minutes = p_tm->tm_min ;
    RTC_TimeStruct.Hours   = p_tm->tm_hour;
    RTC_DateStruct.Date    = p_tm->tm_mday;
    RTC_DateStruct.Month   = p_tm->tm_mon + 1 ;
    RTC_DateStruct.Year    = p_tm->tm_year - 100;
    RTC_DateStruct.WeekDay = p_tm->tm_wday + 1;

    if (HAL_RTC_SetTime(&RTC_Handler, &RTC_TimeStruct, RTC_FORMAT_BIN) != HAL_OK)
    {
        return -RT_ERROR;
    }
    if (HAL_RTC_SetDate(&RTC_Handler, &RTC_DateStruct, RTC_FORMAT_BIN) != HAL_OK)
    {
        return -RT_ERROR;
    }

    LOG_D("set rtc time.");
    HAL_RTCEx_BKUPWrite(&RTC_Handler, RTC_BKP_DR1, BKUP_REG_DATA);
    return RT_EOK;
}

static void rt_rtc_init(void)
{
#ifndef SOC_SERIES_STM32H7
    __HAL_RCC_PWR_CLK_ENABLE();
#endif

    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
#ifdef BSP_RTC_USING_LSI
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
    RCC_OscInitStruct.LSIState = RCC_LSI_ON;
#else
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    RCC_OscInitStruct.LSEState = RCC_LSE_ON;
    RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
#endif
    HAL_RCC_OscConfig(&RCC_OscInitStruct);
}

static rt_err_t rt_rtc_config(struct rt_device *dev)
{
    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

    HAL_PWR_EnableBkUpAccess();
    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
#ifdef BSP_RTC_USING_LSI
    PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
#else
    PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
#endif
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);

    /* Enable RTC Clock */
    __HAL_RCC_RTC_ENABLE();

    RTC_Handler.Instance = RTC;
    if (HAL_RTCEx_BKUPRead(&RTC_Handler, RTC_BKP_DR1) != BKUP_REG_DATA)
    {
        LOG_I("RTC hasn't been configured, please use <date> command to config.");

#if defined(SOC_SERIES_STM32F1)
        RTC_Handler.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
        RTC_Handler.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
#elif defined(SOC_SERIES_STM32F0)

        /* set the frequency division */
#ifdef BSP_RTC_USING_LSI
        RTC_Handler.Init.AsynchPrediv = 0XA0;
        RTC_Handler.Init.SynchPrediv = 0xFA;
#else
        RTC_Handler.Init.AsynchPrediv = 0X7F;
        RTC_Handler.Init.SynchPrediv = 0x0130;
#endif /* BSP_RTC_USING_LSI */

        RTC_Handler.Init.HourFormat = RTC_HOURFORMAT_24;
        RTC_Handler.Init.OutPut = RTC_OUTPUT_DISABLE;
        RTC_Handler.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
        RTC_Handler.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
#elif defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32H7)

        /* set the frequency division */
#ifdef BSP_RTC_USING_LSI
        RTC_Handler.Init.AsynchPrediv = 0X7D;
#else
        RTC_Handler.Init.AsynchPrediv = 0X7F;
#endif /* BSP_RTC_USING_LSI */
        RTC_Handler.Init.SynchPrediv = 0XFF;

        RTC_Handler.Init.HourFormat = RTC_HOURFORMAT_24;
        RTC_Handler.Init.OutPut = RTC_OUTPUT_DISABLE;
        RTC_Handler.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
        RTC_Handler.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
#endif
        if (HAL_RTC_Init(&RTC_Handler) != HAL_OK)
        {
            return -RT_ERROR;
        }
    }
    return RT_EOK;
}

static rt_err_t set_rtc_alarm_stamp(struct rt_rtc_wkalarm wkalarm)
{
    RTC_AlarmTypeDef sAlarm = {0};
    //struct tm *p_tm;
    //p_tm = localtime(&time_stamp);
    if(wkalarm.enable == RT_FALSE)
    {
        if (HAL_RTC_DeactivateAlarm(&RTC_Handler,RTC_ALARM_A) != HAL_OK)
           {
               return -RT_ERROR;
           }
           LOG_D("stop rtc alarm.");
    }else {
        /** Enable the Alarm A
        */
        sAlarm.AlarmTime.Hours = wkalarm.tm_hour;
        sAlarm.AlarmTime.Minutes = wkalarm.tm_min;
        sAlarm.AlarmTime.Seconds = wkalarm.tm_sec;
        sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
        sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
        sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY;
        sAlarm.Alarm = RTC_ALARM_A;
        if (HAL_RTC_SetAlarm_IT(&RTC_Handler, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
        {
            return -RT_ERROR;
        }
        LOG_D("set rtc alarm.");
    }

    return RT_EOK;
}
/**
  * @brief This function handles RTC alarms A and B interrupt through EXTI line 17.
  */
void RTC_Alarm_IRQHandler(void)
{
  /* USER CODE BEGIN RTC_Alarm_IRQn 0 */
  /* USER CODE END RTC_Alarm_IRQn 0 */
  HAL_RTC_AlarmIRQHandler(&RTC_Handler);
  /* USER CODE BEGIN RTC_Alarm_IRQn 1 */
  /* USER CODE END RTC_Alarm_IRQn 1 */
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    rt_alarm_update(0,0);
}
struct rt_rtc_wkalarm get_rtc_alarm_stamp(void)
{
    RTC_AlarmTypeDef sAlarm = {0};
    //struct tm tm_new = {0};
    struct rt_rtc_wkalarm wkalarm;
    if (HAL_RTC_GetAlarm(&RTC_Handler, &sAlarm,RTC_ALARM_A, RTC_FORMAT_BIN) != HAL_OK)
   {
        LOG_D("get rtc alarm fail!.");
   }
    wkalarm.tm_sec  = sAlarm.AlarmTime.Seconds;
    wkalarm.tm_min = sAlarm.AlarmTime.Minutes;
    wkalarm.tm_hour = sAlarm.AlarmTime.Hours;
    // 打印获取到的 RTC 闹钟时间信息
//    rt_kprintf("get rtc alarm: %d %d %d\r\n", wkalarm.tm_hour, wkalarm.tm_min, wkalarm.tm_sec);
    return wkalarm;
}


static rt_err_t rt_rtc_control(rt_device_t dev, int cmd, void *args)
{
    rt_err_t result = RT_EOK;
    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_time_stamp(*(rt_uint32_t *)args))
        {
            result = -RT_ERROR;
        }
        LOG_D("RTC: set rtc_time %x\n", *(rt_uint32_t *)args);
        break;
    case RT_DEVICE_CTRL_RTC_SET_ALARM:
        if (set_rtc_alarm_stamp(*(struct rt_rtc_wkalarm *)args))
       {
           result = -RT_ERROR;
       }
        LOG_D("RTC: set rtc_alarm %x\n", *(rt_uint32_t *)args);
        break;
    case RT_DEVICE_CTRL_RTC_GET_ALARM:
        *(struct rt_rtc_wkalarm *)args = get_rtc_alarm_stamp();
        LOG_D("RTC: get rtc_alarm %x\n", *(rt_uint32_t *)args);
        break;
    }
    return result;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops rtc_ops =
{
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    rt_rtc_control
};
#endif

static rt_err_t rt_hw_rtc_register(rt_device_t device, const char *name, rt_uint32_t flag)
{
    RT_ASSERT(device != RT_NULL);

    rt_rtc_init();
    if (rt_rtc_config(device) != RT_EOK)
    {
        return -RT_ERROR;
    }
#ifdef RT_USING_DEVICE_OPS
    device->ops         = &rtc_ops;
#else
    device->init        = RT_NULL;
    device->open        = RT_NULL;
    device->close       = RT_NULL;
    device->read        = RT_NULL;
    device->write       = RT_NULL;
    device->control     = rt_rtc_control;
#endif
    device->type        = RT_Device_Class_RTC;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;
    device->user_data   = RT_NULL;

    /* register a character device */
    return rt_device_register(device, name, flag);
}

int rt_hw_rtc_init(void)
{
    /* RTC interrupt DeInit */
    HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);

    rt_err_t result;
    result = rt_hw_rtc_register(&rtc, "rtc", RT_DEVICE_FLAG_RDWR);
    if (result != RT_EOK)
    {
        LOG_E("rtc register err code: %d", result);
        return result;
    }
    LOG_D("rtc init success");
    return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_rtc_init);

#endif /* BSP_USING_ONCHIP_RTC */

获取RTC时间的shell指令,如果有需要可以放在drv_rtc.c文件中使用

cpp 复制代码
void getrtc(void)
{
    RTC_TimeTypeDef RTC_TimeStruct = {0};
    RTC_DateTypeDef RTC_DateStruct = {0};
    struct tm tm_new;

    HAL_RTC_GetTime(&RTC_Handler, &RTC_TimeStruct, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&RTC_Handler, &RTC_DateStruct, RTC_FORMAT_BIN);

    tm_new.tm_sec  = RTC_TimeStruct.Seconds;
    tm_new.tm_min  = RTC_TimeStruct.Minutes;
    tm_new.tm_hour = RTC_TimeStruct.Hours;
    tm_new.tm_mday = RTC_DateStruct.Date;
    tm_new.tm_mon  = RTC_DateStruct.Month - 1;
    tm_new.tm_year = RTC_DateStruct.Year + 100;

    rt_kprintf("GET RTC time: %04d-%02d-%02d %02d:%02d:%02d\n",
            tm_new.tm_year,  // tm_year是从1900年开始计算的
            tm_new.tm_mon,      // tm_mon是从0开始的,所以要加1
            tm_new.tm_mday,
            tm_new.tm_hour,
            tm_new.tm_min,
            tm_new.tm_sec);
}

MSH_CMD_EXPORT(getrtc,alarm sample);

2.2Alarm测试代码

以下为Alarm测试代码,可以放在main.c中使用

cpp 复制代码
/*
 * Copyright (c) 2006-2025, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-05-08     RT-Thread    first version
 */

#include <rtthread.h>
#include <rtdevice.h>
#include <stdlib.h>
#include <time.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include "board.h"

int main(void)
{
//    int count = 1;
//
//    while (count++)
//    {
//        LOG_D("Hello RT-Thread!");
//        rt_thread_mdelay(1000);
//    }

    return RT_EOK;
}

/*
** 程序清单:这是一个 RTC 设备使用例程
** 例程导出了 alarm_sample 命令到控制终端
** 命令调用格式:alarm_sample
** 程序功能:设置RTC时间,创建闹钟,模式:每秒触发,启动闹钟
**/

void user_alarm_callback(rt_alarm_t alarm, time_t timestamp)
{
    rt_kprintf("user alarm callback function.\n");
}

void alarm_sample(void)
{
//  设置RTC时间,
    set_time(00, 00, 00);//北京时间,alarm为UTC时间
//  配置Alarm闹钟
    time_t now;
    struct tm timeinfo;
    rt_device_t rtc_dev = rt_device_find("rtc");
    if (rtc_dev == RT_NULL)
    {
        rt_kprintf("Find RTC device failed!\n");
        return;
    }

    rt_device_open(rtc_dev, RT_DEVICE_OFLAG_RDWR);

    rt_err_t result = rt_device_control(rtc_dev, RT_DEVICE_CTRL_RTC_GET_TIME, &now);
    if (result != RT_EOK)
    {
        rt_kprintf("Get RTC time failed! Error code: %d\n", result);
        rt_device_close(rtc_dev);
        return;
    }
//  RTC闹钟设置为本地时间可以触发,但是alarm线程会将时间改为UTC时间,所以建议使用UTC时间设置闹钟,这样可以将时间统一。
    gmtime_r(&now, &timeinfo);//UTC时间0时
//    localtime_r(&now, &timeinfo);//本地北京时间8时

    rt_kprintf("timeinfo time: %04d-%02d-%02d %02d:%02d:%02d\n",
               timeinfo.tm_year + 1900,  // tm_year是从1900年开始计算的
               timeinfo.tm_mon + 1,      // tm_mon是从0开始的,所以要加1
               timeinfo.tm_mday,
               timeinfo.tm_hour,
               timeinfo.tm_min,
               timeinfo.tm_sec);


    struct rt_alarm_setup setup;
    struct rt_alarm * alarm = RT_NULL;

    setup.flag = RT_ALARM_SECOND;
    setup.wktime.tm_year = timeinfo.tm_year;
    setup.wktime.tm_mon = timeinfo.tm_mon;
    setup.wktime.tm_mday = timeinfo.tm_mday;
    setup.wktime.tm_wday = timeinfo.tm_wday;
    setup.wktime.tm_hour = timeinfo.tm_hour;
    setup.wktime.tm_min = timeinfo.tm_min;
    setup.wktime.tm_sec = timeinfo.tm_sec;



    alarm = rt_alarm_create(user_alarm_callback, &setup);
    if(RT_NULL != alarm)
    {
        rt_alarm_start(alarm);
    }


    rt_kprintf("net time: %02d:%02d:%02d\n",
            alarm->wktime.tm_hour,
            alarm->wktime.tm_min,
            alarm->wktime.tm_sec);

}
/* export msh cmd */
MSH_CMD_EXPORT(alarm_sample,alarm sample);

附:

项目工程:

https://gitee.com/Z-cjie/rtts-rtc-alarm-component.git

相关推荐
Sandman6z1 小时前
STM32CubeMX安装及使用分享
stm32·嵌入式硬件
Nice__J2 小时前
智芯Z20K144x MCU开发之时钟架构
单片机·嵌入式硬件·架构
DIY机器人工房4 小时前
[6-1] TIM定时中断 江协科技学习笔记(45个知识点)
笔记·科技·stm32·单片机·学习
Lester_11015 小时前
嵌入式学习笔记 - 关于单片机的位数
笔记·单片机·学习
憧憬一下7 小时前
stm32之USART
stm32·单片机·嵌入式硬件·串口·嵌入式·usart
又熟了10 小时前
WDG看门狗(独立看门狗和窗口看门狗)
c语言·stm32·单片机·嵌入式硬件
我不是帅戈20 小时前
STM32+安信可Ai-WB2-12F连接阿里云物联网平台
stm32·物联网·阿里云·云计算·安信可
教练、我想打篮球20 小时前
04 基于 STM32 的时钟展示程序
stm32·单片机·嵌入式硬件
大鱼YY20 小时前
STM32系统定时器以及微秒延时函数分析
stm32·滴答定时器