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

相关推荐
信奥洪老师1 小时前
2025年12 电子学会 机器人三级等级考试真题
单片机·嵌入式硬件·机器人
程序员zgh1 小时前
MCU 锁步(Lockstep)
单片机·嵌入式硬件
恶魔泡泡糖1 小时前
最小系统组成部分
c语言·单片机
czhaii2 小时前
USB拓展库及使用示例
单片机·嵌入式硬件·硬件工程
iCxhust2 小时前
8088单板机C语言汇编混合编程实验方法与步骤
c语言·汇编·单片机·嵌入式硬件·微机原理
逆小舟2 小时前
【RTOS】任务间通信IPC
单片机·嵌入式硬件
电化学仪器白超3 小时前
《可编程固定阻值电子负载的制作与自动化标定技术》
python·单片机·嵌入式硬件·自动化
三佛科技-134163842123 小时前
LP3799FAES-B 反激式电源控制器芯片 典型应用电路
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
风行男孩3 小时前
stm32基础学习——按键的使用
stm32·嵌入式硬件·学习