/************************************************************************** * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file RTC.c * * @date 27-Nov-2019 * @author D. Navaei * * @brief Monitor/Controller Real Time Clock * **************************************************************************/ #include // For calculating epoch #include "Common.h" #include "Timers.h" #include "RTC.h" #include "mibspi.h" #include "SystemCommMessages.h" #include "OperationModes.h" // ********** Definitions ********** #define RTC_REG_1_12_HOUR_MODE_MASK 0x0004 // 12-hour mode mask #define RTC_REG_1_PORO 0x0008 // Power On Reset Override #define RTC_REG_1_CLK_STOPPED_MASK 0x0020 // RTC source clock mask #define RTC_REG_1_UNUSED_MASK 0x0040 // Unused mask #define RTC_REG_1_EXT_CLK_MODE_MASK 0x0080 // RTC external clock test mode mask #define RTC_REG_2_MSF_MASK 0x0080 // Minute or second interrupt mask #define RTC_REG_2_CDTF_MASK 0x0008 // Countdown timer interrupt mask #define RTC_REG_2_AF_MASK 0x0010 // Alarm interrupt mask #define RTC_REG_2_TSF2_MASK 0x0020 // Timestamp interrupt mask #define RTC_REG_3_BF_MASK 0x0008 // Battery status interrupt flag #define RTC_REG_3_BLF_MASK 0x0004 // Battery status low flag // Indices used to check values read from RTC #define RTC_REG_1_INDEX 1U #define RTC_REG_2_INDEX 2U #define RTC_REG_3_INDEX 3U #define RTC_SECONDS_INDEX 4U #define RTC_MINUTES_INDEX 5U #define RTC_HOURS_INDEX 6U #define RTC_DAYS_INDEX 7U #define RTC_WEEKDAYS_INDEX 8U #define RTC_MONTHS_INDEX 9U #define RTC_YEARS_INDEX 10U // Time and date acceptable ranges #define MAX_ALLOWED_SECONDS 59U #define MAX_ALLOWED_MINUTES 59U #define MAX_ALLWOED_HOURS 23U #define MAX_ALLOWED_DAYS 31U #define MIN_ALLOWED_DAYS 1U #define MAX_ALLOWED_MONTHS 12U #define MIN_ALLOWED_MONTHS 1U #define MAX_ALLOWED_YEARS 99U #define BUFFER_INDEX_0 0U #define BUFFER_INDEX_1 1U #define BUFFER_INDEX_2 2U #define BUFFER_INDEX_3 3U #define BUFFER_INDEX_4 4U #define MIBSPI_MAX_BUFFER_LENGTH 127U #define MIBSPI_CONTINUOUS_MODE 4U #define MIBSPI_CHIP_SELECT_ACTIVE 1U #define MIBSPI_CHIP_SELECT_DEACTIVE 0U #define MIBSPI_NO_WDELAY 0U #define MIBSPI_LOCK_TG 0U #define MIBSPI_DATA_FORMAT_ZERO 0U #define MIBSPI_GROUP_ZERO 0U #define MIBSPI_BUFFER_MODE_BIT_SHIFT_13 13U #define MIBSPI_CHIP_SELECT_BIT_SHIFT_12 12U #define MIBSPI_NO_WDELAY_BIT_SHIFT_10 10U #define MIBSPI_LOCK_TRANS_BIT_SHIFT_11 11U #define MIBSPI_DATA_FORMAT_ZERO_BIT_SHIFT_8 8U #define MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 8U #define RTC_RAM_PREP_BUFFER_LENGTH 3U #define RTC_TIMESTAMP_BUFFER_LENGTH 8U #define RTC_GENERAL_BUFFER_LENGTH 11U #define RTC_PREP_RAM_INDEX 0U #define RTC_RAM_HIGH_ADDRESS_INDEX 1U #define RTC_RAM_LOW_ADDRESS_INDEX 2U #define RTC_RAM_COMMAND_INDEX 3U #define RTC_READ_FROM_REG0 0x00A0 // RTC read from address 0 #define RTC_WRITE_TO_REG3 0x0023 // Seconds register #define RTC_WRITE_TO_REG0 0x0020 #define RTC_PREP_RAM_READ_WRITE 0x003A // RTC cmd prior to RAM ops #define RTC_WRITE_TO_RAM 0x003C // RTC RAM write #define RTC_READ_FROM_RAM 0x00BD // RTC RAM read #define RTC_ACCURACY_TIMEOUT 1000U // ms #define RTC_ACCURACY_TIMEOUT_TOLERANCE 1050U // ms #define RTC_PUBLISH_INTERVAL 18U // Task general counts #define TIMER_COUNTER_TO_REQUEST_READ 18U #define MAX_ALLOWED_FAILED_RTC_TRANSFERS 3U #define MAX_ALLOWED_RTC_RAM_BYTES 100U #define MAX_ALLOWED_RTC_RAM_ADDRESS 512U #define TEN 10U #define YEAR_2000 2000U #define EPOCH_YEAR 1900U #define YEAR_1900_TO_1970_SECONDS_DIFF 2208988800U #ifdef _VECTORCAST_ #define LOCAL_TO_GTM_TIME_CONVERSION 8U #endif typedef enum RTC_Self_Test_States { RTC_SELF_TEST_STATE_START = 0, RTC_SELF_TEST_STATE_CHECK_CTRL_REGS, RTC_SELF_TEST_STATE_WAIT_FOR_FIRST_SECOND, RTC_SELF_TEST_STATE_WAIT_FOR_SECOND_SECOND, RTC_SELF_TEST_STATE_CHECK_ACCURACY, RTC_SELF_TEST_STATE_COMPLETE, NUM_OF_RTC_SELF_TEST_STATES } RTC_SELF_TEST_STATE_T; typedef enum RTC_Read_Data { RTC_SEND_COMMAND = 0, RTC_WAIT_FOR_TRANSFER_AND_READ, RTC_SERVICE_COMPLETE, NUM_OF_RTC_SERVICE_STATES } RTC_GET_DATA_STATE_T; typedef enum RTC_Exec_State { RTC_EXEC_STATE_WAIT_FOR_POST = 0, RTC_EXEC_STATE_IDLE, RTC_EXEC_STATE_PREP_RAM, RTC_EXEC_STATE_WRITE_TO_RAM, RTC_EXEC_STATE_READ_FROM_RAM, RTC_EXEC_STATE_READ, RTC_EXEC_STATE_WRITE, RTC_EXEC_STATE_FAULT, NUM_OF_RTC_EXEC_STATES } RTC_EXEC_STATE_T; #pragma pack(push,4) typedef struct { U16 seconds; U16 minutes; U16 hours; U16 days; U16 weekdays; U16 months; U16 years; } RTC_TIMESTAMP_T; #pragma pack(pop) // ********** private data ********** static RTC_SELF_TEST_STATE_T RTCSelfTestState = RTC_SELF_TEST_STATE_START; static RTC_GET_DATA_STATE_T RTCServiceState = RTC_SEND_COMMAND; static RTC_EXEC_STATE_T RTCExecState = RTC_EXEC_STATE_WAIT_FOR_POST; static SELF_TEST_STATUS_T RTCSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; static RTC_RAM_STATUS_T RTCRAMStatus = RTC_RAM_STATUS_IDLE; static RTC_RAM_STATE_T RTCRAMState = RTC_RAM_STATE_READY; static RTC_TIMESTAMP_T RTCTimestampStruct; static RTC_TIMESTAMP_T RTCNewTimestampStruct; static U32 RTCSelfTestTimer = 0; static U32 RTCPreviousSecond = 0; // Previous second is used to compare seconds in POST static U32 RAMBufferLength = 0; static U32 lastEpochTime = 0; // Last value that has been converted to epoch static U32 previousTransferLength = 0; static U32 timeCounter = 1; static U32 numberOfFailedRTCTransfers = 1; static BOOL hasWriteToRTCRequested = FALSE; static BOOL hasWriteToRAMRequested = FALSE; static BOOL hasReadFromRAMRequested = FALSE; static BOOL isRTCServiceOnEntry = FALSE; static BOOL isTimestampBufferReady = FALSE; static U16 rxBuffer[ MIBSPI_MAX_BUFFER_LENGTH + 1 ]; static U16 txBuffer[ MIBSPI_MAX_BUFFER_LENGTH + 1 ]; static U16 prepRAMBuffer[ RTC_RAM_PREP_BUFFER_LENGTH ]; static U16 RAMBuffer[ MIBSPI_MAX_BUFFER_LENGTH ]; // ********** 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 convertTime2Epoch( void ); static void updateReadTimestampStruct( void ); static BOOL setMibSPIBufferLength( U16 length ); static void prepBufferForReadCommand( U08 length ); // Puts the read command static RTC_SELF_TEST_STATE_T handleSelfTestStart( void ); static RTC_SELF_TEST_STATE_T handleSelfTestCheckCtrlRegs( void ); static RTC_SELF_TEST_STATE_T handleSelfTestWaitForFirstSecond( void ); static RTC_SELF_TEST_STATE_T handleSelfTestWaitForSecondSecond( void ); static RTC_SELF_TEST_STATE_T handleSelfTestCheckAccuracy( void ); static RTC_EXEC_STATE_T handleExecWaitForPostState( void ); static RTC_EXEC_STATE_T handleExecIdleState( void ); static RTC_EXEC_STATE_T handleExecReadState( void ); static RTC_EXEC_STATE_T handleExecPrepRAMState( void ); static RTC_EXEC_STATE_T handleExecWriteToRAMState( void ); static RTC_EXEC_STATE_T handleExecReadFromRAMState( void ); static RTC_EXEC_STATE_T handleExecWriteState( void ); // ********** Public functions ********** /************************************************************************* * @brief initRTC * The initRTC initializes the RTC * @details * Inputs : none * Outputs : The function is empty for now * @param none * @return none *************************************************************************/ void initRTC( void ) { RTCSelfTestState = RTC_SELF_TEST_STATE_START; RTCServiceState = RTC_SEND_COMMAND; } /************************************************************************* * @brief setRTCTimestamp * The setRTCTimestamp gets the timestamp values, converts them into BCD * and inserts them into the txBuffer to be written into the RTC * @details * Inputs : seconds, minutes, hours, days, months, years * Outputs : txBuffer will be prepared with the timestamp * @param none * @return none *************************************************************************/ BOOL setRTCTimestamp( U08 secs, U08 mins, U08 hours, U08 days, U08 months, U32 years ) { BOOL isDataOk = TRUE; if ( secs > MAX_ALLOWED_SECONDS ) { isDataOk = FALSE; } else if ( mins > MAX_ALLOWED_MINUTES ) { isDataOk = FALSE; } else if ( hours > MAX_ALLWOED_HOURS ) { isDataOk = FALSE; } else if ( days < MIN_ALLOWED_DAYS || days > MAX_ALLOWED_DAYS ) { isDataOk = FALSE; } else if ( months < MIN_ALLOWED_MONTHS || months > MAX_ALLOWED_MONTHS ) { isDataOk = FALSE; } else if ( ( years - YEAR_2000 ) > MAX_ALLOWED_YEARS ) { isDataOk = FALSE; } else { hasWriteToRTCRequested = TRUE; isTimestampBufferReady = FALSE; RTCNewTimestampStruct.seconds = secs; RTCNewTimestampStruct.minutes = mins; RTCNewTimestampStruct.hours = hours; RTCNewTimestampStruct.days = days; RTCNewTimestampStruct.weekdays = 0; // Weekdays will not be used RTCNewTimestampStruct.months = months; RTCNewTimestampStruct.years = years; } return isDataOk; } /************************************************************************* * @brief execRTCSelfTest * The execRTCSelfTest runs the RTC POST during the self test * @details * Inputs : none * Outputs : SELF_TEST_STATUS_T * @param none * @return SELF_TEST_STATUS_T *************************************************************************/ SELF_TEST_STATUS_T execRTCSelfTest( void ) { switch ( RTCSelfTestState ) { case RTC_SELF_TEST_STATE_START: RTCSelfTestState = handleSelfTestStart(); break; case RTC_SELF_TEST_STATE_CHECK_CTRL_REGS: RTCSelfTestState = handleSelfTestCheckCtrlRegs(); break; case RTC_SELF_TEST_STATE_WAIT_FOR_FIRST_SECOND: RTCSelfTestState = handleSelfTestWaitForFirstSecond(); break; case RTC_SELF_TEST_STATE_WAIT_FOR_SECOND_SECOND: RTCSelfTestState = handleSelfTestWaitForSecondSecond(); break; case RTC_SELF_TEST_STATE_CHECK_ACCURACY: RTCSelfTestState = handleSelfTestCheckAccuracy(); break; case RTC_SELF_TEST_STATE_COMPLETE: // Done with the state // TODO: If POST failed, set the proper alarm break; default: // TODO: Add the alarms RTCSelfTestResult = SELF_TEST_STATUS_FAILED; break; } return RTCSelfTestResult; } /************************************************************************* * @brief execRTC * The execRTC runs the RTC during normal operations * @details * Inputs : none * Outputs : none * @param none * @return none *************************************************************************/ void execRTC( void ) { switch ( RTCExecState ) { case RTC_EXEC_STATE_WAIT_FOR_POST: RTCExecState = handleExecWaitForPostState(); break; case RTC_EXEC_STATE_IDLE: RTCExecState = handleExecIdleState(); break; case RTC_EXEC_STATE_PREP_RAM: RTCExecState = handleExecPrepRAMState(); break; case RTC_EXEC_STATE_WRITE_TO_RAM: RTCExecState = handleExecWriteToRAMState(); break; case RTC_EXEC_STATE_READ_FROM_RAM: RTCExecState = handleExecReadFromRAMState(); break; case RTC_EXEC_STATE_WRITE: RTCExecState = handleExecWriteState(); break; case RTC_EXEC_STATE_READ: RTCExecState = handleExecReadState(); break; case RTC_EXEC_STATE_FAULT: // Something failed set the alarms // TODO: set the alarms and stuff break; default: break; } } /************************************************************************* * @brief getRTCTimestamp * The getRTCTimestamp returns the current time in epoch * @details * Inputs : none * Outputs : current time in epoch * @param none * @return lastEpochTime *************************************************************************/ U32 getRTCTimestamp( void ) { return lastEpochTime; } /************************************************************************* * @brief writeToRAM * The writeToRAM checks whether the RAM status is idle and if it is, it * will check input address and length to make sure they are within the * range. If everything is fine, it will prepare the txBuffer and set the * RAM status to busy * @details * Inputs : address, data, length * Outputs : RTC_RAM_STATUS_T * @param none * @return RTC_RAM_STATUS_T *************************************************************************/ RTC_RAM_STATUS_T writeToRAM( U16 address, U16* data, U32 length ) { RTC_RAM_STATUS_T status = RTCRAMStatus; if ( status == RTC_RAM_STATUS_IDLE ) { if ( address > MAX_ALLOWED_RTC_RAM_ADDRESS ) { status = RTC_RAM_STATUS_ILLEGAL_ADDRESS; } else if ( length > MAX_ALLOWED_RTC_RAM_BYTES ) { status = RTC_RAM_STATUS_BYTES_EXCEEDED; } else { U08 i; RTCRAMStatus = status = RTC_RAM_STATUS_IN_PROGRESS; RTCRAMState = RTC_RAM_STATE_BUSY; hasWriteToRAMRequested = TRUE; RAMBufferLength = length; prepRAMBuffer[ RTC_PREP_RAM_INDEX ] = RTC_PREP_RAM_READ_WRITE; prepRAMBuffer[ RTC_RAM_HIGH_ADDRESS_INDEX ] = ( address >> SHIFT_8_BITS_FOR_BYTE_SHIFT ); prepRAMBuffer[ RTC_RAM_LOW_ADDRESS_INDEX ] = ( address & MASK_OFF_MSB ); txBuffer[ BUFFER_INDEX_0 ] = RTC_WRITE_TO_RAM; for ( i = 0; i < RAMBufferLength; i++ ) { txBuffer[ i + BUFFER_INDEX_1 ] = data[ i ]; } } } return status; } /************************************************************************* * @brief readFromRAM * The readFromRAM checks whether the RAM status is idle and if it is, it * will check input address and length to make sure they are within the * range. If everything is fine, it will prepare the txBuffer and set the * RAM status to busy * @details * Inputs : address, length * Outputs : RTC_RAM_STATUS_T * @param none * @return RTC_RAM_STATUS_T *************************************************************************/ RTC_RAM_STATUS_T readFromRAM( U16 address, U32 length ) { RTC_RAM_STATUS_T status = RTCRAMStatus; if ( status == RTC_RAM_STATUS_IDLE ) { if ( address > MAX_ALLOWED_RTC_RAM_ADDRESS ) { status = RTC_RAM_STATUS_ILLEGAL_ADDRESS; } else if ( length > MAX_ALLOWED_RTC_RAM_BYTES ) { status = RTC_RAM_STATUS_BYTES_EXCEEDED; } else { U08 i; status = RTCRAMStatus = RTC_RAM_STATUS_IN_PROGRESS; RTCRAMState = RTC_RAM_STATE_BUSY; hasReadFromRAMRequested = TRUE; RAMBufferLength = length; prepRAMBuffer[ RTC_PREP_RAM_INDEX ] = RTC_PREP_RAM_READ_WRITE; prepRAMBuffer[ RTC_RAM_HIGH_ADDRESS_INDEX ] = ( address >> SHIFT_8_BITS_FOR_BYTE_SHIFT ); prepRAMBuffer[ RTC_RAM_LOW_ADDRESS_INDEX ] = ( address & MASK_OFF_MSB ); txBuffer[ BUFFER_INDEX_0 ] = RTC_READ_FROM_RAM; for ( i = 0; i < RAMBufferLength; i++ ) { txBuffer[ i + BUFFER_INDEX_1 ] = 0x0000; } } } return status; } /************************************************************************* * @brief getRTCRAMState * The getRTCRAMState returns the RAM state * @details * Inputs : none * Outputs : RTC_RAM_STATE_T * @param none * @return RTC_RAM_STATE_T *************************************************************************/ RTC_RAM_STATE_T getRTCRAMState( void ) { return RTCRAMState; } /************************************************************************* * @brief getRTCRAMStatus * The getRTCRAMStatus returns the RAM state. If the RAM status is complete * it will set the RAM status to Idle and the RAM state to Ready * @details * Inputs : none * Outputs : RTC_RAM_STATE_T * @param none * @return RTC_RAM_STATE_T *************************************************************************/ RTC_RAM_STATUS_T getRTCRAMStatus( void ) { if ( RTCRAMStatus == RTC_RAM_STATUS_COMPLETE ) { RTCRAMStatus = RTC_RAM_STATUS_IDLE; RTCRAMState = RTC_RAM_STATE_READY; } return RTCRAMStatus; } /************************************************************************* * @brief getDataFromRAM * The getDataFromRAM populates the provided external buffer with the data * in the specified address in the RAM * @details * Inputs : external buffer (pointer), length * Outputs : none * @param none * @return none *************************************************************************/ void getDataFromRAM( U16* externalBuffer, U32 length ) { U08 i; for ( i = 0; i < length; i++ ) { externalBuffer[ i ] = RAMBuffer[ i + 1 ]; } } // ********** Private functions ********* /*void mibspiNotification(mibspiBASE_t *mibspi, uint32 flags) { } void mibspiGroupNotification(mibspiBASE_t *mibspi, uint32 group) { } */ /************************************************************************* * @brief serviceRTC * The serviceRTC is the interface to the RTC chip: * If it was called for the first time, it will send the command * If the provided buffer length is not the same as previous, it will * set the buffer length. Otherwise it will ignore it * If it was not called for the first time, it will wait to the buffer * to be transmitted and then it will populate the provided receive buffer * If the transaction failed, it will try 3 time before it fails * @details * Inputs : bufferTransmit, bufferReceive, bufferLength * Outputs : result (bool) * @param none * @return result (bool) *************************************************************************/ static BOOL serviceRTC( U16* bufferTransmit, U16* bufferReceive, U16 bufferLength ) { BOOL result = FALSE; BOOL isBufferOk = FALSE; if ( isRTCServiceOnEntry ) { RTCServiceState = RTC_SEND_COMMAND; } switch ( RTCServiceState ) { case RTC_SEND_COMMAND: if ( previousTransferLength == bufferLength ) { isBufferOk = TRUE; } else if ( setMibSPIBufferLength( bufferLength ) ) { isBufferOk = TRUE; } if ( isBufferOk ) { mibspiSetData( mibspiREG3, MIBSPI_GROUP_ZERO, bufferTransmit ); mibspiTransfer( mibspiREG3, MIBSPI_GROUP_ZERO ); numberOfFailedRTCTransfers = 0; previousTransferLength = bufferLength; RTCServiceState = RTC_WAIT_FOR_TRANSFER_AND_READ; } else { RTCServiceState = RTC_SERVICE_COMPLETE; } isRTCServiceOnEntry = FALSE; break; case RTC_WAIT_FOR_TRANSFER_AND_READ: if ( mibspiIsTransferComplete( mibspiREG3, MIBSPI_GROUP_ZERO ) ) { mibspiGetData( mibspiREG3, MIBSPI_GROUP_ZERO, bufferReceive ); RTCServiceState = RTC_SERVICE_COMPLETE; result = TRUE; } else if ( numberOfFailedRTCTransfers >= MAX_ALLOWED_FAILED_RTC_TRANSFERS ) { RTCServiceState = RTC_SERVICE_COMPLETE; } else { // Transfer to RTC failed. This transfer // should be done in 50ms numberOfFailedRTCTransfers++; } // Done with read (successful of failed) // get ready for another call isRTCServiceOnEntry = TRUE; break; case RTC_SERVICE_COMPLETE: // Done with reading and transfer break; default: // We should never get here break; } return result; } /************************************************************************* * @brief isRTCFunctional * The isRTCFunctional checks whether the RTC is still functional by checking * the bits in the first 3 control registers. The function ignore the clear * flags * @details * Inputs : none * Outputs : hasTestPassed (bool) * @param none * @return hasTestPassed (bool) *************************************************************************/ static BOOL isRTCFunctional( void ) { // TODO: Add alarms BOOL hasTestPassed = TRUE; U16 controlReg1 = rxBuffer[ RTC_REG_1_INDEX ]; U16 controlReg2 = rxBuffer[ RTC_REG_2_INDEX ]; U16 controlReg3 = rxBuffer[ RTC_REG_3_INDEX ]; controlReg1 = controlReg1 & MASK_OFF_MSB; controlReg2 = controlReg2 & MASK_OFF_MSB; controlReg3 = controlReg3 & MASK_OFF_MSB; // Ignore the clear flags controlReg2 = controlReg2 & ~RTC_REG_2_MSF_MASK; controlReg2 = controlReg2 & ~RTC_REG_2_CDTF_MASK; controlReg2 = controlReg2 & ~RTC_REG_2_AF_MASK; controlReg2 = controlReg2 & ~RTC_REG_2_TSF2_MASK; controlReg3 = controlReg3 & ~RTC_REG_3_BF_MASK; if ( controlReg1 & RTC_REG_1_12_HOUR_MODE_MASK ) { hasTestPassed = FALSE; } if ( controlReg1 & RTC_REG_1_PORO ) { hasTestPassed = FALSE; } if ( controlReg1 & RTC_REG_1_CLK_STOPPED_MASK ) { hasTestPassed = FALSE; } if ( controlReg1 & RTC_REG_1_UNUSED_MASK ) { hasTestPassed = FALSE; } if ( controlReg1 & RTC_REG_1_EXT_CLK_MODE_MASK ) { hasTestPassed = FALSE; } if ( controlReg3 & RTC_REG_3_BLF_MASK ) { hasTestPassed = FALSE; } return hasTestPassed; } /************************************************************************* * @brief convertBCD2Decimal * The convertBCD2Decimal converts the BCD values to decimal * @details * Inputs : bcd value * Outputs : decimal value * @param none * @return decimal value *************************************************************************/ static U08 convertBCD2Decimal( U08 bcd ) { U08 bcdHigh; U08 bcdLow; U08 decimal; bcdHigh = ( bcd & MASK_OFF_NIBBLE_LSB ) >> SHIFT_BITS_BY_4; bcdLow = ( bcd & MASK_OFF_NIBBLE_MSB ); if ( bcdHigh > MAX_SINGLE_DIGIT_DECIMAL || bcdLow > MAX_SINGLE_DIGIT_DECIMAL ) { // ToDo software fault decimal = 0; } else { decimal = ( bcdHigh * TEN ) + bcdLow; } return decimal; } /************************************************************************* * @brief convertDecimal2BCD * The convertDecimal2BCD converts the decimal values to BCD * @details * Inputs : decimal value * Outputs : bcd value * @param none * @return bcd value *************************************************************************/ static U08 convertDecimal2BCD( U08 decimal ) { U08 decimalHigh; U08 decimalLow; U08 bcd; if ( decimal > MAX_DOUBLE_DIGIT_DECIMAL ) { // TODO: Add software fault bcd = 0; } else { decimalHigh = decimal / TEN; decimalLow = decimal % TEN; bcd = ( decimalHigh << SHIFT_BITS_BY_4 ) + decimalLow; } return bcd; } /************************************************************************* * @brief convertTime2Epoch * The convertTime2Epoch converts the time into epoch (seconds from Jan 1 1970) * @details * Inputs : none * Outputs : epochTime value * @param none * @return epochTime value *************************************************************************/ static U32 convertTime2Epoch( void ) { struct tm t; time_t epochTime; t.tm_sec = RTCTimestampStruct.seconds; t.tm_min = RTCTimestampStruct.minutes; t.tm_hour = RTCTimestampStruct.hours; t.tm_mday = RTCTimestampStruct.days; // 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; // Disabled daylight saving // If daylight saving is not disabled, MinGW in VectorCAST // may or may not calculate the value right t.tm_isdst= 0; #ifdef _VECTORCAST_ // MinGW in VectorCAST assumes the time is local while // the time is in GMT so 8 hours is subtracted from the hour // to simulate GMT for VectorCAST t.tm_hour = t.tm_hour - LOCAL_TO_GTM_TIME_CONVERSION; epochTime = mktime(&t); #else epochTime = mktime(&t); epochTime = epochTime - YEAR_1900_TO_1970_SECONDS_DIFF; #endif return (U32)epochTime; } /************************************************************************* * @brief updateReadTimestampStruct * The updateReadTimestampStruct function updates the time struct after every * read * @details * Inputs : none * Outputs : none * @param none * @return none *************************************************************************/ static void updateReadTimestampStruct( void ) { RTCTimestampStruct.seconds = convertBCD2Decimal( rxBuffer[ RTC_SECONDS_INDEX ] ); RTCTimestampStruct.minutes = convertBCD2Decimal( rxBuffer[ RTC_MINUTES_INDEX ] ); RTCTimestampStruct.hours = convertBCD2Decimal( rxBuffer[ RTC_HOURS_INDEX ] ); RTCTimestampStruct.days = convertBCD2Decimal( rxBuffer[ RTC_DAYS_INDEX ] ); RTCTimestampStruct.weekdays = 0; // Weekdays will not be used RTCTimestampStruct.months = convertBCD2Decimal( rxBuffer[ RTC_MONTHS_INDEX ] ); RTCTimestampStruct.years = convertBCD2Decimal( rxBuffer[ RTC_YEARS_INDEX ] ); } /************************************************************************* * @brief setMibSPIBufferLength * The setMibSPIBufferLength sets the MibSPI buffer length prior to every * RTC transaction * @details * Inputs : length * Outputs : ransferStatus (bool) * @param none * @return transferStatus (bool) *************************************************************************/ static BOOL setMibSPIBufferLength( U16 length ) { BOOL transferStatus = FALSE; // The max allowed buffer length in the // MibSPI RAM is 127 if ( length <= MIBSPI_MAX_BUFFER_LENGTH ) { U32 i = 0; mibspiREG3->TGCTRL[0U] &= ~(uint32)( (uint32)MIBSPI_MAX_BUFFER_LENGTH << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[1U] &= ~(uint32)( (uint32)MIBSPI_MAX_BUFFER_LENGTH << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[1U] |= (uint32)( (uint32)length << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[2U] &= ~(uint32)( (uint32)MIBSPI_MAX_BUFFER_LENGTH << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[2U] |= (uint32)( (uint32)length << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[3U] &= ~(uint32)( (uint32)MIBSPI_MAX_BUFFER_LENGTH << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[3U] |= (uint32)( (uint32)length << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[4U] &= ~(uint32)( (uint32)MIBSPI_MAX_BUFFER_LENGTH << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[4U] |= (uint32)( (uint32)length << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[5U] &= ~(uint32)( (uint32)MIBSPI_MAX_BUFFER_LENGTH << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[5U] |= (uint32)( (uint32)length << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[6U] &= ~(uint32)( (uint32)MIBSPI_MAX_BUFFER_LENGTH << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[6U] |= (uint32)( (uint32)length << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[7U] &= ~(uint32)( (uint32)MIBSPI_MAX_BUFFER_LENGTH << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[7U] |= (uint32)( (uint32)length << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); mibspiREG3->TGCTRL[8U] = (uint32)length << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8; mibspiREG3->LTGPEND = ( mibspiREG3->LTGPEND & 0xFFFF00FFU ) | (uint32)( ((uint32)length - 1U) << MIBSPI_BUFFER_TRANS_BIT_SHIFT_8 ); while ( i < ( length - 1U ) ) { mibspiRAM3->tx[i].control = (uint16)( (uint16)MIBSPI_CONTINUOUS_MODE << MIBSPI_BUFFER_MODE_BIT_SHIFT_13 ) /* buffer mode */ | (uint16)( (uint16)MIBSPI_CHIP_SELECT_ACTIVE << MIBSPI_CHIP_SELECT_BIT_SHIFT_12 ) /* chip select hold */ | (uint16)( (uint16)MIBSPI_NO_WDELAY << MIBSPI_NO_WDELAY_BIT_SHIFT_10 ) /* enable WDELAY */ | (uint16)( (uint16)MIBSPI_LOCK_TG << MIBSPI_LOCK_TRANS_BIT_SHIFT_11 ) /* lock transmission */ | (uint16)( (uint16)MIBSPI_DATA_FORMAT_ZERO << MIBSPI_DATA_FORMAT_ZERO_BIT_SHIFT_8 ) /* data format */ | ((uint16)( ~((uint16)0xFFU ^ (uint16)CS_0)) & (uint16)0x00FFU ); /* chip select */ i++; } mibspiRAM3->tx[i].control = (uint16)( (uint16)MIBSPI_CONTINUOUS_MODE << MIBSPI_BUFFER_MODE_BIT_SHIFT_13 ) /* buffer mode */ | (uint16)( (uint16)MIBSPI_CHIP_SELECT_DEACTIVE << MIBSPI_CHIP_SELECT_BIT_SHIFT_12 ) /* chip select hold */ | (uint16)( (uint16)MIBSPI_NO_WDELAY << MIBSPI_NO_WDELAY_BIT_SHIFT_10 ) /* enable WDELAY */ | (uint16)( (uint16)MIBSPI_DATA_FORMAT_ZERO << MIBSPI_DATA_FORMAT_ZERO_BIT_SHIFT_8 ) /* data format */ | ((uint16)( ~((uint16)0xFFU ^ (uint16)CS_0)) & (uint16)0x00FFU ); /* chip select */ transferStatus = TRUE; } else { transferStatus = FALSE; } return transferStatus; } /************************************************************************* * @brief prepBufferForReadCommand * The prepBufferForReadCommand sets the txBuffer for a read * @details * Inputs : length * Outputs : none * @param none * @return none *************************************************************************/ static void prepBufferForReadCommand( U08 length ) { U08 i; txBuffer[ BUFFER_INDEX_0 ] = RTC_READ_FROM_REG0; for ( i = 1; i < length; i++ ) { txBuffer[ i ] = 0x0000; } } /************************************************************************* * @brief handleExecWaitForPostState * The handleExecWaitForPostState checks whether POST has completed and what * was the result of the POST test. It will either go to Idle of Fault * @details * Inputs : none * Outputs : result (RTC_EXEC_STATE_T) * @param none * @return result (RTC_EXEC_STATE_T) *************************************************************************/ static RTC_EXEC_STATE_T handleExecWaitForPostState( void ) { RTC_EXEC_STATE_T result = RTC_EXEC_STATE_WAIT_FOR_POST; if ( RTCSelfTestState == RTC_SELF_TEST_STATE_COMPLETE && RTCSelfTestResult == SELF_TEST_STATUS_PASSED ) { result = RTC_EXEC_STATE_IDLE; } else if ( RTCSelfTestState == RTC_SELF_TEST_STATE_COMPLETE ) { result = RTC_EXEC_STATE_IDLE; } return result; } /************************************************************************* * @brief handleExecIdleState * The handleExecIdleState checks whether read or write to RTC or RAM has * been requested. If none of them have been requested, it will increment the * timer * @details * Inputs : none * Outputs : result (RTC_EXEC_STATE_T) * @param none * @return result (RTC_EXEC_STATE_T) *************************************************************************/ static RTC_EXEC_STATE_T handleExecIdleState( void ) { RTC_EXEC_STATE_T result = RTC_EXEC_STATE_IDLE; if ( hasWriteToRTCRequested ) { result = RTC_EXEC_STATE_WRITE; } else if ( hasWriteToRAMRequested ) { result = RTC_EXEC_STATE_PREP_RAM; } else if ( hasReadFromRAMRequested ) { result = RTC_EXEC_STATE_PREP_RAM; } else if ( timeCounter == TIMER_COUNTER_TO_REQUEST_READ ) { prepBufferForReadCommand( RTC_GENERAL_BUFFER_LENGTH ); result = RTC_EXEC_STATE_READ; } else { timeCounter++; } return result; } /************************************************************************* * @brief handleExecWriteState * The handleExecWriteState writes timestamp to RTC * @details * Inputs : none * Outputs : result (RTC_EXEC_STATE_T) * @param none * @return result (RTC_EXEC_STATE_T) *************************************************************************/ static RTC_EXEC_STATE_T handleExecWriteState( void ) { if ( !isTimestampBufferReady ) { txBuffer[ 0 ] = RTC_WRITE_TO_REG0; txBuffer[ 1 ] = 0x0000; txBuffer[ 2 ] = 0x0000; txBuffer[ 3 ] = 0x0000; txBuffer[ 4 ] = convertDecimal2BCD( RTCNewTimestampStruct.seconds ); txBuffer[ 5 ] = convertDecimal2BCD( RTCNewTimestampStruct.minutes ); txBuffer[ 6 ] = convertDecimal2BCD( RTCNewTimestampStruct.hours ); txBuffer[ 7 ] = convertDecimal2BCD( RTCNewTimestampStruct.days ); txBuffer[ 8 ] = convertDecimal2BCD( RTCNewTimestampStruct.weekdays ); // Weekdays will not be used txBuffer[ 9 ] = convertDecimal2BCD( RTCNewTimestampStruct.months ); txBuffer[ 10 ] = convertDecimal2BCD( ( RTCNewTimestampStruct.years - YEAR_2000 ) ); isTimestampBufferReady = TRUE; } RTC_EXEC_STATE_T result = RTC_EXEC_STATE_WRITE; BOOL isStatusOk = serviceRTC( txBuffer, rxBuffer, RTC_GENERAL_BUFFER_LENGTH ); if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { // Reset the counter to start with the new read timeCounter = 1; hasWriteToRTCRequested = FALSE; result = RTC_EXEC_STATE_IDLE; } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { result = RTC_EXEC_STATE_FAULT; hasWriteToRTCRequested = FALSE; } return result; } /************************************************************************* * @brief handleExecPrepRAMState * The handleExecPrepRAMState prepares the RAM for read or write * @details * Inputs : none * Outputs : result (RTC_EXEC_STATE_T) * @param none * @return result (RTC_EXEC_STATE_T) *************************************************************************/ static RTC_EXEC_STATE_T handleExecPrepRAMState( void ) { RTC_EXEC_STATE_T result = RTC_EXEC_STATE_PREP_RAM; BOOL isStatusOk = serviceRTC( prepRAMBuffer, RAMBuffer, RTC_RAM_PREP_BUFFER_LENGTH ); if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { if ( hasWriteToRAMRequested ) { result = RTC_EXEC_STATE_WRITE_TO_RAM; } else if ( hasReadFromRAMRequested ) { result = RTC_EXEC_STATE_READ_FROM_RAM; } } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { result = RTC_EXEC_STATE_FAULT; } return result; } /************************************************************************* * @brief handleExecWriteToRAMState * The handleExecWriteToRAMState writes to RAM * @details * Inputs : none * Outputs : result (RTC_EXEC_STATE_T) * @param none * @return result (RTC_EXEC_STATE_T) *************************************************************************/ static RTC_EXEC_STATE_T handleExecWriteToRAMState( void ) { RTC_EXEC_STATE_T result = RTC_EXEC_STATE_WRITE_TO_RAM; BOOL isStatusOk = serviceRTC( txBuffer, RAMBuffer, RAMBufferLength + 1 ); if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { result = RTC_EXEC_STATE_IDLE; RTCRAMStatus = RTC_RAM_STATUS_COMPLETE; hasWriteToRAMRequested = FALSE; } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { result = RTC_EXEC_STATE_IDLE; RTCRAMStatus = RTC_RAM_STATUS_FAILED; hasWriteToRAMRequested = FALSE; } return result; } /************************************************************************* * @brief handleExecReadFromRAMState * The handleExecReadFromRAMState read from RAM * @details * Inputs : none * Outputs : result (RTC_EXEC_STATE_T) * @param none * @return result (RTC_EXEC_STATE_T) *************************************************************************/ static RTC_EXEC_STATE_T handleExecReadFromRAMState( void ) { RTC_EXEC_STATE_T result = RTC_EXEC_STATE_READ_FROM_RAM; BOOL isStatusOk = serviceRTC( txBuffer, RAMBuffer, RAMBufferLength + 1 ); if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { result = RTC_EXEC_STATE_IDLE; RTCRAMStatus = RTC_RAM_STATUS_COMPLETE; hasReadFromRAMRequested = FALSE; } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { result = RTC_EXEC_STATE_IDLE; RTCRAMStatus = RTC_RAM_STATUS_COMPLETE; hasReadFromRAMRequested = FALSE; } return result; } /************************************************************************* * @brief handleExecReadState * The handleExecReadState reads timestamp from RTC. The function calls * other functions to update the time struct and convert the latest time * to epoch * @details * Inputs : none * Outputs : result (RTC_EXEC_STATE_T) * @param none * @return result (RTC_EXEC_STATE_T) *************************************************************************/ static RTC_EXEC_STATE_T handleExecReadState( void ) { RTC_EXEC_STATE_T result = RTC_EXEC_STATE_READ; BOOL isStatusOk = serviceRTC( txBuffer, rxBuffer, RTC_GENERAL_BUFFER_LENGTH ); if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { if ( isRTCFunctional() ) { updateReadTimestampStruct(); lastEpochTime = convertTime2Epoch(); timeCounter = 1; broadcastRTCEpoch( lastEpochTime ); result = RTC_EXEC_STATE_IDLE; } else { result = RTC_EXEC_STATE_FAULT; } } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { result = RTC_EXEC_STATE_FAULT; } return result; } /************************************************************************* * @brief handleSelfTestStart * The handleSelfTestStart calls another function to prepare the txBuffer * for a read and sets the state to control registers * @details * Inputs : none * Outputs : result (RTC_SELF_TEST_STATE_T) * @param none * @return result (RTC_SELF_TEST_STATE_T) *************************************************************************/ static RTC_SELF_TEST_STATE_T handleSelfTestStart( void ) { RTC_SELF_TEST_STATE_T result = RTC_SELF_TEST_STATE_START; RTCSelfTestResult = SELF_TEST_STATUS_IN_PROGRESS; prepBufferForReadCommand( RTC_GENERAL_BUFFER_LENGTH ); result = RTC_SELF_TEST_STATE_CHECK_CTRL_REGS; return result; } /************************************************************************* * @brief handleSelfTestCheckCtrlRegs * The handleSelfTestCheckCtrlRegs receives the control registers and calls * another function to check whether RTC is function or not. If the RTC is * functional, it will update the RTCPreviousSecond variable and sets the * state machine to the next state * @details * Inputs : none * Outputs : result (RTC_SELF_TEST_STATE_T) * @param none * @return result (RTC_SELF_TEST_STATE_T) *************************************************************************/ static RTC_SELF_TEST_STATE_T handleSelfTestCheckCtrlRegs( void ) { RTC_SELF_TEST_STATE_T result = RTC_SELF_TEST_STATE_CHECK_CTRL_REGS; BOOL isStatusOk = serviceRTC( txBuffer, rxBuffer, RTC_GENERAL_BUFFER_LENGTH ); if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { if ( isRTCFunctional() ) { U32 RTCCurrentSecond = rxBuffer[ RTC_SECONDS_INDEX ]; RTCPreviousSecond = RTCCurrentSecond; result = RTC_SELF_TEST_STATE_WAIT_FOR_FIRST_SECOND; } else { RTCSelfTestResult = SELF_TEST_STATUS_FAILED; result = RTC_SELF_TEST_STATE_COMPLETE; } } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { RTCSelfTestResult = SELF_TEST_STATUS_FAILED; result = RTC_SELF_TEST_STATE_COMPLETE; } return result; } /************************************************************************* * @brief handleSelfTestWaitForFirstSecond * The handleSelfTestWaitForFirstSecond continuously reads the RTC and compares * latest second from RTC to the previous second and if it has changed, it will * start the timer and set the state machine to the next state * @details * Inputs : none * Outputs : result (RTC_SELF_TEST_STATE_T) * @param none * @return result (RTC_SELF_TEST_STATE_T) *************************************************************************/ static RTC_SELF_TEST_STATE_T handleSelfTestWaitForFirstSecond( void ) { RTC_SELF_TEST_STATE_T result = RTC_SELF_TEST_STATE_WAIT_FOR_FIRST_SECOND; BOOL isStatusOk = serviceRTC( txBuffer, rxBuffer, RTC_GENERAL_BUFFER_LENGTH ); if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { if ( isRTCFunctional() ) { U32 RTCCurrentSecond = rxBuffer[ RTC_SECONDS_INDEX ]; if ( RTCCurrentSecond != RTCPreviousSecond ) { RTCPreviousSecond = RTCCurrentSecond; RTCSelfTestTimer = getMSTimerCount(); result = RTC_SELF_TEST_STATE_WAIT_FOR_SECOND_SECOND; } } else { RTCSelfTestResult = SELF_TEST_STATUS_FAILED; result = RTC_SELF_TEST_STATE_COMPLETE; } } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { RTCSelfTestResult = SELF_TEST_STATUS_FAILED; result = RTC_SELF_TEST_STATE_COMPLETE; } return result; } /************************************************************************* * @brief handleSelfTestWaitForSecondSecond * The handleSelfTestWaitForSecondSecond continuously reads the RTC and compares * latest second from RTC to the previous second and if it has changed, it will * set the state machine to the next state * @details * Inputs : none * Outputs : result (RTC_SELF_TEST_STATE_T) * @param none * @return result (RTC_SELF_TEST_STATE_T) *************************************************************************/ static RTC_SELF_TEST_STATE_T handleSelfTestWaitForSecondSecond( void ) { RTC_SELF_TEST_STATE_T result = RTC_SELF_TEST_STATE_WAIT_FOR_SECOND_SECOND; BOOL isStatusOk = serviceRTC( txBuffer, rxBuffer, RTC_GENERAL_BUFFER_LENGTH ); if ( RTCServiceState == RTC_SERVICE_COMPLETE && isStatusOk ) { if ( isRTCFunctional() ) { U32 RTCCurrentSecond = rxBuffer[ RTC_SECONDS_INDEX ]; if ( RTCCurrentSecond != RTCPreviousSecond ) { result = RTC_SELF_TEST_STATE_CHECK_ACCURACY; } } else { RTCSelfTestResult = SELF_TEST_STATUS_FAILED; result = RTC_SELF_TEST_STATE_COMPLETE; } } else if ( RTCServiceState == RTC_SERVICE_COMPLETE ) { RTCSelfTestResult = SELF_TEST_STATUS_FAILED; result = RTC_SELF_TEST_STATE_COMPLETE; } return result; } /************************************************************************* * @brief handleSelfTestCheckAccuracy * The handleSelfTestCheckAccuracy checks whether the time has been elapsed * within the specified time tolerance. It will call another function to check * whether one second has elapsed. If the time has elapsed, how much time has * passed since the last second that was read from RTC * @details * Inputs : none * Outputs : result (RTC_SELF_TEST_STATE_T) * @param none * @return result (RTC_SELF_TEST_STATE_T) *************************************************************************/ static RTC_SELF_TEST_STATE_T handleSelfTestCheckAccuracy( void ) { RTC_SELF_TEST_STATE_T result = RTC_SELF_TEST_STATE_CHECK_ACCURACY; if ( didTimeout( RTCSelfTestTimer, RTC_ACCURACY_TIMEOUT ) ) { U32 elapsedTime = calcTimeSince( RTCSelfTestTimer ); if ( elapsedTime > RTC_ACCURACY_TIMEOUT_TOLERANCE ) { RTCSelfTestResult = SELF_TEST_STATUS_FAILED; } else { RTCSelfTestResult = SELF_TEST_STATUS_PASSED; } result = RTC_SELF_TEST_STATE_COMPLETE; } return result; }