66 * Change Logs:
77 * Date Author Notes
88 * 2018-01-30 armink the first version
9+ * 2025-10-30 dongly fix timespec/timeval error, optimize soft_rtc implementation
910 */
1011
1112#include <sys/time.h>
12- #include <string.h>
1313#include <rtthread.h>
1414#include <rtdevice.h>
1515
2020#ifdef RT_USING_SOFT_RTC
2121
2222/* 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}
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 }
2525
2626#ifndef SOFT_RTC_TIME_DEFAULT
27- #define SOFT_RTC_TIME_DEFAULT RTC_TIME_INIT(2018, 1, 1, 0, 0 , 0)
27+ #define SOFT_RTC_TIME_DEFAULT RTC_TIME_INIT(2018, 1, 1, 0, 0, 0)
2828#endif
2929
3030#ifndef RTC_AUTO_SYNC_FIRST_DELAY
3737static struct rt_work rtc_sync_work ;
3838
3939static 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-
4440#ifdef RT_USING_KTIME
45- static struct timespec init_ts = {0 };
41+ static struct timespec init_ktime_ts = { 0 };
42+ #else
43+ static rt_tick_t init_tick ;
4644#endif
45+ static struct timespec init_ts = { 0 };
4746
4847#ifdef RT_USING_ALARM
4948
5049static struct rt_rtc_wkalarm wkalarm ;
5150static struct rt_timer alarm_time ;
5251
52+ static RT_DEFINE_SPINLOCK (_spinlock );
53+
5354static void alarm_timeout (void * param )
5455{
5556 rt_alarm_update (param , 1 );
@@ -73,17 +74,61 @@ static void soft_rtc_alarm_update(struct rt_rtc_wkalarm *palarm)
7374
7475#endif
7576
76- static void set_rtc_time (time_t t )
77+ static void set_rtc_time (struct timespec * ts )
7778{
78- init_time = t - (rt_tick_get () - init_tick ) / RT_TICK_PER_SECOND ;
79+ rt_base_t level = rt_spin_lock_irqsave (& _spinlock );
80+ init_ts .tv_sec = ts -> tv_sec ;
81+ init_ts .tv_nsec = ts -> tv_nsec ;
82+ #ifdef RT_USING_KTIME
83+ rt_ktime_boottime_get_ns (& init_ktime_ts );
84+ #else
85+ init_tick = rt_tick_get ();
86+ #endif
87+ rt_spin_unlock_irqrestore (& _spinlock , level );
7988#ifdef RT_USING_ALARM
8089 soft_rtc_alarm_update (& wkalarm );
8190#endif
8291}
8392
93+ static void get_rtc_time (struct timespec * ts )
94+ {
95+ rt_base_t level ;
96+
97+ if (!ts )
98+ return ;
99+
100+ level = rt_spin_lock_irqsave (& _spinlock );
101+ #ifdef RT_USING_KTIME
102+ struct timespec current_ts ;
103+ rt_ktime_boottime_get_ns (& current_ts );
104+
105+ /* Calculate time difference */
106+ ts -> tv_sec = init_ktime_ts .tv_sec + (current_ts .tv_sec - init_ktime_ts .tv_sec );
107+ ts -> tv_nsec = init_ktime_ts .tv_nsec + (current_ts .tv_nsec - init_ktime_ts .tv_nsec );
108+ #else
109+ rt_tick_t tick = rt_tick_get_delta (init_tick );
110+ ts -> tv_sec = init_ts .tv_sec + tick / RT_TICK_PER_SECOND ;
111+ ts -> tv_nsec = init_ts .tv_nsec + ((tick % RT_TICK_PER_SECOND ) * (1000000000UL / RT_TICK_PER_SECOND ));
112+ #endif
113+ /* Handle nanosecond overflow */
114+ if (ts -> tv_nsec >= 1000000000L )
115+ {
116+ ts -> tv_sec ++ ;
117+ ts -> tv_nsec -= 1000000000L ;
118+ }
119+ /* Handle nanosecond underflow */
120+ if (ts -> tv_nsec < 0 )
121+ {
122+ ts -> tv_sec -- ;
123+ ts -> tv_nsec += 1000000000L ;
124+ }
125+ rt_spin_unlock_irqrestore (& _spinlock , level );
126+ }
127+
84128static rt_err_t soft_rtc_control (rt_device_t dev , int cmd , void * args )
85129{
86130 time_t * t ;
131+ rt_base_t level ;
87132 struct tm time_temp ;
88133
89134 RT_ASSERT (dev != RT_NULL );
@@ -93,94 +138,87 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args)
93138 {
94139 case RT_DEVICE_CTRL_RTC_GET_TIME :
95140 {
96- t = (time_t * ) args ;
97- * t = init_time + (rt_tick_get () - init_tick ) / RT_TICK_PER_SECOND ;
141+ if (!args )
142+ return - RT_EINVAL ;
143+ t = (time_t * )args ;
144+ struct timespec ts ;
145+ get_rtc_time (& ts );
146+ * t = ts .tv_sec ;
98147 break ;
99148 }
100149 case RT_DEVICE_CTRL_RTC_SET_TIME :
101150 {
102- t = (time_t * ) args ;
103- set_rtc_time (* t );
151+ if (!args )
152+ return - RT_EINVAL ;
153+ t = (time_t * )args ;
154+ struct timespec ts = { * t , 0 };
155+ set_rtc_time (& ts );
104156 break ;
105157 }
106158#ifdef RT_USING_ALARM
107159 case RT_DEVICE_CTRL_RTC_GET_ALARM :
160+ if (!args )
161+ return - RT_EINVAL ;
108162 * ((struct rt_rtc_wkalarm * )args ) = wkalarm ;
109163 break ;
110164 case RT_DEVICE_CTRL_RTC_SET_ALARM :
165+ if (!args )
166+ return - RT_EINVAL ;
111167 wkalarm = * ((struct rt_rtc_wkalarm * )args );
112168 soft_rtc_alarm_update (& wkalarm );
113169 break ;
114170#endif
115- #ifdef RT_USING_KTIME
116171 case RT_DEVICE_CTRL_RTC_GET_TIMEVAL :
117172 {
118- struct timeval _tv ;
173+ if (!args )
174+ return - RT_EINVAL ;
119175 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 ;
176+ struct timespec ts ;
177+ get_rtc_time (& ts );
178+ tv -> tv_sec = ts .tv_sec ;
179+ tv -> tv_usec = ts .tv_nsec / 1000 ;
123180 break ;
124181 }
125182 case RT_DEVICE_CTRL_RTC_SET_TIMEVAL :
126183 {
127- struct timeval _tv ;
184+ if (!args )
185+ return - RT_EINVAL ;
128186 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 ;
187+ struct timespec ts = { tv -> tv_sec , tv -> tv_usec * 1000 };
188+ set_rtc_time (& ts );
132189 break ;
133190 }
134191 case RT_DEVICE_CTRL_RTC_GET_TIMESPEC :
135192 {
136- struct timespec _ts ;
193+ if (!args )
194+ return - RT_EINVAL ;
137195 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 ;
196+ get_rtc_time (ts );
141197 break ;
142198 }
143199 case RT_DEVICE_CTRL_RTC_SET_TIMESPEC :
144200 {
145- struct timespec _ts ;
201+ if (!args )
202+ return - RT_EINVAL ;
146203 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 ;
204+ set_rtc_time (ts );
150205 break ;
151206 }
152207 case RT_DEVICE_CTRL_RTC_GET_TIMERES :
153208 {
209+ if (!args )
210+ return - RT_EINVAL ;
154211 struct timespec * ts = (struct timespec * )args ;
155- ts -> tv_sec = 0 ;
212+ level = rt_spin_lock_irqsave (& _spinlock );
213+ ts -> tv_sec = 0 ;
214+ #ifdef RT_USING_KTIME
156215 ts -> tv_nsec = (rt_ktime_cputimer_getres () / RT_KTIME_RESMUL );
157- break ;
158- }
159216#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 ;
180217 ts -> tv_nsec = (1000UL * 1000 * 1000 ) / RT_TICK_PER_SECOND ;
218+ #endif
219+ rt_spin_unlock_irqrestore (& _spinlock , level );
181220 break ;
182221 }
183- #endif /* RT_USING_KTIME */
184222 default :
185223 return - RT_EINVAL ;
186224 }
@@ -189,8 +227,7 @@ static rt_err_t soft_rtc_control(rt_device_t dev, int cmd, void *args)
189227}
190228
191229#ifdef RT_USING_DEVICE_OPS
192- const static struct rt_device_ops soft_rtc_ops =
193- {
230+ const static struct rt_device_ops soft_rtc_ops = {
194231 RT_NULL ,
195232 RT_NULL ,
196233 RT_NULL ,
@@ -209,7 +246,7 @@ static int rt_soft_rtc_init(void)
209246 {
210247 return 0 ;
211248 }
212- /* make sure only one 'rtc' device */
249+ /* Make sure only one 'rtc' device */
213250#if defined(RT_USING_SOFT_RTC ) && defined(BSP_USING_ONCHIP_RTC )
214251#warning "Please note: Currently only one RTC device is allowed in the system, and the name is "rtc"."
215252#endif
@@ -221,27 +258,31 @@ static int rt_soft_rtc_init(void)
221258 alarm_timeout ,
222259 & soft_rtc_dev ,
223260 0 ,
224- RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_ONE_SHOT );
261+ RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_ONE_SHOT );
225262#endif
226263
264+ #ifdef RT_USING_KTIME
265+ rt_ktime_boottime_get_ns (& init_ktime_ts );
266+ #else
227267 init_tick = rt_tick_get ();
228- init_time = timegm (& time_new );
268+ #endif
269+ init_ts .tv_sec = timegm (& time_new );
229270
230- soft_rtc_dev .type = RT_Device_Class_RTC ;
271+ soft_rtc_dev .type = RT_Device_Class_RTC ;
231272
232- /* register rtc device */
273+ /* Register RTC device */
233274#ifdef RT_USING_DEVICE_OPS
234- soft_rtc_dev .ops = & soft_rtc_ops ;
275+ soft_rtc_dev .ops = & soft_rtc_ops ;
235276#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 ;
277+ soft_rtc_dev .init = RT_NULL ;
278+ soft_rtc_dev .open = RT_NULL ;
279+ soft_rtc_dev .close = RT_NULL ;
280+ soft_rtc_dev .read = RT_NULL ;
281+ soft_rtc_dev .write = RT_NULL ;
241282 soft_rtc_dev .control = soft_rtc_control ;
242283#endif
243284
244- /* no private */
285+ /* No private data */
245286 soft_rtc_dev .user_data = RT_NULL ;
246287
247288 rt_device_register (& soft_rtc_dev , "rtc" , RT_DEVICE_FLAG_RDWR );
@@ -259,7 +300,8 @@ rt_err_t rt_soft_rtc_sync(void)
259300 time_t time = 0 ;
260301
261302 rt_device_control (& soft_rtc_dev , RT_DEVICE_CTRL_RTC_GET_TIME , & time );
262- set_rtc_time (time );
303+ struct timespec ts = { time , 0 };
304+ set_rtc_time (& ts );
263305 return RT_EOK ;
264306}
265307
@@ -284,15 +326,15 @@ rt_err_t rt_soft_rtc_set_source(const char *name)
284326#include <finsh.h>
285327static void cmd_rtc_sync (int argc , char * * argv )
286328{
287- struct timeval tv = {0 };
288- struct timezone tz = {0 };
289- time_t now = (time_t )0 ;
329+ struct timeval tv = { 0 };
330+ struct timezone tz = { 0 };
331+ time_t now = (time_t )0 ;
290332
291333 rt_soft_rtc_sync ();
292334
293335 gettimeofday (& tv , & tz );
294336 now = tv .tv_sec ;
295- /* output current time */
337+ /* Output current time */
296338 rt_kprintf ("local time: %.*s" , 25 , ctime (& now ));
297339 rt_kprintf ("timestamps: %ld\n" , (long )tv .tv_sec );
298340}
0 commit comments