/** mbed Microcontroller Library ****************************************************************************** * @file rtc_api.c * @author * @version V1.0.0 * @date 2016-08-01 * @brief This file provides mbed API for RTC. ****************************************************************************** * @attention * * This module is a confidential and proprietary property of RealTek and * possession or use of this module requires written permission of RealTek. * * Copyright(c) 2016, Realtek Semiconductor Corporation. All rights reserved. ****************************************************************************** */ #include "rtc_api.h" #include #include "timer_api.h" static struct tm rtc_timeinfo; static int rtc_en = 0; static alarm_irq_handler rtc_alarm_handler; const static u8 dim[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /** * @brief This function is used to tell a year is a leap year or not. * @param year: The year need to be told. * @retval value: * - 1: This year is leap year. * - 0: This year is not leap year. */ static inline bool is_leap_year(unsigned int year) { u32 full_year = year + 1900; return (!(full_year % 4) && (full_year % 100)) || !(full_year % 400); } /** * @brief This function tells how many days in a month of a year. * @param year: Specified year * @param month: Specified month * @retval value: Number of days in the month. */ static u8 days_in_month (u8 month, u8 year) { u8 ret = dim[month]; if (ret == 0) ret = is_leap_year (year) ? 29 : 28; return ret; } /** * @brief This function is used to calculate month and day of month according to year and day of the year. * @param year: years since 1900. * @param yday: day of the year. * @param mon: pointer to the variable which stores month, the value can be 0-11 * @param mday: pointer to the variable which stores day of month, the value can be 1-31 * @retval value: none */ static void rtc_calculate_mday(int year, int yday, int* mon, int* mday) { int t_mon = -1, t_yday = yday + 1; while(t_yday > 0){ t_mon ++; t_yday -= days_in_month(t_mon, year); } *mon = t_mon; *mday = t_yday + days_in_month(t_mon, year); } /** * @brief This function is used to calculate day of week according to date. * @param year: years since 1900. * @param mon: which month of the year * @param mday: pointer to the variable which store day of month * @param wday: pointer to the variable which store day of week, the value can be 0-6, and 0 means Sunday * @retval value: none */ static void rtc_calculate_wday(int year, int mon, int mday, int* wday) { int t_year = year + 1900, t_mon = mon + 1; if(t_mon == 1 || t_mon == 2){ t_year --; t_mon += 12; } int c = t_year / 100; int y = t_year % 100; int week = (c / 4) - 2 * c + (y + y / 4) + (26 * (t_mon + 1) / 10) + mday -1; while(week < 0){ week += 7; } week %= 7; *wday = week; } /** * @brief This function is used to restore rtc_timeinfo global variable whose value is lost after system reset. * @param none * @retval value: none */ static void rtc_restore_timeinfo(void) { u32 value, days_in_year; RTC_TimeTypeDef RTC_TimeStruct; RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct); rtc_timeinfo.tm_sec = RTC_TimeStruct.RTC_Seconds; rtc_timeinfo.tm_min = RTC_TimeStruct.RTC_Minutes; rtc_timeinfo.tm_hour = RTC_TimeStruct.RTC_Hours; rtc_timeinfo.tm_yday = RTC_TimeStruct.RTC_Days; value = BKUP_Read(0); rtc_timeinfo.tm_year = (value & BIT_RTC_BACKUP) >> 8; days_in_year = (is_leap_year(rtc_timeinfo.tm_year) ? 366 : 365); if(rtc_timeinfo.tm_yday > days_in_year - 1){ rtc_timeinfo.tm_year ++; rtc_timeinfo.tm_yday -= days_in_year; /* over one year, update days in RTC_TR */ RTC_TimeStruct.RTC_Days = rtc_timeinfo.tm_yday; RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct); } rtc_calculate_mday(rtc_timeinfo.tm_year, rtc_timeinfo.tm_yday, &rtc_timeinfo.tm_mon, &rtc_timeinfo.tm_mday); rtc_calculate_wday(rtc_timeinfo.tm_year, rtc_timeinfo.tm_mon, rtc_timeinfo.tm_mday, &rtc_timeinfo.tm_wday); } /** * @brief This function is used to backup tm_year parameter in rtc_timeinfo global variable before system reset. * @param none * @retval value: none */ void rtc_backup_timeinfo(void) { u32 value = BKUP_Read(0); value = (value & ~BIT_RTC_BACKUP) | (rtc_timeinfo.tm_year << 8); BKUP_Write(0, value); BKUP_Set(0, BIT_RTC_RESTORE); } /** * @brief Initializes the RTC device, include clock, RTC registers and function. * @param none * @retval none */ void rtc_init(void) { RTC_InitTypeDef RTC_InitStruct; RTC_ClokSource(0); RTC_StructInit(&RTC_InitStruct); RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24; RTC_Init(&RTC_InitStruct); /* 32760 need add need add 15 cycles (256Hz) every 4 min*/ //RTC_SmoothCalibConfig(RTC_CalibSign_Positive, 15, // RTC_CalibPeriod_4MIN, RTC_Calib_Enable); rtc_en = 1; } /** * @brief Deinitializes the RTC device. * @param none * @retval none */ void rtc_free(void) { rtc_en = 0; rtc_alarm_handler = NULL; } /** * @brief This function tells whether RTC is enabled or not. * @param none * @retval status: * - 1: RTC is enable. * - 0: RTC is disable. */ int rtc_isenabled(void) { return rtc_en; } /** * @brief Set the specified timestamp in seconds to RTC. * @param t: Seconds from 1970.1.1 00:00:00 to specified data and time * which is to be set. * @retval none */ void rtc_write(time_t t) { /* Convert the time in to a tm*/ struct tm *timeinfo = localtime(&t); RTC_TimeTypeDef RTC_TimeStruct; /*set time in RTC */ RTC_TimeStruct.RTC_H12_PMAM = RTC_H12_AM; RTC_TimeStruct.RTC_Days = timeinfo->tm_yday; RTC_TimeStruct.RTC_Hours = timeinfo->tm_hour; RTC_TimeStruct.RTC_Minutes = timeinfo->tm_min; RTC_TimeStruct.RTC_Seconds = timeinfo->tm_sec; RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct); /* Set rtc_timeinfo*/ _memcpy((void*)&rtc_timeinfo, (void*)timeinfo, sizeof(struct tm)); } /** * @brief Get current timestamp in seconds from RTC. * @param none * @retval value: The current timestamp in seconds which is calculated from * 1970.1.1 00:00:00. */ time_t rtc_read(void) { time_t t; struct tm tm_temp; RTC_TimeTypeDef RTC_TimeStruct; u32 delta_days = 0; if(BKUP_Read(0) & BIT_RTC_RESTORE){ rtc_restore_timeinfo(); BKUP_Clear(0, BIT_RTC_RESTORE); } _memcpy((void*)&tm_temp, (void*)&rtc_timeinfo, sizeof(struct tm)); /*hour, min, sec get from RTC*/ RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct); tm_temp.tm_sec = RTC_TimeStruct.RTC_Seconds; tm_temp.tm_min = RTC_TimeStruct.RTC_Minutes; tm_temp.tm_hour = RTC_TimeStruct.RTC_Hours; /* calculate how many days later from last time update rtc_timeinfo */ delta_days = RTC_TimeStruct.RTC_Days - tm_temp.tm_yday; /* calculate wday, mday, yday, mon, year*/ tm_temp.tm_wday += delta_days; if(tm_temp.tm_wday >= 7){ tm_temp.tm_wday = tm_temp.tm_wday % 7; } tm_temp.tm_yday += delta_days; tm_temp.tm_mday += delta_days; while(tm_temp.tm_mday > days_in_month(tm_temp.tm_mon, tm_temp.tm_year)){ tm_temp.tm_mday -= days_in_month(tm_temp.tm_mon, tm_temp.tm_year); tm_temp.tm_mon++; if(tm_temp.tm_mon >= 12){ tm_temp.tm_mon -= 12; tm_temp.tm_yday -= is_leap_year(tm_temp.tm_year) ? 366 : 365; tm_temp.tm_year ++; /* over one year, update days in RTC_TR */ RTC_TimeStruct.RTC_Days = tm_temp.tm_yday; RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct); /* update rtc_timeinfo */ _memcpy((void*)&rtc_timeinfo, (void*)&tm_temp, sizeof(struct tm)); } } /* Convert to timestamp(seconds from 1970.1.1 00:00:00)*/ t = mktime(&tm_temp); return t; } /** * @brief RTC alarm interrupt handler function. * @param data: RTC IRQ callback data * @retval none */ void rtc_alarm_intr_handler(u32 data) { alarm_irq_handler hdl; /*clear alarm flag*/ RTC_AlarmClear(); /* execute user handler*/ if(rtc_alarm_handler != NULL){ hdl = (alarm_irq_handler)rtc_alarm_handler; hdl(); } /*disable alarm*/ rtc_disable_alarm(); } /** * @brief Set the specified RTC Alarm and interrupt. * @param alarm: alarm object define in application software. * @param alarmHandler: alarm interrupt callback function. * @retval status: * - 1: success * - Others: failure */ u32 rtc_set_alarm(alarm_t *alrm, alarm_irq_handler alarmHandler) { RTC_AlarmTypeDef RTC_AlarmStruct_temp; rtc_alarm_handler = alarmHandler; /* set alarm */ RTC_AlarmStructInit(&RTC_AlarmStruct_temp); RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_H12_PMAM = RTC_H12_AM; RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_Days = alrm->yday; RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_Hours = alrm->hour; RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_Minutes = alrm->min; RTC_AlarmStruct_temp.RTC_AlarmTime.RTC_Seconds = alrm->sec; RTC_AlarmStruct_temp.RTC_AlarmMask = RTC_AlarmMask_None; RTC_AlarmStruct_temp.RTC_Alarm2Mask = RTC_Alarm2Mask_None; RTC_SetAlarm(RTC_Format_BIN, &RTC_AlarmStruct_temp); RTC_AlarmCmd(ENABLE); InterruptRegister((IRQ_FUN)rtc_alarm_intr_handler, RTC_IRQ, (u32)alrm, 4); InterruptEn(RTC_IRQ, 4); return _TRUE; } /** * @brief Disable RTC Alarm and function. * @param none * @retval none */ void rtc_disable_alarm(void) { InterruptDis(RTC_IRQ); InterruptUnRegister(RTC_IRQ); RTC_AlarmCmd(DISABLE); rtc_alarm_handler = NULL; }