1 /** mbed Microcontroller Library
2  ******************************************************************************
3  * @file	rtc_api.c
4  * @author
5  * @version V1.0.0
6  * @date	2016-08-01
7  * @brief	This file provides mbed API for RTC.
8  ******************************************************************************
9  * @attention
10  *
11  * This module is a confidential and proprietary property of RealTek and
12  * possession or use of this module requires written permission of RealTek.
13  *
14  * Copyright(c) 2016, Realtek Semiconductor Corporation. All rights reserved.
15  ******************************************************************************
16  */
17 
18 #include "rtc_api.h"
19 
20 #include <time.h>
21 #include "timer_api.h"
22 
23 static struct tm rtc_timeinfo;
24 static int rtc_en = 0;
25 static alarm_irq_handler rtc_alarm_handler;
26 
27 static const u8 dim[12] = {
28 	31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
29 
30 /** @addtogroup AmebaD_Mbed_API
31   * @{
32   */
33 
34 /** @defgroup MBED_RTC
35  *  @brief      MBED_RTC driver modules.
36  *  @{
37  */
38 
39 /** @defgroup MBED_RTC_Exported_Functions MBED_RTC Exported Functions
40   * @{
41   */
42 
43 /**
44   * @brief  This function is used to tell a year is a leap year or not.
45   * @param  year: The year need to be told.
46   * @retval value:
47   *             - 1: This year is leap year.
48   *             - 0: This year is not leap year.
49   */
is_leap_year(unsigned int year)50 static inline bool is_leap_year(unsigned int year)
51 {
52 	u32 full_year = year + 1900;
53 	return (!(full_year % 4) && (full_year % 100)) || !(full_year % 400);
54 }
55 
56 
57 /**
58   * @brief  This function tells how many days in a month of a year.
59   * @param  year: Specified year
60   * @param  month: Specified month
61   * @retval value: Number of days in the month.
62   */
days_in_month(u8 month,u8 year)63 static u8 days_in_month (u8 month, u8 year)
64 {
65 	u8 ret = dim[month];
66 	if (ret == 0)
67 		ret = is_leap_year (year) ? 29 : 28;
68 	return ret;
69 }
70 
71 /**
72   * @brief  This function is used to calculate month and day of month according to year and day of the year.
73   * @param  year: years since 1900.
74   * @param  yday: day of the year.
75   * @param  mon: pointer to the variable which stores month,  the value can be 0-11
76   * @param  mday: pointer to the variable which stores day of month, the value can be 1-31
77   * @retval value: none
78   */
rtc_calculate_mday(int year,int yday,int * mon,int * mday)79 static void rtc_calculate_mday(int year, int yday, int* mon, int* mday)
80 {
81 	int t_mon = -1, t_yday = yday + 1;
82 
83 	while(t_yday > 0){
84 		t_mon ++;
85 		t_yday -= days_in_month(t_mon, year);
86 	}
87 
88 	*mon = t_mon;
89 	*mday = t_yday + days_in_month(t_mon, year);
90 }
91 
92 /**
93   * @brief  This function is used to calculate day of week according to date.
94   * @param  year: years since 1900.
95   * @param  mon: which month of the year
96   * @param  mday: pointer to the variable which store day of month
97   * @param  wday: pointer to the variable which store day of week, the value can be 0-6, and 0 means Sunday
98   * @retval value: none
99   */
rtc_calculate_wday(int year,int mon,int mday,int * wday)100 static void rtc_calculate_wday(int year, int mon, int mday, int* wday)
101 {
102 	int t_year = year + 1900, t_mon = mon + 1;
103 
104 	if(t_mon == 1 || t_mon == 2){
105 		t_year --;
106 		t_mon += 12;
107 	}
108 
109 	int c = t_year / 100;
110 	int y = t_year % 100;
111 	int week = (c / 4) - 2 * c + (y + y / 4) + (26 * (t_mon + 1) / 10) + mday -1;
112 
113 	while(week < 0){
114 		week += 7;
115 	}
116 	week %= 7;
117 
118 	*wday = week;
119 }
120 
121 /**
122   * @brief  This function is used to restore rtc_timeinfo global variable whose value is lost after system reset.
123   * @param  none
124   * @retval value: none
125   */
rtc_restore_timeinfo(void)126 static void rtc_restore_timeinfo(void)
127 {
128 	u32 value;
129 	int days_in_year;
130 
131 	RTC_TimeTypeDef RTC_TimeStruct;
132         RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct);
133 	rtc_timeinfo.tm_sec = RTC_TimeStruct.RTC_Seconds;
134 	rtc_timeinfo.tm_min = RTC_TimeStruct.RTC_Minutes;
135 	rtc_timeinfo.tm_hour = RTC_TimeStruct.RTC_Hours;
136 	rtc_timeinfo.tm_yday = RTC_TimeStruct.RTC_Days;
137 
138 	value = BKUP_Read(0);
139 	rtc_timeinfo.tm_year = (value & BIT_RTC_BACKUP) >> BIT_RTC_BACKUP_SHIFT;
140 
141 	days_in_year =  (is_leap_year(rtc_timeinfo.tm_year) ? 366 : 365);
142 	if(rtc_timeinfo.tm_yday > (days_in_year - 1)){
143 		rtc_timeinfo.tm_year ++;
144 		rtc_timeinfo.tm_yday -= days_in_year;
145 
146 		/* over one year, update days in RTC_TR */
147 		RTC_TimeStruct.RTC_Days = rtc_timeinfo.tm_yday;
148 		RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct);
149 	}
150 
151 	rtc_calculate_mday(rtc_timeinfo.tm_year, rtc_timeinfo.tm_yday, &rtc_timeinfo.tm_mon, &rtc_timeinfo.tm_mday);
152 	rtc_calculate_wday(rtc_timeinfo.tm_year, rtc_timeinfo.tm_mon, rtc_timeinfo.tm_mday, &rtc_timeinfo.tm_wday);
153 }
154 
155 /**
156   * @brief  This function is used to backup tm_year parameter in rtc_timeinfo global variable before system reset.
157   * @param  none
158   * @retval value: none
159   */
rtc_backup_timeinfo(void)160 void rtc_backup_timeinfo(void)
161 {
162 	u32 value = BKUP_Read(0);
163 	value = (value & ~BIT_RTC_BACKUP) | (rtc_timeinfo.tm_year << BIT_RTC_BACKUP_SHIFT);
164 
165 	BKUP_Write(0, value);
166 
167 	BKUP_Set(0, BIT_RTC_RESTORE);
168 }
169 
170 /**
171   * @brief  Initializes the RTC device, include clock, RTC registers and function.
172   * @param  none
173   * @retval  none
174   */
rtc_init(void)175 void rtc_init(void)
176 {
177 	RTC_InitTypeDef RTC_InitStruct;
178 
179 	RCC_PeriphClockSource_RTC(0);
180 
181 	RTC_StructInit(&RTC_InitStruct);
182 	RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24;
183 
184 	RTC_Init(&RTC_InitStruct);
185 
186 	/* 32760 need add need add 15 cycles (256Hz) every 4 min*/
187 	//RTC_SmoothCalibConfig(RTC_CalibSign_Positive, 15,
188 	//	RTC_CalibPeriod_4MIN, RTC_Calib_Enable);
189 
190 	rtc_en = 1;
191 }
192 
193 /**
194   * @brief  Deinitializes the RTC device.
195   * @param  none
196   * @retval  none
197   */
rtc_free(void)198 void rtc_free(void)
199 {
200 	rtc_en = 0;
201 	rtc_alarm_handler = NULL;
202 }
203 
204 /**
205   * @brief  This function tells whether RTC is enabled or not.
206   * @param  none
207   * @retval  status:
208   *              - 1: RTC is enable.
209   *              - 0: RTC is disable.
210   */
rtc_isenabled(void)211 int rtc_isenabled(void)
212 {
213 	return rtc_en;
214 }
215 
216 /**
217   * @brief  Set the specified timestamp in seconds to RTC.
218   * @param  t: Seconds from 1970.1.1 00:00:00 to specified data and time
219   *              which is to be set.
220   * @retval  none
221   */
rtc_write(time_t t)222 void rtc_write(time_t t)
223 {
224 	/* Convert the time in to a tm*/
225 	struct tm *timeinfo = localtime(&t);
226 
227 	RTC_TimeTypeDef RTC_TimeStruct;
228 
229 	/*set time in RTC */
230 	RTC_TimeStruct.RTC_H12_PMAM = RTC_H12_AM;
231 	RTC_TimeStruct.RTC_Days = timeinfo->tm_yday;
232 	RTC_TimeStruct.RTC_Hours = timeinfo->tm_hour;
233 	RTC_TimeStruct.RTC_Minutes = timeinfo->tm_min;
234 	RTC_TimeStruct.RTC_Seconds = timeinfo->tm_sec;
235 	RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct);
236 
237 	/* Set rtc_timeinfo*/
238 	_memcpy((void*)&rtc_timeinfo, (void*)timeinfo, sizeof(struct tm));
239 }
240 
241 /**
242   * @brief  Get current timestamp in seconds from RTC.
243   * @param  none
244   * @retval : The current timestamp in seconds which is calculated from 1970.1.1 00:00:00.
245   */
rtc_read(void)246 time_t rtc_read(void)
247 {
248 	time_t t;
249 	struct tm tm_temp;
250 	RTC_TimeTypeDef RTC_TimeStruct;
251 	u32 delta_days = 0;
252 
253 	if(BKUP_Read(0) & BIT_RTC_RESTORE){
254 		rtc_restore_timeinfo();
255 		BKUP_Clear(0, BIT_RTC_RESTORE);
256 	}
257 
258 	_memcpy((void*)&tm_temp, (void*)&rtc_timeinfo, sizeof(struct tm));
259 
260 	/*hour, min, sec get from RTC*/
261 	RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct);
262 	tm_temp.tm_sec = RTC_TimeStruct.RTC_Seconds;
263 	tm_temp.tm_min = RTC_TimeStruct.RTC_Minutes;
264 	tm_temp.tm_hour = RTC_TimeStruct.RTC_Hours;
265 
266 	/* calculate how many days later from last time update rtc_timeinfo */
267 	delta_days = RTC_TimeStruct.RTC_Days - tm_temp.tm_yday;
268 
269 	/* calculate  wday, mday, yday, mon, year*/
270 	tm_temp.tm_wday += delta_days;
271 	if(tm_temp.tm_wday >= 7){
272 		tm_temp.tm_wday = tm_temp.tm_wday % 7;
273 	}
274 
275 	tm_temp.tm_yday += delta_days;
276 	tm_temp.tm_mday += delta_days;
277 
278 	while(tm_temp.tm_mday > days_in_month(tm_temp.tm_mon, tm_temp.tm_year)){
279 		tm_temp.tm_mday -= days_in_month(tm_temp.tm_mon, tm_temp.tm_year);
280 		tm_temp.tm_mon++;
281 
282 		if(tm_temp.tm_mon >= 12){
283 			tm_temp.tm_mon -= 12;
284 			tm_temp.tm_yday -= is_leap_year(tm_temp.tm_year) ? 366 : 365;
285 			tm_temp.tm_year ++;
286 
287 			/* over one year, update days in RTC_TR */
288 			RTC_TimeStruct.RTC_Days = tm_temp.tm_yday;
289 			RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct);
290 
291 			/* update rtc_timeinfo */
292 			_memcpy((void*)&rtc_timeinfo, (void*)&tm_temp, sizeof(struct tm));
293 		}
294 	}
295 
296 	/* Convert to timestamp(seconds from 1970.1.1 00:00:00)*/
297 	t = mktime(&tm_temp);
298 
299 	return t;
300 }
301 
302 
303 /**
304   * @brief  Disable RTC Alarm and function.
305   * @param  none
306   * @retval   none
307   */
rtc_disable_alarm(void)308 void rtc_disable_alarm(void)
309 {
310 	InterruptDis(RTC_IRQ);
311 	InterruptUnRegister(RTC_IRQ);
312 	RTC_AlarmCmd(DISABLE);
313 
314 	rtc_alarm_handler = NULL;
315 }
316 
317 /**
318   * @brief  RTC alarm interrupt handler function.
319   * @param  data: RTC IRQ callback data
320   * @retval   none
321   */
rtc_alarm_intr_handler(u32 data)322 void rtc_alarm_intr_handler(u32 data)
323 {
324 	/* To avoid gcc warnings */
325 	( void ) data;
326 
327 	alarm_irq_handler hdl;
328 
329 	/*clear alarm flag*/
330 	RTC_AlarmClear();
331 
332 	/* execute user handler*/
333 	if(rtc_alarm_handler != NULL){
334 		hdl = (alarm_irq_handler)rtc_alarm_handler;
335 		hdl();
336 	}
337 
338 	/*disable alarm*/
339 	rtc_disable_alarm();
340 }
341 
342 /**
343   * @brief  Set the specified RTC Alarm and interrupt.
344   * @param  alarm: alarm object define in application software.
345   * @param  alarmHandler:  alarm interrupt callback function.
346   * @retval   status:
347   *            - 1: success
348   *            - Others: failure
349   */
rtc_set_alarm(alarm_t * alrm,alarm_irq_handler alarmHandler)350 u32 rtc_set_alarm(alarm_t *alrm, alarm_irq_handler alarmHandler)
351 {
352 	RTC_AlarmTypeDef RTC_AlarmStruct_temp;
353 
354 	rtc_alarm_handler = alarmHandler;
355 
356 	/* set alarm */
357 	RTC_AlarmStructInit(&RTC_AlarmStruct_temp);
358 	RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_H12_PMAM = RTC_H12_AM;
359 	RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_Days = alrm->yday;
360 	RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_Hours = alrm->hour;
361 	RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_Minutes = alrm->min;
362 	RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_Seconds = alrm->sec;
363 
364 	RTC_AlarmStruct_temp.RTC_AlarmMask = RTC_AlarmMask_None;
365 	RTC_AlarmStruct_temp.RTC_Alarm2Mask = RTC_Alarm2Mask_None;
366 
367 	RTC_SetAlarm(RTC_Format_BIN, &RTC_AlarmStruct_temp);
368 
369 	RTC_AlarmCmd(ENABLE);
370 	InterruptRegister((IRQ_FUN)rtc_alarm_intr_handler, RTC_IRQ, (u32)alrm, 5);
371 	InterruptEn(RTC_IRQ, 5);
372 
373 	return _TRUE;
374 }
375 
376 /**
377   * @}
378   */
379 
380 /**
381   * @}
382   */
383 
384 /**
385   * @}
386   */
387