Repository for import to local machine
Dependencies: DMBasicGUI DMSupport
Diff: GCRealTimeClock.cpp
- Revision:
- 1:a5258871b33d
diff -r 47c880c1463d -r a5258871b33d GCRealTimeClock.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GCRealTimeClock.cpp Thu Jul 20 08:42:29 2017 +0000 @@ -0,0 +1,278 @@ +/* + This class consists of static functions that handle the real time clock [RTC] on the GC. + It contains only static functions, and cannot be instantiated. +*/ + +#include "GCRealTimeClock.h" + + +/* + Gets the various time fields from the relevant GC registers + using the "QRTC000r" command, where 'r' is the register number + for each field. Returns the command responses, as strings, + in the buffers specified. + + Note that, to avoid rollover (see comments in function 'GetGCClockTime'), + we do minimal processing between getting each response. + + Args: a pointer to the USBHostGC instance that corresponds to the GC, + a pointer to the USBDeviceConnected instance that (also) corresponds to the GC + pointers to the buffers to contain the time fields - it is up to the caller + to make sure these are large enough for a GC response + + Returns true if it succeeded, false if it failed (e.g. the GC gave an error, etc) +*/ +bool GCRealTimeClock::GetTimeFieldsFromGC(USBDeviceConnected* usbDevice, + USBHostGC* usbHostGC, + char *gcSecondsResponse, + char *gcMinutesResponse, + char *gcHoursResponse, + char *gcDateResponse, + char *gcMonthResponse, + char *gcYearResponse) +{ + while(usbHostGC->ExecutingSetDeviceReport()) {} + usbHostGC->SetDeviceReport(usbDevice, "QRTC0000", gcSecondsResponse); + if(gcSecondsResponse[0] == 'E') { + // Assume "EPKT" + return false; + } + + while(usbHostGC->ExecutingSetDeviceReport()) {} + usbHostGC->SetDeviceReport(usbDevice, "QRTC0001", gcMinutesResponse); + if(gcMinutesResponse[0] == 'E') { + // Assume "EPKT" + return false; + } + + while(usbHostGC->ExecutingSetDeviceReport()) {} + usbHostGC->SetDeviceReport(usbDevice, "QRTC0002", gcHoursResponse); + if(gcHoursResponse[0] == 'E') { + // Assume "EPKT" + return false; + } + + while(usbHostGC->ExecutingSetDeviceReport()) {} + usbHostGC->SetDeviceReport(usbDevice, "QRTC0004", gcDateResponse); + if(gcDateResponse[0] == 'E') { + // Assume "EPKT" + return false; + } + + while(usbHostGC->ExecutingSetDeviceReport()) {} + usbHostGC->SetDeviceReport(usbDevice, "QRTC0005", gcMonthResponse); + if(gcMonthResponse[0] == 'E') { + // Assume "EPKT" + return false; + } + + while(usbHostGC->ExecutingSetDeviceReport()) {} + usbHostGC->SetDeviceReport(usbDevice, "QRTC0006", gcYearResponse); + if(gcYearResponse[0] == 'E') { + // Assume "EPKT" + return false; + } + + // If we get here, all the above must have succeeded + return true; +} + +/* + Constructs a time value from the GC responses returned by 'GetTimeFieldsFromGC()'. + Fills a 'tm' struct - see the <ctime> (time.h) system include file. + + We expect the responses to look like "DRTCnnnn", where "nnnn" is the value + of the relevant register, so we expect the register value to start in array element 4. + + For a full description of the register values, see the DS1307 Real-Time Clock documentation, + which can currently be found here: + + V:\Development\500 Series GC\Main Instrument\Documentation\Electronic\DS1307 IIC RTCC Chip.pdf + + [V: is the Ellutia File Server.] + + *** The register values are in binary coded decimal format. *** + + + Args: a pointer to a 'tm' struct to be filled with the time value + pointers to the buffers that contain each of the time fields + returned by the GC in our 'GetTimeFieldsFromGC()' function + + No return code. +*/ +void GCRealTimeClock::MakeTimeValueFromGCTimeFields(struct tm *timeValue, + char *gcSecondsResponse, + char *gcMinutesResponse, + char *gcHoursResponse, + char *gcDateResponse, + char *gcMonthResponse, + char *gcYearResponse) +{ + int secondsValue; + sscanf(&gcSecondsResponse[4], "%d", &secondsValue); + + int secondsUnitsDigit = secondsValue & 0xF; + int secondsTensDigit = (secondsValue & 0x70) >> 4; + timeValue->tm_sec = secondsUnitsDigit + (secondsTensDigit * 10); + + + int minutesValue; + sscanf(&gcMinutesResponse[4], "%d", &minutesValue); + + int minutesUnitsDigit = minutesValue & 0xF; + int minutesTensDigit = (minutesValue & 0x70) >> 4; + timeValue->tm_min = minutesUnitsDigit + (minutesTensDigit * 10); + + + int hoursValue; + sscanf(&gcHoursResponse[4], "%d", &hoursValue); + + // Note that, while the GC real-time clock can work in 12 or 24 hour mode, + // the 'tm' struct can only handle 24 hour clock values. + int hoursUnitsDigit = hoursValue & 0xF; + int hoursTensDigit = 0; + if(hoursValue & 0x40) { + // 12 hour mode - hours range is 01-12 - + // but tm struct needs hours to start at zero, not one + hoursTensDigit = (hoursValue & 0x10) >> 4; + timeValue->tm_hour = hoursUnitsDigit + (hoursTensDigit * 10) - 1; + if(hoursValue & 0x20) { + // pm (else am) + timeValue->tm_hour += 12; + } + } else { + // 24 hour mode - hours range is 00-23 + hoursTensDigit = (hoursValue & 0x30) >> 4; + timeValue->tm_hour = hoursUnitsDigit + (hoursTensDigit * 10); + } + + + int dateValue; + sscanf(&gcDateResponse[4], "%d", &dateValue); + + int dateUnitsDigit = dateValue & 0xF; + int dateTensDigit = (dateValue & 0x30) >> 4; + timeValue->tm_mday = dateUnitsDigit + (dateTensDigit * 10); + + + int monthValue; + sscanf(&gcMonthResponse[4], "%d", &monthValue); + + // GC month value range is 1-12 - but 'tm' struct month range is 0-11 + int monthUnitsDigit = monthValue & 0xF; + int monthTensDigit = (monthValue & 0x10) >> 4; + timeValue->tm_mon = monthUnitsDigit + (monthTensDigit * 10) - 1; + + + int yearValue; + sscanf(&gcYearResponse[4], "%d", &yearValue); + + // GC year value is 00-99 - but 'tm' struct year value + // is the number of years since 1900 + int yearUnitsDigit = yearValue & 0xF; + int yearTensDigit = (yearValue & 0xF0) >> 4; + timeValue->tm_year = yearUnitsDigit + (yearTensDigit * 10) + 100; +} + + +/* + Get the time and date from the GC's real time clock, + and return it in a time_t variable provided by the caller + + Args: a pointer to the USBHostGC instance that corresponds to the GC, + a pointer to the USBDeviceConnected instance that (also) corresponds to the GC + a pointer to a time_t variable, which we will set to the GC time + + Returns true if it succeeded, false if it failed (e.g. the GC gave an error, etc) +*/ +bool GCRealTimeClock::GetGCClockTime(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, time_t *clockTime) +{ + // The seconds, minutes, hours, date (i.e. day of the month), month and year are kept in separate registers + // in the GC, so we get them separately. This means we must guard against 'rollover' - + // e.g. the GC time when we get the minutes is 11:59, but when we get the hours + // it is 12:00, so we think the time is 12:59. + // + // We do this in two ways: + // + // 1 - we do minimal processing between getting each value (see also function 'GetTimeFieldsFromGC') + // 2 - we get the time twice - if the first value is greater than the second, + // we assume that the first must have rolled over, so we use the second, + // otherwise we use the first (they surely cannot both rollover unless + // they are exactly 60 seconds apart, which is very unlikely - or 60 minutes, + // 1 day, etc, which is even more unlikely). Also note that, since we get + // the components in ascending order of 'size', any rollover will always + // make the time value greater than it should be, not less. + + + // Date and time value 1 + char seconds1[20]; + char minutes1[20]; + char hours1[20]; + char date1[20]; + char month1[20]; + char year1[20]; + if(!GetTimeFieldsFromGC(usbDevice, usbHostGC, seconds1, minutes1, hours1, date1, month1, year1)) { + return false; + } + + // *** Do nothing between these two 'GetTimeFieldsFromGC' calls *** + + // Date and time value 2 + char seconds2[20]; + char minutes2[20]; + char hours2[20]; + char date2[20]; + char month2[20]; + char year2[20]; + if(!GetTimeFieldsFromGC(usbDevice, usbHostGC, seconds2, minutes2, hours2, date2, month2, year2)) { + return false; + } + + + struct tm time1; + MakeTimeValueFromGCTimeFields(&time1, seconds1, minutes1, hours1, date1, month1, year1); + + time_t time_t1 = mktime(&time1); + + + struct tm time2; + MakeTimeValueFromGCTimeFields(&time2, seconds2, minutes2, hours2, date2, month2, year2); + + time_t time_t2 = mktime(&time2); + + + // time_t1 should be less than time_t2, since we obtained the values from which we calculated it (slightly) earlier. + // If so, use time_t1. If not, assume one or more of time_t1's constituent values must have been affected by rollover, + // and use time_t2. (Note that if time_t2 was affected by rollover, its value will (even more) be greater than time_t1, + // so we will ignore it anyway.) + + *clockTime = (time_t1 < time_t2) ? time_t1 : time_t2; + + return true; +} + + +/* + Get the time and date from the GC's real time clock, + and set the LPC4088 RTC to match. + + Args: a pointer to the USBHostGC instance that corresponds to the GC, + a pointer to the USBDeviceConnected instance that (also) corresponds to the GC + + Returns true if it succeeded, false if it failed (e.g. the GC gave an error, etc) +*/ +bool GCRealTimeClock::SetLPC4088RealTimeClockToMatchGC(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) +{ + time_t timeToSet; + + if(GetGCClockTime(usbDevice, usbHostGC, &timeToSet)) { + + set_time(timeToSet); + + return true; + } + + // 'else' ... + return false; +} +