Index: RTC.c =================================================================== diff -u -r57b76af2edf0f845fec8bfef5cd126a41223b58c -r949e97e3140d4d0327df2533ba3fa2e8394fa740 --- RTC.c (.../RTC.c) (revision 57b76af2edf0f845fec8bfef5cd126a41223b58c) +++ RTC.c (.../RTC.c) (revision 949e97e3140d4d0327df2533ba3fa2e8394fa740) @@ -14,8 +14,6 @@ * @date (original) 11-Jan-2020 * ***************************************************************************/ -#include // For calculating epoch - #include "mibspi.h" #include "FPGA.h" @@ -25,6 +23,10 @@ #include "Timers.h" #include "Utilities.h" +#ifdef USE_LIBRARY_TIME_FUNCTIONS +#include // For epoch conversion functions mktime and gmtime +#endif + /** * @addtogroup RTC * @{ @@ -116,13 +118,29 @@ #define MAX_ALLOWED_RTC_RAM_ADDRESS 512U ///< Max allowed RTC RAM legal address (512) #define TEN 10U ///< Ten #define YEAR_2000 2000U ///< Year 2000 -#define EPOCH_YEAR 1900U ///< Reference year to calculate epoch (1900) -#define YEAR_1900_TO_1970_SECONDS_DIFF 2208988800U ///< Difference in seconds from 1/1/1900 to 1/1/1970 (2208988800) +#ifndef USE_LIBRARY_TIME_FUNCTIONS + #define EPOCH_BASE_YEAR 1970U ///< Reference year to calculate epoch (1970) +#else + #define EPOCH_BASE_YEAR 1900U ///< Reference year to calculate epoch (1900) + #define YEAR_1900_TO_1970_SECONDS_DIFF 2208988800U ///< Difference in seconds from 1/1/1900 to 1/1/1970 (2208988800) + /// Six hour offset is needed because TI library works in CST. + #define OFFSET_6_HRS (6 * MIN_PER_HOUR * SEC_PER_MIN) -#ifdef _VECTORCAST_ -#define LOCAL_TO_GTM_TIME_CONVERSION 8U ///< Local time to GTM conversion for VectorCAST + #ifdef _VECTORCAST_ + #define LOCAL_TO_GTM_TIME_CONVERSION 8U ///< Local time to GTM conversion for VectorCAST + #endif #endif +#define SECS_IN_HOUR 3600 ///< Number of seconds in an hour. +#define MONTHS_IN_YEAR 12 ///< Number of months in a year. +#define LEAP_YEAR_MONTH ( 2 - 1 ) ///< Leap year month is February. Subtract 1 month for zero-based months. +#define SECONDS_IN_NORMAL_YEAR ( SECONDS_IN_A_DAY * 365 ) ///< Number of seconds in a normal year. +#define SECONDS_IN_LEAP_YEAR ( SECONDS_IN_A_DAY * 366 ) ///< Number of seconds in a leap year. +/// Number of seconds in 4 years. Includes an extra day for 1 leap within any 4 year period. +static const U32 SECONDS_IN_4_YEARS = SECONDS_IN_NORMAL_YEAR * 3 + SECONDS_IN_LEAP_YEAR; +/// Number of seconds in 2 years. Assumes no leap year in 2 year period. +static const U32 SECONDS_IN_2_YEARS = SECONDS_IN_NORMAL_YEAR * 2; + /// RTC self-test state enumeration. typedef enum RTC_Self_Test_States { @@ -203,14 +221,21 @@ static U16 RAMBuffer[ MIBSPI_MAX_BUFFER_LENGTH ]; ///< Buffer to read RTC RAM data. static U16 previousFPGATimerCount = 0; ///< Previous FPGA timer count; +/// Array of days in each month. Assumes non-leap year. Must adjust days in February if leap year. +static U32 daysInMonth[ 12 ] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // ********** Private function prototypes ********* static BOOL serviceRTC( U16* bufferTransmit, U16* bufferReceive, U16 bufferLength ); static BOOL isRTCFunctional( void ); static U08 convertBCD2Decimal( U08 bcd ); static U08 convertDecimal2BCD( U08 decimal ); +static U32 convertDateTime2Epoch( RTC_TIMESTAMP_T dateTime ); +static BOOL convertEpoch2DateTime( U32 epoch ); +#ifdef USE_LIBRARY_TIME_FUNCTIONS static U32 convertTime2Epoch( void ); +static void convertEpoch2Time( U32 epoch ); +#endif static void updateReadTimestampStruct( void ); static BOOL setMibSPIBufferLength( U16 length ); static void prepBufferForReadCommand( U08 length ); // Puts the read command @@ -228,8 +253,6 @@ static RTC_EXEC_STATE_T handleExecReadFromRAMState( void ); static RTC_EXEC_STATE_T handleExecWriteState( void ); -// ********** Public functions ********** - /*********************************************************************//** * @brief * The initRTC initializes the RTC module. @@ -243,7 +266,7 @@ RTCServiceState = RTC_SEND_COMMAND; } - /*********************************************************************//** +/*********************************************************************//** * @brief * The setRTCTimestamp gets the timestamp values from caller, converts them * into BCD format and inserts them into the txBuffer to be written into the RTC. @@ -302,6 +325,33 @@ } /*********************************************************************//** +* @brief +* The setRTCEpoch sets the clock from a given epoch. +* @details Inputs: none +* @details Outputs: RTCNewTimestampStruct +* @param epoch date/time stamp epoch +* @return TRUE if clock set, FALSE if not. +*************************************************************************/ +BOOL setRTCEpoch( U32 epoch ) +{ + BOOL result; + +#ifndef USE_LIBRARY_TIME_FUNCTIONS + result = convertEpoch2DateTime( epoch ); +#else + convertEpoch2Time( epoch ); +#endif + result = setRTCTimestamp( RTCNewTimestampStruct.seconds, + RTCNewTimestampStruct.minutes, + RTCNewTimestampStruct.hours, + RTCNewTimestampStruct.days, + RTCNewTimestampStruct.months, + RTCNewTimestampStruct.years); + + return result; +} + +/*********************************************************************//** * @brief * The execRTCSelfTest runs the RTC POST during the self-test. * @details Inputs: RTCSelfTestState @@ -779,6 +829,95 @@ /*********************************************************************//** * @brief + * The convertTime2Epoch converts a given date/time record into an epoch + * (seconds from Jan 1 1970). + * @details Inputs: none + * @details Outputs: none + * @param dateTime UTC Date & Time structure to convert to epoch + * @return epoch time (seconds since Jan 1, 1970) + *************************************************************************/ +static U32 convertDateTime2Epoch( RTC_TIMESTAMP_T dateTime ) +{ + U32 epoch; + U32 i, dayCnt; + U32 secs = dateTime.seconds; // Expecting seconds to be 0..59. + U32 mins = dateTime.minutes; // Expecting minutes to be 0..59. + U32 hrs = dateTime.hours; // Expecting hours to be 0..23. + U32 days = dateTime.days - 1; // Expecting days to be 1..31. Current (partial) day covered by hrs, min, sec so subtract current day. + U32 yrs = dateTime.years + YEAR_2000 - EPOCH_BASE_YEAR; // Expecting 2-digit year - no century from our RTC - so must assume year is >= 2000 - then subtract epoch base year. + U32 lyrs = ( yrs / 4 ) + ( yrs % 4 > 2 ? 1 : 0 ); // First leap year after epoch base year (1970) is 1972 (so every 3rd of every 4 years). + BOOL leap = ( dateTime.years % 4 == 0 ? TRUE : FALSE ); + + // Compute number of full days since start of given year. + daysInMonth[ LEAP_YEAR_MONTH ] = ( TRUE == leap ? 29 : 28 ); + dayCnt = 0; + for ( i = 1; i < dateTime.months; i++ ) // Expecting months to be 1..12 + { + dayCnt += daysInMonth[ i-1 ]; + } + + // Compute epoch from given date/time. + epoch = secs + ( mins * SEC_PER_MIN ) + ( hrs * SECS_IN_HOUR ); + epoch += ( days * SECONDS_IN_A_DAY ); + epoch += ( dayCnt * SECONDS_IN_A_DAY ); + epoch += ( lyrs * SECONDS_IN_LEAP_YEAR ); + epoch += ( ( yrs - lyrs ) * SECONDS_IN_NORMAL_YEAR ); + + return epoch; +} + +/*********************************************************************//** + * @brief + * The convertEpoch2DateTime converts a given epoch to a date/time record (UTC). + * @details Inputs: none + * @details Outputs: Sets the RTC to the date/time converted from given epoch. + * @param epoch Number of seconds since Jan 1, 1970 + * @return TRUE if conversion successful, FALSE if not + *************************************************************************/ +static BOOL convertEpoch2DateTime( U32 epoch ) +{ + BOOL result; + RTC_TIMESTAMP_T dtTime; + U32 i, dayCnt; + U32 fourYears = epoch / SECONDS_IN_4_YEARS; + U32 twoYrEpoch = epoch - ( fourYears * SECONDS_IN_4_YEARS ); + U32 twoYears = twoYrEpoch / SECONDS_IN_2_YEARS; + U32 oneYrEpoch = twoYrEpoch - ( twoYears * SECONDS_IN_2_YEARS ); + U32 oneYears = ( twoYears > 0 ? oneYrEpoch / SECONDS_IN_LEAP_YEAR : oneYrEpoch / SECONDS_IN_NORMAL_YEAR ); + U32 days = ( twoYears > 0 ? ( oneYrEpoch - SECONDS_IN_LEAP_YEAR ) / SECONDS_IN_A_DAY : ( oneYrEpoch - SECONDS_IN_NORMAL_YEAR ) / SECONDS_IN_A_DAY ); + U32 wholeYears = ( fourYears * 4 ) + ( twoYears * 2 ) + oneYears; + BOOL leapYear = ( twoYears > 0 && 0 == oneYears ? TRUE : FALSE ); + U32 oneDayEpoch= oneYrEpoch % SECONDS_IN_A_DAY; + U32 hours = oneDayEpoch / SECS_IN_HOUR; + U32 oneHrEpoch = oneDayEpoch - ( hours * SECS_IN_HOUR ); + U32 mins = oneHrEpoch / SEC_PER_MIN; + U32 secs = oneHrEpoch - ( mins * SEC_PER_MIN ); + + dtTime.years = EPOCH_BASE_YEAR + wholeYears; + dtTime.months = 0; + dayCnt = 0; + daysInMonth[ LEAP_YEAR_MONTH ] = ( TRUE == leapYear ? 29 : 28 ); + for ( i = 0; i < MONTHS_IN_YEAR; i++ ) + { + if ( days >= dayCnt ) + { + dtTime.months = i + 1; + dtTime.days = days - dayCnt; + } + dayCnt += daysInMonth[ i ]; + } + dtTime.days += 1; // days will be number of full days in current month so need to add 1 for the partial current day to get day of month + dtTime.hours = hours; + dtTime.minutes = mins; + dtTime.seconds = secs; + result = setRTCTimestamp( dtTime.seconds, dtTime.minutes, dtTime.hours, dtTime.days, dtTime.months, dtTime.years ); + + return result; +} + +#ifdef USE_LIBRARY_TIME_FUNCTIONS +/*********************************************************************//** + * @brief * The convertTime2Epoch converts the time into epoch (seconds from Jan 1 1970). * @details Inputs: RTCTimestampStruct * @details Outputs: none @@ -795,7 +934,7 @@ // In epoch conversion, the months are 0-11 // so the months is decremented t.tm_mon = RTCTimestampStruct.months - 1; - t.tm_year = RTCTimestampStruct.years + YEAR_2000 - EPOCH_YEAR; + t.tm_year = RTCTimestampStruct.years + YEAR_2000 - EPOCH_BASE_YEAR; // Disabled daylight saving // If daylight saving is not disabled, MinGW in VectorCAST // may or may not calculate the value right @@ -811,11 +950,41 @@ epochTime = mktime(&t); epochTime = epochTime - YEAR_1900_TO_1970_SECONDS_DIFF; #endif + return (U32)epochTime; } +#endif +#ifdef USE_LIBRARY_TIME_FUNCTIONS /*********************************************************************//** * @brief + * The convertEpoch2Time converts a given epoch date/time to a date/time + * structure that can be used to set the clock. + * @details Inputs: none + * @details Outputs: RTCNewTimestampStruct + * @param epoch epoch time stamp (seconds since 1/1/1970) + * @return none + *************************************************************************/ +static void convertEpoch2Time( U32 epoch ) +{ + struct tm *ptm; + + // Convert epoch to date/time structure + epoch += YEAR_1900_TO_1970_SECONDS_DIFF; + epoch -= OFFSET_6_HRS; // For some reason, TI works in CST time zone instead of GMT + ptm = gmtime( &epoch ); + // Copy date/time stamp to our structure + RTCNewTimestampStruct.years = ptm->tm_year + EPOCH_BASE_YEAR; // epoch is seconds from 1/1/1900. + RTCNewTimestampStruct.months = ptm->tm_mon + 1; // months from gmtime() are 0..11 - convert to 1..12. + RTCNewTimestampStruct.days = ptm->tm_mday; + RTCNewTimestampStruct.hours = ptm->tm_hour; + RTCNewTimestampStruct.minutes = ptm->tm_min; + RTCNewTimestampStruct.seconds = ptm->tm_sec; +} +#endif + +/*********************************************************************//** + * @brief * The updateReadTimestampStruct function updates the time structure * after every read. * @details Inputs: RTCTimestampStruct @@ -1149,7 +1318,11 @@ if ( isRTCFunctional() ) { updateReadTimestampStruct(); +#ifndef USE_LIBRARY_TIME_FUNCTIONS + lastEpochTime = convertDateTime2Epoch( RTCTimestampStruct ); +#else lastEpochTime = convertTime2Epoch(); +#endif timeCounter = 1; broadcastRTCEpoch( lastEpochTime ); }