13
13
* See the License for the specific language governing permissions and
14
14
* limitations under the License.
15
15
*/
16
-
16
+
17
17
#include "rtc_api.h"
18
18
19
19
#if DEVICE_RTC
24
24
#include "nu_miscutil.h"
25
25
#include "mbed_mktime.h"
26
26
27
- #define YEAR0 1900
28
- //#define EPOCH_YR 1970
27
+ /* Micro seconds per second */
28
+ #define NU_US_PER_SEC 1000000
29
+ /* Timer clock per second
30
+ *
31
+ * NOTE: This dependents on real hardware.
32
+ */
33
+ #define NU_RTCCLK_PER_SEC ((CLK->CLKSEL3 & CLK_CLKSEL3_SC0SEL_Msk) ? __LIRC : __LXT)
34
+
35
+ /* Strategy for implementation of RTC HAL
36
+ *
37
+ * H/W RTC just supports year range 2000~2099, which cannot fully cover POSIX time (starting since 2970)
38
+ * and date time of struct TM (starting since 1900).
39
+ *
40
+ * To conquer the difficulty, we don't use H/W RTC to keep real date time. Instead, we use it to keep
41
+ * elapsed time in seconds since one reference time point. The strategy would be:
42
+ *
43
+ * 1. Choose DATETIME_HWRTC_ORIGIN (00:00:00 UTC, Saturday, 1 January 2000) as reference time point of H/W RTC.
44
+ * 2. t_hwrtc_origin = DATETIME_HWRTC_ORIGIN in POSIX time
45
+ * 3. t_hwrtc_elapsed = t_hwrtc_origin + elapsed time since t_hwrtc_origin
46
+ * 4. t_write = POSIX time set by rtc_write().
47
+ * 5. t_present = rtc_read() = t_write + (t_hwrtc_elapsed - t_hwrtc_origin)
48
+ *
49
+ * 1900
50
+ * |---------------------------------------------------------------------------------|
51
+ * 1970 t_write t_present
52
+ * |---------|-------|-----------------|---------------------------------------------|
53
+ *
54
+ * 2000
55
+ * |-----------------|---------------------------------------------------------------|
56
+ * t_hwrtc_origin t_hwrtc_elapsed
57
+ *
58
+ */
59
+ /* Start year of struct TM*/
60
+ #define NU_TM_YEAR0 1900
61
+ /* Start year of POSIX time (set_time()/time()) */
62
+ #define NU_POSIX_YEAR0 1970
63
+ /* Start year of H/W RTC */
64
+ #define NU_HWRTC_YEAR0 2000
65
+
66
+ /* RTC H/W origin time: 00:00:00 UTC, Saturday, 1 January 2000 */
67
+ static const S_RTC_TIME_DATA_T DATETIME_HWRTC_ORIGIN = {
68
+ 2000 , /* Year value, range between 2000 ~ 2099 */
69
+ 1 , /* Month value, range between 1 ~ 12 */
70
+ 1 , /* Day value, range between 1 ~ 31 */
71
+ RTC_SATURDAY , /* Day of the week */
72
+ 0 , /* Hour value, range between 0 ~ 23 */
73
+ 0 , /* Minute value, range between 0 ~ 59 */
74
+ 0 , /* Second value, range between 0 ~ 59 */
75
+ RTC_CLOCK_24 , /* 12-Hour (RTC_CLOCK_12) / 24-Hour (RTC_CLOCK_24) */
76
+ 0 /* RTC_AM / RTC_PM (used only for 12-Hour) */
77
+ };
78
+ /* t_hwrtc_origin initialized or not? */
79
+ static bool t_hwrtc_origin_inited = 0 ;
80
+ /* POSIX time of DATETIME_HWRTC_ORIGIN (since 00:00:00 UTC, Thursday, 1 January 1970) */
81
+ static time_t t_hwrtc_origin = 0 ;
82
+ /* POSIX time set by rtc_write() */
83
+ static time_t t_write = 0 ;
84
+ /* Convert date time from H/W RTC to struct TM */
85
+ static void rtc_convert_datetime_hwrtc_to_tm (struct tm * datetime_tm , const S_RTC_TIME_DATA_T * datetime_hwrtc );
29
86
30
87
static const struct nu_modinit_s rtc_modinit = {RTC_0 , RTC_MODULE , 0 , 0 , 0 , RTC_IRQn , NULL };
31
88
@@ -34,8 +91,11 @@ void rtc_init(void)
34
91
if (rtc_isenabled ()) {
35
92
return ;
36
93
}
37
-
94
+
38
95
RTC_Open (NULL );
96
+
97
+ /* POSIX time origin (00:00:00 UTC, Thursday, 1 January 1970) */
98
+ rtc_write (0 );
39
99
}
40
100
41
101
void rtc_free (void )
@@ -50,56 +110,47 @@ int rtc_isenabled(void)
50
110
// Enable IP clock
51
111
CLK_EnableModuleClock (rtc_modinit .clkidx );
52
112
}
53
-
113
+
54
114
// NOTE: Check RTC Init Active flag to support crossing reset cycle.
55
115
return !! (RTC -> INIT & RTC_INIT_ACTIVE_Msk );
56
116
}
57
117
58
- /*
59
- struct tm
60
- tm_sec seconds after the minute 0-61
61
- tm_min minutes after the hour 0-59
62
- tm_hour hours since midnight 0-23
63
- tm_mday day of the month 1-31
64
- tm_mon months since January 0-11
65
- tm_year years since 1900
66
- tm_wday days since Sunday 0-6
67
- tm_yday days since January 1 0-365
68
- tm_isdst Daylight Saving Time flag
69
- */
70
-
71
118
time_t rtc_read (void )
72
119
{
73
- // NOTE: After boot, RTC time registers are not synced immediately, about 1 sec latency.
74
- // RTC time got (through RTC_GetDateAndTime()) in this sec would be last-synced and incorrect.
120
+ /* NOTE: After boot, RTC time registers are not synced immediately, about 1 sec latency.
121
+ * RTC time got (through RTC_GetDateAndTime()) in this sec would be last-synced and incorrect.
122
+ */
75
123
if (! rtc_isenabled ()) {
76
124
rtc_init ();
77
125
}
78
-
79
- S_RTC_TIME_DATA_T rtc_datetime ;
80
- RTC_GetDateAndTime (& rtc_datetime );
81
-
82
- struct tm timeinfo ;
83
-
84
- // Convert struct tm to S_RTC_TIME_DATA_T
85
- timeinfo .tm_year = rtc_datetime .u32Year - YEAR0 ;
86
- timeinfo .tm_mon = rtc_datetime .u32Month - 1 ;
87
- timeinfo .tm_mday = rtc_datetime .u32Day ;
88
- timeinfo .tm_wday = rtc_datetime .u32DayOfWeek ;
89
- timeinfo .tm_hour = rtc_datetime .u32Hour ;
90
- if (rtc_datetime .u32TimeScale == RTC_CLOCK_12 && rtc_datetime .u32AmPm == RTC_PM ) {
91
- timeinfo .tm_hour += 12 ;
126
+
127
+ /* Used for intermediary between date time of H/W RTC and POSIX time */
128
+ struct tm datetime_tm ;
129
+
130
+ if (! t_hwrtc_origin_inited ) {
131
+ t_hwrtc_origin_inited = 1 ;
132
+
133
+ /* Convert date time from H/W RTC to struct TM */
134
+ rtc_convert_datetime_hwrtc_to_tm (& datetime_tm , & DATETIME_HWRTC_ORIGIN );
135
+ /* Convert date time of struct TM to POSIX time */
136
+ if (! _rtc_maketime (& datetime_tm , & t_hwrtc_origin , RTC_FULL_LEAP_YEAR_SUPPORT )) {
137
+ return 0 ;
138
+ }
92
139
}
93
- timeinfo .tm_min = rtc_datetime .u32Minute ;
94
- timeinfo .tm_sec = rtc_datetime .u32Second ;
95
140
96
- // Convert to timestamp
97
- time_t t ;
98
- if (_rtc_maketime (& timeinfo , & t , RTC_FULL_LEAP_YEAR_SUPPORT ) == false) {
141
+ S_RTC_TIME_DATA_T hwrtc_datetime_2K_present ;
142
+ RTC_GetDateAndTime (& hwrtc_datetime_2K_present );
143
+ /* Convert date time from H/W RTC to struct TM */
144
+ rtc_convert_datetime_hwrtc_to_tm (& datetime_tm , & hwrtc_datetime_2K_present );
145
+ /* Convert date time of struct TM to POSIX time */
146
+ time_t t_hwrtc_elapsed ;
147
+ if (! _rtc_maketime (& datetime_tm , & t_hwrtc_elapsed , RTC_FULL_LEAP_YEAR_SUPPORT )) {
99
148
return 0 ;
100
149
}
101
150
102
- return t ;
151
+ /* Present time in POSIX time */
152
+ time_t t_present = t_write + (t_hwrtc_elapsed - t_hwrtc_origin );
153
+ return t_present ;
103
154
}
104
155
105
156
void rtc_write (time_t t )
@@ -108,28 +159,37 @@ void rtc_write(time_t t)
108
159
rtc_init ();
109
160
}
110
161
111
- // Convert timestamp to struct tm
112
- struct tm timeinfo ;
113
- if (_rtc_localtime (t , & timeinfo , RTC_FULL_LEAP_YEAR_SUPPORT ) == false) {
114
- return ;
115
- }
162
+ t_write = t ;
116
163
117
- S_RTC_TIME_DATA_T rtc_datetime ;
118
-
119
- // Convert S_RTC_TIME_DATA_T to struct tm
120
- rtc_datetime .u32Year = timeinfo .tm_year + YEAR0 ;
121
- rtc_datetime .u32Month = timeinfo .tm_mon + 1 ;
122
- rtc_datetime .u32Day = timeinfo .tm_mday ;
123
- rtc_datetime .u32DayOfWeek = timeinfo .tm_wday ;
124
- rtc_datetime .u32Hour = timeinfo .tm_hour ;
125
- rtc_datetime .u32Minute = timeinfo .tm_min ;
126
- rtc_datetime .u32Second = timeinfo .tm_sec ;
127
- rtc_datetime .u32TimeScale = RTC_CLOCK_24 ;
128
-
129
- // NOTE: Timing issue with write to RTC registers. This delay is empirical, not rational.
130
- RTC_SetDateAndTime (& rtc_datetime );
131
- //nu_nop(6000);
132
- wait_us (100 );
164
+ RTC_SetDateAndTime ((S_RTC_TIME_DATA_T * ) & DATETIME_HWRTC_ORIGIN );
165
+ /* NOTE: When engine is clocked by low power clock source (LXT/LIRC), we need to wait for 3 engine clocks. */
166
+ wait_us ((NU_US_PER_SEC / NU_RTCCLK_PER_SEC ) * 3 );
167
+ }
168
+
169
+ /*
170
+ struct tm
171
+ tm_sec seconds after the minute 0-61
172
+ tm_min minutes after the hour 0-59
173
+ tm_hour hours since midnight 0-23
174
+ tm_mday day of the month 1-31
175
+ tm_mon months since January 0-11
176
+ tm_year years since 1900
177
+ tm_wday days since Sunday 0-6
178
+ tm_yday days since January 1 0-365
179
+ tm_isdst Daylight Saving Time flag
180
+ */
181
+ static void rtc_convert_datetime_hwrtc_to_tm (struct tm * datetime_tm , const S_RTC_TIME_DATA_T * datetime_hwrtc )
182
+ {
183
+ datetime_tm -> tm_year = datetime_hwrtc -> u32Year - NU_TM_YEAR0 ;
184
+ datetime_tm -> tm_mon = datetime_hwrtc -> u32Month - 1 ;
185
+ datetime_tm -> tm_mday = datetime_hwrtc -> u32Day ;
186
+ datetime_tm -> tm_wday = datetime_hwrtc -> u32DayOfWeek ;
187
+ datetime_tm -> tm_hour = datetime_hwrtc -> u32Hour ;
188
+ if (datetime_hwrtc -> u32TimeScale == RTC_CLOCK_12 && datetime_hwrtc -> u32AmPm == RTC_PM ) {
189
+ datetime_tm -> tm_hour += 12 ;
190
+ }
191
+ datetime_tm -> tm_min = datetime_hwrtc -> u32Minute ;
192
+ datetime_tm -> tm_sec = datetime_hwrtc -> u32Second ;
133
193
}
134
194
135
195
#endif
0 commit comments