1 /*
2 * Copyright (c) 2006-2024 RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2018-01-30 armink the first version
9 */
10
11 #include <sys/time.h>
12 #include <string.h>
13 #include <rtthread.h>
14 #include <rtdevice.h>
15
16 #ifdef RT_USING_KTIME
17 #include <ktime.h>
18 #endif
19
20 #ifdef RT_USING_SOFT_RTC
21
22 /* 2018-01-30 14:44:50 = RTC_TIME_INIT(2018, 1, 30, 14, 44, 50) */
23 #define RTC_TIME_INIT(year, month, day, hour, minute, second) \
24 {.tm_year = year - 1900, .tm_mon = month - 1, .tm_mday = day, .tm_hour = hour, .tm_min = minute, .tm_sec = second}
25
26 #ifndef SOFT_RTC_TIME_DEFAULT
27 #define SOFT_RTC_TIME_DEFAULT RTC_TIME_INIT(2018, 1, 1, 0, 0 ,0)
28 #endif
29
30 #ifndef RTC_AUTO_SYNC_FIRST_DELAY
31 #define RTC_AUTO_SYNC_FIRST_DELAY 25
32 #endif
33 #ifndef RTC_AUTO_SYNC_PERIOD
34 #define RTC_AUTO_SYNC_PERIOD 3600
35 #endif
36
37 static struct rt_work rtc_sync_work;
38
39 static struct rt_device soft_rtc_dev;
40 static rt_tick_t init_tick;
41 static time_t init_time;
42 static struct timeval init_tv = {0};
43
44 #ifdef RT_USING_KTIME
45 static struct timespec init_ts = {0};
46 #endif
47
48 #ifdef RT_USING_ALARM
49
50 static struct rt_rtc_wkalarm wkalarm;
51 static struct rt_timer alarm_time;
52
alarm_timeout(void * param)53 static void alarm_timeout(void *param)
54 {
55 rt_alarm_update(param, 1);
56 }
57
soft_rtc_alarm_update(struct rt_rtc_wkalarm * palarm)58 static void soft_rtc_alarm_update(struct rt_rtc_wkalarm *palarm)
59 {
60 rt_tick_t next_tick;
61
62 if (palarm->enable)
63 {
64 next_tick = RT_TICK_PER_SECOND;
65 rt_timer_control(&alarm_time, RT_TIMER_CTRL_SET_TIME, &next_tick);
66 rt_timer_start(&alarm_time);
67 }
68 else
69 {
70 rt_timer_stop(&alarm_time);
71 }
72 }
73
74 #endif
75
set_rtc_time(time_t t)76 static void set_rtc_time(time_t t)
77 {
78 init_time = t - (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND;
79 #ifdef RT_USING_ALARM
80 soft_rtc_alarm_update(&wkalarm);
81 #endif
82 }
83
soft_rtc_control(rt_device_t dev,int cmd,void * args)84 static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args)
85 {
86 time_t *t;
87 struct tm time_temp;
88
89 RT_ASSERT(dev != RT_NULL);
90 rt_memset(&time_temp, 0, sizeof(struct tm));
91
92 switch (cmd)
93 {
94 case RT_DEVICE_CTRL_RTC_GET_TIME:
95 {
96 t = (time_t *) args;
97 *t = init_time + (rt_tick_get() - init_tick) / RT_TICK_PER_SECOND;
98 break;
99 }
100 case RT_DEVICE_CTRL_RTC_SET_TIME:
101 {
102 t = (time_t *) args;
103 set_rtc_time(*t);
104 break;
105 }
106 #ifdef RT_USING_ALARM
107 case RT_DEVICE_CTRL_RTC_GET_ALARM:
108 *((struct rt_rtc_wkalarm *)args) = wkalarm;
109 break;
110 case RT_DEVICE_CTRL_RTC_SET_ALARM:
111 wkalarm = *((struct rt_rtc_wkalarm *)args);
112 soft_rtc_alarm_update(&wkalarm);
113 break;
114 #endif
115 #ifdef RT_USING_KTIME
116 case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
117 {
118 struct timeval _tv;
119 struct timeval *tv = (struct timeval *)args;
120 rt_ktime_boottime_get_us(&_tv);
121 tv->tv_sec = init_time + _tv.tv_sec;
122 tv->tv_usec = init_tv.tv_usec + _tv.tv_usec;
123 break;
124 }
125 case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
126 {
127 struct timeval _tv;
128 struct timeval *tv = (struct timeval *)args;
129 rt_ktime_boottime_get_us(&_tv);
130 set_rtc_time(tv->tv_sec);
131 init_tv.tv_usec = tv->tv_usec - _tv.tv_usec;
132 break;
133 }
134 case RT_DEVICE_CTRL_RTC_GET_TIMESPEC:
135 {
136 struct timespec _ts;
137 struct timespec *ts = (struct timespec *)args;
138 rt_ktime_boottime_get_ns(&_ts);
139 ts->tv_sec = init_time + _ts.tv_sec;
140 ts->tv_nsec = init_ts.tv_nsec + _ts.tv_nsec;
141 break;
142 }
143 case RT_DEVICE_CTRL_RTC_SET_TIMESPEC:
144 {
145 struct timespec _ts;
146 struct timespec *ts = (struct timespec *)args;
147 rt_ktime_boottime_get_ns(&_ts);
148 set_rtc_time(ts->tv_sec);
149 init_ts.tv_nsec = ts->tv_nsec - _ts.tv_nsec;
150 break;
151 }
152 case RT_DEVICE_CTRL_RTC_GET_TIMERES:
153 {
154 struct timespec *ts = (struct timespec *)args;
155 ts->tv_sec = 0;
156 ts->tv_nsec = (rt_ktime_cputimer_getres() / RT_KTIME_RESMUL);
157 break;
158 }
159 #else
160 case RT_DEVICE_CTRL_RTC_GET_TIMEVAL:
161 {
162 struct timeval *tv = (struct timeval *)args;
163 rt_tick_t tick = rt_tick_get() - init_tick;
164 tv->tv_sec = init_time + tick / RT_TICK_PER_SECOND;
165 tv->tv_usec = init_tv.tv_usec + ((tick % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND));
166 break;
167 }
168 case RT_DEVICE_CTRL_RTC_SET_TIMEVAL:
169 {
170 struct timeval *tv = (struct timeval *)args;
171 rt_tick_t tick = rt_tick_get() - init_tick;
172 set_rtc_time(tv->tv_sec);
173 init_tv.tv_usec = tv->tv_usec - ((tick % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND));
174 break;
175 }
176 case RT_DEVICE_CTRL_RTC_GET_TIMERES:
177 {
178 struct timespec *ts = (struct timespec *)args;
179 ts->tv_sec = 0;
180 ts->tv_nsec = (1000UL * 1000 * 1000) / RT_TICK_PER_SECOND;
181 break;
182 }
183 #endif /* RT_USING_KTIME */
184 default:
185 return -RT_EINVAL;
186 }
187
188 return RT_EOK;
189 }
190
191 #ifdef RT_USING_DEVICE_OPS
192 const static struct rt_device_ops soft_rtc_ops =
193 {
194 RT_NULL,
195 RT_NULL,
196 RT_NULL,
197 RT_NULL,
198 RT_NULL,
199 soft_rtc_control
200 };
201 #endif
202
rt_soft_rtc_init(void)203 static int rt_soft_rtc_init(void)
204 {
205 static rt_bool_t init_ok = RT_FALSE;
206 struct tm time_new = SOFT_RTC_TIME_DEFAULT;
207
208 if (init_ok)
209 {
210 return 0;
211 }
212 /* make sure only one 'rtc' device */
213 #if defined(RT_USING_SOFT_RTC) && defined(BSP_USING_ONCHIP_RTC)
214 #warning "Please note: Currently only one RTC device is allowed in the system, and the name is "rtc"."
215 #endif
216 RT_ASSERT(!rt_device_find("rtc"));
217
218 #ifdef RT_USING_ALARM
219 rt_timer_init(&alarm_time,
220 "alarm",
221 alarm_timeout,
222 &soft_rtc_dev,
223 0,
224 RT_TIMER_FLAG_SOFT_TIMER|RT_TIMER_FLAG_ONE_SHOT);
225 #endif
226
227 init_tick = rt_tick_get();
228 init_time = timegm(&time_new);
229
230 soft_rtc_dev.type = RT_Device_Class_RTC;
231
232 /* register rtc device */
233 #ifdef RT_USING_DEVICE_OPS
234 soft_rtc_dev.ops = &soft_rtc_ops;
235 #else
236 soft_rtc_dev.init = RT_NULL;
237 soft_rtc_dev.open = RT_NULL;
238 soft_rtc_dev.close = RT_NULL;
239 soft_rtc_dev.read = RT_NULL;
240 soft_rtc_dev.write = RT_NULL;
241 soft_rtc_dev.control = soft_rtc_control;
242 #endif
243
244 /* no private */
245 soft_rtc_dev.user_data = RT_NULL;
246
247 rt_device_register(&soft_rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR);
248
249 init_ok = RT_TRUE;
250
251 return 0;
252 }
253 INIT_DEVICE_EXPORT(rt_soft_rtc_init);
254
255 #ifdef RT_USING_SYSTEM_WORKQUEUE
256
rt_soft_rtc_sync(void)257 rt_err_t rt_soft_rtc_sync(void)
258 {
259 time_t time = 0;
260
261 rt_device_control(&soft_rtc_dev, RT_DEVICE_CTRL_RTC_GET_TIME, &time);
262 set_rtc_time(time);
263 return RT_EOK;
264 }
265
rtc_sync_work_func(struct rt_work * work,void * work_data)266 static void rtc_sync_work_func(struct rt_work *work, void *work_data)
267 {
268 rt_soft_rtc_sync();
269 rt_work_submit(work, rt_tick_from_millisecond(RTC_AUTO_SYNC_PERIOD * 1000));
270 }
271
rt_soft_rtc_set_source(const char * name)272 rt_err_t rt_soft_rtc_set_source(const char *name)
273 {
274 RT_ASSERT(name != RT_NULL);
275 RT_ASSERT(rt_device_find(name)); /* make sure source is exist*/
276
277 rt_work_init(&rtc_sync_work, rtc_sync_work_func, RT_NULL);
278 rt_work_submit(&rtc_sync_work, rt_tick_from_millisecond(RTC_AUTO_SYNC_FIRST_DELAY * 1000));
279
280 return RT_EOK;
281 }
282
283 #ifdef FINSH_USING_MSH
284 #include <finsh.h>
cmd_rtc_sync(int argc,char ** argv)285 static void cmd_rtc_sync(int argc, char **argv)
286 {
287 struct timeval tv = {0};
288 struct timezone tz = {0};
289 time_t now = (time_t)0;
290
291 rt_soft_rtc_sync();
292
293 gettimeofday(&tv, &tz);
294 now = tv.tv_sec;
295 /* output current time */
296 rt_kprintf("local time: %.*s", 25, ctime(&now));
297 rt_kprintf("timestamps: %ld\n", (long)tv.tv_sec);
298 }
299 MSH_CMD_EXPORT_ALIAS(cmd_rtc_sync, rtc_sync, Update time by soft rtc);
300 #endif
301
302 #endif /* RT_USING_SYSTEM_WORKQUEUE */
303
304 #endif /* RT_USING_SOFT_RTC */
305