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的目的为了更快的搭载工程,使用Rt-Thread丰富的组件和第三方包资源,解耦硬件,在更换芯片时可以移植应用层代码。你是要RTT的目的什么呢? 文章项目背景 以STM32L475RCT6为例 RTC使用的为LSE外部低速32 .756k Hz 的…...
MySQL解决主从复制的报错问题
MySQL 8.4 非 GTID 模式部分数据库主从复制指南 在进行MySQL 8.4非GTID模式下部分数据库主从复制时,以下是详细的操作步骤以及对应的执行位置说明,还有报错处理方法介绍: 操作步骤 1. 备份主库指定数据库(db1、db2)…...

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

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

用python清除PDF文件中的水印(Adobe Acrobat 无法删除)
学校老师发的资料,有时候会带水印,有点强迫症的都想给它去掉。用Adobe Acrobat试了下,检测不到水印,无法删除!分析发现原来这类PDF文件是用word编辑的,其中的水印是加在了页眉中! 自己动手想办法…...
kotlin 数据类
一 kotlin数据类与java普通类区别 Kotlin 的 data class 与 Java 中的普通类(POJO)相比,确实大大减少了样板代码(boilerplate),但它的优势不止于自动生成 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数据库
数据管理和分析的领域,将Excel中的数据导入到Oracle数据库是一个常见的需求,无论是为了利用Oracle强大的数据处理能力,还是为了实现数据的集中存储和管理,这一过程都需要一定的步骤和技巧,本文将详细介绍如何从Excel导…...
PyTorch API 6 - 编译、fft、fx、函数转换、调试、符号追踪
文章目录 torch.compiler延伸阅读 torch.fft快速傅里叶变换辅助函数 torch.func什么是可组合的函数变换?为什么需要可组合的函数变换?延伸阅读 torch.futurestorch.fx概述编写转换函数图结构快速入门图操作直接操作计算图使用 replace_pattern() 进行子图…...

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

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

Webug4.0通关笔记25- 第30关SSRF
目录 一、SSRF简介 1.SSRF原理 2.渗透方法 二、第30关SSRF渗透实战 1.打开靶场 2.渗透实战 (1)Windows靶场修复 (2)Docker靶场修复 (3)获取敏感文件信息 (4)内网端口与服务…...
Android学习总结之线程池篇
一、线程池参数调优实战真题 真题 1:直播 APP 弹幕加载线程池设计 题目描述:直播 APP 需要实时加载弹幕数据(网络请求,IO 密集型),同时渲染弹幕视图(UI 操作需切主线程)࿰…...

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

MacOS 上构建 gem5
MacOS 中只存在 python3,但是scons 只认 python,不在 系统中创建 软连接,一个是因为比较难操作;另一个是尽量不要更改系统。所以独立构件python 和scons: 1,安装python 下载源代码: 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函数:字符串函数
目录 为什么需要字符串函数? 1️⃣ LENGTH(str) — 这个字符串有几个“字节”? 2️⃣ CHAR_LENGTH(str) — 这个字符串有几个“字符”? 3️⃣ TRIM(str) — 把两边的空格剪掉 4️⃣ REPLACE(str, a, b) — 把 a 替换成 b 使用这些函数时…...
DAY04:Vue.js 指令与事件处理深度解析之从基础到实战
1. 指令系统核心概念 1.1 插值表达式与基础指令 Vue.js 的指令系统是其响应式编程模型的核心,我们首先从最基础的插值表达式开始: <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 对比 一、微调概述 微调(Fine-tun…...

云效 MCP Server:AI 驱动的研发协作新范式
作者:黄博文、李晔彬 云效 MCP Server 是什么? 云效 MCP(Model Context Protocol)是阿里云云效平台推出的模型上下文协议标准化接口系统,作为连接 AI 助手与 DevOps 平台的核心桥梁,通过模型上下文协议将…...

Linux常见指令解析(三)
通配符 * *可以匹配任意名称的文件,如: ls * 列出当前目录下的所有非隐藏文件和目录,并展开目录内容 ls *.c 列出当前目录下以.c为结尾的文件 rm -rf * 删除所有非隐藏文件 alias指令 alias指令用于给命令取别名。如: 给ls …...
消息队列如何保证消息可靠性(kafka以及RabbitMQ)
目录 RabbitMQ保证消息可靠性 生产者丢失消息 MQ丢失消息 消费端丢失了数据 Kakfa的消息可靠性 生产者的消息可靠性 Kakfa的消息可靠性 消费者的消息可靠性 RabbitMQ保证消息可靠性 生产者丢失消息 1.事务消息保证 生产者在发送消息之前,开启事务消息随后生…...

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

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

Android RxJava框架分析:它的执行流程是如何的?它的线程是如何切换的?如何自定义RxJava操作符?
目录 RxJava是什么?为什么使用。RxJava是如何使用的呢?RxJava如何和Retrofit一起使用。RxJava源码分析。 (1)他执行流程是如何的。(2)map(3)线程的切换。 如何自定义RxJava操作符…...

MySQL及线程关于锁的面试题
目录 1.了解过 MySQL 死锁问题吗? 2.什么是线程死锁?死锁相关面试题 2.1 什么是死锁: 2.2 形成死锁的四个必要条件是什么? 2.3 如何避免线程死锁? 3. MySQL 怎么排查死锁问题? 4.Java线上死锁问题如…...
前端vue+elementplus实现上传通用组件
上传组件: <template><div class"upload-file"><el-uploadmultiple:action"uploadFileUrl":before-upload"handleBeforeUpload":file-list"fileList":limit"limit":on-error"handleUploadE…...
联合类型的逻辑或关系与类型保护
在 TypeScript 中,联合类型(Union Types)是一种强大的类型工具,它允许一个变量可以是几种不同类型中的一种。联合类型通过逻辑“或”关系(|)连接多个类型。这种类型的灵活性使得我们能够处理多样化的数据输…...