RS232 control for TVOne products

Dependents:   SPK-DVIMXR

Committer:
tobyspark
Date:
Wed Dec 05 16:02:31 2012 +0000
Revision:
18:d765b0d9271c
Parent:
17:68b9bb89da2b
Child:
19:350d1191e466
Reworkings / Overhauls for robustness

Who changed what in which revision?

UserRevisionLine numberNew contents of line
tobyspark 0:533cfae24a1b 1 // *spark audio-visual
tobyspark 0:533cfae24a1b 2 // RS232 Control for TV-One products
tobyspark 0:533cfae24a1b 3 // Good for 1T-C2-750, others will need some extra work
tobyspark 1:349d6da461df 4
tobyspark 1:349d6da461df 5 /* Copyright (c) 2011 Toby Harris, MIT License
tobyspark 1:349d6da461df 6 *
tobyspark 1:349d6da461df 7 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
tobyspark 1:349d6da461df 8 * and associated documentation files (the "Software"), to deal in the Software without restriction,
tobyspark 1:349d6da461df 9 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
tobyspark 1:349d6da461df 10 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
tobyspark 1:349d6da461df 11 * furnished to do so, subject to the following conditions:
tobyspark 1:349d6da461df 12 *
tobyspark 1:349d6da461df 13 * The above copyright notice and this permission notice shall be included in all copies or
tobyspark 1:349d6da461df 14 * substantial portions of the Software.
tobyspark 1:349d6da461df 15 *
tobyspark 1:349d6da461df 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
tobyspark 1:349d6da461df 17 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
tobyspark 1:349d6da461df 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
tobyspark 1:349d6da461df 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
tobyspark 1:349d6da461df 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
tobyspark 1:349d6da461df 21 */
tobyspark 0:533cfae24a1b 22
tobyspark 0:533cfae24a1b 23 #include "spk_tvone_mbed.h"
tobyspark 0:533cfae24a1b 24 #include "mbed.h"
tobyspark 0:533cfae24a1b 25
tobyspark 0:533cfae24a1b 26 SPKTVOne::SPKTVOne(PinName txPin, PinName rxPin, PinName signWritePin, PinName signErrorPin, Serial *debugSerial)
tobyspark 0:533cfae24a1b 27 {
tobyspark 0:533cfae24a1b 28 // Create Serial connection for TVOne unit comms
tobyspark 0:533cfae24a1b 29 // Creating our own as this is exclusively for TVOne comms
tobyspark 9:42c83cac2f6d 30 serial = new Serial(txPin, rxPin);
tobyspark 0:533cfae24a1b 31 serial->baud(57600);
tobyspark 0:533cfae24a1b 32
tobyspark 0:533cfae24a1b 33 if (signWritePin != NC) writeDO = new DigitalOut(signWritePin);
tobyspark 0:533cfae24a1b 34 else writeDO = NULL;
tobyspark 0:533cfae24a1b 35
tobyspark 0:533cfae24a1b 36 if (signErrorPin != NC) errorDO = new DigitalOut(signErrorPin);
tobyspark 0:533cfae24a1b 37 else errorDO = NULL;
tobyspark 0:533cfae24a1b 38
tobyspark 18:d765b0d9271c 39 processor.version = -1;
tobyspark 18:d765b0d9271c 40 processor.productType = -1;
tobyspark 18:d765b0d9271c 41 processor.boardType = -1;
tobyspark 18:d765b0d9271c 42
tobyspark 18:d765b0d9271c 43 resetCommandPeriods();
tobyspark 14:da403a01f9ef 44
tobyspark 14:da403a01f9ef 45 timer.start();
tobyspark 17:68b9bb89da2b 46 timerCheckTicker.attach(this, &SPKTVOne::timerCheck, 60);
tobyspark 14:da403a01f9ef 47
tobyspark 0:533cfae24a1b 48 // Link up debug Serial object
tobyspark 0:533cfae24a1b 49 // Passing in shared object as debugging is shared between all DVI mixer functions
tobyspark 0:533cfae24a1b 50 debug = debugSerial;
tobyspark 0:533cfae24a1b 51 }
tobyspark 0:533cfae24a1b 52
tobyspark 10:5f398fc9b5c1 53 bool SPKTVOne::command(uint8_t channel, uint8_t window, int32_t func, int32_t payload)
tobyspark 10:5f398fc9b5c1 54 {
tobyspark 14:da403a01f9ef 55 int ackBuff[standardAckLength] = {0};
tobyspark 10:5f398fc9b5c1 56
tobyspark 11:90e5a72a0034 57 bool success = command(writeCommandType, ackBuff, standardAckLength, channel, window, func, payload);
tobyspark 11:90e5a72a0034 58
tobyspark 11:90e5a72a0034 59 // TASK: Check return payload is what we tried to set it to
tobyspark 11:90e5a72a0034 60 char payloadStr[7];
tobyspark 11:90e5a72a0034 61 for (int i = 0; i < 6; i++)
tobyspark 11:90e5a72a0034 62 {
tobyspark 11:90e5a72a0034 63 payloadStr[i] = ackBuff[11+i];
tobyspark 11:90e5a72a0034 64 }
tobyspark 11:90e5a72a0034 65 payloadStr[6] = NULL;
tobyspark 11:90e5a72a0034 66
tobyspark 11:90e5a72a0034 67 int payloadBack = strtol (payloadStr, NULL, 16);
tobyspark 11:90e5a72a0034 68
tobyspark 11:90e5a72a0034 69 if (payload != payloadBack)
tobyspark 11:90e5a72a0034 70 {
tobyspark 11:90e5a72a0034 71 success = false;
tobyspark 13:4d659b89a457 72 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);
tobyspark 11:90e5a72a0034 73 }
tobyspark 11:90e5a72a0034 74 return success;
tobyspark 11:90e5a72a0034 75 }
tobyspark 11:90e5a72a0034 76
tobyspark 11:90e5a72a0034 77 bool SPKTVOne::readCommand(uint8_t channel, uint8_t window, int32_t func, int32_t &payload)
tobyspark 11:90e5a72a0034 78 {
tobyspark 14:da403a01f9ef 79 int ackBuff[standardAckLength] = {0};
tobyspark 11:90e5a72a0034 80
tobyspark 11:90e5a72a0034 81 bool success = command(readCommandType, ackBuff, standardAckLength, channel, window, func, payload);
tobyspark 11:90e5a72a0034 82
tobyspark 11:90e5a72a0034 83 char payloadStr[7];
tobyspark 11:90e5a72a0034 84 for (int i = 0; i < 6; i++)
tobyspark 11:90e5a72a0034 85 {
tobyspark 11:90e5a72a0034 86 payloadStr[i] = ackBuff[11+i];
tobyspark 11:90e5a72a0034 87 }
tobyspark 11:90e5a72a0034 88 payloadStr[6] = NULL;
tobyspark 11:90e5a72a0034 89
tobyspark 11:90e5a72a0034 90 payload = strtol (payloadStr, NULL, 16);
tobyspark 10:5f398fc9b5c1 91
tobyspark 10:5f398fc9b5c1 92 return success;
tobyspark 10:5f398fc9b5c1 93 }
tobyspark 10:5f398fc9b5c1 94
tobyspark 10:5f398fc9b5c1 95 bool SPKTVOne::command(commandType readWrite, int* ackBuffer, int ackLength, uint8_t channel, uint8_t window, int32_t func, int32_t payload)
tobyspark 14:da403a01f9ef 96 {
tobyspark 14:da403a01f9ef 97 if (debug) debug->printf("TVOne %s Channel: %#x, Window: %#x, Function: %#x Payload: %i \r\n", (readWrite == writeCommandType) ? "Write" : "Read", channel, window, func, payload);
tobyspark 14:da403a01f9ef 98
tobyspark 0:533cfae24a1b 99 // TASK: Sign start of serial command write
tobyspark 0:533cfae24a1b 100 if (writeDO) *writeDO = 1;
tobyspark 0:533cfae24a1b 101
tobyspark 14:da403a01f9ef 102 // TASK: Prepare to issue command to the TVOne unit
tobyspark 14:da403a01f9ef 103 // - discard anything waiting to be read in the return serial buffer
tobyspark 14:da403a01f9ef 104 // - make sure we're past the minimum time between command sends as the unit can get overloaded
tobyspark 18:d765b0d9271c 105 while (serial->readable() || timer.read_ms() < commandMinimumPeriod) {
tobyspark 14:da403a01f9ef 106 if (serial->readable()) serial->getc();
tobyspark 0:533cfae24a1b 107 }
tobyspark 0:533cfae24a1b 108
tobyspark 0:533cfae24a1b 109 // TASK: Create the bytes of command
tobyspark 0:533cfae24a1b 110
tobyspark 0:533cfae24a1b 111 uint8_t cmd[8];
tobyspark 0:533cfae24a1b 112 uint8_t checksum = 0;
tobyspark 0:533cfae24a1b 113
tobyspark 0:533cfae24a1b 114 // CMD
tobyspark 10:5f398fc9b5c1 115 cmd[0] = readWrite<<7 | 1<<2;
tobyspark 0:533cfae24a1b 116 // CHA
tobyspark 0:533cfae24a1b 117 cmd[1] = channel;
tobyspark 0:533cfae24a1b 118 // WINDOW
tobyspark 0:533cfae24a1b 119 cmd[2] = window;
tobyspark 0:533cfae24a1b 120 // OUTPUT & FUNCTION
tobyspark 0:533cfae24a1b 121 // cmd[3] cmd[4]
tobyspark 0:533cfae24a1b 122 // output 0 = 0000xxx xxxxxxx
tobyspark 0:533cfae24a1b 123 // function = xxxXXXX XXXXXXX
tobyspark 0:533cfae24a1b 124 cmd[3] = func >> 8;
tobyspark 0:533cfae24a1b 125 cmd[4] = func & 0xFF;
tobyspark 0:533cfae24a1b 126 // PAYLOAD
tobyspark 0:533cfae24a1b 127 cmd[5] = (payload >> 16) & 0xFF;
tobyspark 0:533cfae24a1b 128 cmd[6] = (payload >> 8) & 0xFF;
tobyspark 0:533cfae24a1b 129 cmd[7] = payload & 0xFF;
tobyspark 0:533cfae24a1b 130
tobyspark 0:533cfae24a1b 131 // TASK: Write the bytes of command to RS232 as correctly packaged 20 characters of ASCII
tobyspark 10:5f398fc9b5c1 132
tobyspark 11:90e5a72a0034 133 if (readWrite == writeCommandType)
tobyspark 0:533cfae24a1b 134 {
tobyspark 14:da403a01f9ef 135 for (int i=0; i<8; i++) checksum += cmd[i];
tobyspark 10:5f398fc9b5c1 136 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);
tobyspark 0:533cfae24a1b 137 }
tobyspark 11:90e5a72a0034 138 if (readWrite == readCommandType)
tobyspark 10:5f398fc9b5c1 139 {
tobyspark 14:da403a01f9ef 140 for (int i=0; i<5; i++) checksum += cmd[i];
tobyspark 10:5f398fc9b5c1 141 serial->printf("F%02X%02X%02X%02X%02X%02X\r", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], checksum);
tobyspark 10:5f398fc9b5c1 142 }
tobyspark 10:5f398fc9b5c1 143
tobyspark 0:533cfae24a1b 144 // TASK: Check the unit's return string, to enable return to main program as soon as unit is ready
tobyspark 0:533cfae24a1b 145
tobyspark 0:533cfae24a1b 146 // Handling the timing of this return is critical to effective control.
tobyspark 0:533cfae24a1b 147 // Returning the instant something is received back overloads the processor, as does anything until the full 20 char acknowledgement.
tobyspark 0:533cfae24a1b 148 // TVOne turn out to say that receipt of the ack doesn't guarantee the unit is ready for the next command.
tobyspark 0:533cfae24a1b 149 // According to the manual, operations typically take 30ms, and to simplify programming you can throttle commands to every 100ms.
tobyspark 0:533cfae24a1b 150 // 100ms is too slow for us. Going with returning after 30ms if we've received an acknowledgement, returning after 100ms otherwise.
tobyspark 10:5f398fc9b5c1 151
tobyspark 14:da403a01f9ef 152 bool success = false;
tobyspark 14:da403a01f9ef 153 int ackPos = 0;
tobyspark 14:da403a01f9ef 154 timer.reset();
tobyspark 0:533cfae24a1b 155
tobyspark 18:d765b0d9271c 156 while (timer.read_ms() < commandTimeoutPeriod)
tobyspark 10:5f398fc9b5c1 157 {
tobyspark 14:da403a01f9ef 158 if (serial->readable())
tobyspark 10:5f398fc9b5c1 159 {
tobyspark 14:da403a01f9ef 160 if (ackPos == 0)
tobyspark 9:42c83cac2f6d 161 {
tobyspark 14:da403a01f9ef 162 ackBuffer[0] = serial->getc();
tobyspark 14:da403a01f9ef 163 if (ackBuffer[0] == 'F') ackPos = 1;
tobyspark 14:da403a01f9ef 164 }
tobyspark 14:da403a01f9ef 165 else
tobyspark 14:da403a01f9ef 166 {
tobyspark 14:da403a01f9ef 167 ackBuffer[ackPos] = serial->getc();
tobyspark 14:da403a01f9ef 168 ackPos++;
tobyspark 14:da403a01f9ef 169 if (ackPos == ackLength) break;
tobyspark 0:533cfae24a1b 170 }
tobyspark 10:5f398fc9b5c1 171 }
tobyspark 9:42c83cac2f6d 172 }
tobyspark 14:da403a01f9ef 173
tobyspark 14:da403a01f9ef 174 // Return true if we got the no error acknowledgement from the unit. The rest of the ack will be verified elsewhere if needed.
tobyspark 14:da403a01f9ef 175 if (ackPos > 2 && ackBuffer[1] == '4')
tobyspark 14:da403a01f9ef 176 {
tobyspark 14:da403a01f9ef 177 success = true;
tobyspark 14:da403a01f9ef 178 }
tobyspark 0:533cfae24a1b 179
tobyspark 0:533cfae24a1b 180 // TASK: Sign end of write
tobyspark 0:533cfae24a1b 181
tobyspark 0:533cfae24a1b 182 if (writeDO) *writeDO = 0;
tobyspark 0:533cfae24a1b 183
tobyspark 0:533cfae24a1b 184 if (!success) {
tobyspark 0:533cfae24a1b 185 if (errorDO) {
tobyspark 0:533cfae24a1b 186 signErrorTimeout.detach();
tobyspark 0:533cfae24a1b 187 signErrorTimeout.attach(this, &SPKTVOne::signErrorOff, 0.25);
tobyspark 0:533cfae24a1b 188 *errorDO = 1;
tobyspark 0:533cfae24a1b 189 }
tobyspark 0:533cfae24a1b 190
tobyspark 0:533cfae24a1b 191 if (debug) {
tobyspark 14:da403a01f9ef 192 debug->printf("TVOne serial error. Time from finishing writing command: %ims. Received %i ack chars:", timer.read_ms(), ackPos);
tobyspark 14:da403a01f9ef 193 for (int i = 0; i<ackLength; i++)
tobyspark 14:da403a01f9ef 194 {
tobyspark 14:da403a01f9ef 195 debug->printf("%c", ackBuffer[i]);
tobyspark 14:da403a01f9ef 196 }
tobyspark 14:da403a01f9ef 197 debug->printf("\r\n");
tobyspark 0:533cfae24a1b 198 }
tobyspark 0:533cfae24a1b 199 };
tobyspark 0:533cfae24a1b 200
tobyspark 0:533cfae24a1b 201 return success;
tobyspark 0:533cfae24a1b 202 }
tobyspark 0:533cfae24a1b 203
tobyspark 18:d765b0d9271c 204 void SPKTVOne::setCommandTimeoutPeriod(int millis)
tobyspark 18:d765b0d9271c 205 {
tobyspark 18:d765b0d9271c 206 commandTimeoutPeriod = millis;
tobyspark 18:d765b0d9271c 207 }
tobyspark 18:d765b0d9271c 208
tobyspark 18:d765b0d9271c 209 void SPKTVOne::setCommandMinimumPeriod(int millis)
tobyspark 18:d765b0d9271c 210 {
tobyspark 18:d765b0d9271c 211 commandMinimumPeriod = millis;
tobyspark 18:d765b0d9271c 212 }
tobyspark 18:d765b0d9271c 213
tobyspark 18:d765b0d9271c 214 void SPKTVOne::increaseCommandPeriods(int millis)
tobyspark 18:d765b0d9271c 215 {
tobyspark 18:d765b0d9271c 216 commandTimeoutPeriod += millis;
tobyspark 18:d765b0d9271c 217 commandMinimumPeriod += millis;
tobyspark 18:d765b0d9271c 218
tobyspark 18:d765b0d9271c 219 if (debug) debug->printf("Command periods increased; minimum: %i, timeout: %i", commandMinimumPeriod, commandTimeoutPeriod);
tobyspark 18:d765b0d9271c 220 }
tobyspark 18:d765b0d9271c 221
tobyspark 18:d765b0d9271c 222 void SPKTVOne::resetCommandPeriods()
tobyspark 18:d765b0d9271c 223 {
tobyspark 18:d765b0d9271c 224 commandTimeoutPeriod = kTV1CommandTimeoutMillis;
tobyspark 18:d765b0d9271c 225 commandMinimumPeriod = kTV1CommandMinimumMillis;
tobyspark 18:d765b0d9271c 226 }
tobyspark 18:d765b0d9271c 227
tobyspark 14:da403a01f9ef 228 int SPKTVOne::millisSinceLastCommandSent()
tobyspark 14:da403a01f9ef 229 {
tobyspark 14:da403a01f9ef 230 return timer.read_ms();
tobyspark 14:da403a01f9ef 231 }
tobyspark 14:da403a01f9ef 232
tobyspark 18:d765b0d9271c 233 bool SPKTVOne::uploadCustomResolutions()
tobyspark 0:533cfae24a1b 234 {
tobyspark 11:90e5a72a0034 235 bool ok = true;;
tobyspark 11:90e5a72a0034 236 int unlocked = 0;
tobyspark 11:90e5a72a0034 237 int locked = 1;
tobyspark 11:90e5a72a0034 238
tobyspark 11:90e5a72a0034 239 ok = ok && command(0, 0, kTV1FunctionAdjustFrontPanelLock, locked);
tobyspark 11:90e5a72a0034 240
tobyspark 11:90e5a72a0034 241 ok = ok && set1920x480(kTV1ResolutionTripleHeadVGAp60);
tobyspark 11:90e5a72a0034 242 ok = ok && set1600x600(kTV1ResolutionDualHeadSVGAp60);
tobyspark 11:90e5a72a0034 243 ok = ok && set2048x768(kTV1ResolutionDualHeadXGAp60);
tobyspark 11:90e5a72a0034 244
tobyspark 11:90e5a72a0034 245 ok = ok && command(0, 0, kTV1FunctionAdjustFrontPanelLock, unlocked);
tobyspark 11:90e5a72a0034 246
tobyspark 11:90e5a72a0034 247 return ok;
tobyspark 0:533cfae24a1b 248 }
tobyspark 0:533cfae24a1b 249
tobyspark 18:d765b0d9271c 250 bool SPKTVOne::setResolution(int resolution, int edidSlot)
tobyspark 18:d765b0d9271c 251 {
tobyspark 18:d765b0d9271c 252 bool ok;
tobyspark 18:d765b0d9271c 253
tobyspark 18:d765b0d9271c 254 int minPeriodOnEntry = commandMinimumPeriod;
tobyspark 18:d765b0d9271c 255 int outPeriodOnEntry = commandTimeoutPeriod;
tobyspark 18:d765b0d9271c 256
tobyspark 18:d765b0d9271c 257 for (int i=0; i < 3; i++)
tobyspark 18:d765b0d9271c 258 {
tobyspark 18:d765b0d9271c 259 ok = command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsOutputResolution, resolution);
tobyspark 18:d765b0d9271c 260
tobyspark 18:d765b0d9271c 261 if (ok) break;
tobyspark 18:d765b0d9271c 262 else increaseCommandPeriods(500);
tobyspark 18:d765b0d9271c 263 }
tobyspark 18:d765b0d9271c 264 commandMinimumPeriod = minPeriodOnEntry;
tobyspark 18:d765b0d9271c 265 commandTimeoutPeriod = outPeriodOnEntry;
tobyspark 18:d765b0d9271c 266 if (!ok) return ok;
tobyspark 18:d765b0d9271c 267
tobyspark 18:d765b0d9271c 268 for (int i=0; i < 3; i++)
tobyspark 18:d765b0d9271c 269 {
tobyspark 18:d765b0d9271c 270 ok = command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, edidSlot);
tobyspark 18:d765b0d9271c 271 ok = ok && command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceEDID, edidSlot);
tobyspark 18:d765b0d9271c 272
tobyspark 18:d765b0d9271c 273 if (ok) break;
tobyspark 18:d765b0d9271c 274 else increaseCommandPeriods(500);
tobyspark 18:d765b0d9271c 275 }
tobyspark 18:d765b0d9271c 276 commandMinimumPeriod = minPeriodOnEntry;
tobyspark 18:d765b0d9271c 277 commandTimeoutPeriod = outPeriodOnEntry;
tobyspark 18:d765b0d9271c 278
tobyspark 18:d765b0d9271c 279 return ok;
tobyspark 18:d765b0d9271c 280 }
tobyspark 18:d765b0d9271c 281
tobyspark 3:03e7e7b7a870 282 bool SPKTVOne::setHDCPOn(bool state)
tobyspark 0:533cfae24a1b 283 {
tobyspark 18:d765b0d9271c 284 bool ok;
tobyspark 18:d765b0d9271c 285
tobyspark 18:d765b0d9271c 286 int minPeriodOnEntry = commandMinimumPeriod;
tobyspark 18:d765b0d9271c 287 int outPeriodOnEntry = commandTimeoutPeriod;
tobyspark 18:d765b0d9271c 288
tobyspark 18:d765b0d9271c 289 // HDCP can sometimes take a little time to settle down
tobyspark 18:d765b0d9271c 290 for (int i=0; i < 3; i++)
tobyspark 18:d765b0d9271c 291 {
tobyspark 18:d765b0d9271c 292 // Turn HDCP off on the output
tobyspark 18:d765b0d9271c 293 ok = command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsHDCPRequired, state);
tobyspark 18:d765b0d9271c 294
tobyspark 18:d765b0d9271c 295 // Likewise on inputs A and B
tobyspark 18:d765b0d9271c 296 ok = ok && command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPAdvertize, state);
tobyspark 18:d765b0d9271c 297 ok = ok && command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPAdvertize, state);
tobyspark 18:d765b0d9271c 298
tobyspark 18:d765b0d9271c 299 // Verify (read only, but write command as implemented here will check return value)
tobyspark 18:d765b0d9271c 300 ok = ok && command(0, kTV1WindowIDA, kTV1FunctionAdjustOutputsHDCPStatus, state);
tobyspark 18:d765b0d9271c 301 ok = ok && command(kTV1SourceRGB1, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPStatus, state);
tobyspark 18:d765b0d9271c 302 ok = ok && command(kTV1SourceRGB2, kTV1WindowIDA, kTV1FunctionAdjustSourceHDCPStatus, state);
tobyspark 18:d765b0d9271c 303
tobyspark 18:d765b0d9271c 304 if (ok) break;
tobyspark 18:d765b0d9271c 305 else increaseCommandPeriods(500);
tobyspark 18:d765b0d9271c 306 }
tobyspark 18:d765b0d9271c 307 commandMinimumPeriod = minPeriodOnEntry;
tobyspark 18:d765b0d9271c 308 commandTimeoutPeriod = outPeriodOnEntry;
tobyspark 0:533cfae24a1b 309
tobyspark 18:d765b0d9271c 310 return ok;
tobyspark 0:533cfae24a1b 311 }
tobyspark 0:533cfae24a1b 312
tobyspark 11:90e5a72a0034 313 bool SPKTVOne::set1920x480(int resStoreNumber)
tobyspark 0:533cfae24a1b 314 {
tobyspark 11:90e5a72a0034 315 bool ok;
tobyspark 11:90e5a72a0034 316
tobyspark 11:90e5a72a0034 317 ok = command(0, 0, kTV1FunctionAdjustResolutionImageToAdjust, resStoreNumber);
tobyspark 18:d765b0d9271c 318
tobyspark 18:d765b0d9271c 319 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionInterlaced, 0);
tobyspark 18:d765b0d9271c 320 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionFreqCoarseH, 31475);
tobyspark 18:d765b0d9271c 321 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionFreqFineH, 31475);
tobyspark 18:d765b0d9271c 322 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionActiveH, 1920);
tobyspark 18:d765b0d9271c 323 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionActiveV, 480);
tobyspark 18:d765b0d9271c 324 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionStartH, 240);
tobyspark 18:d765b0d9271c 325 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionStartV, 5);
tobyspark 18:d765b0d9271c 326 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionCLKS, 2400);
tobyspark 18:d765b0d9271c 327 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionLines, 525);
tobyspark 18:d765b0d9271c 328 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncH, 192);
tobyspark 18:d765b0d9271c 329 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncV, 30);
tobyspark 18:d765b0d9271c 330 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncPolarity, 0);
tobyspark 11:90e5a72a0034 331
tobyspark 11:90e5a72a0034 332 return ok;
tobyspark 0:533cfae24a1b 333 }
tobyspark 0:533cfae24a1b 334
tobyspark 11:90e5a72a0034 335 bool SPKTVOne::set1600x600(int resStoreNumber)
tobyspark 0:533cfae24a1b 336 {
tobyspark 11:90e5a72a0034 337 bool ok;
tobyspark 11:90e5a72a0034 338
tobyspark 11:90e5a72a0034 339 ok = command(0, 0, kTV1FunctionAdjustResolutionImageToAdjust, resStoreNumber);
tobyspark 18:d765b0d9271c 340
tobyspark 18:d765b0d9271c 341 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionInterlaced, 0);
tobyspark 18:d765b0d9271c 342 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionFreqCoarseH, 37879);
tobyspark 18:d765b0d9271c 343 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionFreqFineH, 37879);
tobyspark 18:d765b0d9271c 344 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionActiveH, 1600);
tobyspark 18:d765b0d9271c 345 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionActiveV, 600);
tobyspark 18:d765b0d9271c 346 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionStartH, 192);
tobyspark 18:d765b0d9271c 347 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionStartV, 14);
tobyspark 18:d765b0d9271c 348 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionCLKS, 2112);
tobyspark 18:d765b0d9271c 349 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionLines, 628);
tobyspark 18:d765b0d9271c 350 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncH, 160);
tobyspark 18:d765b0d9271c 351 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncV, 13);
tobyspark 18:d765b0d9271c 352 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncPolarity, 0);
tobyspark 11:90e5a72a0034 353
tobyspark 11:90e5a72a0034 354 return ok;
tobyspark 0:533cfae24a1b 355 }
tobyspark 0:533cfae24a1b 356
tobyspark 11:90e5a72a0034 357 bool SPKTVOne::set2048x768(int resStoreNumber)
tobyspark 2:af9e9ab63b23 358 {
tobyspark 11:90e5a72a0034 359 bool ok;
tobyspark 11:90e5a72a0034 360
tobyspark 11:90e5a72a0034 361 ok = command(0, 0, kTV1FunctionAdjustResolutionImageToAdjust, resStoreNumber);
tobyspark 18:d765b0d9271c 362
tobyspark 18:d765b0d9271c 363 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionInterlaced, 0);
tobyspark 18:d765b0d9271c 364 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionFreqCoarseH, 48363);
tobyspark 18:d765b0d9271c 365 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionFreqFineH, 48363);
tobyspark 18:d765b0d9271c 366 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionActiveH, 2048);
tobyspark 18:d765b0d9271c 367 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionActiveV, 768);
tobyspark 18:d765b0d9271c 368 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionStartH, 224);
tobyspark 18:d765b0d9271c 369 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionStartV, 11);
tobyspark 18:d765b0d9271c 370 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionCLKS, 2688);
tobyspark 18:d765b0d9271c 371 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionLines, 806);
tobyspark 18:d765b0d9271c 372 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncH, 368);
tobyspark 18:d765b0d9271c 373 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncV, 24);
tobyspark 18:d765b0d9271c 374 ok = ok && command(0, 0, kTV1FunctionAdjustResolutionSyncPolarity, 0);
tobyspark 11:90e5a72a0034 375
tobyspark 11:90e5a72a0034 376 return ok;
tobyspark 2:af9e9ab63b23 377 }
tobyspark 2:af9e9ab63b23 378
tobyspark 0:533cfae24a1b 379 void SPKTVOne::signErrorOff() {
tobyspark 0:533cfae24a1b 380 *errorDO = 0;
tobyspark 0:533cfae24a1b 381 }
tobyspark 14:da403a01f9ef 382
tobyspark 18:d765b0d9271c 383 SPKTVOne::processorType SPKTVOne::getProcessorType()
tobyspark 18:d765b0d9271c 384 {
tobyspark 18:d765b0d9271c 385 bool ok;
tobyspark 18:d765b0d9271c 386 int32_t payload = 0;
tobyspark 18:d765b0d9271c 387
tobyspark 18:d765b0d9271c 388 if (processor.version == -1)
tobyspark 18:d765b0d9271c 389 {
tobyspark 18:d765b0d9271c 390 ok = readCommand(0, kTV1WindowIDA, kTV1FunctionReadSoftwareVersion, payload);
tobyspark 18:d765b0d9271c 391 if (ok && payload > 0) processor.version = payload;
tobyspark 18:d765b0d9271c 392 }
tobyspark 18:d765b0d9271c 393
tobyspark 18:d765b0d9271c 394 if (processor.productType == -1)
tobyspark 18:d765b0d9271c 395 {
tobyspark 18:d765b0d9271c 396 ok = readCommand(0, kTV1WindowIDA, kTV1FunctionReadProductType, payload);
tobyspark 18:d765b0d9271c 397 if (ok && payload > 0) processor.productType = payload;
tobyspark 18:d765b0d9271c 398 }
tobyspark 18:d765b0d9271c 399
tobyspark 18:d765b0d9271c 400 if (processor.boardType == -1)
tobyspark 18:d765b0d9271c 401 {
tobyspark 18:d765b0d9271c 402 ok = readCommand(0, kTV1WindowIDA, kTV1FunctionReadBoardType, payload);
tobyspark 18:d765b0d9271c 403 if (ok && payload > 0) processor.boardType = payload;
tobyspark 18:d765b0d9271c 404 }
tobyspark 18:d765b0d9271c 405
tobyspark 18:d765b0d9271c 406 if (debug) debug->printf("v: %i, p: %i, b: %i", processor.version, processor.productType, processor.boardType);
tobyspark 18:d765b0d9271c 407
tobyspark 18:d765b0d9271c 408 return processor;
tobyspark 18:d765b0d9271c 409 }
tobyspark 18:d765b0d9271c 410
tobyspark 17:68b9bb89da2b 411 void SPKTVOne::timerCheck() {
tobyspark 17:68b9bb89da2b 412 // 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.
tobyspark 17:68b9bb89da2b 413 // this method is called once a minute, and resets the timer if we've been idle for 25mins.
tobyspark 18:d765b0d9271c 414 if (timer.read_ms() > 1000*60*25)
tobyspark 17:68b9bb89da2b 415 {
tobyspark 17:68b9bb89da2b 416 if (debug) debug->printf("TVOne Timer reset at %ims", timer.read_ms());
tobyspark 17:68b9bb89da2b 417 timer.reset();
tobyspark 17:68b9bb89da2b 418 }
tobyspark 17:68b9bb89da2b 419 }
tobyspark 17:68b9bb89da2b 420
tobyspark 16:ed8d08386034 421 bool SPKTVOne::uploadEDID(FILE *file, int edidSlotIndex)
tobyspark 14:da403a01f9ef 422 {
tobyspark 16:ed8d08386034 423 bool success;
tobyspark 14:da403a01f9ef 424
tobyspark 14:da403a01f9ef 425 // To write EDID, its broken into chunks and sent as a series of extra-long commands
tobyspark 14:da403a01f9ef 426 // Command: 8 bytes of command (see code below) + 32 bytes of EDID payload + End byte
tobyspark 14:da403a01f9ef 427 // Acknowledgement: 53 02 40 95 (Hex)
tobyspark 16:ed8d08386034 428 // We want to upload full EDID slot, ie. zero out to 256 even if edidData is only 128bytes.
tobyspark 14:da403a01f9ef 429
tobyspark 16:ed8d08386034 430 if (debug) debug->printf("Upload EDID to index %i \r\n", edidSlotIndex);
tobyspark 16:ed8d08386034 431
tobyspark 16:ed8d08386034 432 success = uploadFile(0x07, file, 256, edidSlotIndex);
tobyspark 16:ed8d08386034 433
tobyspark 16:ed8d08386034 434 return success;
tobyspark 16:ed8d08386034 435 }
tobyspark 16:ed8d08386034 436
tobyspark 16:ed8d08386034 437 bool SPKTVOne::uploadImage(FILE *file, int sisIndex)
tobyspark 16:ed8d08386034 438 {
tobyspark 16:ed8d08386034 439 bool success;
tobyspark 16:ed8d08386034 440
tobyspark 16:ed8d08386034 441 int imageDataLength = 0;
tobyspark 16:ed8d08386034 442
tobyspark 16:ed8d08386034 443 while (fgetc(file) != EOF) imageDataLength++ ;
tobyspark 16:ed8d08386034 444
tobyspark 16:ed8d08386034 445 if (debug) debug->printf("Upload Image with length %i to index %i \r\n", imageDataLength, sisIndex);
tobyspark 16:ed8d08386034 446
tobyspark 16:ed8d08386034 447 success = uploadFile(0x00, file, imageDataLength, sisIndex);
tobyspark 16:ed8d08386034 448
tobyspark 16:ed8d08386034 449 return success;
tobyspark 16:ed8d08386034 450 }
tobyspark 16:ed8d08386034 451
tobyspark 16:ed8d08386034 452 bool SPKTVOne::uploadFile(char instruction, FILE* file, int dataLength, int index)
tobyspark 16:ed8d08386034 453 {
tobyspark 16:ed8d08386034 454 // TASK: Upload Data
tobyspark 16:ed8d08386034 455
tobyspark 18:d765b0d9271c 456 // Lets be conservative with timings
tobyspark 18:d765b0d9271c 457 setCommandMinimumPeriod(100);
tobyspark 18:d765b0d9271c 458 setCommandTimeoutPeriod(300);
tobyspark 18:d765b0d9271c 459
tobyspark 16:ed8d08386034 460 // This command is reverse engineered. It implements an 'S' command, not the documented 'F'.
tobyspark 14:da403a01f9ef 461
tobyspark 14:da403a01f9ef 462 bool success = false;
tobyspark 14:da403a01f9ef 463
tobyspark 16:ed8d08386034 464 int dataChunkSize = 32;
tobyspark 14:da403a01f9ef 465 int ackLength = 4;
tobyspark 14:da403a01f9ef 466 char goodAck[] = {0x53, 0x02, 0x40, 0x95};
tobyspark 14:da403a01f9ef 467
tobyspark 16:ed8d08386034 468 fseek(file, 0, SEEK_SET);
tobyspark 16:ed8d08386034 469
tobyspark 16:ed8d08386034 470 for (int i=0; i<dataLength; i=i+dataChunkSize)
tobyspark 14:da403a01f9ef 471 {
tobyspark 16:ed8d08386034 472 int dataRemaining = dataLength - i;
tobyspark 18:d765b0d9271c 473 int actualDataChunkSize = (dataRemaining < dataChunkSize) ? dataRemaining : dataChunkSize;
tobyspark 16:ed8d08386034 474
tobyspark 16:ed8d08386034 475 int commandLength = 8+dataChunkSize+1;
tobyspark 14:da403a01f9ef 476 char command[commandLength];
tobyspark 14:da403a01f9ef 477
tobyspark 14:da403a01f9ef 478 command[0] = 0x53;
tobyspark 18:d765b0d9271c 479 command[1] = 6 + actualDataChunkSize + 1; // Subsequent number of bytes in command
tobyspark 14:da403a01f9ef 480 command[2] = 0x22;
tobyspark 16:ed8d08386034 481 command[3] = instruction;
tobyspark 16:ed8d08386034 482 command[4] = index;
tobyspark 14:da403a01f9ef 483 command[5] = 0;
tobyspark 16:ed8d08386034 484 command[6] = (i / dataChunkSize) & 0xFF; // chunk index LSB
tobyspark 16:ed8d08386034 485 command[7] = ((i / dataChunkSize) >> 8) & 0xFF; // chunk index MSB
tobyspark 14:da403a01f9ef 486
tobyspark 18:d765b0d9271c 487 for (int j=0; j<actualDataChunkSize; j++)
tobyspark 14:da403a01f9ef 488 {
tobyspark 16:ed8d08386034 489 char data = fgetc(file);
tobyspark 16:ed8d08386034 490 if (!feof(file))
tobyspark 16:ed8d08386034 491 *(command+8+j) = data;
tobyspark 14:da403a01f9ef 492 else
tobyspark 14:da403a01f9ef 493 *(command+8+j) = 0;
tobyspark 14:da403a01f9ef 494 }
tobyspark 14:da403a01f9ef 495
tobyspark 18:d765b0d9271c 496 command[8+actualDataChunkSize] = 0x3F;
tobyspark 14:da403a01f9ef 497
tobyspark 14:da403a01f9ef 498 if (debug)
tobyspark 14:da403a01f9ef 499 {
tobyspark 16:ed8d08386034 500 debug->printf("Command: ");
tobyspark 14:da403a01f9ef 501 for (int k=0; k < commandLength; k++) debug->printf(" %x", command[k]);
tobyspark 14:da403a01f9ef 502 debug->printf("\r\n");
tobyspark 14:da403a01f9ef 503 }
tobyspark 14:da403a01f9ef 504
tobyspark 18:d765b0d9271c 505 while (serial->readable() || timer.read_ms() < commandMinimumPeriod)
tobyspark 14:da403a01f9ef 506 {
tobyspark 14:da403a01f9ef 507 if (serial->readable()) serial->getc();
tobyspark 14:da403a01f9ef 508 }
tobyspark 14:da403a01f9ef 509
tobyspark 14:da403a01f9ef 510 for (int k=0; k < commandLength; k++) serial->putc(command[k]);
tobyspark 14:da403a01f9ef 511
tobyspark 14:da403a01f9ef 512 timer.reset();
tobyspark 14:da403a01f9ef 513
tobyspark 14:da403a01f9ef 514 char ackBuffer[4];
tobyspark 14:da403a01f9ef 515 int ackPos = 0;
tobyspark 18:d765b0d9271c 516 while (timer.read_ms() < commandTimeoutPeriod)
tobyspark 14:da403a01f9ef 517 {
tobyspark 14:da403a01f9ef 518 if (serial->readable()) ackBuffer[ackPos++] = serial->getc();
tobyspark 14:da403a01f9ef 519 if (ackPos == 4) break;
tobyspark 14:da403a01f9ef 520 }
tobyspark 16:ed8d08386034 521
tobyspark 14:da403a01f9ef 522 if (memcmp(ackBuffer, goodAck, ackLength) == 0)
tobyspark 14:da403a01f9ef 523 {
tobyspark 14:da403a01f9ef 524 success = true;
tobyspark 14:da403a01f9ef 525 }
tobyspark 14:da403a01f9ef 526 else
tobyspark 14:da403a01f9ef 527 {
tobyspark 14:da403a01f9ef 528 success = false;
tobyspark 14:da403a01f9ef 529 if (debug)
tobyspark 14:da403a01f9ef 530 {
tobyspark 18:d765b0d9271c 531 debug->printf("Data Part write failed. Ack:");
tobyspark 14:da403a01f9ef 532 for (int k = 0; k < ackLength; k++) debug->printf(" %x", ackBuffer[k]);
tobyspark 14:da403a01f9ef 533 debug->printf("\r\n");
tobyspark 14:da403a01f9ef 534 }
tobyspark 14:da403a01f9ef 535 break;
tobyspark 14:da403a01f9ef 536 }
tobyspark 14:da403a01f9ef 537 }
tobyspark 14:da403a01f9ef 538
tobyspark 18:d765b0d9271c 539 resetCommandPeriods();
tobyspark 18:d765b0d9271c 540
tobyspark 14:da403a01f9ef 541 return success;
tobyspark 14:da403a01f9ef 542 }