/**************************************************************************//** * \file RTC.c * \brief Real time clock driver file * \details * \author Zhang Xuan * \version V1.0.0 * \date 21-Nov-2018 * \par History: * V1.0.0 Initial release * \par Copyright: * (c) Heilongjiang TYW Electronics co., LTD ******************************************************************************/ /* Includes ------------------------------------------------------------------*/ #include "RTC.h" /* Private typedef -----------------------------------------------------------*/ typedef struct { uint16_t Year; uint8_t Month; uint8_t Date; } RTC_Date_st_t; volatile uint16_t RTCRollingCounter; /* Private define ------------------------------------------------------------*/ /*============================================================================= Set the start year of RTC, MUST be a leap year. Since 2100 is not a leap year, but 2100 can be divisible by 4, setting the starting year to 1920, making the RTC year counted in the range of 1920 - 2099. In this range, all years that can be divisible by 4 are leap years, simplifying leap year calculation. Limitations of the current leap year algorithm must be considered when modifying RTC_YEAR_BASE. =============================================================================*/ #define RTC_YEAR_BASE 1920U #define RTC_YEAR_TOP (RTC_YEAR_BASE + 179U) /* Private macro -------------------------------------------------------------*/ #if ((RTC_DEFAULT_YEAR < RTC_YEAR_BASE) || (RTC_DEFAULT_YEAR >=RTC_YEAR_TOP)) #error RTC default year out of range. #endif #if (RTC_DEFAULT_MONTH < 1U) || (RTC_DEFAULT_MONTH > 12U) #error Bad RTC default month setting. #endif #if (RTC_DEFAULT_DATE < 1U) || (RTC_DEFAULT_DATE > 31U) #error Bad RTC default date setting. #endif #if (RTC_DEFAULT_HOUR > 23U) #error Bad RTC default hour setting. #endif #if (RTC_DEFAULT_MINUTE > 59U) #error Bad RTC default minute setting. #endif #if (RTC_DEFAULT_SECOND > 59U) #error Bad RTC default second setting. #endif /* Private variables ---------------------------------------------------------*/ static const uint8_t RTCMonthCalcTable[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30 , 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30 , 31, 30, 31}, }; volatile RTC_Time_st_t RTCTime; uint16_t RTCDaysBackup; /* Private function prototypes -----------------------------------------------*/ static void RTC_Days_To_Date_Conv ( uint16_t Days, RTC_Date_st_t *pDate ); static uint16_t RTC_Date_To_Days_Conv ( RTC_Date_st_t *pDate ); static uint8_t RTC_Determine_Leap_Year ( uint16_t Year ); /* Private functions ---------------------------------------------------------*/ /**************************************************************************//** * \brief Real time clock pre-initialization * \details Configure and start real time clock. Write default time into * real time clock. Call this function only once when power is * first turned on. * \retval None ******************************************************************************/ void RTC_Pre_Init(void) { uint16_t Days; un_rtc_wrt_t RTCTimeReg; RTC_Date_st_t RTCDefaultDate; /* Disable RTC Interrupts */ RTC_WINE = 0U; /* Reset internal counters before starting the RTC */ RTC_WTCR_ST = 1U; while (RTC_WTSR_RUN == 0U){} RTC_WTCR_ST = 0U; /* Write default time settings */ RTCDefaultDate.Year = RTC_DEFAULT_YEAR; RTCDefaultDate.Month = RTC_DEFAULT_MONTH; RTCDefaultDate.Date = RTC_DEFAULT_DATE; RTCTimeReg.stcField.u6WTSR = RTC_DEFAULT_SECOND; RTCTimeReg.stcField.u6WTMR = RTC_DEFAULT_MINUTE; RTCTimeReg.stcField.u5WTHR = RTC_DEFAULT_HOUR; Days = RTC_Date_To_Days_Conv (&RTCDefaultDate); while (RTC_WTSR_RUN){} RTC_RTR1_WTDR = Days; RTC_WRT = RTCTimeReg.u32Register; /* Select source clock */ RTC_WTCR_RCKSEL = 0U; /* Main clock (CLK_MAIN/2) is selected as CLKRTC */ /* Sub-Second Register */ RTC_WTBR = (CLK_MAIN_OSC_FREQ >> 2U) - 1U+(143u);/*校正92+46+23*/ /* Clear interrupts */ RTC_WINC = 0x0000007FUL; /* Debug mode */ #ifdef __DEBUG RTC_DEBUG_DBGEN = 1U; #else RTC_DEBUG_DBGEN = 0U; #endif /* Restart RTC */ RTC_WTCR_ST = 1U; } /**************************************************************************//** * \brief Initialization real time clock * \details Load time into registers. Call this function once when MCU * starts running. * \retval None ******************************************************************************/ void RTC_Init(void) { uint16_t Days; uint16_t DaysComp; un_rtc_wrt_t RTCTimeReg; RTC_Date_st_t RTCCurrentDate; do { Days = RTC_RTR1_WTDR; RTCTimeReg.u32Register = RTC_WRT; DaysComp = RTC_RTR1_WTDR; } while (Days != DaysComp); RTCDaysBackup = Days; RTC_Days_To_Date_Conv(Days, &RTCCurrentDate); RTCTime.Year = RTCCurrentDate.Year; RTCTime.Month = RTCCurrentDate.Month; RTCTime.Date = RTCCurrentDate.Date; RTCTime.Hour = (uint8_t)RTCTimeReg.stcField.u5WTHR; RTCTime.Minute = (uint8_t)RTCTimeReg.stcField.u6WTMR; RTCTime.Second = (uint8_t)RTCTimeReg.stcField.u6WTSR; RTCTime.LeapYear = RTC_Determine_Leap_Year ( RTCTime.Year ); } /**************************************************************************//** * \brief Set new time to real time clock * \param pTime: Pointer to a time setting structure * \retval None ******************************************************************************/ void RTC_Set_Time(RTC_Time_Setting_st_t *pTime) { uint8_t IsLeapYear; uint16_t Days; un_rtc_wrt_t RTCTimeReg; RTC_Date_st_t RTCCurrentDate; if (pTime != NULL) { if ((pTime -> Year >= RTC_YEAR_BASE) && (pTime -> Year < RTC_YEAR_TOP) && \ (pTime -> Month > 0U) && (pTime -> Month < 13U) && \ (pTime -> Hour < 24U) && (pTime -> Minute < 60U) && (pTime -> Second < 60U)) { IsLeapYear = RTC_Determine_Leap_Year (pTime -> Year); if ((pTime -> Date > 0U) && (pTime -> Date <= RTCMonthCalcTable[IsLeapYear][pTime -> Month - 1U])) { RTC_WTCR_ST = 0U; RTCCurrentDate.Year = pTime -> Year; RTCCurrentDate.Month = pTime -> Month; RTCCurrentDate.Date = pTime -> Date; RTCTimeReg.stcField.u6WTSR = pTime -> Second; RTCTimeReg.stcField.u6WTMR = pTime -> Minute; RTCTimeReg.stcField.u5WTHR = pTime -> Hour; Days = RTC_Date_To_Days_Conv ( &RTCCurrentDate ); RTCTime.Year = pTime -> Year; RTCTime.Month = pTime -> Month; RTCTime.Date = pTime -> Date; RTCTime.Hour = pTime -> Hour; RTCTime.Minute = pTime -> Minute; RTCTime.Second = pTime -> Second; RTCTime.LeapYear = IsLeapYear; RTCDaysBackup = Days; while (RTC_WTSR_RUN){} RTC_RTR1_WTDR = Days; RTC_WRT = RTCTimeReg.u32Register; RTC_WTCR_ST = 1U; } } } } /**************************************************************************//** * \brief Rreal time clock timing control * \attention Call this function every 100 ms. * \retval None ******************************************************************************/ void RTC_Timing_Service(void) { uint16_t Days; uint16_t DaysComp; un_rtc_wrt_t RTCTimeReg; RTC_Date_st_t RTCCurrentDate; do { Days = RTC_RTR1_WTDR; RTCTimeReg.u32Register = RTC_WRT; DaysComp = RTC_RTR1_WTDR; } while (Days != DaysComp); if(Days < 65380U) { RTCTime.Hour = (uint8_t)RTCTimeReg.stcField.u5WTHR; RTCTime.Minute = (uint8_t)RTCTimeReg.stcField.u6WTMR; RTCTime.Second = (uint8_t)RTCTimeReg.stcField.u6WTSR; if (Days != RTCDaysBackup) { RTC_Days_To_Date_Conv(Days, &RTCCurrentDate); RTCTime.Year = RTCCurrentDate.Year; RTCTime.Month = RTCCurrentDate.Month; RTCTime.Date = RTCCurrentDate.Date; RTCTime.LeapYear = RTC_Determine_Leap_Year(RTCTime.Year); RTCDaysBackup = Days; } } else { RTC_WTCR_ST = 0U; while (RTC_WTSR_RUN){} RTC_RTR1_WTDR = 0x0000U; RTC_WRT = 0x00000000UL; RTC_WTCR_ST = 1U; RTCTime.Hour = 0U; RTCTime.Minute = 0U; RTCTime.Second = 0U; RTCTime.Year = RTC_YEAR_BASE; RTCTime.Month = 1U; RTCTime.Date = 1U; RTCTime.LeapYear = 1U; RTCDaysBackup = 0U; } } /**************************************************************************//** * \brief Convert days to date * \param Days : Total days since Jan 1, RTC_YEAR_BASE * \param pDate: Pointer to a date structure * \retval None ******************************************************************************/ static void RTC_Days_To_Date_Conv(uint16_t Days, RTC_Date_st_t *pDate) { uint8_t i; uint8_t IsLeapYear; pDate -> Year = RTC_YEAR_BASE; pDate -> Year += ((Days / 1461U) << 2U); /* (Days / (366 + 365 * 3)) * 4 */ Days %= 1461U; if (Days > 365U) { IsLeapYear = 0U; pDate -> Year += (Days - 1U) / 365U; Days = (Days - 1U) % 365U; } else { IsLeapYear = 1U; } i = 1U; while (Days >= RTCMonthCalcTable[IsLeapYear][i - 1U]) { Days -= RTCMonthCalcTable[IsLeapYear][i - 1U]; i++; } pDate -> Month = i; pDate -> Date = (uint8_t)Days + 1U; } /**************************************************************************//** * \brief Convert date to days * \param pDate: Pointer to a date structure * \retval Total days since Jan 1, RTC_YEAR_BASE ******************************************************************************/ static uint16_t RTC_Date_To_Days_Conv ( RTC_Date_st_t *pDate ) { uint8_t i; uint8_t IsLeapYear; uint16_t Years; uint16_t TotalDays; TotalDays = 0U; Years = pDate -> Year; Years -= RTC_YEAR_BASE; TotalDays += Years * 365U; TotalDays += (Years + 3U) >> 2U; /* One day per leap year */ IsLeapYear = RTC_Determine_Leap_Year(pDate -> Year); for (i = 1U; i < pDate -> Month; i++) { TotalDays += RTCMonthCalcTable[IsLeapYear][i - 1U]; } TotalDays += (uint16_t)pDate -> Date - 1U; return TotalDays; } /**************************************************************************//** * \brief Determine if a year is leap year * \param Year: the year to be determined * \retval \arg 0: Not leap year * \arg 1: Leap year ******************************************************************************/ static uint8_t RTC_Determine_Leap_Year(uint16_t Year) { uint8_t IsLeapYear; if (Year & 0x0003U) { IsLeapYear = 0U; } else { IsLeapYear = 1U; } return IsLeapYear; }