Statistics
| Revision:

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(&param, 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, &param, 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*) &param)[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, &param, 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
}