/************************************************************************** * * Copyright (c) 2019-2019 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 "Common.h" #include "Timers.h" #include "RTC.h" #include "mibspi.h" #include // ********** private definitions ********** #define RTC_REG_1_12_HOUR_MODE 0X0004 #define RTC_REG_1_PORO 0X0008 #define RTC_REG_1_CLK_STOPPED 0X0020 #define RTC_REG_1_UNUSED 0X0040 #define RTC_REG_1_EXT_CLK_MODE 0X0080 #define RTC_REG_2_MASK 0X0000 #define RTC_REG_3_MASK 0X0008 // RTC registers indices #define RTC_REG_1_INDEX 1 #define RTC_REG_2_INDEX 2 #define RTC_REG_3_INDEX 3 #define RTC_SECONDS_INDEX 4 #define RTC_MINUTES_INDEX 5 #define RTC_HOURS_INDEX 6 #define RTC_DAYS_INDEX 7 #define RTC_WEEKDAYS_INDEX 8 #define RTC_MONTHS_INDEX 9 #define RTC_YEARS_INDEX 10 // This command puts RTC into read mode from // address 0 #define RTC_READ_FROM_REG0 0x00A0 #define RTC_WRITE_TO_REG3 0x0023 #define RTC_ACCURACY_TIMEOUT 1000 //ms #define RTC_ACCURACY_TIMEOUT_TOLERANCE 1050 //ms #define NUM_OF_ITEMS_TO_READ 11 #define TIMER_COUNTER_TO_REQUEST_READ 19 typedef enum RTC_Self_Test_States { RTC_SELF_TEST_STATE_START = 0, RTC_SELF_TEST_STATE_CHECK_CTRL_REGS, RTC_SELF_TEST_STATE_COMPARE_SECONDS, RTC_SELF_TEST_STATE_CHECK_ACCURACY, RTC_SELF_TEST_STATE_COMPLETE } RTC_SELF_TEST_STATE_T; typedef enum RTC_Read_Data { RTC_SEND_COMMAND = 0, RTC_WAIT_FOR_TRANSFER_AND_READ, RTC_READ_COMPLETE } RTC_GET_DATA_STATE_T; typedef enum RTC_Exec_State { RTC_EXEC_STATE_WAIT_FOR_POST = 0, RTC_EXEC_STATE_IDLE, RTC_EXEC_STATE_READ, RTC_EXEC_STATE_WRITE, RTC_EXEC_STATE_FAULT } RTC_EXEC_STATE_T; static struct RTCReadTimestamp { U16 seconds; U16 minutes; U16 hours; U16 days; U16 weekdays; U16 months; U16 years; } read_ts; static struct RTCWriteTimestamp { U16 seconds; U16 minutes; U16 hours; U16 days; U16 weekdays; U16 months; U16 years; } write_ts; 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 U32 RTCSelfTestTimer = 0; static U32 RTCPreviousSecond = 0; static U32 timeCounter = 1; static BOOL hasWriteTimestampRequested = FALSE; static U16 rxBuffer[NUM_OF_ITEMS_TO_READ]; static U16 txBuffer[] = {RTC_READ_FROM_REG0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; // ********** Private functions prototype ********* // ********** Private functions ********* static void serviceRTC( U16* buffer ) { switch ( RTCServiceState ) { case RTC_SEND_COMMAND: mibspiSetData(mibspiREG3, 0, buffer); mibspiTransfer(mibspiREG3, 0); RTCServiceState = RTC_WAIT_FOR_TRANSFER_AND_READ; break; case RTC_WAIT_FOR_TRANSFER_AND_READ: if ( mibspiIsTransferComplete(mibspiREG3, 0) ) { mibspiGetData(mibspiREG3, 0, rxBuffer); RTCServiceState = RTC_READ_COMPLETE; } break; case RTC_READ_COMPLETE: // Done with reading and transfer do nothing break; default: // TODO: Set an alarm or try multiple times (3 times) before failing break; } } static BOOL isRTCFunctional() { BOOL hasTestPassed = TRUE; U16 controlReg1 = rxBuffer[RTC_REG_1_INDEX]; U16 controlReg2 = rxBuffer[RTC_REG_2_INDEX]; U16 controlReg3 = rxBuffer[RTC_REG_2_INDEX]; if ( ! controlReg1 & RTC_REG_1_12_HOUR_MODE ) { // Set the alarm for 24 hour mode hasTestPassed = FALSE; } if ( ! controlReg1 & RTC_REG_1_PORO ) { // Set the alarm for PORO low mode hasTestPassed = FALSE; } if ( ! controlReg1 & RTC_REG_1_CLK_STOPPED ) { // Set the alarm for clock stopped mode hasTestPassed = FALSE; } if ( ! controlReg1 & RTC_REG_1_UNUSED ) { // Set the alarm for unused bit set to 1 mode hasTestPassed = FALSE; } if ( ! controlReg1 & RTC_REG_1_EXT_CLK_MODE ) { // Set the alarm for clock set on external mode hasTestPassed = FALSE; } if ( ! controlReg2 & RTC_REG_2_MASK ) { // Set the alarm for register 2 hasTestPassed = FALSE; } if ( ! controlReg3 & RTC_REG_3_MASK ) { // Set the alarm for register 3 hasTestPassed = FALSE; } return hasTestPassed; } static U08 convertBCD2Decimal( U08 bcd ) { U08 bcdHigh; U08 bcdLow; U08 decimal; bcdHigh = ( bcd & 0xF0 ) >> 4; bcdLow = ( bcd & 0x0F ); decimal = ( bcdHigh * 10 ) + bcdLow; return decimal; } static U08 convertDecimal2BCD( U08 decimal ) { U08 decimalHigh; U08 decimalLow; U08 bcd; // Assuming all the decimal provided // are maximum 2 digits decimalHigh = decimal / 10; decimalLow = decimal % 10; bcd = (decimalHigh << 4) + decimalLow; return bcd; } static U32 convertTime2Epoch() { struct tm t; time_t epochTime; t.tm_sec = read_ts.seconds; t.tm_min = read_ts.minutes; t.tm_hour = read_ts.hours; t.tm_mday = read_ts.days; t.tm_mon = read_ts.months - 1; t.tm_year = read_ts.years + 2000 - 1970; epochTime = mktime(&t); return (U32)epochTime; } static void updateReadTimestampStruct() { U16 decimalSeconds = convertBCD2Decimal( rxBuffer[RTC_SECONDS_INDEX] ); U16 decimalMins = convertBCD2Decimal( rxBuffer[RTC_MINUTES_INDEX] ); U16 decimalHours = convertBCD2Decimal( rxBuffer[RTC_HOURS_INDEX] ); U16 decimalDays = convertBCD2Decimal( rxBuffer[RTC_DAYS_INDEX] ); U16 decimalMonths = convertBCD2Decimal( rxBuffer[RTC_MONTHS_INDEX] ); U16 decimalYears = convertBCD2Decimal( rxBuffer[RTC_YEARS_INDEX] ); read_ts.seconds = decimalSeconds; read_ts.minutes = decimalMins; read_ts.hours = decimalHours; read_ts.days = decimalDays; read_ts.weekdays = 0; // Weekdays will not be used read_ts.months = decimalMonths; read_ts.years = decimalYears; } // ********** Public functions ********** void initRTC() { // Do nothing for now } void setRTCTimestamp( U08 secs, U08 mins, U08 hours, U08 days, U08 months, U16 years ) { hasWriteTimestampRequested = TRUE; U16 decimalSeconds = convertDecimal2BCD( secs ); U16 decimalMins = convertDecimal2BCD( mins ); U16 decimalHours = convertDecimal2BCD( hours ); U16 decimalDays = convertDecimal2BCD( days ); U16 decimalMonths = convertDecimal2BCD( months ); U16 decimalYears = convertDecimal2BCD( years - 2000 ); write_ts.seconds = decimalSeconds; write_ts.minutes = decimalMins; write_ts.hours = decimalHours; write_ts.days = decimalDays; write_ts.weekdays = 0; // Weekdays will not be used write_ts.months = decimalMonths; write_ts.years = decimalYears; } SELF_TEST_STATUS_T execRTCSelfTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; switch ( RTCSelfTestState ) { case RTC_SELF_TEST_STATE_START: serviceRTC( &txBuffer[0] ); RTCSelfTestState = RTC_SELF_TEST_STATE_CHECK_CTRL_REGS; break; case RTC_SELF_TEST_STATE_CHECK_CTRL_REGS: serviceRTC( &txBuffer[0] ); if ( RTCServiceState == RTC_READ_COMPLETE ) { // Reset the states RTCServiceState = RTC_SEND_COMMAND; if ( isRTCFunctional() ) { RTCSelfTestState = RTC_SELF_TEST_STATE_COMPARE_SECONDS; } else { result = SELF_TEST_STATUS_FAILED; RTCSelfTestState = RTC_SELF_TEST_STATE_COMPLETE; } } break; case RTC_SELF_TEST_STATE_COMPARE_SECONDS: serviceRTC( &txBuffer[0] ); if ( RTCServiceState == RTC_READ_COMPLETE ) { // Reset the states RTCServiceState = RTC_SEND_COMMAND; U32 RTCCurrentSecond = rxBuffer[RTC_SECONDS_INDEX]; if ( RTCPreviousSecond == 0 ) { RTCPreviousSecond = RTCCurrentSecond; } else if ( RTCCurrentSecond > RTCPreviousSecond ) { RTCSelfTestTimer = getMSTimerCount(); RTCSelfTestState = RTC_SELF_TEST_STATE_CHECK_ACCURACY; } } break; case RTC_SELF_TEST_STATE_CHECK_ACCURACY: if ( didTimeout( RTCSelfTestTimer, RTC_ACCURACY_TIMEOUT ) ) { U32 elapsedTime = calcTimeSince(RTCSelfTestTimer); if ( elapsedTime > RTC_ACCURACY_TIMEOUT_TOLERANCE ) { result = SELF_TEST_STATUS_FAILED; } else { result = SELF_TEST_STATUS_PASSED; } RTCSelfTestState = RTC_SELF_TEST_STATE_COMPLETE; } break; case RTC_SELF_TEST_STATE_COMPLETE: break; default: // TODO: Add the alarms result = SELF_TEST_STATUS_FAILED; break; } return result; } void execRTC() { switch ( RTCExecState ) { case RTC_EXEC_STATE_WAIT_FOR_POST: if ( RTCSelfTestState == RTC_SELF_TEST_STATE_COMPLETE ) { RTCExecState = RTC_EXEC_STATE_IDLE; } break; case RTC_EXEC_STATE_IDLE: if ( hasWriteTimestampRequested ) { // FOR DEBUGGING ONLY, REMOVE THIS CODE setRTCTimestamp(0, 10, 17, 8, 12, 2019); U16 timestamp[8] = {RTC_WRITE_TO_REG3, write_ts.seconds, write_ts.minutes, write_ts.hours, write_ts.days, write_ts.weekdays, write_ts.months, write_ts.years}; serviceRTC( ×tamp[0] ); RTCExecState = RTC_EXEC_STATE_WRITE; } // If write to RTC has been requested, we don't have to read // write must be finished first else if ( timeCounter == TIMER_COUNTER_TO_REQUEST_READ ) { // Reset the states RTCServiceState = RTC_SEND_COMMAND; serviceRTC( &txBuffer[0] ); RTCExecState = RTC_EXEC_STATE_READ; } else { timeCounter++; } break; case RTC_EXEC_STATE_WRITE: serviceRTC( 0 ); if ( RTCServiceState == RTC_READ_COMPLETE ) { // Write finished. Reset the counter to start // the read again timeCounter = 1; hasWriteTimestampRequested = FALSE; RTCExecState = RTC_EXEC_STATE_IDLE; } break; case RTC_EXEC_STATE_READ: serviceRTC( &txBuffer[0] ); if ( RTCServiceState == RTC_READ_COMPLETE ) { if ( isRTCFunctional() ) { // FOR TESTING ONLY REMOVE THE CODE //U08 testDate = (U08)rxBuffer[RTC_SECONDS_INDEX]; //convertBCD2Decimal( testDate ); updateReadTimestampStruct(); convertTime2Epoch(); timeCounter = 1; RTCExecState = RTC_EXEC_STATE_IDLE; } else { RTCExecState = RTC_EXEC_STATE_FAULT; } } break; case RTC_EXEC_STATE_FAULT: // Something failed set the alarms // TODO: set the alarms and stuff break; default: break; } }