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 的…...
VSCode如何解决打开html页面中文乱码的问题
VSCode如何解决打开html页面中文乱码的问题 (1)打开扩展商店: (2)点击左侧菜单栏的扩展图标(或使用快捷键CtrlShiftX)。 (3)搜索并安装插件: …...
Java学习手册:单体架构到微服务演进
一、单体架构概述 单体架构是一种传统的软件架构风格,所有的功能模块都构建在一个统一的部署单元中。这种架构的优点是简单直接,便于开发、测试和部署。然而,随着应用规模的增长和需求的复杂化,单体架构的弊端逐渐显现࿰…...
android动态调试
在 Android 应用逆向工程中,动态调试 Smali 代码是分析应用运行时行为的重要手段。以下是详细的步骤和注意事项: 1. 准备工作 工具准备: Apktool:反编译 APK 生成 Smali 代码。Android Studio/IntelliJ IDEA:安装 smal…...
Google的A2A和MCP什么关系
作者:蛙哥 原文:https://zhuanlan.zhihu.com/p/1893738350252385035 Agent2Agent和MCP在功能上各有侧重,A2A专注于Agent之间的协作,MCP关注于Agent与外部数据源的集成。因此,MCP并不完全覆盖 A2A 的能力场景࿰…...
计算几何图形算法经典问题整理
几何算法经典问题 文章目录 几何算法经典问题一、几何基础问题1. 判断两条线段是否相交2. 判断点是否在多边形内3. 凸包计算4. 判断一个有序点集的方向(顺时针 or 逆时针)5. 求多边形面积和重心 二、 高阶图形问题6. 最小外接矩形(Minimum Bo…...
系分论文《论多云架构治理的分析和应用》
系统分析师论文范文系列 【摘要】 2022年3月,我所在公司承接了某金融机构“混合云资源管理与优化平台”的设计与实施项目。我作为系统分析师,主导了多云架构的规划与治理工作。该项目旨在构建一个兼容多家公有云及私有云资源的统一管理平台,解…...
(三)毛子整洁架构(Infrastructure层/DapperHelper/乐观锁)
文章目录 项目地址一、Infrastructure Layer1.1 创建Application层需要的服务1. Clock服务2. Email 服务3. 注册服务 1.2 数据库服务1. 表配置Configurations2. Respository实现3. 数据库链接Factory实现4. Dapper的DataOnly服务实现5. 所有数据库服务注册 1.3 基于RowVersion的…...
Femap许可使用数据分析
在当今竞争激烈的市场环境中,企业对资源使用效率和成本控制的关注日益增加。Femap作为一款业界领先的有限元分析软件,其许可使用数据分析功能为企业提供了深入洞察和智能决策的支持。本文将详细介绍Femap许可使用数据分析工具的特点、优势以及如何应用这…...
进入虚拟机单用户模式(Linux系统故障排查)
故障概述 虚拟机备份或者克隆后,无法通过编辑虚拟机IP,且忘记虚拟机密码,无法通过登录控制台修改虚拟机网络配置: 解决步骤 重启虚拟机并进入单用户模式修改网络配配置、设置密码等、大致两个步骤: 1、重启虚拟机 2、进…...
Python 数据分析与可视化:开启数据洞察之旅(5/10)
一、Python 数据分析与可视化简介 在当今数字化时代,数据就像一座蕴藏无限价值的宝藏,等待着我们去挖掘和探索。而 Python,作为数据科学领域的明星语言,凭借其丰富的库和强大的功能,成为了开启这座宝藏的关键钥匙&…...
7、三维机械设计、装配与运动仿真组件 - /设计与仿真组件/3d-mechanical-designer
76个工业组件库示例汇总 三维机械设计、装配与运动仿真通用组件 这是一个基于Three.js开发的三维机械设计、装配与运动仿真通用组件,可以实现工业机器人关节结构设计与运动仿真功能。 功能特点 直观的三维设计界面:提供基于WebGL的3D设计空间&#x…...
传统数据展示 vs 可视化:谁更打动人心?
数据,每天都在我们身边流动:从你手机里的健康步数,到企业财报中的营收增长,再到国家发布的经济指标。但问题是——你怎么“看”这些数据? 过去,我们习惯用表格、文字和报告来展示数据,这种方式…...
CSdiy java 07
1 || 运用逻辑运算符 在 Java 代码里,|| 是逻辑或(Logical OR)运算符,它的作用是对两个布尔表达式进行逻辑或运算。下面结合具体的代码片段来详细说明: 一、|| 的基本含义 在 Java 中,|| 运算符遵循以下…...
从零打造企业级Android木马:数据窃取与远程控制实战
简介 木马病毒已从简单的恶意软件演变为复杂的攻击工具,尤其在2025年企业级攻击中,木马病毒正成为黑客组织的主要武器之一。 本文将深入探讨如何制作具备数据窃取和远程控制功能的Android木马,从基础原理到企业级防御绕过技术,同时提供详细的代码实现,帮助开发者理解木马…...
金仓数据库永久增量备份技术原理与操作
先用一张图说明一下常见的备份方式 为什么需要永久增量备份 传统的数据库备份方案通常是间隔7天对数据库做一次全量备份(完整备份),每天会基于全量备份做一次增量备份,如此循环,这种备份方案在全备数据量过大场景下…...
为特定领域微调嵌入模型:打造专属的自然语言处理利器
🧠 向所有学习者致敬! “学习不是装满一桶水,而是点燃一把火。” —— 叶芝 我的博客主页: https://lizheng.blog.csdn.net 🌐 欢迎点击加入AI人工智能社区! 🚀 让我们一起努力,共创…...
SQLite 转换为 MySQL 数据库
一、导出 SQLite 数据库 1. 使用 SQLite 命令行工具 • 打开终端(在 Linux 或 macOS 上)或命令提示符(在 Windows 上)。 • 输入sqlite3 your_database_name.db(将 your_database_name.db 替换为你的 SQLite 数据库…...
cnas软件检测实验室质量管理体系文件思维导图,快速理清全部文件
软件检测实验室在申请CNAS资质时,需要根据认可文件的要求,建立实验室质量管理体系,明晰地展示组织架构、合理地安排人员岗位职责和能力要求、全面地覆盖认可文件要求的质量要素。这是一项非常庞大的工作,涉及到的文件类型非常多&a…...
31【干货】Arcgis属性表常用查询表达式实战大全
GIS数据属性表的查询在工作中常常用到,本文对常见的基本运算符进行详细介绍,并以实例的形式,针对SQL查询常用的语句进行实例分类解析,大家可以结合项目需求,自行更改对应的语句,提高工作效率。特别注意文末…...
uniapp 不同路由之间的区别
在UniApp中,路由跳转是实现页面导航的核心功能,常见的路由跳转方式包括navigateTo、redirectTo、reLaunch、switchTab和navigateBack。这些方法在跳转行为和适用场景上有所不同。 一、路由跳转的类型与区别 1. uni.navigateTo(OBJECT) 特点࿱…...
前台--Android开发
在 Android 开发中,“前台(Foreground)” 是一个非常重要的概念,它用于描述当前用户正在与之交互的组件或应用状态。理解“前台”的含义有助于更好地管理资源、生命周期和用户体验。 ✅ 一、什么是前台? 简单定义&…...
python: update() 函数的用法和例子
Python 中 update() 函数的用法和例子 在 Python 中,update() 函数通常用于字典(dictionary)对象,以更新其键值对。该函数会将另一个字典或可迭代对象中的元素添加到当前字典中,如果键已经存在,则覆盖对应…...
动态规划-62.不同路径-力扣(LeetCode)
一、题目解析 机器人只能向下或向左,要从Start位置到Finish位置。 二、算法原理 1.状态表示 我们要求到Finish位置一共有多少种方法,记Finish为[i,j],此时dp[i,j]表示:到[i,j]位置时,一共有多少种方法,满…...
排序算法总结
在讲解排序算法之前,我们需要先了解一下排序 所谓排序,就是将数据按照我们的想法将其按照一定规律组合在一起 稳定性:一组数据中的数据是否在排序前后都保持的一定的前后顺序关系,比如在排序前a[3]2 a[5]2,这时他们有着…...
kafka学习笔记(四、生产者、消费者(客户端)深入研究(三)——事务详解及代码实例)
1.事务简介 Kafka事务是Apache Kafka在流处理场景中实现Exactly-Once语义的核心机制。它允许生产者在跨多个分区和主题的操作中,以原子性(Atomicity)的方式提交或回滚消息,确保数据处理的最终一致性。例如,在流处理中…...
【Git】查看tag
文章目录 1. 查看当前提交是否有tag2. 查看最近的tag3. 查看所有tag 有时候需要基于某个tag拉分支,记录下怎么查看tag。 1. 查看当前提交是否有tag git tag --points-at HEAD该命令可直接检查当前提交(HEAD)是否关联了任何tag。 若当前提交…...
开源数字人框架 AWESOME - DIGITAL - HUMAN:技术革新与行业标杆价值剖析
一、项目核心价值:解锁数字人技术新境界 1. 技术普及:降低准入门槛,推动行业民主化 AWESOME - DIGITAL - HUMAN 项目犹如一场技术春雨,为数字人领域带来了普惠甘霖。它集成了 ASR、LLM、TTS 等关键能力,并提供模块化扩展接口,将原本复杂高深的数字人开发流程,转化为一…...
Android系统架构模式分析
本文系统梳理Android系统架构模式的演进路径与设计哲学,希望能够借此探索未来系统的发展方向。有想法的同学可以留言讨论。 1 Android层次化架构体系 1.1 整体分层架构 Android系统采用五层垂直架构,各层之间通过严格接口定义实现解耦: 应用…...
【MYSQL错误连接太多】
com.mysql.cj.exceptions.CJException: null, message from server: "Host 192.168.0.200 is blocked because of many connection errors; unblock with mysqladmin flush-hosts"方法一:通过配置文件永久更改 找到你的 MySQL 配置文件(通常…...
C23 与 MISRA C:2025:嵌入式 C 语言的进化之路
引言 在 Rust、Go 等现代语言蓬勃发展的今天,C 语言依然以 27.7% 的 TIOBE 指数(2024 年 6 月数据)稳居编程语言前三甲。其核心竞争力不仅在于高效的底层控制能力,更在于持续进化的标准体系。2024 年发布的 C23(ISO/I…...
HunyuanCustom, 腾讯混元开源的多模态定制视频生成框架
HunyuanCustom是一款由腾讯混元团队开发的多模态驱动定制视频生成框架,能够支持图像、音频、视频和文本等多种输入方式。该框架专注于生成高质量的视频,能够实现特定主体和场景的精准呈现。 HunyuanCustom是什么 HunyuanCustom是腾讯混元团队推出的一种…...
el-menu 折叠后小箭头不会消失
官方示例 <template><el-radio-group v-model"isCollapse" style"margin-bottom: 20px"><el-radio-button :value"false">expand</el-radio-button><el-radio-button :value"true">collapse</el-ra…...
Spring Boot中的拦截器!
每次用户请求到达Spring Boot服务端,你是否需要重复写日志、权限检查或请求格式化代码?这些繁琐的“前置后置”工作让人头疼!好在,Spring Boot拦截器如同一道智能关卡,统一处理请求的横切逻辑,让代码优雅又…...
Docker宿主机IP获取
1.Linux: ip addr show docker0 2. macOS/Windows 环境(Docker Desktop) 在Docker Desktop中,宿主机(你的物理机)通过host.docker.internal主机名暴露给容器,无需手动查找IP。 方法1:在容器…...
Flink之Table API
Apache Flink 的 Table API 是 Flink 提供的一种高级抽象,用于以声明式方式处理批处理和流处理数据。它是基于关系模型的 API,用户可以像编写 SQL 一样,以简洁、类型安全的方式编写数据处理逻辑。 一、基本概念 1. 什么是 Table API…...
Kubernetes生产实战:NodePort端口范围的隐藏规则与调优指南
在Kubernetes中暴露服务时,很多开发者第一次看到NodePort的端口号都会惊呼:"为什么我的服务被分配了3万多的端口?"。这背后隐藏着Kubernetes设计者的深思熟虑,今天我们就来揭开这个"数字谜团"。 一、默认端口…...
读取传感器发来的1Byte数据:分低位先行和高位先行的处理方法
目录 一、写在前面 二、伪代码的逻辑实现 1、从高位到低位 2、从低位到高位 一、写在前面 在接收数据之前我们需要事先知道数据的发送规则,是高位先行还是低位先行,并按照规则接收数据,否则收到的数据很可能是错的 高位先行:…...
在 Ubuntu 上安装并运行 ddns-go 教程
在 Ubuntu 上安装并运行 ddns-go 教程 什么是 ddns-go? ddns-go 是一款开源的轻量级 DDNS(动态域名解析)客户端,支持多家 DNS 服务商(如阿里云、腾讯云、Cloudflare、Dnspod 等),适合在家用宽…...
2025.05.07-淘天算法岗-第三题
📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 03. 信号增强最小操作次数 问题描述 卢小姐正在进行一项信号处理实验。她有一个长度为 n n n...
边缘大型语言模型综述:设计、执行和应用
(2025-08-31) A Review on Edge Large Language Models: Design, Execution, and Applications (Edge 大型语言模型综述:设计、执行和应用) 作者: Yue Zheng; Yuhao Chen; Bin Qian; Xiufang Shi; Yuanchao Shu; Jiming Chen;期刊: ACM Computing Surveys (发表日期: 2025-08…...
谷云科技iPaaS发布 MCP Server加速业务系统API 跨入 MCP 时代
在数字化浪潮中,集成技术与 AI 技术的融合成为企业智能化转型的关键。谷云科技作为 iPaaS 集成技术领域的佼佼者,我们率先在iPaaS中全新推出 MCP Server,这不仅是对谷云科技现有产品线的有力补充,更是我们顺应 AI 发展潮流、深化集…...
rabbitmq学习笔记快速使用
主要是快速了解使用,对于强要求比如说数据安全(也就是spring配置先不要求) 那么开始 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>…...
PMIC电源管理模块的PCB设计
目录 PMU模块简介 PMU的PCB设计 PMU模块简介 PMIC(电源管理集成电路)是现代电子设备的核心模块,负责高效协调多路电源的转换、分配与监控。它通过集成DC-DC降压/升压、LDO线性稳压、电池充电管理、功耗状态切换等功能,替代传统分…...
云原生CAE软件
云原生CAE软件是一种在设计和实现时就充分考虑了云环境特点的软件,能够充分利用云资源,实现高效、可扩展和灵活的仿真分析。 定义和特点 云原生CAE软件是一种在云端构建和运行的CAE(Computer Aided Engineering,计算机辅助工…...
计算机视觉】OpenCV项目实战:eye_mouse_movement:基于opencv实战眼睛控制鼠标
eye_mouse_movement:基于视觉追踪的实时眼控交互系统 一、项目概述与技术背景1.1 项目核心价值1.2 技术指标对比1.3 技术演进路线 二、环境配置与系统部署2.1 硬件要求2.2 软件安装基础环境搭建关键组件说明 2.3 模型文件部署 三、核心算法解析3.1 系统架构设计3.2 …...
《大规模电动汽车充换电设施可调能力聚合评估与预测》MATLAB实现计划
模型概述 根据论文,我将复刻实现结合长短期记忆网络(LSTM)和条件变分自编码器(CVAE)的预测方法,用于电动汽车充换电设施可调能力的聚合评估与预测。 实现步骤 1. 数据预处理 导入充电数据 (Charging_Data.csv)导入天气数据 (Weather_Data.csv)导入电…...
【C++进阶】第2课—多态
文章目录 1. 认识多态2. 多态的定义和实现2.1 构成多态的必要条件2.2 虚函数2.3 虚函数的重写或覆盖2.4 协变(了解)2.5 析构函数的重写2.6 override和final关键字2.7 重载、重写、隐藏对比 3. 纯虚函数和抽象类4. 多态原理4.1 虚函数表指针4.2 多态的实现4.3 静态绑定和动态绑定…...
Mysql--基础知识点--91.2--processlist
在 MySQL 中,SHOW PROCESSLIST 是一个常用命令,用于查看当前数据库服务器上所有正在运行的线程(进程)信息。以下是关键点说明: 1. 命令用法 SHOW FULL PROCESSLIST;输出字段: 列名含义Id线程唯一标识符&am…...
【阿里云免费领取域名以及ssl证书,通过Nginx反向代理web服务】
文章目录 前言一、申请域名1.1 访问阿里云官网1.2 输入自定义域名1.3 创建个人模板1.4 支付1元可以使用域名1年1.5 按照提示实名认证1.6 实名认证成功 二、域名解析2.1 选择域名解析2.2 解析设置2.3 快速添加解析2.4 选择对应类型2.5 解析成功 三、申请免费ssl证书3.1 访问阿里…...