root / Ports / examples / isp_capture / isp_capture.pde
History | View | Annotate | Download (20 KB)
| 1 | //>>> The latest version of this code can be found at https://github.com/jcw/ !! |
|---|---|
| 2 | |
| 3 | // Variant of isp_flash which supports the Flash Board with on-board EEPROM |
| 4 | // Originally derived from http://arduino.cc/en/Tutorial/ArduinoISP |
| 5 | // see http://news.jeelabs.org/2010/04/24/isp-plug/ |
| 6 | // $Id: isp_capture.pde 7763 2011-12-11 01:28:16Z jcw $ |
| 7 | |
| 8 | // This is an STK500-/AVRISP-compatible programmer with a twist: when used with |
| 9 | // the Flash Board which has EEPROM memory on board, a copy of all programming |
| 10 | // commands is captured in EEPROM, as well as a copy of the replies sent back to |
| 11 | // the programmer. Everything is stored: data bytes, fuses, verifications, etc. |
| 12 | // Data capture ends when no further commands have been received for 3 seconds. |
| 13 | // |
| 14 | // There is a button on the Flash Board, which starts the programmer in a |
| 15 | // special "playback" mode, re-playing all the commands saved in EEPROM, and |
| 16 | // intercepting all replies to compare them with the data stored in EEPROM. |
| 17 | // In other words: pressing the button will repeat the last captured AVRISP |
| 18 | // programming cycle, without requiring a connected PC or any software. |
| 19 | // Successful programming is indicated with a brief but regularly flashing LED. |
| 20 | // |
| 21 | // This mechanism is transparent: the PC can treat this programmer like any |
| 22 | // other STK500-/AVRISP-compatible programmer, for ATmega / ATtiny / anything. |
| 23 | // The only difference is that this programmer can then be used stand-alone. |
| 24 | // |
| 25 | // jcw, 2010-04-26 |
| 26 | |
| 27 | // Two improvements, 2010-05-18: |
| 28 | // - Start with slow pulses until fuse settings have been performed, to avoid |
| 29 | // with factory-fresh chips preset to a 1 Mhz clock. |
| 30 | // - Ignore fuse reads in playback mode, because they may differ, depending on |
| 31 | // what they were previously set to (can use serial hookup to see details). |
| 32 | |
| 33 | #include <Ports.h> |
| 34 | #include <RF12.h> // needed to avoid a linker error :( |
| 35 | #include <avr/sleep.h> |
| 36 | |
| 37 | // set to 1 to get some more output on the serial port during playback |
| 38 | #define DEBUG 1 |
| 39 | |
| 40 | // set to 1 to save in MemoryStream right away (this may have timing issues) |
| 41 | #define IMMEDIATE_SAVE 0 |
| 42 | |
| 43 | // this optimization cuts the programming time by half, approximately |
| 44 | // the code needs to be adjusted if the pin assignments are changed |
| 45 | #define OPTIMIZED 1 // use fast pin I/O, avoid digitalRead / digitalWrite |
| 46 | |
| 47 | // pin assignments for the target ISP connnector |
| 48 | #define PIN_SCK 14 // AIO1 |
| 49 | #define PIN_MISO 4 // DIO1 |
| 50 | #define PIN_MOSI 17 // AIO4 |
| 51 | #define PIN_RESET 7 // DIO4 |
| 52 | |
| 53 | #define LED_PMODE 15 // on while programming (LED to VCC via resistor) |
| 54 | #define START_BTN 5 // DIO2 - active low - starts programming target |
| 55 | |
| 56 | // original comments: |
| 57 | // |
| 58 | // October 2009 by David A. Mellis |
| 59 | // - Added support for the read signature command |
| 60 | // |
| 61 | // February 2009 by Randall Bohn |
| 62 | // - Added support for writing to EEPROM (what took so long?) |
| 63 | // Windows users should consider WinAVR's avrdude instead of the |
| 64 | // avrdude included with Arduino software. |
| 65 | // |
| 66 | // January 2008 by Randall Bohn |
| 67 | // - Thanks to Amplificar for helping me with the STK500 protocol |
| 68 | // - The AVRISP/STK500 (mk I) protocol is used in the arduino bootloader |
| 69 | // - The SPI functions herein were developed for the AVR910_ARD programmer |
| 70 | // - More information at http://code.google.com/p/mega-isp |
| 71 | |
| 72 | #define HWVER 2 |
| 73 | #define SWMAJ 1 |
| 74 | #define SWMIN 18 |
| 75 | |
| 76 | // STK Definitions |
| 77 | #define STK_OK '\x10' |
| 78 | #define STK_FAILED '\x11' |
| 79 | #define STK_UNKNOWN '\x12' |
| 80 | #define STK_INSYNC '\x14' |
| 81 | #define STK_NOSYNC '\x15' |
| 82 | #define CRC_EOP '\x20' //ok it is a space... |
| 83 | |
| 84 | // access to the 128 kbyte on-board EEPROM memory |
| 85 | PortI2C i2cBus (3); |
| 86 | MemoryPlug mem (i2cBus); |
| 87 | MemoryStream stream (mem, 1); // starts on page 1, page 0 is for parameters |
| 88 | |
| 89 | #define DONE_TIMEOUT 3000 // recording is done when idle for 3 seconds |
| 90 | MilliTimer doneTimer; // will fire when recording has finished |
| 91 | |
| 92 | enum { RECORDING, PASS_THROUGH, PLAYBACK } mode; // programmer mode
|
| 93 | |
| 94 | int here; // address for reading and writing, set by 'U' command |
| 95 | byte data[256]; // global block storage for the avrisp() code |
| 96 | byte recbuf[127]; // used to capture data bytes during recording |
| 97 | byte recfill; // fill index into recbuf or remaining count during playback |
| 98 | byte recdir; // recording direction: INPUT = cmds, OUTPUT = replies |
| 99 | word start; // start time, used to track total programming time |
| 100 | byte speed; // used to slow down ISP pulses in case chip runs @ 1 Mhz |
| 101 | byte lastC, lastA; // need to avoid playback mismatch on fuse reads, and such |
| 102 | |
| 103 | #if !IMMEDIATE_SAVE |
| 104 | byte buffer [500]; |
| 105 | int bufpos; |
| 106 | #endif |
| 107 | |
| 108 | struct {
|
| 109 | byte devicecode; |
| 110 | byte revision; |
| 111 | byte progtype; |
| 112 | byte parmode; |
| 113 | byte polling; |
| 114 | byte selftimed; |
| 115 | byte lockbytes; |
| 116 | byte fusebytes; |
| 117 | byte sig0, sig1, sig2; // copy of the read sig request |
| 118 | int flashpoll; |
| 119 | int eeprompoll; |
| 120 | int pagesize; |
| 121 | int eepromsize; |
| 122 | long flashsize; |
| 123 | // programmer info |
| 124 | long programsize; |
| 125 | } param; |
| 126 | |
| 127 | // see http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235780325/10#10 |
| 128 | static void (*reset)() = 0; |
| 129 | |
| 130 | static void setLed (byte on) {
|
| 131 | digitalWrite(LED_PMODE, !on); |
| 132 | } |
| 133 | |
| 134 | static byte getButton () {
|
| 135 | return digitalRead(START_BTN) == 0; |
| 136 | } |
| 137 | |
| 138 | static void saveToStream () {
|
| 139 | #if !IMMEDIATE_SAVE |
| 140 | for (int i = 0; i < bufpos; ++i) |
| 141 | stream.put(buffer[i]); |
| 142 | bufpos = 0; |
| 143 | #endif |
| 144 | } |
| 145 | |
| 146 | // used to record both incoming commands and outgoing replies |
| 147 | // don't let the recording buffer fill up beyond 127 bytes |
| 148 | // flush the recording buffer when full or when the recording direction changes |
| 149 | // store buffer count in front of the data bytes (bits 0..6), bit 7 = direction |
| 150 | static void record (byte direction, byte value) {
|
| 151 | if ((recfill >= sizeof recbuf || direction != recdir) && recfill > 0) {
|
| 152 | byte marker = recdir == OUTPUT ? recfill | 0x80 : recfill; |
| 153 | #if IMMEDIATE_SAVE |
| 154 | stream.put(marker); |
| 155 | for (byte i = 0; i < recfill; ++i) |
| 156 | stream.put(recbuf[i]); |
| 157 | #else |
| 158 | // safety measure: if buffer doesn't have enough room, clear it first |
| 159 | // shouldn't happen due to recordFlush calls in program_page & read_page |
| 160 | if (bufpos + recfill + 1 >= sizeof buffer) |
| 161 | saveToStream(); |
| 162 | // save recbuf in an extra buffer to postpone the slow EEPROM writes |
| 163 | buffer[bufpos++] = marker; |
| 164 | for (byte i = 0; i < recfill; ++i) |
| 165 | buffer[bufpos++] = recbuf[i]; |
| 166 | #endif |
| 167 | recfill = 0; |
| 168 | } |
| 169 | recbuf[recfill++] = value; |
| 170 | recdir = direction; |
| 171 | // keep arming (i.e. extending) the done timer as long as there is data |
| 172 | doneTimer.set(DONE_TIMEOUT); |
| 173 | } |
| 174 | |
| 175 | static void recordFlush () {
|
| 176 | record(!recdir, 0); // force recbuf flushing by switching mode |
| 177 | recfill = 0; |
| 178 | saveToStream(); |
| 179 | stream.flush(); |
| 180 | } |
| 181 | |
| 182 | // Success is indicated with a brief LED flash every second for 4 minutes. |
| 183 | // This code should be changed to use the watchdog and run in low-power mode. |
| 184 | // TODO: wake up from button press after power down, using pin-change interrupt. |
| 185 | |
| 186 | static void successBlink () {
|
| 187 | MilliTimer t; |
| 188 | for (byte i = 0; i < 240; ++i) {
|
| 189 | setLed(1); |
| 190 | t.set(50); |
| 191 | while (!t.poll()) |
| 192 | if (getButton()) |
| 193 | return; |
| 194 | setLed(0); |
| 195 | t.set(950); |
| 196 | while (!t.poll()) |
| 197 | if (getButton()) |
| 198 | return; |
| 199 | } |
| 200 | // power down completely after 4 minutes |
| 201 | cli(); |
| 202 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
| 203 | sleep_mode(); |
| 204 | // never returns, needs hard reset to start up again |
| 205 | } |
| 206 | |
| 207 | // play back one byte from EEPROM, as captured in an earlier recording session |
| 208 | // arg specifies whether the data is an INPUT or an OUTPUT capture |
| 209 | // if it doesn't match, then something went wrong: report on serial and reset |
| 210 | // if EOF is reached, then report success and some stats, and also reset |
| 211 | static byte playback (byte direction) {
|
| 212 | if (recfill == 0) {
|
| 213 | // Serial.print('<');
|
| 214 | // Serial.print(stream.position(0)); |
| 215 | // Serial.print('>');
|
| 216 | |
| 217 | if (stream.position(0) >= param.programsize) {
|
| 218 | start = millis() / 100 - start; |
| 219 | Serial.print("Done in ");
|
| 220 | Serial.print(start / 10); |
| 221 | Serial.print('.');
|
| 222 | Serial.print(start % 10); |
| 223 | Serial.println(" seconds.");
|
| 224 | // wait for button release to avoid immediately restarting |
| 225 | while (getButton()) |
| 226 | ; |
| 227 | successBlink(); |
| 228 | reset(); |
| 229 | } |
| 230 | |
| 231 | byte b = stream.get(); |
| 232 | recdir = b & 0x80 ? OUTPUT : INPUT; |
| 233 | recfill = b & 0x7F; |
| 234 | } |
| 235 | if (direction != recdir) {
|
| 236 | Serial.print("Direction mismatch @ ");
|
| 237 | Serial.print(stream.position(0)); |
| 238 | Serial.print(" : ");
|
| 239 | Serial.println(direction, DEC); |
| 240 | delay(10); // let UART drain |
| 241 | reset(); |
| 242 | } |
| 243 | --recfill; |
| 244 | return stream.get(); |
| 245 | } |
| 246 | |
| 247 | static byte getch() {
|
| 248 | byte b; |
| 249 | if (mode == PLAYBACK) |
| 250 | b = playback(INPUT); |
| 251 | else {
|
| 252 | while (!Serial.available()) |
| 253 | ; |
| 254 | b = Serial.read(); |
| 255 | if (mode == RECORDING) |
| 256 | record(INPUT, b); |
| 257 | } |
| 258 | return b; |
| 259 | } |
| 260 | |
| 261 | static void putch(byte c) {
|
| 262 | if (mode == PLAYBACK) {
|
| 263 | byte e = playback(OUTPUT); |
| 264 | if (c != e) {
|
| 265 | Serial.print("Reply mismatch @ ");
|
| 266 | Serial.print(stream.position(0)); |
| 267 | Serial.print(" : ");
|
| 268 | Serial.print(c, HEX); |
| 269 | Serial.print('/');
|
| 270 | Serial.print(e, HEX); |
| 271 | // Special code to avoid aborting on a playback mismatch, when |
| 272 | // reading fuse bits or the RC calibration byte, because these |
| 273 | // will differ between brand-new and previously-used chips. |
| 274 | switch (lastC == 'V' ? lastA : 0) {
|
| 275 | default: |
| 276 | Serial.println(); |
| 277 | delay(100); // let UART drain |
| 278 | reset(); |
| 279 | // never returns |
| 280 | case 0x38: |
| 281 | case 0x50: |
| 282 | case 0x58: |
| 283 | Serial.println(" - ignored");
|
| 284 | } |
| 285 | } |
| 286 | } else {
|
| 287 | Serial.print(c); |
| 288 | if (mode == RECORDING) |
| 289 | record(OUTPUT, c); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | static void readbytes(int n) {
|
| 294 | for (byte x = 0; x < n; x++) |
| 295 | data[x] = getch(); |
| 296 | } |
| 297 | |
| 298 | static void spi_init() {
|
| 299 | digitalWrite(PIN_SCK, 1); |
| 300 | digitalWrite(PIN_MISO, 1); |
| 301 | digitalWrite(PIN_MOSI, 1); |
| 302 | digitalWrite(PIN_RESET, 1); |
| 303 | |
| 304 | pinMode(PIN_SCK, OUTPUT); |
| 305 | pinMode(PIN_MISO, INPUT); |
| 306 | pinMode(PIN_MOSI, OUTPUT); |
| 307 | pinMode(PIN_RESET, OUTPUT); |
| 308 | } |
| 309 | |
| 310 | static byte spi_send(byte b) {
|
| 311 | byte reply = 0; |
| 312 | for (byte i = 0; i < 8; ++i) {
|
| 313 | #if OPTIMIZED |
| 314 | // this is hardwired for pin 17, i.e. PC3 |
| 315 | bitWrite(PORTC, 3, b >> 7); |
| 316 | // this is hardwired for pin 14, i.e. PC0 |
| 317 | delayMicroseconds(speed); |
| 318 | bitClear(PORTC, 0); |
| 319 | delayMicroseconds(speed); |
| 320 | bitSet(PORTC, 0); |
| 321 | delayMicroseconds(speed); |
| 322 | b <<= 1; |
| 323 | // this is hardwired for pin 4, i.e. PD4 |
| 324 | reply = (reply << 1) | bitRead(PIND, 4); |
| 325 | #else |
| 326 | digitalWrite(PIN_MOSI, b & 0x80); |
| 327 | digitalWrite(PIN_SCK, 0); // slow pulse, max 60KHz |
| 328 | digitalWrite(PIN_SCK, 1); |
| 329 | b <<= 1; |
| 330 | reply = (reply << 1) | digitalRead(PIN_MISO); |
| 331 | #endif |
| 332 | } |
| 333 | return reply; |
| 334 | } |
| 335 | |
| 336 | static byte spi_transaction(byte a, byte b, byte c, byte d) {
|
| 337 | spi_send(a); |
| 338 | spi_send(b); |
| 339 | spi_send(c); |
| 340 | return spi_send(d); |
| 341 | } |
| 342 | |
| 343 | static byte spi_transaction_wait(byte a, byte b, byte c, byte d) {
|
| 344 | byte reply = spi_transaction(a, b, c, d); |
| 345 | while (spi_transaction(0xF0, 0, 0, 0) & 1) |
| 346 | ; |
| 347 | return reply; |
| 348 | } |
| 349 | |
| 350 | static void empty_reply() {
|
| 351 | if (getch() == CRC_EOP) {
|
| 352 | putch(STK_INSYNC); |
| 353 | putch(STK_OK); |
| 354 | } else |
| 355 | putch(STK_NOSYNC); |
| 356 | } |
| 357 | |
| 358 | static void breply(byte b) {
|
| 359 | if (getch() == CRC_EOP) {
|
| 360 | putch(STK_INSYNC); |
| 361 | putch(b); |
| 362 | putch(STK_OK); |
| 363 | } else |
| 364 | putch(STK_NOSYNC); |
| 365 | } |
| 366 | |
| 367 | static void get_version(byte c) {
|
| 368 | switch(c) {
|
| 369 | case 0x80: breply(HWVER); break; |
| 370 | case 0x81: breply(SWMAJ); break; |
| 371 | case 0x82: breply(SWMIN); break; |
| 372 | case 0x93: breply('S'); break;
|
| 373 | default: breply(0); |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | static void set_parameters() {
|
| 378 | // call this after reading parameter packet into data[] |
| 379 | if (DEBUG && mode == PLAYBACK) {
|
| 380 | for (byte i = 0; i < 20; ++i) {
|
| 381 | Serial.print(' ');
|
| 382 | Serial.print(data[i], HEX); |
| 383 | } |
| 384 | Serial.println(); |
| 385 | } |
| 386 | memcpy(¶m, data, 9); |
| 387 | // following fields are big endian |
| 388 | param.eeprompoll = data[10] * 0x0100 + data[11]; |
| 389 | param.pagesize = data[12] * 0x0100 + data[13]; |
| 390 | param.eepromsize = data[14] * 0x0100 + data[15]; |
| 391 | param.flashsize = (data[16] << 8) + data[17]; |
| 392 | param.flashsize <<= 16; |
| 393 | param.flashsize |= (word) ((data[18] << 8) + data[19]); |
| 394 | } |
| 395 | |
| 396 | static void start_pmode() {
|
| 397 | spi_init(); |
| 398 | // following delays may not work on all targets... |
| 399 | pinMode(PIN_RESET, OUTPUT); |
| 400 | digitalWrite(PIN_RESET, HIGH); |
| 401 | pinMode(PIN_SCK, OUTPUT); |
| 402 | digitalWrite(PIN_SCK, LOW); |
| 403 | delay(50); |
| 404 | digitalWrite(PIN_RESET, LOW); |
| 405 | delay(50); |
| 406 | pinMode(PIN_MISO, INPUT); |
| 407 | pinMode(PIN_MOSI, OUTPUT); |
| 408 | spi_transaction_wait(0xAC, 0x53, 0x00, 0x00); |
| 409 | setLed(1); |
| 410 | } |
| 411 | |
| 412 | static void end_pmode() {
|
| 413 | setLed(0); |
| 414 | pinMode(PIN_MISO, INPUT); |
| 415 | pinMode(PIN_MOSI, INPUT); |
| 416 | pinMode(PIN_SCK, INPUT); |
| 417 | pinMode(PIN_RESET, INPUT); |
| 418 | } |
| 419 | |
| 420 | static void universal() {
|
| 421 | readbytes(4); |
| 422 | lastA = data[0]; // remember last type of command, see putch() |
| 423 | if (DEBUG && mode == PLAYBACK) {
|
| 424 | Serial.print(lastA, HEX); |
| 425 | Serial.print(':');
|
| 426 | } |
| 427 | breply(spi_transaction_wait(data[0], data[1], data[2], data[3])); |
| 428 | } |
| 429 | |
| 430 | static void flash(byte hilo, int addr, byte value) {
|
| 431 | spi_transaction_wait(0x40+8*hilo, addr >> 8, addr, value); |
| 432 | } |
| 433 | |
| 434 | static void commit(int addr) {
|
| 435 | spi_transaction_wait(0x4C, addr >> 8, addr, 0); |
| 436 | } |
| 437 | |
| 438 | static int current_page(int addr) {
|
| 439 | return here & ~(param.pagesize/2-1); |
| 440 | } |
| 441 | |
| 442 | static byte write_flash(int length) {
|
| 443 | if (param.pagesize < 1) return STK_FAILED; |
| 444 | int page = current_page(here); |
| 445 | int x = 0; |
| 446 | while (x < length) {
|
| 447 | if (page != current_page(here)) {
|
| 448 | commit(page); |
| 449 | page = current_page(here); |
| 450 | } |
| 451 | flash(LOW, here, data[x++]); |
| 452 | flash(HIGH, here, data[x++]); |
| 453 | here++; |
| 454 | } |
| 455 | commit(page); |
| 456 | return STK_OK; |
| 457 | } |
| 458 | |
| 459 | static byte write_eeprom(int length) {
|
| 460 | // this writes byte-by-byte, page writing may be faster (4 bytes at a time) |
| 461 | for (int x = 0; x < length; x++) |
| 462 | spi_transaction_wait(0xC0, 0x00, here*2+x, data[x]); |
| 463 | return STK_OK; |
| 464 | } |
| 465 | |
| 466 | static void program_page() {
|
| 467 | char result = STK_FAILED; |
| 468 | int length = 256 * getch(); |
| 469 | length += getch(); |
| 470 | if (length > 256) {
|
| 471 | putch(STK_FAILED); |
| 472 | return; |
| 473 | } |
| 474 | char memtype = getch(); |
| 475 | readbytes(length); |
| 476 | if (getch() == CRC_EOP) {
|
| 477 | putch(STK_INSYNC); |
| 478 | if (memtype == 'F') result = write_flash(length); |
| 479 | if (memtype == 'E') result = write_eeprom(length); |
| 480 | // flush saved-up data here, while it's allowed to take some time |
| 481 | if (mode == RECORDING) |
| 482 | recordFlush(); |
| 483 | putch(result); |
| 484 | } else |
| 485 | putch(STK_NOSYNC); |
| 486 | } |
| 487 | |
| 488 | static byte flash_read(byte hilo, int addr) {
|
| 489 | return spi_transaction(0x20 + hilo * 8, addr >> 8, addr, 0); |
| 490 | } |
| 491 | |
| 492 | static char flash_read_page(int length) {
|
| 493 | for (int x = 0; x < length; x+=2) {
|
| 494 | putch(flash_read(LOW, here)); |
| 495 | putch(flash_read(HIGH, here)); |
| 496 | here++; |
| 497 | } |
| 498 | return STK_OK; |
| 499 | } |
| 500 | |
| 501 | static char eeprom_read_page(int length) {
|
| 502 | for (int x = 0; x < length; x++) |
| 503 | putch(spi_transaction(0xA0, 0x00, here*2+x, 0xFF)); |
| 504 | return STK_OK; |
| 505 | } |
| 506 | |
| 507 | static void read_page() {
|
| 508 | int length = 256 * getch(); |
| 509 | length += getch(); |
| 510 | char memtype = getch(); |
| 511 | if (getch() == CRC_EOP) {
|
| 512 | putch(STK_INSYNC); |
| 513 | char result = STK_FAILED; |
| 514 | if (memtype == 'F') result = flash_read_page(length); |
| 515 | if (memtype == 'E') result = eeprom_read_page(length); |
| 516 | // flush saved-up data here, while it's allowed to take some time |
| 517 | if (mode == RECORDING) |
| 518 | recordFlush(); |
| 519 | putch(result); |
| 520 | } else |
| 521 | putch(STK_NOSYNC); |
| 522 | } |
| 523 | |
| 524 | static void read_signature() {
|
| 525 | if (getch() == CRC_EOP) {
|
| 526 | param.sig0 = spi_transaction(0x30, 0, 0, 0); |
| 527 | param.sig1 = spi_transaction(0x30, 0, 1, 0); |
| 528 | param.sig2 = spi_transaction(0x30, 0, 2, 0); |
| 529 | putch(STK_INSYNC); |
| 530 | putch(param.sig0); |
| 531 | putch(param.sig1); |
| 532 | putch(param.sig2); |
| 533 | putch(STK_OK); |
| 534 | } else |
| 535 | putch(STK_NOSYNC); |
| 536 | } |
| 537 | |
| 538 | static int avrisp() {
|
| 539 | char c = lastC = getch(); |
| 540 | if (DEBUG && mode == PLAYBACK && ' ' <= c && c <= '~') |
| 541 | Serial.print(c); |
| 542 | switch (c) {
|
| 543 | case '0': // signon |
| 544 | empty_reply(); |
| 545 | break; |
| 546 | case '1': |
| 547 | if (getch() == CRC_EOP) {
|
| 548 | putch(STK_INSYNC); |
| 549 | for (const char* p = "AVR ISP"; *p != 0; ++p) |
| 550 | putch(*p); |
| 551 | putch(STK_OK); |
| 552 | } |
| 553 | break; |
| 554 | case 'A': |
| 555 | get_version(getch()); |
| 556 | break; |
| 557 | case 'B': |
| 558 | readbytes(20); |
| 559 | set_parameters(); |
| 560 | empty_reply(); |
| 561 | break; |
| 562 | case 'E': // extended parameters - ignore for now |
| 563 | readbytes(5); |
| 564 | empty_reply(); |
| 565 | break; |
| 566 | case 'P': |
| 567 | start_pmode(); |
| 568 | empty_reply(); |
| 569 | break; |
| 570 | case 'U': |
| 571 | here = getch(); |
| 572 | here += 256 * getch(); |
| 573 | speed = 1; // switch to fast programming once fuses have been set |
| 574 | empty_reply(); |
| 575 | break; |
| 576 | case '`': //STK_PROG_FLASH |
| 577 | getch(); |
| 578 | getch(); |
| 579 | empty_reply(); |
| 580 | break; |
| 581 | case 'a': //STK_PROG_DATA |
| 582 | getch(); |
| 583 | empty_reply(); |
| 584 | break; |
| 585 | case 'd': //STK_PROG_PAGE |
| 586 | program_page(); |
| 587 | break; |
| 588 | case 't': //STK_READ_PAGE |
| 589 | read_page(); |
| 590 | break; |
| 591 | case 'V': |
| 592 | universal(); |
| 593 | break; |
| 594 | case 'Q': |
| 595 | end_pmode(); |
| 596 | empty_reply(); |
| 597 | break; |
| 598 | case 'u': //STK_READ_SIGN |
| 599 | read_signature(); |
| 600 | break; |
| 601 | // expecting a command, not CRC_EOP |
| 602 | // this is how we can get back in sync |
| 603 | case CRC_EOP: |
| 604 | putch(STK_NOSYNC); |
| 605 | break; |
| 606 | // anything else we will return STK_UNKNOWN |
| 607 | default: |
| 608 | putch(getch() == CRC_EOP ? STK_UNKNOWN : STK_NOSYNC); |
| 609 | } |
| 610 | } |
| 611 | |
| 612 | static void showInfo () {
|
| 613 | // restore parameters from page 0 |
| 614 | mem.load(0, ¶m, 0, sizeof param); |
| 615 | // report saved info for debugging |
| 616 | Serial.print("ISP bytes: ");
|
| 617 | Serial.println(param.programsize); |
| 618 | Serial.print("Code size: ");
|
| 619 | //FIXME: Serial.println(param.flashsize); |
| 620 | Serial.println((word) param.flashsize); |
| 621 | Serial.print("Page size: ");
|
| 622 | Serial.println(param.pagesize); |
| 623 | Serial.print("Data size: ");
|
| 624 | Serial.println(param.eepromsize); |
| 625 | Serial.print("Signature:");
|
| 626 | for (byte i = 0; i < 11; ++i) {
|
| 627 | Serial.print(' ');
|
| 628 | byte b = ((const byte*) ¶m)[i]; |
| 629 | Serial.print(b >> 4, HEX); |
| 630 | Serial.print(b & 0x0F, HEX); |
| 631 | } |
| 632 | Serial.println(); |
| 633 | } |
| 634 | |
| 635 | void setup() {
|
| 636 | Serial.begin(19200); |
| 637 | |
| 638 | pinMode(START_BTN, INPUT); |
| 639 | digitalWrite(START_BTN, 1); // pull-up |
| 640 | pinMode(LED_PMODE, OUTPUT); |
| 641 | setLed(0); |
| 642 | |
| 643 | #if DEBUG |
| 644 | // three quick blinks after reset |
| 645 | for (byte i = 1; i <= 6; ++i) {
|
| 646 | setLed(i & 1); |
| 647 | delay(100); |
| 648 | } |
| 649 | #endif |
| 650 | |
| 651 | mode = mem.isPresent() ? RECORDING : PASS_THROUGH; |
| 652 | speed = 10; // start of programming slowly, speed up once data gets sent |
| 653 | } |
| 654 | |
| 655 | void loop(void) {
|
| 656 | if (doneTimer.poll()) {
|
| 657 | recordFlush(); |
| 658 | param.programsize = stream.position(1); // save parameter info in page 0 |
| 659 | mem.save(0, ¶m, 0, sizeof param); |
| 660 | |
| 661 | setLed(1); |
| 662 | delay(100); |
| 663 | setLed(0); |
| 664 | delay(100); |
| 665 | setLed(1); |
| 666 | delay(100); |
| 667 | setLed(0); |
| 668 | delay(500); |
| 669 | |
| 670 | reset(); |
| 671 | } |
| 672 | |
| 673 | if (mode == RECORDING && getButton()) {
|
| 674 | Serial.begin(57600); // for debugging |
| 675 | Serial.println("\n[isp_capture]");
|
| 676 | showInfo(); |
| 677 | stream.reset(); // seek to start again |
| 678 | mode = PLAYBACK; |
| 679 | recfill = 0; |
| 680 | Serial.println("Programming...");
|
| 681 | start = millis() / 100; |
| 682 | } |
| 683 | |
| 684 | if (mode == PLAYBACK || Serial.available()) |
| 685 | avrisp(); |
| 686 | } |