RS232 control for TVOne products

Dependents:   SPK-DVIMXR

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers spk_tvone_mbed.cpp Source File

spk_tvone_mbed.cpp

00001 // *spark audio-visual
00002 // RS232 Control for TV-One products
00003 // Good for 1T-C2-750, others will need some extra work
00004 
00005 /* Copyright (c) 2011 Toby Harris, MIT License
00006  *
00007  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
00008  * and associated documentation files (the "Software"), to deal in the Software without restriction, 
00009  * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
00010  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
00011  * furnished to do so, subject to the following conditions:
00012  *
00013  * The above copyright notice and this permission notice shall be included in all copies or 
00014  * substantial portions of the Software.
00015  *
00016  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
00017  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
00018  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
00020  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00021  */
00022 
00023 #include "spk_tvone_mbed.h"
00024 #include "mbed.h"
00025 
00026 SPKTVOne::SPKTVOne(PinName txPin, PinName rxPin, PinName signWritePin, PinName signErrorPin, Serial *debugSerial)
00027 {
00028     // Create Serial connection for TVOne unit comms
00029     // Creating our own as this is exclusively for TVOne comms
00030     serial = new Serial(txPin, rxPin);
00031     serial->baud(57600);
00032     
00033     if (signWritePin != NC) writeDO = new DigitalOut(signWritePin);
00034     else writeDO = NULL;
00035     
00036     if (signErrorPin != NC) errorDO = new DigitalOut(signErrorPin);
00037     else errorDO = NULL;
00038     
00039     processor.version = -1;
00040     processor.productType = -1;
00041     processor.boardType = -1;
00042     
00043     resetCommandPeriods();
00044     
00045     timer.start();
00046     timerCheckTicker.attach(this, &SPKTVOne::timerCheck, 60);
00047     
00048     // Link up debug Serial object
00049     // Passing in shared object as debugging is shared between all DVI mixer functions
00050     debug = debugSerial;
00051 }
00052 
00053 bool SPKTVOne::command(uint8_t channel, uint8_t window, int32_t func, int32_t payload)
00054 {
00055     int ackBuff[standardAckLength] = {0};
00056     
00057     bool success = command(writeCommandType, ackBuff, standardAckLength, channel, window, func, payload);
00058     
00059     // TASK: Check return payload is what we tried to set it to
00060     char payloadStr[7];
00061     for (int i = 0; i < 6; i++)
00062     {
00063         payloadStr[i] = ackBuff[11+i];
00064     }
00065     payloadStr[6] = NULL;
00066     
00067     int payloadBack = strtol (payloadStr, NULL, 16);
00068     
00069     if (payload != payloadBack)
00070     {
00071         success = false;
00072         if (debug) debug->printf("TVOne return value (%d) is not what was set (%d). Channel: %#x, Window: %#x, Function: %#x \r\n", payloadBack, payload, channel, window, func); 
00073     }
00074     return success;
00075 }
00076 
00077 bool SPKTVOne::readCommand(uint8_t channel, uint8_t window, int32_t func, int32_t &payload)
00078 {
00079     int ackBuff[standardAckLength] = {0};
00080     
00081     bool success = command(readCommandType, ackBuff, standardAckLength, channel, window, func, payload);
00082     
00083     if (success)
00084     {    
00085         char payloadStr[7];
00086         for (int i = 0; i < 6; i++)
00087         {
00088             payloadStr[i] = ackBuff[11+i];
00089         }
00090         payloadStr[6] = NULL;
00091         
00092         payload = strtol (payloadStr, NULL, 16);
00093     }
00094     
00095     return success;
00096 }
00097 
00098 bool SPKTVOne::command(commandType readWrite, int* ackBuffer, int ackLength, uint8_t channel, uint8_t window, int32_t func, int32_t payload) 
00099 { 
00100   if (debug) debug->printf("TVOne %s Channel: %#x, Window: %#x, Function: %#x Payload: %i \r\n", (readWrite == writeCommandType) ? "Write" : "Read", channel, window, func, payload);
00101 
00102   // TASK: Sign start of serial command write
00103   if (writeDO) *writeDO = 1;
00104   
00105   // TASK: Prepare to issue command to the TVOne unit
00106   // - discard anything waiting to be read in the return serial buffer
00107   // - make sure we're past the minimum time between command sends as the unit can get overloaded
00108   while (serial->readable() || timer.read_ms() < commandMinimumPeriod) {
00109     if (serial->readable()) serial->getc();
00110   }
00111   
00112   // TASK: Create the bytes of command
00113 
00114   uint8_t cmd[8];
00115   uint8_t checksum = 0;
00116 
00117   // CMD
00118   cmd[0] = readWrite<<7 | 1<<2;
00119   // CHA
00120   cmd[1] = channel;
00121   // WINDOW
00122   cmd[2] = window;
00123   // OUTPUT & FUNCTION
00124   //            cmd[3]  cmd[4]
00125   // output 0 = 0000xxx xxxxxxx
00126   // function = xxxXXXX XXXXXXX
00127   cmd[3] = func >> 8;
00128   cmd[4] = func & 0xFF;
00129   // PAYLOAD
00130   cmd[5] = (payload >> 16) & 0xFF;
00131   cmd[6] = (payload >> 8) & 0xFF;
00132   cmd[7] = payload & 0xFF;
00133 
00134   // TASK: Write the bytes of command to RS232 as correctly packaged 20 characters of ASCII
00135   
00136   if (readWrite == writeCommandType)
00137   {
00138     for (int i=0; i<8; i++) checksum += cmd[i];
00139     serial->printf("F%02X%02X%02X%02X%02X%02X%02X%02X%02X\r", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], checksum);
00140   }
00141   if (readWrite == readCommandType)
00142   {
00143     for (int i=0; i<5; i++) checksum += cmd[i];
00144     serial->printf("F%02X%02X%02X%02X%02X%02X\r", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], checksum);
00145   } 
00146    
00147   // TASK: Check the unit's return string, to enable return to main program as soon as unit is ready
00148 
00149   // Handling the timing of this return is critical to effective control.
00150   // Returning the instant something is received back overloads the processor, as does anything until the full 20 char acknowledgement.
00151   // TVOne turn out to say that receipt of the ack doesn't guarantee the unit is ready for the next command. 
00152   // According to the manual, operations typically take 30ms, and to simplify programming you can throttle commands to every 100ms.
00153   // 100ms is too slow for us. Going with returning after 30ms if we've received an acknowledgement, returning after 100ms otherwise.
00154   
00155   bool success = false;  
00156   int ackPos = 0;
00157   timer.reset();
00158 
00159   while (timer.read_ms() < commandTimeoutPeriod) 
00160   {
00161     if (serial->readable())
00162     {
00163         if (ackPos == 0)
00164         {
00165             ackBuffer[0] = serial->getc();
00166             if (ackBuffer[0] == 'F') ackPos = 1;
00167         }
00168         else
00169         {
00170             ackBuffer[ackPos] = serial->getc();
00171             ackPos++;
00172             if (ackPos == ackLength) break;
00173         }
00174     }
00175   }
00176 
00177   // Return true if we got the no error acknowledgement from the unit. The rest of the ack will be verified elsewhere if needed.
00178   if (ackPos == ackLength && ackBuffer[1] == '4') 
00179   {
00180      success = true;
00181   }
00182   
00183   // TASK: Sign end of write
00184   
00185   if (writeDO) *writeDO = 0;
00186   
00187   if (!success) {
00188         if (errorDO) {
00189             signErrorTimeout.detach();
00190             signErrorTimeout.attach(this, &SPKTVOne::signErrorOff, 0.25);
00191             *errorDO = 1;
00192         }
00193         
00194         if (debug) {
00195             debug->printf("TVOne serial error. Time from finishing writing command: %ims. Received %i ack chars:", timer.read_ms(), ackPos);
00196             for (int i = 0; i<ackLength; i++) 
00197             {
00198                 debug->printf("%c", ackBuffer[i]);
00199             }
00200             debug->printf("\r\n");
00201         }
00202   };
00203 
00204   return success;
00205 }
00206 
00207 void SPKTVOne::setCommandTimeoutPeriod(int millis)
00208 {
00209     commandTimeoutPeriod = millis;
00210 }
00211 
00212 void SPKTVOne::setCommandMinimumPeriod(int millis)
00213 {
00214     commandMinimumPeriod = millis;
00215 }
00216 
00217 void SPKTVOne::increaseCommandPeriods(int millis)
00218 {
00219     commandTimeoutPeriod += millis;
00220     commandMinimumPeriod += millis;
00221     
00222     if (debug) debug->printf("Command periods increased; minimum: %i, timeout: %i", commandMinimumPeriod, commandTimeoutPeriod);
00223 }
00224 
00225 void SPKTVOne::resetCommandPeriods()
00226 {
00227     commandTimeoutPeriod = kTV1CommandTimeoutMillis;
00228     commandMinimumPeriod = kTV1CommandMinimumMillis;
00229 }
00230 
00231 int SPKTVOne::getCommandTimeoutPeriod()
00232 {
00233     return commandTimeoutPeriod;
00234 }
00235 
00236 int SPKTVOne::millisSinceLastCommandSent()
00237 {
00238     return timer.read_ms();
00239 }
00240 
00241 bool SPKTVOne::setMatroxResolutions(bool digitalEdition) 
00242 {
00243   bool lock = true;
00244   bool ok = true;
00245   int unlocked = 0;
00246   int locked = 1;
00247   
00248   lock = lock && command(0, kTV1WindowIDA, kTV1FunctionAdjustFrontPanelLock, locked);
00249   
00250   // TODO: Any other resolutions that have different timings between analogue and digital editions of the matrox boxes.
00251   // ok = ok && set1920x480(kTV1ResolutionTripleHeadVGAp60);
00252   // ok = ok && set1600x600(kTV1ResolutionDualHeadSVGAp60);
00253   ok = ok && set2048x768(kTV1ResolutionDualHeadXGAp60, digitalEdition);
00254   
00255   lock = lock && command(0, kTV1WindowIDA, kTV1FunctionAdjustFrontPanelLock, unlocked);
00256   
00257   return ok;
00258 }
00259 
00260 int SPKTVOne::getEDID()
00261 {
00262     bool ok = true;
00263 
00264     int32_t payload1 = -1;
00265     ok = ok && readCommand(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, payload1);
00266     
00267     int32_t payload2 = -1;
00268     ok = ok && readCommand(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, payload2);
00269     
00270     int EDID = (payload1 == payload2) ? payload1 : -1;
00271     
00272     return ok ? EDID : -1;
00273 }
00274 
00275 int SPKTVOne::getResolution(int device)
00276 {
00277     bool ok = false;
00278     int32_t payload = -1;
00279 
00280     if (device == 0)
00281     {
00282         ok = readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsOutputResolution, payload);
00283     }
00284     else if (device == kTV1WindowIDA || device == kTV1WindowIDB)
00285     {
00286         ok = readCommand(0, device, kTV1FunctionAdjustWindowsSourceResolution, payload);
00287     }
00288     
00289     return ok ? payload : -1;
00290 }
00291 
00292 bool SPKTVOne::setResolution(int resolution, int edidSlot)
00293 {
00294     bool ok;
00295     
00296     int minPeriodOnEntry = commandMinimumPeriod;
00297     int outPeriodOnEntry = commandTimeoutPeriod;
00298     
00299     for (int i=0; i < 3; i++)
00300     {
00301         ok = command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsOutputResolution, resolution);
00302         
00303         if (ok) break;
00304         else    increaseCommandPeriods(500);
00305     }                
00306     commandMinimumPeriod = minPeriodOnEntry;
00307     commandTimeoutPeriod = outPeriodOnEntry;
00308     if (!ok) return ok;
00309 
00310     for (int i=0; i < 3; i++)
00311     {                
00312         ok =    command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, edidSlot);
00313         ok = ok && command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, edidSlot);
00314         
00315         if (ok) break;
00316         else    increaseCommandPeriods(500);
00317     }                
00318     commandMinimumPeriod = minPeriodOnEntry;
00319     commandTimeoutPeriod = outPeriodOnEntry;
00320     
00321     return ok;
00322 }
00323 
00324 bool SPKTVOne::setHDCPOn(bool state) 
00325 {
00326     bool ok;
00327     
00328     int minPeriodOnEntry = commandMinimumPeriod;
00329     int outPeriodOnEntry = commandTimeoutPeriod;
00330       
00331     // HDCP can sometimes take a little time to settle down
00332     for (int i=0; i < 3; i++)
00333     {
00334         // Turn HDCP off on the output
00335         ok =       command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsHDCPRequired, state);
00336         
00337         // Likewise on inputs A and B
00338         ok = ok && command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPAdvertize, state);
00339         ok = ok && command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPAdvertize, state);
00340 
00341 // This verify code is accurate but too misleading for D-Fuser use - eg. actual HDCP state requires source / output connection.      
00342 //        // Now verify whats actually going on. 
00343 //        int32_t payload = -1;
00344 //        ok = ok && readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsHDCPStatus, payload);
00345 //        switch (payload) 
00346 //        {
00347 //            case 0: ok = ok && !state; break;
00348 //            case 1: ok = ok && !state; break;
00349 //            case 2: ok = ok && state; break;
00350 //            case 3: ok = ok && !state; break;
00351 //            case 4: ok = ok && state; break;
00352 //            default: ok = false;
00353 //        }
00354 //        
00355 //        payload = -1;
00356 //        ok = ok && readCommand(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPStatus, payload);
00357 //        ok = ok && (payload == state);
00358 //        
00359 //        payload = -1;
00360 //        ok = ok && readCommand(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPStatus, payload);
00361 //        ok = ok && (payload == state);
00362         
00363         if (ok) break;
00364         else    increaseCommandPeriods(500);
00365     }
00366     commandMinimumPeriod = minPeriodOnEntry;
00367     commandTimeoutPeriod = outPeriodOnEntry;
00368   
00369     return ok;
00370 }
00371 
00372 bool SPKTVOne::getResolutionParams(int resStoreNumber, int &horizpx, int &vertpx)
00373 {
00374     bool ok;
00375     
00376     ok = command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionImageToAdjust, resStoreNumber);
00377     
00378     ok = ok && readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionActiveH, horizpx);
00379     ok = ok && readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionActiveV, vertpx);
00380     
00381     return ok;
00382 }
00383 
00384 SPKTVOne::aspectType SPKTVOne::getAspect()
00385 {
00386     aspectType aspect = aspectFit;;
00387 
00388     int32_t payload1 = -1;
00389     readCommand(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceAspectCorrect, payload1);
00390   
00391     int32_t payload2 = -1;
00392     readCommand(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceAspectCorrect, payload2);
00393     
00394     if (payload1 == payload2) 
00395     {
00396         if (payload1 == aspectFit)   aspect = aspectFit;
00397         if (payload1 == aspectHFill) aspect = aspectSPKFill;
00398         if (payload1 == aspectVFill) aspect = aspectSPKFill;
00399         if (payload1 == aspect1to1)  aspect = aspect1to1;
00400     }
00401     else if (((payload1 == aspectHFill) && (payload2 == aspectVFill)) || ((payload2 == aspectHFill) && (payload1 == aspectVFill)))
00402     {
00403         aspect = aspectSPKFill;
00404     }
00405     else 
00406     {
00407         if (debug) debug->printf("SPKTVOne:getAspect got unknown aspect");
00408     }
00409     
00410     return aspect;
00411 }
00412 
00413 bool SPKTVOne::setAspect(aspectType aspect)
00414 {
00415     bool ok = true;
00416     aspectType aspectFor1 = aspect;
00417     aspectType aspectFor2 = aspect;
00418     
00419     if (aspect == aspectSPKFill)
00420     {
00421         int resNumberOutput = getResolution();
00422         int horizOutput = 0;
00423         int vertOutput = 0;
00424         getResolutionParams(resNumberOutput, horizOutput, vertOutput);
00425         
00426         if (resNumberOutput != -1)
00427         {
00428             float aspectOutput = (float)horizOutput / (float)vertOutput;
00429             
00430             int sourceForWindowA = -1;
00431             int sourceForWindowB = -1;
00432             readCommand(0, kTV1WindowIDA, kTV1FunctionAdjustWindowsWindowSource, sourceForWindowA);
00433             readCommand(0, kTV1WindowIDB, kTV1FunctionAdjustWindowsWindowSource, sourceForWindowB); 
00434             
00435             int windowForRGB1 = -1;
00436             int windowForRGB2 = -1;
00437             switch (sourceForWindowA)
00438             {
00439                 case kTV1SourceRGB1: windowForRGB1 = kTV1WindowIDA; break;
00440                 case kTV1SourceRGB2: windowForRGB2 = kTV1WindowIDA; break;
00441             }
00442             switch (sourceForWindowB)
00443             {
00444                 case kTV1SourceRGB1: windowForRGB1 = kTV1WindowIDB; break;
00445                 case kTV1SourceRGB2: windowForRGB2 = kTV1WindowIDB; break;
00446             }
00447             
00448             if ((windowForRGB1 != -1))
00449             {
00450                 int resNumber1 = getResolution(windowForRGB1);
00451                 int horiz1 = 0;
00452                 int vert1 = 0;
00453                 getResolutionParams(resNumber1, horiz1, vert1);
00454                 float aspect1 = (float)horiz1 / (float)vert1;   
00455                 aspectFor1 = aspectOutput > aspect1 ? aspectHFill : aspectVFill;
00456             }
00457             else
00458             {
00459                 aspectFor1 = aspectHFill;
00460             }
00461             
00462             if (windowForRGB2 != -1)
00463             {
00464                 int resNumber2 = getResolution(windowForRGB2);
00465                 int horiz2 = 0;
00466                 int vert2 = 0;
00467                 getResolutionParams(resNumber2, horiz2, vert2);
00468                 float aspect2 = (float)horiz2 / (float)vert2;
00469                 aspectFor2 = aspectOutput > aspect2 ? aspectHFill : aspectVFill;
00470             }
00471             else
00472             {
00473                 aspectFor2 = aspectHFill;
00474             }
00475         }
00476     }
00477         
00478     ok = ok && command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceAspectCorrect, aspectFor1);
00479     ok = ok && command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceAspectCorrect, aspectFor2);
00480     
00481     return ok;
00482 }
00483 
00484 bool SPKTVOne::set1920x480(int resStoreNumber) 
00485 {
00486   bool ok;
00487 
00488   ok = command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionImageToAdjust, resStoreNumber);
00489 
00490   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionInterlaced, 0);
00491   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionFreqCoarseH, 31475);
00492   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionFreqFineH, 31475);
00493   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionActiveH, 1920);
00494   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionActiveV, 480);
00495   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionStartH, 240); 
00496   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionStartV, 5); 
00497   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionCLKS, 2400); 
00498   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionLines, 525);
00499   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncH, 192);
00500   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncV, 30); 
00501   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncPolarity, 0);
00502   
00503   return ok;
00504 }
00505 
00506 bool SPKTVOne::set1600x600(int resStoreNumber) 
00507 {
00508   bool ok;
00509 
00510   ok = command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionImageToAdjust, resStoreNumber);
00511 
00512   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionInterlaced, 0);
00513   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionFreqCoarseH, 37879);
00514   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionFreqFineH, 37879);
00515   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionActiveH, 1600);
00516   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionActiveV, 600);
00517   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionStartH, 192); 
00518   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionStartV, 14); 
00519   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionCLKS, 2112); 
00520   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionLines, 628);
00521   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncH, 160);
00522   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncV, 13); 
00523   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncPolarity, 0);
00524     
00525   return ok;
00526 }
00527 
00528 bool SPKTVOne::set2048x768(int resStoreNumber, bool de)
00529 {
00530   bool ok;
00531   
00532   ok = command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionImageToAdjust, resStoreNumber);
00533 
00534   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionInterlaced, 0);
00535   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionFreqCoarseH, 48363);
00536   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionFreqFineH, 48363);
00537   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionActiveH, 2048);
00538   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionActiveV, 768);
00539   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionStartH, de ? 224 : 152); 
00540   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionStartV, de ? 11 : 20); 
00541   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionCLKS, de ? 2688 : 2352); 
00542   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionLines, de ? 806 : 806);
00543   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncH, de ? 368 : 64);
00544   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncV, de ? 24 : 15); 
00545   ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustResolutionSyncPolarity, 3);
00546     
00547   return ok;
00548 }
00549 
00550 void SPKTVOne::signErrorOff() {
00551     *errorDO = 0;
00552 }
00553 
00554 SPKTVOne::processorType SPKTVOne::getProcessorType()
00555 {
00556     bool ok;
00557     int32_t payload = 0;
00558     
00559     if (processor.version == -1)
00560     {
00561         ok = readCommand(0, kTV1WindowIDA, kTV1FunctionReadSoftwareVersion, payload);
00562         if (ok && payload > 0) processor.version = payload;
00563     }
00564     
00565     if (processor.productType == -1)
00566     {
00567     ok = readCommand(0, kTV1WindowIDA, kTV1FunctionReadProductType, payload);
00568     if (ok && payload > 0) processor.productType = payload;
00569     }
00570     
00571     if (processor.boardType == -1)
00572     {
00573         ok = readCommand(0, kTV1WindowIDA, kTV1FunctionReadBoardType, payload);
00574         if (ok && payload > 0) processor.boardType = payload;
00575     }
00576     
00577     if (debug) debug->printf("v: %i, p: %i, b: %i", processor.version, processor.productType, processor.boardType);
00578     
00579     return processor;
00580 }
00581 
00582 void SPKTVOne::timerCheck() {
00583     // timers are based on 32-bit int microsecond counters, so can only time up to a maximum of 2^31-1 microseconds i.e. 30 minutes.
00584     // this method is called once a minute, and resets the timer if we've been idle for 25mins.
00585     if (timer.read_ms() > 1000*60*25) 
00586     {
00587         if (debug) debug->printf("TVOne Timer reset at %ims", timer.read_ms());
00588         timer.reset();
00589     }
00590 }
00591 
00592 bool SPKTVOne::uploadEDID(FILE *file, int edidSlotIndex)
00593 {
00594     bool success;
00595     
00596     // To write EDID, its broken into chunks and sent as a series of extra-long commands
00597     // Command: 8 bytes of command (see code below) + 32 bytes of EDID payload + End byte
00598     // Acknowledgement: 53 02 40 95 (Hex)
00599     // We want to upload full EDID slot, ie. zero out to 256 even if edidData is only 128bytes.
00600     
00601     if (debug) debug->printf("Upload EDID to index %i \r\n", edidSlotIndex);
00602     
00603     success = uploadFile(0x07, file, 256, edidSlotIndex);
00604     
00605     return success;
00606 }
00607 
00608 bool SPKTVOne::uploadImage(FILE *file, int sisIndex)
00609 {
00610     bool success;
00611     
00612     int imageDataLength = 0;
00613     
00614     while (fgetc(file) != EOF) imageDataLength++ ;
00615     
00616     if (debug) debug->printf("Upload Image with length %i to index %i \r\n", imageDataLength, sisIndex);
00617     
00618     success = uploadFile(0x00, file, imageDataLength, sisIndex);
00619     
00620     return success;
00621 }
00622 
00623 bool SPKTVOne::uploadFile(char instruction, FILE* file, int dataLength, int index)
00624 {
00625     // TASK: Upload Data
00626 
00627     // Lets be conservative with timings
00628     setCommandMinimumPeriod(100);
00629     setCommandTimeoutPeriod(300);
00630 
00631     // This command is reverse engineered. It implements an 'S' command, not the documented 'F'. 
00632     
00633     bool success = false;
00634 
00635     int dataChunkSize = 32;
00636     int ackLength = 4;
00637     char goodAck[] = {0x53, 0x02, 0x40, 0x95};
00638     
00639     fseek(file, 0, SEEK_SET);
00640     
00641     for (int i=0; i<dataLength; i=i+dataChunkSize)
00642     {
00643         int dataRemaining = dataLength - i;
00644         int actualDataChunkSize = (dataRemaining < dataChunkSize) ? dataRemaining : dataChunkSize;
00645     
00646         int commandLength = 8+dataChunkSize+1;
00647         char command[commandLength];
00648 
00649         command[0] = 0x53;
00650         command[1] = 6 + actualDataChunkSize + 1; // Subsequent number of bytes in command
00651         command[2] = 0x22;
00652         command[3] = instruction;
00653         command[4] = index;
00654         command[5] = 0;
00655         command[6] = (i / dataChunkSize) & 0xFF; // chunk index LSB
00656         command[7] = ((i / dataChunkSize) >> 8) & 0xFF; // chunk index MSB
00657 
00658         for (int j=0; j<actualDataChunkSize; j++)
00659         {
00660           char data = fgetc(file);
00661           if (!feof(file)) 
00662             *(command+8+j) = data;
00663           else 
00664             *(command+8+j) = 0;
00665         }
00666 
00667         command[8+actualDataChunkSize] = 0x3F;
00668 
00669         if (debug)
00670         {
00671             debug->printf("Command: ");
00672             for (int k=0; k < commandLength; k++) debug->printf(" %x", command[k]);
00673             debug->printf("\r\n");
00674         }
00675 
00676         while (serial->readable() || timer.read_ms() < commandMinimumPeriod) 
00677         {
00678            if (serial->readable()) serial->getc();
00679         }
00680  
00681         for (int k=0; k < commandLength; k++) serial->putc(command[k]);
00682         
00683         timer.reset();
00684         
00685         char ackBuffer[4];
00686         int  ackPos = 0;
00687         while (timer.read_ms() < commandTimeoutPeriod) 
00688         {
00689             if (serial->readable()) ackBuffer[ackPos++] = serial->getc();
00690             if (ackPos == 4) break;
00691         }
00692 
00693         if (memcmp(ackBuffer, goodAck, ackLength) == 0) 
00694         {
00695             success = true;
00696         }
00697         else
00698         {
00699             success = false;
00700             if (debug) 
00701             {
00702                 debug->printf("Data Part write failed. Ack:");
00703                 for (int k = 0; k < ackLength; k++) debug->printf(" %x", ackBuffer[k]);
00704                 debug->printf("\r\n");
00705             }
00706             break;
00707         }
00708     }
00709     
00710     resetCommandPeriods();
00711     
00712     return success;
00713 }