RS232 control for TVOne products
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Wed Jul 13 2022 01:23:55 by 1.7.2