Repository for import to local machine

Dependencies:   DMBasicGUI DMSupport

Revision:
1:a5258871b33d
--- /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;
+}
+