1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2012-01-29 aozima first version.
9 * 2012-04-12 aozima optimization: find rtc device only first.
10 * 2012-04-16 aozima add scheduler lock for set_date and set_time.
11 * 2018-02-16 armink add auto sync time by NTP
12 * 2021-05-09 Meco Man remove NTP
13 * 2021-06-11 iysheng implement RTC framework V2.0
14 * 2021-07-30 Meco Man move rtc_core.c to rtc.c
15 */
16
17 #include <string.h>
18 #include <stdlib.h>
19 #include <rtthread.h>
20 #include <drivers/dev_rtc.h>
21
22 #ifdef RT_USING_RTC
23
24 static rt_device_t _rtc_device;
25 /*
26 * This function initializes rtc_core
27 */
rt_rtc_init(struct rt_device * dev)28 static rt_err_t rt_rtc_init(struct rt_device *dev)
29 {
30 rt_rtc_dev_t *rtc_core;
31
32 RT_ASSERT(dev != RT_NULL);
33 rtc_core = (rt_rtc_dev_t *)dev;
34 if (rtc_core->ops->init)
35 {
36 return (rtc_core->ops->init());
37 }
38
39 return -RT_ENOSYS;
40 }
41
rt_rtc_open(struct rt_device * dev,rt_uint16_t oflag)42 static rt_err_t rt_rtc_open(struct rt_device *dev, rt_uint16_t oflag)
43 {
44 return RT_EOK;
45 }
46
rt_rtc_close(struct rt_device * dev)47 static rt_err_t rt_rtc_close(struct rt_device *dev)
48 {
49 /* Add close member function in rt_rtc_ops when need,
50 * then call that function here.
51 * */
52 return RT_EOK;
53 }
54
rt_rtc_control(struct rt_device * dev,int cmd,void * args)55 static rt_err_t rt_rtc_control(struct rt_device *dev, int cmd, void *args)
56 {
57 #define TRY_DO_RTC_FUNC(rt_rtc_dev, func_name, args) \
58 rt_rtc_dev->ops->func_name ? rt_rtc_dev->ops->func_name(args) : -RT_EINVAL;
59
60 rt_rtc_dev_t *rtc_device;
61 rt_err_t ret = -RT_EINVAL;
62
63 RT_ASSERT(dev != RT_NULL);
64 rtc_device = (rt_rtc_dev_t *)dev;
65
66 switch (cmd)
67 {
68 case RT_DEVICE_CTRL_RTC_GET_TIME:
69 ret = TRY_DO_RTC_FUNC(rtc_device, get_secs, args);
70 break;
71 case RT_DEVICE_CTRL_RTC_SET_TIME:
72 ret = TRY_DO_RTC_FUNC(rtc_device, set_secs, args);
73 break;
74 case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
75 ret = TRY_DO_RTC_FUNC(rtc_device, get_timeval, args);
76 break;
77 case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
78 ret = TRY_DO_RTC_FUNC(rtc_device, set_timeval, args);
79 break;
80 case RT_DEVICE_CTRL_RTC_GET_ALARM:
81 ret = TRY_DO_RTC_FUNC(rtc_device, get_alarm, args);
82 break;
83 case RT_DEVICE_CTRL_RTC_SET_ALARM:
84 ret = TRY_DO_RTC_FUNC(rtc_device, set_alarm, args);
85 break;
86 default:
87 break;
88 }
89
90 return ret;
91
92 #undef TRY_DO_RTC_FUNC
93 }
94
95 #ifdef RT_USING_DEVICE_OPS
96 const static struct rt_device_ops rtc_core_ops =
97 {
98 rt_rtc_init,
99 rt_rtc_open,
100 rt_rtc_close,
101 RT_NULL,
102 RT_NULL,
103 rt_rtc_control,
104 };
105 #endif /* RT_USING_DEVICE_OPS */
106
rt_hw_rtc_register(rt_rtc_dev_t * rtc,const char * name,rt_uint32_t flag,void * data)107 rt_err_t rt_hw_rtc_register(rt_rtc_dev_t *rtc,
108 const char *name,
109 rt_uint32_t flag,
110 void *data)
111 {
112 struct rt_device *device;
113 RT_ASSERT(rtc != RT_NULL);
114
115 device = &(rtc->parent);
116
117 device->type = RT_Device_Class_RTC;
118 device->rx_indicate = RT_NULL;
119 device->tx_complete = RT_NULL;
120
121 #ifdef RT_USING_DEVICE_OPS
122 device->ops = &rtc_core_ops;
123 #else
124 device->init = rt_rtc_init;
125 device->open = rt_rtc_open;
126 device->close = rt_rtc_close;
127 device->read = RT_NULL;
128 device->write = RT_NULL;
129 device->control = rt_rtc_control;
130 #endif /* RT_USING_DEVICE_OPS */
131 device->user_data = data;
132
133 /* register a character device */
134 return rt_device_register(device, name, flag);
135 }
136
137 /**
138 * Set system date(time not modify, local timezone).
139 *
140 * @param rt_uint32_t year e.g: 2012.
141 * @param rt_uint32_t month e.g: 12 (1~12).
142 * @param rt_uint32_t day e.g: 31.
143 *
144 * @return rt_err_t if set success, return RT_EOK.
145 */
set_date(rt_uint32_t year,rt_uint32_t month,rt_uint32_t day)146 rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day)
147 {
148 time_t now, old_timestamp = 0;
149 struct tm tm_new = {0};
150 rt_err_t ret = -RT_ERROR;
151
152 if (_rtc_device == RT_NULL)
153 {
154 _rtc_device = rt_device_find("rtc");
155 if (_rtc_device == RT_NULL)
156 {
157 return -RT_ERROR;
158 }
159 }
160
161 /* get current time */
162 ret = rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_GET_TIME, &old_timestamp);
163 if (ret != RT_EOK)
164 {
165 return ret;
166 }
167
168 /* converts calendar time into local time. */
169 localtime_r(&old_timestamp, &tm_new);
170
171 /* update date. */
172 tm_new.tm_year = year - 1900;
173 tm_new.tm_mon = month - 1; /* tm_mon: 0~11 */
174 tm_new.tm_mday = day;
175
176 /* converts the local time into the calendar time. */
177 now = mktime(&tm_new);
178
179 /* update to RTC device. */
180 ret = rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_SET_TIME, &now);
181 return ret;
182 }
183
184 /**
185 * Set system time(date not modify, local timezone).
186 *
187 * @param rt_uint32_t hour e.g: 0~23.
188 * @param rt_uint32_t minute e.g: 0~59.
189 * @param rt_uint32_t second e.g: 0~59.
190 *
191 * @return rt_err_t if set success, return RT_EOK.
192 */
set_time(rt_uint32_t hour,rt_uint32_t minute,rt_uint32_t second)193 rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second)
194 {
195 time_t now, old_timestamp = 0;
196 struct tm tm_new = {0};
197 rt_err_t ret = -RT_ERROR;
198
199 if (_rtc_device == RT_NULL)
200 {
201 _rtc_device = rt_device_find("rtc");
202 if (_rtc_device == RT_NULL)
203 {
204 return -RT_ERROR;
205 }
206 }
207
208 /* get current time */
209 ret = rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_GET_TIME, &old_timestamp);
210 if (ret != RT_EOK)
211 {
212 return ret;
213 }
214
215 /* converts calendar time into local time. */
216 localtime_r(&old_timestamp, &tm_new);
217
218 /* update time. */
219 tm_new.tm_hour = hour;
220 tm_new.tm_min = minute;
221 tm_new.tm_sec = second;
222
223 /* converts the local time into the calendar time. */
224 now = mktime(&tm_new);
225
226 /* update to RTC device. */
227 ret = rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_SET_TIME, &now);
228 return ret;
229 }
230
231 /**
232 * Set timestamp(UTC).
233 *
234 * @param time_t timestamp
235 *
236 * @return rt_err_t if set success, return RT_EOK.
237 */
set_timestamp(time_t timestamp)238 rt_err_t set_timestamp(time_t timestamp)
239 {
240 if (_rtc_device == RT_NULL)
241 {
242 _rtc_device = rt_device_find("rtc");
243 if (_rtc_device == RT_NULL)
244 {
245 return -RT_ERROR;
246 }
247 }
248
249 /* update to RTC device. */
250 return rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_SET_TIME, ×tamp);
251 }
252
253 /**
254 * Get timestamp(UTC).
255 *
256 * @param time_t* timestamp
257 *
258 * @return rt_err_t if set success, return RT_EOK.
259 */
get_timestamp(time_t * timestamp)260 rt_err_t get_timestamp(time_t *timestamp)
261 {
262 if (_rtc_device == RT_NULL)
263 {
264 _rtc_device = rt_device_find("rtc");
265 if (_rtc_device == RT_NULL)
266 {
267 return -RT_ERROR;
268 }
269 }
270
271 /* Get timestamp from RTC device. */
272 return rt_device_control(_rtc_device, RT_DEVICE_CTRL_RTC_GET_TIME, timestamp);
273 }
274
275 #ifdef RT_USING_FINSH
276 #include <finsh.h>
277 /**
278 * get date and time or set (local timezone) [year month day hour min sec]
279 */
date(int argc,char ** argv)280 static void date(int argc, char **argv)
281 {
282 time_t now = (time_t)0;
283
284 if (argc == 1)
285 {
286 struct timeval tv = { 0 };
287 int32_t tz_offset_sec = 0;
288 uint32_t abs_tz_offset_sec = 0U;
289
290 #if defined(RT_LIBC_USING_LIGHT_TZ_DST)
291 tz_offset_sec = rt_tz_get();
292 #endif /* RT_LIBC_USING_LIGHT_TZ_DST */
293
294 gettimeofday(&tv, RT_NULL);
295 now = tv.tv_sec;
296
297 abs_tz_offset_sec = tz_offset_sec > 0 ? tz_offset_sec : -tz_offset_sec;
298 /* output current time */
299 rt_kprintf("local time: %.*s", 25U, ctime(&now));
300 rt_kprintf("timestamps: %ld\n", (long)tv.tv_sec);
301 rt_kprintf("timezone: UTC%c%02d:%02d:%02d\n",
302 tz_offset_sec > 0 ? '+' : '-', abs_tz_offset_sec / 3600U, abs_tz_offset_sec % 3600U / 60U, abs_tz_offset_sec % 3600U % 60U);
303 }
304 else if (argc >= 7)
305 {
306 /* set time and date */
307 struct tm tm_new = {0};
308 time_t old = (time_t)0;
309 rt_err_t err;
310
311 tm_new.tm_year = atoi(argv[1]) - 1900;
312 tm_new.tm_mon = atoi(argv[2]) - 1; /* .tm_min's range is [0-11] */
313 tm_new.tm_mday = atoi(argv[3]);
314 tm_new.tm_hour = atoi(argv[4]);
315 tm_new.tm_min = atoi(argv[5]);
316 tm_new.tm_sec = atoi(argv[6]);
317 if (tm_new.tm_year <= 0)
318 {
319 rt_kprintf("year is out of range [1900-]\n");
320 return;
321 }
322 if (tm_new.tm_mon > 11) /* .tm_min's range is [0-11] */
323 {
324 rt_kprintf("month is out of range [1-12]\n");
325 return;
326 }
327 if (tm_new.tm_mday == 0 || tm_new.tm_mday > 31)
328 {
329 rt_kprintf("day is out of range [1-31]\n");
330 return;
331 }
332 if (tm_new.tm_hour > 23)
333 {
334 rt_kprintf("hour is out of range [0-23]\n");
335 return;
336 }
337 if (tm_new.tm_min > 59)
338 {
339 rt_kprintf("minute is out of range [0-59]\n");
340 return;
341 }
342 if (tm_new.tm_sec > 60)
343 {
344 rt_kprintf("second is out of range [0-60]\n");
345 return;
346 }
347 /* save old timestamp */
348 err = get_timestamp(&old);
349 if (err != RT_EOK)
350 {
351 rt_kprintf("Get current timestamp failed. %d\n", err);
352 return;
353 }
354 /* converts the local time into the calendar time. */
355 now = mktime(&tm_new);
356 err = set_timestamp(now);
357 if (err != RT_EOK)
358 {
359 rt_kprintf("set date failed. %d\n", err);
360 return;
361 }
362 get_timestamp(&now); /* get new timestamp */
363 rt_kprintf("old: %.*s", 25, ctime(&old));
364 rt_kprintf("now: %.*s", 25, ctime(&now));
365 }
366 else
367 {
368 rt_kprintf("please input: date [year month day hour min sec] or date\n");
369 rt_kprintf("e.g: date 2018 01 01 23 59 59 or date\n");
370 }
371 }
372 MSH_CMD_EXPORT(date, get date and time or set (local timezone) [year month day hour min sec])
373 #endif /* RT_USING_FINSH */
374
375 #endif /* RT_USING_RTC */
376