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