当前位置: 首页 > article >正文

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设备

        

/*
** 程序清单:这是一个 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时间和本地时间(北京时间)的函数接口

gmtime和localtime的线程安全版本:
gmtime_r(&now, &timeinfo);//UTC时间若为00 :00:00
localtime_r(&now, &timeinfo);//本地时间即为08 :00:00mktime(&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

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)执行注册好的闹钟回调函数。

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完整代码,仅供参考

/** 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 0xA5A5static 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();
#endifRCC_OscInitTypeDef RCC_OscInitStruct = {0};
#ifdef BSP_RTC_USING_LSIRCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;RCC_OscInitStruct.LSEState = RCC_LSE_OFF;RCC_OscInitStruct.LSIState = RCC_LSI_ON;
#elseRCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;RCC_OscInitStruct.LSEState = RCC_LSE_ON;RCC_OscInitStruct.LSIState = RCC_LSI_OFF;
#endifHAL_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_LSIPeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
#elsePeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
#endifHAL_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_LSIRTC_Handler.Init.AsynchPrediv = 0XA0;RTC_Handler.Init.SynchPrediv = 0xFA;
#elseRTC_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_LSIRTC_Handler.Init.AsynchPrediv = 0X7D;
#elseRTC_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;
#endifif (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
};
#endifstatic 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_OPSdevice->ops         = &rtc_ops;
#elsedevice->init        = RT_NULL;device->open        = RT_NULL;device->close       = RT_NULL;device->read        = RT_NULL;device->write       = RT_NULL;device->control     = rt_rtc_control;
#endifdevice->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文件中使用

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开始的,所以要加1tm_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中使用

/** 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开始的,所以要加1timeinfo.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

相关文章:

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

使用Rt-Thread的目的为了更快的搭载工程&#xff0c;使用Rt-Thread丰富的组件和第三方包资源&#xff0c;解耦硬件&#xff0c;在更换芯片时可以移植应用层代码。你是要RTT的目的什么呢&#xff1f; 文章项目背景 以STM32L475RCT6为例 RTC使用的为LSE外部低速32 .756k Hz 的…...

MySQL解决主从复制的报错问题

MySQL 8.4 非 GTID 模式部分数据库主从复制指南 在进行MySQL 8.4非GTID模式下部分数据库主从复制时&#xff0c;以下是详细的操作步骤以及对应的执行位置说明&#xff0c;还有报错处理方法介绍&#xff1a; 操作步骤 1. 备份主库指定数据库&#xff08;db1、db2&#xff09;…...

用ffmpeg压缩视频参数建议

注意:代码中的斜杠\可以删除 一、基础压缩命令&#xff08;画质优先) libx265​​推荐配置 ffmpeg -i input.mp4 -c:v libx265 -crf 25 -preset medium -c:a aac -b:a 128k output.mp4-crf&#xff1a;建议25-28&#xff08;值越小画质越高&#xff09; -preset&#xff1a;平…...

输入顶点坐标输出立方体长宽高的神经网络 Snipaste贴图软件安装

写一个神经网络&#xff0c;我输入立方体投影线段的三视图坐标&#xff0c;输出分类和长宽高 放这了明天接着搞 -------------------------------------------- 开搞 然而我的数据是这样的 winget install Snipaste f1启动&#xff0c;双击贴图隐藏 用右边4个数据做输入…...

用python清除PDF文件中的水印(Adobe Acrobat 无法删除)

学校老师发的资料&#xff0c;有时候会带水印&#xff0c;有点强迫症的都想给它去掉。用Adobe Acrobat试了下&#xff0c;检测不到水印&#xff0c;无法删除&#xff01;分析发现原来这类PDF文件是用word编辑的&#xff0c;其中的水印是加在了页眉中&#xff01; 自己动手想办法…...

kotlin 数据类

一 kotlin数据类与java普通类区别 Kotlin 的 data class 与 Java 中的普通类&#xff08;POJO&#xff09;相比&#xff0c;确实大大减少了样板代码&#xff08;boilerplate&#xff09;&#xff0c;但它的优势不止于自动生成 getter/setter、copy()、equals()、toString()&am…...

豆瓣电影Top250数据工程实践:从爬虫到智能存储的技术演进(含完整代码)

目录 引言:当豆瓣榜单遇见大数据技术 项目文档 1.1 选题背景 1.2 项目目标 2. 项目概述 2.1 系统架构设计 2.2 技术选型 2.3 项目环境搭建 2.3.1 基础环境准备 2.3.2 爬虫环境配置 2.3.3 Docker安装ES连接Kibana 安装IK插件 2.3.4 vscode依赖服务安装 3. 核心模…...

把Excel数据文件导入到Oracle数据库

数据管理和分析的领域&#xff0c;将Excel中的数据导入到Oracle数据库是一个常见的需求&#xff0c;无论是为了利用Oracle强大的数据处理能力&#xff0c;还是为了实现数据的集中存储和管理&#xff0c;这一过程都需要一定的步骤和技巧&#xff0c;本文将详细介绍如何从Excel导…...

PyTorch API 6 - 编译、fft、fx、函数转换、调试、符号追踪

文章目录 torch.compiler延伸阅读 torch.fft快速傅里叶变换辅助函数 torch.func什么是可组合的函数变换&#xff1f;为什么需要可组合的函数变换&#xff1f;延伸阅读 torch.futurestorch.fx概述编写转换函数图结构快速入门图操作直接操作计算图使用 replace_pattern() 进行子图…...

Dagster Pipes系列-1:调用外部Python脚本

本文是"Dagster Pipes教程"的第一部分&#xff0c;介绍如何通过Dagster资产调用外部Python脚本并集成到数据管道中。首先&#xff0c;创建Dagster资产subprocess_asset&#xff0c;利用PipesSubprocessClient资源执行外部脚本external_code.py&#xff0c;实现跨进程…...

python shutil 指定文件夹打包文件为 zip 压缩包

python shutil 指定文件夹打包文件为 zip 压缩包&#xff0c;具体代码如下&#xff1a; import shutil# 指定要打包的文件夹路径 src_doc ./test# 指定输出的压缩包文件名&#xff08;不包含扩展名&#xff09; output_filename testfromat_ zip# 打包并压缩文件夹为 ZIP …...

Webug4.0通关笔记25- 第30关SSRF

目录 一、SSRF简介 1.SSRF原理 2.渗透方法 二、第30关SSRF渗透实战 1.打开靶场 2.渗透实战 &#xff08;1&#xff09;Windows靶场修复 &#xff08;2&#xff09;Docker靶场修复 &#xff08;3&#xff09;获取敏感文件信息 &#xff08;4&#xff09;内网端口与服务…...

Android学习总结之线程池篇

一、线程池参数调优实战真题 真题 1&#xff1a;直播 APP 弹幕加载线程池设计 题目描述&#xff1a;直播 APP 需要实时加载弹幕数据&#xff08;网络请求&#xff0c;IO 密集型&#xff09;&#xff0c;同时渲染弹幕视图&#xff08;UI 操作需切主线程&#xff09;&#xff0…...

OpenCV 中用于背景分割的一个类cv::bgsegm::BackgroundSubtractorLSBP

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::bgsegm::BackgroundSubtractorLSBP 是 OpenCV 中用于背景分割的一个类&#xff0c;它基于局部样本二进制模式&#xff08;Local Sample Bina…...

MacOS 上构建 gem5

MacOS 中只存在 python3&#xff0c;但是scons 只认 python&#xff0c;不在 系统中创建 软连接&#xff0c;一个是因为比较难操作&#xff1b;另一个是尽量不要更改系统。所以独立构件python 和scons&#xff1a; 1&#xff0c;安装python 下载源代码&#xff1a; Python S…...

认识中间件-以及两个简单的示例

认识中间件-以及两个简单的示例 什么是中间件一个响应处理中间件老朋友 nest g如何使用为某个module引入全局引入编写逻辑一个日志中间件nest g mi 生成引入思考代码进度什么是中间件 官方文档 中间件是在路由处理程序之前调用的函数。中间件函数可以访问请求和响应对象,以及…...

(五)毛子整洁架构(分布式日志/Redis缓存/OutBox Pattern)

文章目录 项目地址一、结构化日志1.1 使用Serilog1. 安装所需要的包2. 注册服务和配置3. 安装Seq服务 1.2 添加分布式id中间件1. 添加中间件2. 注册服务3. 修改Application的LoggingBehavior 二、Redis缓存2.1 添加缓存1. 创建接口ICaching接口2. 实现ICaching接口3. 注册Cachi…...

SQL:MySQL函数:字符串函数

目录 为什么需要字符串函数&#xff1f; 1️⃣ LENGTH(str) — 这个字符串有几个“字节”&#xff1f; 2️⃣ CHAR_LENGTH(str) — 这个字符串有几个“字符”&#xff1f; 3️⃣ TRIM(str) — 把两边的空格剪掉 4️⃣ REPLACE(str, a, b) — 把 a 替换成 b 使用这些函数时…...

DAY04:Vue.js 指令与事件处理深度解析之从基础到实战

1. 指令系统核心概念 1.1 插值表达式与基础指令 Vue.js 的指令系统是其响应式编程模型的核心&#xff0c;我们首先从最基础的插值表达式开始&#xff1a; <div id"app"><!-- 基础文本插值 --><p>{{ message }}</p><!-- JavaScript 表达…...

大模型微调终极方案:LoRA、QLoRA原理详解与LLaMA-Factory、Xtuner实战对比

文章目录 一、微调概述1.1 微调步骤1.2 微调场景 二、微调方法2.1 三种方法2.2 方法对比2.3 关键结论 三、微调技术3.1 微调依据3.2 LoRA3.2.1 原理3.2.2 示例 3.3 QLoRA3.4 适用场景 四、微调框架4.1 LLaMA-Factory4.2 Xtuner4.3 对比 一、微调概述 微调&#xff08;Fine-tun…...

云效 MCP Server:AI 驱动的研发协作新范式

作者&#xff1a;黄博文、李晔彬 云效 MCP Server 是什么&#xff1f; 云效 MCP&#xff08;Model Context Protocol&#xff09;是阿里云云效平台推出的模型上下文协议标准化接口系统&#xff0c;作为连接 AI 助手与 DevOps 平台的核心桥梁&#xff0c;通过模型上下文协议将…...

Linux常见指令解析(三)

通配符 * *可以匹配任意名称的文件&#xff0c;如&#xff1a; ls * 列出当前目录下的所有非隐藏文件和目录&#xff0c;并展开目录内容 ls *.c 列出当前目录下以.c为结尾的文件 rm -rf * 删除所有非隐藏文件 alias指令 alias指令用于给命令取别名。如&#xff1a; 给ls …...

消息队列如何保证消息可靠性(kafka以及RabbitMQ)

目录 RabbitMQ保证消息可靠性 生产者丢失消息 MQ丢失消息 消费端丢失了数据 Kakfa的消息可靠性 生产者的消息可靠性 Kakfa的消息可靠性 消费者的消息可靠性 RabbitMQ保证消息可靠性 生产者丢失消息 1.事务消息保证 生产者在发送消息之前&#xff0c;开启事务消息随后生…...

HTTP学习

HTTP知识 01. 经典五层模型 应用层 为应用软件提供了很多服务&#xff0c;构建于协议之上。 传输层 数据的传输都是在这层定义的&#xff0c;数据过大分包&#xff0c;分片。 网络层 为数据在节点之间传输创建逻辑链路 数据链路层 通讯实体间建立数据链路连接 物理层 主要作用…...

go语言实现IP归属地查询

效果: 实现代码main.go package mainimport ("encoding/json""fmt""io/ioutil""net/http""os" )type AreaData struct {Continent string json:"continent"Country string json:"country"ZipCode …...

一站式解决技术平台访问难题:合规优化方案助力高效开发(2025 最新)

摘要 在技术开发与协作中&#xff0c;GitHub 等国际平台的访问效率及国内社区的使用体验常受网络环境影响。本文严格遵循网络安全规范&#xff0c;聚焦本地化 DNS 优化、官方镜像加速、浏览器工具提效等合规技术&#xff0c;提供覆盖国内外技术平台的访问优化方案&#xff0c;…...

Android RxJava框架分析:它的执行流程是如何的?它的线程是如何切换的?如何自定义RxJava操作符?

目录 RxJava是什么&#xff1f;为什么使用。RxJava是如何使用的呢&#xff1f;RxJava如何和Retrofit一起使用。RxJava源码分析。 &#xff08;1&#xff09;他执行流程是如何的。&#xff08;2&#xff09;map&#xff08;3&#xff09;线程的切换。 如何自定义RxJava操作符…...

MySQL及线程关于锁的面试题

目录 1.了解过 MySQL 死锁问题吗&#xff1f; 2.什么是线程死锁&#xff1f;死锁相关面试题 2.1 什么是死锁&#xff1a; 2.2 形成死锁的四个必要条件是什么&#xff1f; 2.3 如何避免线程死锁&#xff1f; 3. MySQL 怎么排查死锁问题&#xff1f; 4.Java线上死锁问题如…...

前端vue+elementplus实现上传通用组件

上传组件&#xff1a; <template><div class"upload-file"><el-uploadmultiple:action"uploadFileUrl":before-upload"handleBeforeUpload":file-list"fileList":limit"limit":on-error"handleUploadE…...

联合类型的逻辑或关系与类型保护

在 TypeScript 中&#xff0c;联合类型&#xff08;Union Types&#xff09;是一种强大的类型工具&#xff0c;它允许一个变量可以是几种不同类型中的一种。联合类型通过逻辑“或”关系&#xff08;|&#xff09;连接多个类型。这种类型的灵活性使得我们能够处理多样化的数据输…...