ChibiOS  21.6.0
hal_rtc.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 /*
17  Concepts and parts of this file have been contributed by Uladzimir Pylinsky
18  aka barthess.
19  */
20 
21 /**
22  * @file hal_rtc.c
23  * @brief RTC Driver code.
24  *
25  * @addtogroup RTC
26  * @{
27  */
28 
29 #include "hal.h"
30 
31 #if (HAL_USE_RTC == TRUE) || defined(__DOXYGEN__)
32 
33 /*===========================================================================*/
34 /* Driver local definitions. */
35 /*===========================================================================*/
36 
37 /*===========================================================================*/
38 /* Driver exported variables. */
39 /*===========================================================================*/
40 
41 /*===========================================================================*/
42 /* Driver local variables and types. */
43 /*===========================================================================*/
44 
45 /*
46  * Lookup table with months' length
47  */
48 static const uint8_t month_len[12] = {
49  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
50 };
51 
52 static const uint16_t accu_month_len[12] = {
53  0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
54 };
55 
56 /*===========================================================================*/
57 /* Driver local functions. */
58 /*===========================================================================*/
59 
60 /*===========================================================================*/
61 /* Driver exported functions. */
62 /*===========================================================================*/
63 
64 /**
65  * @brief RTC Driver initialization.
66  * @note This function is implicitly invoked by @p halInit(), there is
67  * no need to explicitly initialize the driver.
68  *
69  * @init
70  */
71 void rtcInit(void) {
72 
73  rtc_lld_init();
74 }
75 
76 /**
77  * @brief Initializes a generic RTC driver object.
78  * @details The HW dependent part of the initialization has to be performed
79  * outside, usually in the hardware initialization code.
80  *
81  * @param[out] rtcp pointer to RTC driver structure
82  *
83  * @init
84  */
85 void rtcObjectInit(RTCDriver *rtcp) {
86 
87 #if RTC_HAS_STORAGE == TRUE
88  rtcp->vmt = &_rtc_lld_vmt;
89 #else
90  (void)rtcp;
91 #endif
92 }
93 
94 /**
95  * @brief Set current time.
96  * @note This function can be called from any context but limitations
97  * could be imposed by the low level implementation. It is
98  * guaranteed that the function can be called from thread
99  * context.
100  * @note The function can be reentrant or not reentrant depending on
101  * the low level implementation.
102  *
103  * @param[in] rtcp pointer to RTC driver structure
104  * @param[in] timespec pointer to a @p RTCDateTime structure
105  *
106  * @special
107  */
108 void rtcSetTime(RTCDriver *rtcp, const RTCDateTime *timespec) {
109 
110  osalDbgCheck((rtcp != NULL) && (timespec != NULL));
111 
112  rtc_lld_set_time(rtcp, timespec);
113 }
114 
115 /**
116  * @brief Get current time.
117  * @note This function can be called from any context but limitations
118  * could be imposed by the low level implementation. It is
119  * guaranteed that the function can be called from thread
120  * context.
121  * @note The function can be reentrant or not reentrant depending on
122  * the low level implementation.
123  *
124  * @param[in] rtcp pointer to RTC driver structure
125  * @param[out] timespec pointer to a @p RTCDateTime structure
126  *
127  * @special
128  */
129 void rtcGetTime(RTCDriver *rtcp, RTCDateTime *timespec) {
130 
131  osalDbgCheck((rtcp != NULL) && (timespec != NULL));
132 
133  rtc_lld_get_time(rtcp, timespec);
134 }
135 
136 #if (RTC_ALARMS > 0) || defined(__DOXYGEN__)
137 /**
138  * @brief Set alarm time.
139  * @note This function can be called from any context but limitations
140  * could be imposed by the low level implementation. It is
141  * guaranteed that the function can be called from thread
142  * context.
143  * @note The function can be reentrant or not reentrant depending on
144  * the low level implementation.
145  *
146  * @param[in] rtcp pointer to RTC driver structure
147  * @param[in] alarm alarm identifier
148  * @param[in] alarmspec pointer to a @p RTCAlarm structure or @p NULL
149  *
150  * @special
151  */
153  rtcalarm_t alarm,
154  const RTCAlarm *alarmspec) {
155 
156  osalDbgCheck((rtcp != NULL) && (alarm < (rtcalarm_t)RTC_ALARMS));
157 
158  rtc_lld_set_alarm(rtcp, alarm, alarmspec);
159 }
160 
161 /**
162  * @brief Get current alarm.
163  * @note If an alarm has not been set then the returned alarm specification
164  * is not meaningful.
165  * @note This function can be called from any context but limitations
166  * could be imposed by the low level implementation. It is
167  * guaranteed that the function can be called from thread
168  * context.
169  * @note The function can be reentrant or not reentrant depending on
170  * the low level implementation.
171  *
172  * @param[in] rtcp pointer to RTC driver structure
173  * @param[in] alarm alarm identifier
174  * @param[out] alarmspec pointer to a @p RTCAlarm structure
175  *
176  * @special
177  */
179  rtcalarm_t alarm,
180  RTCAlarm *alarmspec) {
181 
182  osalDbgCheck((rtcp != NULL) &&
183  (alarm < (rtcalarm_t)RTC_ALARMS) &&
184  (alarmspec != NULL));
185 
186  rtc_lld_get_alarm(rtcp, alarm, alarmspec);
187 }
188 #endif /* RTC_ALARMS > 0 */
189 
190 #if (RTC_SUPPORTS_CALLBACKS == TRUE) || defined(__DOXYGEN__)
191 /**
192  * @brief Enables or disables RTC callbacks.
193  * @details This function enables or disables the callback, use a @p NULL
194  * pointer in order to disable it.
195  * @note This function can be called from any context but limitations
196  * could be imposed by the low level implementation. It is
197  * guaranteed that the function can be called from thread
198  * context.
199  * @note The function can be reentrant or not reentrant depending on
200  * the low level implementation.
201  *
202  * @param[in] rtcp pointer to RTC driver structure
203  * @param[in] callback callback function pointer or @p NULL
204  *
205  * @special
206  */
207 void rtcSetCallback(RTCDriver *rtcp, rtccb_t callback) {
208 
209  osalDbgCheck(rtcp != NULL);
210 
211  rtc_lld_set_callback(rtcp, callback);
212 }
213 #endif /* RTC_SUPPORTS_CALLBACKS == TRUE */
214 
215 /**
216  * @brief Convert @p RTCDateTime to broken-down time structure.
217  *
218  * @param[in] timespec pointer to a @p RTCDateTime structure
219  * @param[out] timp pointer to a broken-down time structure
220  * @param[out] tv_msec pointer to milliseconds value or @p NULL
221  *
222  * @api
223  */
225  struct tm *timp,
226  uint32_t *tv_msec) {
227  int sec, year;
228  bool is_leap_year;
229 
230  timp->tm_year = (int)timespec->year + (int)(RTC_BASE_YEAR - 1900U);
231  timp->tm_mon = (int)timespec->month - 1;
232  timp->tm_mday = (int)timespec->day;
233  timp->tm_isdst = (int)timespec->dstflag;
234  timp->tm_wday = (int)timespec->dayofweek - 1;
235 
236  sec = (int)timespec->millisecond / 1000;
237  timp->tm_hour = sec / 3600;
238  sec %= 3600;
239  timp->tm_min = sec / 60;
240  timp->tm_sec = sec % 60;
241 
242  if (NULL != tv_msec) {
243  *tv_msec = (uint32_t)timespec->millisecond % 1000U;
244  }
245 
246  /* Day of the year calculation.*/
247  year = timp->tm_year + 1900;
248  timp->tm_yday = timp->tm_mday - 1;
249  timp->tm_yday += (int)accu_month_len[timp->tm_mon];
250  is_leap_year = (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0);
251  if (is_leap_year && (timp->tm_mon > 1)) {
252  timp->tm_yday++;
253  }
254 }
255 
256 /**
257  * @brief Convert broken-down time structure to @p RTCDateTime.
258  *
259  * @param[in] timp pointer to a broken-down time structure
260  * @param[in] tv_msec milliseconds value
261  * @param[out] timespec pointer to a @p RTCDateTime structure
262  *
263  * @api
264  */
265 void rtcConvertStructTmToDateTime(const struct tm *timp,
266  uint32_t tv_msec,
267  RTCDateTime *timespec) {
268 
269  /*lint -save -e9034 [10.4] Verified assignments to bit fields.*/
270  timespec->year = (uint32_t)timp->tm_year - (RTC_BASE_YEAR - 1900U);
271  timespec->month = (uint32_t)timp->tm_mon + 1U;
272  timespec->day = (uint32_t)timp->tm_mday;
273  timespec->dayofweek = (uint32_t)timp->tm_wday + 1U;
274  if (-1 == timp->tm_isdst) {
275  timespec->dstflag = 0U; /* Set zero if dst is unknown.*/
276  }
277  else {
278  timespec->dstflag = (uint32_t)timp->tm_isdst;
279  }
280  /*lint -restore*/
281  /*lint -save -e9033 [10.8] Verified assignments to bit fields.*/
282  timespec->millisecond = tv_msec + (uint32_t)(((timp->tm_hour * 3600) +
283  (timp->tm_min * 60) +
284  timp->tm_sec) * 1000);
285  /*lint -restore*/
286 }
287 
288 /**
289  * @brief Get current time in format suitable for usage in FAT file system.
290  * @note The information about day of week and DST is lost in DOS
291  * format, the second field loses its least significant bit.
292  *
293  * @param[out] timespec pointer to a @p RTCDateTime structure
294  * @return FAT date/time value.
295  *
296  * @api
297  */
298 uint32_t rtcConvertDateTimeToFAT(const RTCDateTime *timespec) {
299  uint32_t fattime;
300  uint32_t sec, min, hour, day, month;
301 
302  sec = timespec->millisecond / 1000U;
303  hour = sec / 3600U;
304  sec %= 3600U;
305  min = sec / 60U;
306  sec %= 60U;
307  day = timespec->day;
308  month = timespec->month;
309 
310  /* Handle DST flag.*/
311  if (1U == timespec->dstflag) {
312  hour += 1U;
313  if (hour == 24U) {
314  hour = 0U;
315  day += 1U;
316  if (day > (uint32_t)month_len[month - 1U]) {
317  day = 1U;
318  month += 1U;
319  }
320  }
321  }
322 
323  fattime = sec >> 1U;
324  fattime |= min << 5U;
325  fattime |= hour << 11U;
326  fattime |= day << 16U;
327  fattime |= month << 21U;
328  fattime |= (uint32_t)timespec->year << 25U;
329 
330  return fattime;
331 }
332 
333 #endif /* HAL_USE_RTC == TRUE */
334 
335 /** @} */
rtc_lld_get_alarm
void rtc_lld_get_alarm(RTCDriver *rtcp, rtcalarm_t alarm, RTCAlarm *alarmspec)
Get alarm time.
Definition: hal_rtc_lld.c:141
RTCDateTime::month
uint32_t month
Months 1..12.
Definition: hal_rtc.h:101
hal.h
HAL subsystem header.
rtcSetAlarm
void rtcSetAlarm(RTCDriver *rtcp, rtcalarm_t alarm, const RTCAlarm *alarmspec)
Set alarm time.
Definition: hal_rtc.c:152
rtccb_t
void(* rtccb_t)(RTCDriver *rtcp, rtcevent_t event)
Type of a generic RTC callback.
Definition: hal_rtc_lld.h:95
RTCDateTime::dstflag
uint32_t dstflag
DST correction flag.
Definition: hal_rtc.h:102
RTCDateTime::day
uint32_t day
Day of the month 1..31.
Definition: hal_rtc.h:104
rtcObjectInit
void rtcObjectInit(RTCDriver *rtcp)
Initializes a generic RTC driver object.
Definition: hal_rtc.c:85
rtc_lld_get_time
void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec)
Get current time.
Definition: hal_rtc_lld.c:103
RTCDateTime::year
uint32_t year
Years since 1980.
Definition: hal_rtc.h:100
RTCDateTime::dayofweek
uint32_t dayofweek
Day of week 1..7.
Definition: hal_rtc.h:103
RTCDriver
Structure representing an RTC driver.
Definition: hal_rtc.h:145
RTCAlarm
Type of a structure representing an RTC alarm time stamp.
Definition: hal_rtc_lld.h:101
rtcConvertDateTimeToStructTm
void rtcConvertDateTimeToStructTm(const RTCDateTime *timespec, struct tm *timp, uint32_t *tv_msec)
Convert RTCDateTime to broken-down time structure.
Definition: hal_rtc.c:224
rtcInit
void rtcInit(void)
RTC Driver initialization.
Definition: hal_rtc.c:71
RTC_BASE_YEAR
#define RTC_BASE_YEAR
Base year of the calendar.
Definition: hal_rtc.h:45
rtcGetTime
void rtcGetTime(RTCDriver *rtcp, RTCDateTime *timespec)
Get current time.
Definition: hal_rtc.c:129
rtc_lld_set_alarm
void rtc_lld_set_alarm(RTCDriver *rtcp, rtcalarm_t alarm, const RTCAlarm *alarmspec)
Set alarm time.
Definition: hal_rtc_lld.c:122
rtcConvertDateTimeToFAT
uint32_t rtcConvertDateTimeToFAT(const RTCDateTime *timespec)
Get current time in format suitable for usage in FAT file system.
Definition: hal_rtc.c:298
RTCDriver::vmt
const struct RTCDriverVMT * vmt
Virtual Methods Table.
Definition: hal_rtc.h:150
rtcSetCallback
void rtcSetCallback(RTCDriver *rtcp, rtccb_t callback)
Enables or disables RTC callbacks.
Definition: hal_rtc.c:207
rtc_lld_init
void rtc_lld_init(void)
RTC driver identifier.
Definition: hal_rtc_lld.c:69
rtcConvertStructTmToDateTime
void rtcConvertStructTmToDateTime(const struct tm *timp, uint32_t tv_msec, RTCDateTime *timespec)
Convert broken-down time structure to RTCDateTime.
Definition: hal_rtc.c:265
RTC_ALARMS
#define RTC_ALARMS
Number of alarms available.
Definition: hal_rtc_lld.h:50
osalDbgCheck
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:284
rtcSetTime
void rtcSetTime(RTCDriver *rtcp, const RTCDateTime *timespec)
Set current time.
Definition: hal_rtc.c:108
rtcalarm_t
unsigned int rtcalarm_t
Type of an RTC alarm number.
Definition: hal_rtc.h:93
rtc_lld_set_time
void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec)
Set current time.
Definition: hal_rtc_lld.c:88
RTCDateTime::millisecond
uint32_t millisecond
Milliseconds since midnight.
Definition: hal_rtc.h:105
RTCDateTime
Type of a structure representing an RTC date/time stamp.
Definition: hal_rtc.h:98
rtcGetAlarm
void rtcGetAlarm(RTCDriver *rtcp, rtcalarm_t alarm, RTCAlarm *alarmspec)
Get current alarm.
Definition: hal_rtc.c:178