diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index 9f4147f9fdc4b90bc3d17aa37c4b0607c4a0d96a..d2fd3f5fc2acde85784258bf63034799be08f7d9 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -78,6 +78,25 @@ config RT_USING_RTC bool "Using RTC device drivers" default n + if RT_USING_RTC + config RT_USING_SOFT_RTC + bool "Using software simulation RTC device" + default n + config RTC_SYNC_USING_NTP + bool "Using NTP auto sync RTC time" + select PKG_NETUTILS_NTP + default n + + if RTC_SYNC_USING_NTP + config RTC_NTP_FIRST_SYNC_DELAY + int "NTP first sync delay time(second) for network connect" + default 30 + config RTC_NTP_SYNC_PERIOD + int "NTP auto sync period(second)" + default 3600 + endif + endif + config RT_USING_SDIO bool "Using SD/MMC device drivers" default n diff --git a/components/drivers/include/drivers/rtc.h b/components/drivers/include/drivers/rtc.h index 18108091c8a092edec30ec5022f7639e302256dd..7e89bd370435bf54a7908131675946857bba284a 100644 --- a/components/drivers/include/drivers/rtc.h +++ b/components/drivers/include/drivers/rtc.h @@ -22,15 +22,13 @@ * 2012-10-10 aozima first version. */ -#ifndef RTC_H_INCLUDED -#define RTC_H_INCLUDED +#ifndef __RTC_H__ +#define __RTC_H__ -extern rt_err_t set_date(rt_uint32_t year, - rt_uint32_t month, - rt_uint32_t day); +rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day); +rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second); -extern rt_err_t set_time(rt_uint32_t hour, - rt_uint32_t minute, - rt_uint32_t second); +int rt_soft_rtc_init(void); +int rt_rtc_ntp_sync_init(void); -#endif // RTC_H_INCLUDED +#endif /* __RTC_H__ */ diff --git a/components/drivers/rtc/README.md b/components/drivers/rtc/README.md new file mode 100644 index 0000000000000000000000000000000000000000..23010cf4b7161de4ce1ed53f6e573b83ea87ffb6 --- /dev/null +++ b/components/drivers/rtc/README.md @@ -0,0 +1,80 @@ +# RT-Thread RTC 设备 + +## 1、介绍 + +RT-Thread 的 RTC (实时时钟)设备为操作系统的时间系统提供了基础服务。面对越来越多的 IoT 场景,RTC 已经成为产品的标配,甚至在诸如 SSL 的安全传输过程中,RTC 已经成为不可或缺的部分。 + +## 2、使用 + +应用层对于 RTC 设备一般不存在直接调用的 API ,如果使用到 C 标准库中的时间 API (目前主要是获取当前时间的 `time_t time(time_t *t)`),则会间接通过设备的 control 接口完成交互。 + +> 注意:目前系统内只允许存在一个 RTC 设备,且名称为 `"rtc"` 。 + +### 2.1 设置日期 + +```C +rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day) +``` + +|参数 |描述| +|:----- |:----| +|year |待设置生效的年份| +|month |待设置生效的月份| +|day |待设置生效的日| + +### 2.2 设置时间 + +```C +rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second) +``` + +|参数 |描述| +|:----- |:----| +|hour |待设置生效的时| +|minute |待设置生效的分| +|second |待设置生效的秒| + +### 2.3 使用 Finsh/MSH 命令 查看/设置 日期和时间 + +#### 2.3.1 查看日期和时间 + +输入 `date` 即可,大致效果如下: + +``` +msh />date +Fri Feb 16 01:11:56 2018 +msh /> +``` + +#### 2.3.2 设置日期和时间 + +同样使用 `date` 命令,在命令后面再依次输入 `年` `月` `日` `时` `分` `秒` (中间空格隔开, 24H 制),大致效果如下: + +``` +msh />date 2018 02 16 01 15 30 # 设置当前时间为 2018-02-16 01:15:30 +msh /> +``` + +### 2.4 启用 NTP 时间自动同步 + +如果 RT-Thread 已接入互联网,可启用 NTP 时间自动同步功能,定期同步本地时间。 + +在 menuconfig 中启用 `RTC_SYNC_USING_NTP` 配置。启用该功能后,会自动开启 [netutils package](https://github.com/RT-Thread-packages/netutils) 的 NTP 功能。同时务必确保 RT-Thread 网络访问正常。 + +启用该配置后,还有两个配置是用户可选配置: + +- `RTC_NTP_FIRST_SYNC_DELAY`: 首次执行 NTP 时间同步的延时。延时的目的在于,给网络连接预留一定的时间,尽量提高第一次执行 NTP 时间同步时的成功率。默认时间为 30S; +- `RTC_NTP_SYNC_PERIOD`: NTP 自动同步周期,单位为秒,默认一小时(即 3600S)同步一次。 + +> 注意:如果没有使用组件自动初始化功能,则需手动调用 `int rt_rtc_ntp_sync_init(void)` ,完成该功能初始化。 + +### 2.5 启用 Soft RTC (软件模拟 RTC) + +这个模式非常适用于对时间精度要求不高,没有硬件 RTC 的产品。 + +#### 2.5.1 使用方法 + +在 menuconfig 中启用 `RT_USING_SOFT_RTC` 配置。 + +> 注意:如果没有使用组件自动初始化功能,则需手动调用 `int rt_soft_rtc_init(void)` ,完成该功能初始化。 + diff --git a/components/drivers/rtc/SConscript b/components/drivers/rtc/SConscript index 9191ffec97db8d8d6d96a32913da8505c056ee57..e1f8f433c1671af179e9f131b737a87d12f57620 100644 --- a/components/drivers/rtc/SConscript +++ b/components/drivers/rtc/SConscript @@ -7,6 +7,8 @@ rtc = ['rtc.c'] rtc_alarm = ['alarm.c'] +soft_rtc = ['soft_rtc.c'] + CPPPATH = [cwd + '/../include'] group = [] @@ -14,6 +16,8 @@ if GetDepend(['RT_USING_RTC']): src = src + rtc if GetDepend(['RT_USING_ALARM']): src = src + rtc_alarm + if GetDepend(['RT_USING_SOFT_RTC']): + src = src + soft_rtc group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_RTC'], CPPPATH = CPPPATH) diff --git a/components/drivers/rtc/rtc.c b/components/drivers/rtc/rtc.c index f87df754e7033afead71f8f377d51c80b19e9c65..a33e1dfccf99f9fa510443ce83b64d736ab59529 100644 --- a/components/drivers/rtc/rtc.c +++ b/components/drivers/rtc/rtc.c @@ -22,16 +22,31 @@ * 2012-01-29 aozima first version. * 2012-04-12 aozima optimization: find rtc device only first. * 2012-04-16 aozima add scheduler lock for set_date and set_time. + * 2018-02-16 armink add auto sync time by NTP */ #include #include #include -/** \brief returns the current time. +/* Using NTP auto sync RTC time */ +#ifdef RTC_SYNC_USING_NTP +/* NTP first sync delay time for network connect, unit: second */ +#ifndef RTC_NTP_FIRST_SYNC_DELAY +#define RTC_NTP_FIRST_SYNC_DELAY (30) +#endif +/* NTP sync period, unit: second */ +#ifndef RTC_NTP_SYNC_PERIOD +#define RTC_NTP_SYNC_PERIOD (1L*60L*60L) +#endif +#endif /* RTC_SYNC_USING_NTP */ + +/** + * Returns the current time. + * + * @param time_t * t the timestamp pointer, if not used, keep NULL. * - * \param time_t * t the timestamp pointer, if not used, keep NULL. - * \return time_t return timestamp current. + * @return time_t return timestamp current. * */ /* for IAR 6.2 later Compiler */ @@ -70,12 +85,19 @@ time_t time(time_t *t) return time_now; } -/** \brief set system date(time not modify). +RT_WEAK clock_t clock(void) +{ + return rt_tick_get(); +} + +/** + * Set system date(time not modify). * - * \param rt_uint32_t year e.g: 2012. - * \param rt_uint32_t month e.g: 12 (1~12). - * \param rt_uint32_t day e.g: e.g: 31. - * \return rt_err_t if set success, return RT_EOK. + * @param rt_uint32_t year e.g: 2012. + * @param rt_uint32_t month e.g: 12 (1~12). + * @param rt_uint32_t day e.g: 31. + * + * @return rt_err_t if set success, return RT_EOK. * */ rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day) @@ -118,12 +140,14 @@ rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day) return ret; } -/** \brief set system time(date not modify). +/** + * Set system time(date not modify). + * + * @param rt_uint32_t hour e.g: 0~23. + * @param rt_uint32_t minute e.g: 0~59. + * @param rt_uint32_t second e.g: 0~59. * - * \param rt_uint32_t hour e.g: 0~23. - * \param rt_uint32_t minute e.g: 0~59. - * \param rt_uint32_t second e.g: 0~59. - * \return rt_err_t if set success, return RT_EOK. + * @return rt_err_t if set success, return RT_EOK. * */ rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second) @@ -166,6 +190,45 @@ rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second) return ret; } +#ifdef RTC_SYNC_USING_NTP +static void ntp_sync_thread_enrty(void *param) +{ + extern time_t ntp_sync_to_rtc(void); + /* first sync delay for network connect */ + rt_thread_delay(RTC_NTP_FIRST_SYNC_DELAY * RT_TICK_PER_SECOND); + + while (1) + { + ntp_sync_to_rtc(); + rt_thread_delay(RTC_NTP_SYNC_PERIOD * RT_TICK_PER_SECOND); + } +} + +int rt_rtc_ntp_sync_init(void) +{ + static rt_bool_t init_ok = RT_FALSE; + rt_thread_t thread; + + if (init_ok) + { + return 0; + } + + thread = rt_thread_create("ntp_sync", ntp_sync_thread_enrty, RT_NULL, 1536, 26, 2); + if (thread) + { + rt_thread_startup(thread); + } + else + { + return -RT_ENOMEM; + } + + init_ok = RT_TRUE; +} +INIT_COMPONENT_EXPORT(rt_rtc_ntp_sync_init); +#endif /* RTC_SYNC_USING_NTP */ + #ifdef RT_USING_FINSH #include #include @@ -181,4 +244,68 @@ FINSH_FUNCTION_EXPORT(list_date, show date and time.) FINSH_FUNCTION_EXPORT(set_date, set date. e.g: set_date(2010,2,28)) FINSH_FUNCTION_EXPORT(set_time, set time. e.g: set_time(23,59,59)) -#endif + +#if defined(RT_USING_FINSH) +static void date(uint8_t argc, char **argv) +{ + if (argc == 1) + { + time_t now; + /* output current time */ + now = time(RT_NULL); + rt_kprintf("%s", ctime(&now)); + } + else if (argc >= 7) + { + /* set time and date */ + uint16_t year; + uint8_t month, day, hour, min, sec; + year = atoi(argv[1]); + month = atoi(argv[2]); + day = atoi(argv[3]); + hour = atoi(argv[4]); + min = atoi(argv[5]); + sec = atoi(argv[6]); + if (year > 2099 || year < 2000) + { + rt_kprintf("year is out of range [2000-2099]\n"); + return; + } + if (month == 0 || month > 12) + { + rt_kprintf("month is out of range [1-12]\n"); + return; + } + if (day == 0 || day > 31) + { + rt_kprintf("day is out of range [1-31]\n"); + return; + } + if (hour > 23) + { + rt_kprintf("hour is out of range [0-23]\n"); + return; + } + if (min > 59) + { + rt_kprintf("minute is out of range [0-59]\n"); + return; + } + if (sec > 59) + { + rt_kprintf("second is out of range [0-59]\n"); + return; + } + set_time(hour, min, sec); + set_date(year, month, day); + } + else + { + rt_kprintf("please input: date [year month day hour min sec] or date\n"); + rt_kprintf("e.g: date 2018 01 01 23 59 59 or date\n"); + } +} +MSH_CMD_EXPORT(date, get date and time or set [year month day hour min sec]); +#endif /* defined(RT_USING_FINSH) */ + +#endif /* RT_USING_FINSH */ diff --git a/components/drivers/rtc/soft_rtc.c b/components/drivers/rtc/soft_rtc.c new file mode 100644 index 0000000000000000000000000000000000000000..6db2136ad4b11567f9da32b41da307b438abd116 --- /dev/null +++ b/components/drivers/rtc/soft_rtc.c @@ -0,0 +1,107 @@ +/* + * File : soft_rtc.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2018-01-30 armink the first version + */ + +#include +#include +#include + +#include + +#ifdef RT_USING_SOFT_RTC + +/* 2018-01-30 14:44:50 = RTC_TIME_INIT(2018, 1, 30, 14, 44, 50) */ +#define RTC_TIME_INIT(year, month, day, hour, minute, second) \ + {.tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day, .tm_hour = hour, .tm_min = minute, .tm_sec = second} + +#ifndef SOFT_RTC_TIME_DEFAULT +#define SOFT_RTC_TIME_DEFAULT RTC_TIME_INIT(2018, 1, 1, 0, 0 ,0) +#endif + +static struct rt_device soft_rtc_dev; +static rt_tick_t init_tick; +static time_t init_time; + +static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args) +{ + time_t *time; + struct tm time_temp; + + RT_ASSERT(dev != RT_NULL); + memset(&time_temp, 0, sizeof(struct tm)); + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + time = (time_t *) args; + *time = init_time + (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND; + break; + + case RT_DEVICE_CTRL_RTC_SET_TIME: + { + time = (time_t *) args; + init_time = *time - (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND; + break; + } + } + + return RT_EOK; +} + +int rt_soft_rtc_init(void) +{ + static rt_bool_t init_ok = RT_FALSE; + struct tm time_new = SOFT_RTC_TIME_DEFAULT; + + if (init_ok) + { + return 0; + } + /* make sure only one 'rtc' device */ + RT_ASSERT(!rt_device_find("rtc")); + + init_tick = rt_tick_get(); + init_time = mktime(&time_new); + + soft_rtc_dev.type = RT_Device_Class_RTC; + + /* register rtc device */ + soft_rtc_dev.init = RT_NULL; + soft_rtc_dev.open = RT_NULL; + soft_rtc_dev.close = RT_NULL; + soft_rtc_dev.read = RT_NULL; + soft_rtc_dev.write = RT_NULL; + soft_rtc_dev.control = soft_rtc_control; + + /* no private */ + soft_rtc_dev.user_data = RT_NULL; + + rt_device_register(&soft_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR); + + init_ok = RT_TRUE; + + return 0; +} +INIT_DEVICE_EXPORT(rt_soft_rtc_init); + +#endif /* RT_USING_SOFT_RTC */