// ArduinoISP version 04m3 // Copyright (c) 2008-2011 Randall Bohn // If you require a license, see // http://www.opensource.org/licenses/bsd-license.php // // This sketch turns the Arduino into a AVRISP // using the following arduino pins: // // pin name: not-mega: mega(1280 and 2560) // slave reset: 10: 53 // MOSI: 11: 51 // MISO: 12: 50 // SCK: 13: (std LED) 52 // // Put an LED (with resistor) on the following pins: // 9: Heartbeat - shows the programmer is running // 8: Error - Lights up if something goes wrong (use red if that makes sense) // 7: Programming - In communication with the slave // // 23 July 2011 Randall Bohn // -Address Arduino issue 509 :: Portability of ArduinoISP // http://code.google.com/p/arduino/issues/detail?id=509 // // October 2010 by Randall Bohn // - Write to EEPROM > 256 bytes // - Better use of LEDs: // -- Flash LED_PMODE on each flash commit // -- Flash LED_PMODE while writing EEPROM (both give visual feedback of writing progress) // -- Light LED_ERR whenever we hit a STK_NOSYNC. Turn it off when back in sync. // - Use pins_arduino.h (should also work on Arduino Mega) // // October 2009 by David A. Mellis // - Added support for the read signature command // // February 2009 by Randall Bohn // - Added support for writing to EEPROM (what took so long?) // Windows users should consider WinAVR's avrdude instead of the // avrdude included with Arduino software. // // January 2008 by Randall Bohn // - Thanks to Amplificar for helping me with the STK500 protocol // - The AVRISP/STK500 (mk I) protocol is used in the arduino bootloader // - The SPI functions herein were developed for the AVR910_ARD programmer // - More information at http://code.google.com/p/mega-isp // versions need to be above Atmel programmer to avoid fw update attempts #define HWVER 2 #define SWMAJ 1 #define SWMIN 18 #define BAUDRATE 19200 //#define BAUDRATE 38400 //#define BAUDRATE 115200 // create clock on digital 9 using pwm (timer1), LED_HB must move //#define LADYADA_CLOCK #define RESETDELAY 0 // uncomment if you want to have debug traces // (needs a separate uart so works only on Sanguino, Leonardo, Due...) //#define TRACES // following settings have different defaults on SAM vs. AVR #ifdef __SAM3X8E__ // Select uart to use for programming and debugging: #define SERIAL_PRG SerialUSB #define SERIAL_DBG Serial // comment USE_HARDWARE_SPI to use bitbang spi // use bitbang to make it work with very slow attiny2313 // #define USE_HARDWARE_SPI #else // Select uart to use for programming and debugging: #define SERIAL_PRG Serial #define SERIAL_DBG Serial1 // comment USE_HARDWARE_SPI to use bitbang spi // use bitbang to make it work with very slow attiny2313 #define USE_HARDWARE_SPI #endif /////////////////////////////////////////////// // ideally won't need to edit below here // /////////////////////////////////////////////// #ifdef USE_HARDWARE_SPI #include "SPI.h" #ifdef __AVR__ // this would better go into SPI lib #define SPI_CLOCK_DIV_MAX SPI_CLOCK_DIV128 #else #define SPI_CLOCK_DIV_MAX 255 #endif #endif #include "pins_arduino.h" #define PIN_RESET SS //#define PIN_RESET A0 #define PIN_SCK SCK #define PIN_MOSI MOSI #define PIN_MISO MISO #define LED_HB 9 #define LED_ERR 8 #define LED_PMODE 7 #define PROG_FLICKER true #ifdef LADYADA_CLOCK #ifndef __AVR__ #error "Not yet implemented for non AVR's." #endif // needs timer1 PWM #define CLOCK_PIN 9 #undef LED_HB #define LED_HB 6 #endif #ifdef TRACES #define TRACE_BEGIN(baud) SERIAL_DBG.begin(baud) #define TRACE(x) SERIAL_DBG.print(x) #define TRACELN(x) SERIAL_DBG.println(x) #define TRACE2(x, format) SERIAL_DBG.print(x, format) #define TRACE2LN(x, format) SERIAL_DBG.println(x, format) #else #define TRACE_BEGIN(baud) #define TRACE(x) #define TRACELN(x) #define TRACE2(x, format) #define TRACE2LN(x, format) #endif // STK Definitions #define STK_OK 0x10 #define STK_FAILED 0x11 #define STK_UNKNOWN 0x12 #define STK_INSYNC 0x14 #define STK_NOSYNC 0x15 #define CRC_EOP 0x20 //ok it is a space... void pulse(uint8_t pin, uint8_t times); #ifndef USE_HARDWARE_SPI class BitBangedSPI { public: void begin() { pinMode(PIN_MISO, INPUT); pinMode(PIN_RESET, OUTPUT); pinMode(PIN_SCK, OUTPUT); pinMode(PIN_MOSI, OUTPUT); } void end() {} uint8_t transfer (uint8_t b) { for (unsigned int i = 0; i < 8; ++i) { digitalWrite(PIN_MOSI, b & 0x80); digitalWrite(PIN_SCK, HIGH); b = (b << 1) | digitalRead(PIN_MISO); digitalWrite(PIN_SCK, LOW); // slow pulse } return b; } }; static BitBangedSPI SPI; #endif void setup(void) { SERIAL_PRG.begin(BAUDRATE); #ifdef USE_HARDWARE_SPI SPI.setDataMode(0); SPI.setBitOrder(MSBFIRST); // Clock Div can be 2,4,8,16,32,64, or 128 SPI.setClockDivider(SPI_CLOCK_DIV_MAX); #endif pinMode(LED_PMODE, OUTPUT); pulse(LED_PMODE, 2); pinMode(LED_ERR, OUTPUT); pulse(LED_ERR, 2); pinMode(LED_HB, OUTPUT); pulse(LED_HB, 2); #ifdef LADYADA_CLOCK // setup high freq PWM (timer 1) pinMode(CLOCK_PIN, OUTPUT); uint8_t sreg = SREG; cli(); // disable interrupts to access TCNT1, OCR1A,B // 50% duty cycle -> 8 MHz OCR1A = 0; ICR1 = 1; // OC1A output, fast PWM TCCR1A = _BV(WGM11) | _BV(COM1A1); TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // no clock prescale SREG = sreg; // restore interrupts #endif TRACE_BEGIN(115200); TRACELN("*** setup ***"); } uint8_t error=0; uint8_t pmode=0; uint8_t buff[256]; // global block storage // address for reading and writing, set by 'U' command uint16_t here; // get multi-byte Big Endian values #define beget16(addr) ((uint16_t)*(addr) << 8 | (uint16_t)*((addr)+1) ) #define beget32(a) ((uint32_t)beget16(a) << 16 | (uint32_t)beget16((a)+2) ) struct param { uint8_t devicecode; uint8_t revision; uint8_t progtype; uint8_t parmode; uint8_t polling; uint8_t selftimed; uint8_t lockbytes; uint8_t fusebytes; uint8_t flashpoll; //uint8_t ignored; uint16_t eeprompoll; uint16_t pagesize; uint16_t eepromsize; uint32_t flashsize; } param; // this provides a heartbeat, so you can tell the software is running. uint8_t hbval=128; int8_t hbdelta=8; unsigned long hbprev=0; void heartbeat(void) { if (hbval > 192 || hbval < 32) hbdelta = -hbdelta; hbval += hbdelta; while (millis()-hbprev < 40); // wait a bit if came back too soon analogWrite(LED_HB, hbval); hbprev=millis(); } void loop(void) { // is pmode active? if (pmode) digitalWrite(LED_PMODE, HIGH); else digitalWrite(LED_PMODE, LOW); // is there an error? if (error) digitalWrite(LED_ERR, HIGH); else digitalWrite(LED_ERR, LOW); // light the heartbeat LED heartbeat(); if (SERIAL_PRG.available()) { avrisp(); } } uint8_t getch(void) { while(!SERIAL_PRG.available()); return SERIAL_PRG.read(); } void fill(unsigned n) { for (unsigned x = 0; x < n; x++) { buff[x] = getch(); } } #define PTIME 30 void pulse(uint8_t pin, uint8_t times) { do { digitalWrite(pin, HIGH); delay(PTIME); digitalWrite(pin, LOW); delay(PTIME); } while (times--); } void prog_lamp(uint8_t state) { if (PROG_FLICKER) digitalWrite(LED_PMODE, state); } uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { SPI.transfer(a); SPI.transfer(b); SPI.transfer(c); return SPI.transfer(d); } void empty_reply(void) { if (CRC_EOP == getch()) { SERIAL_PRG.print((char)STK_INSYNC); SERIAL_PRG.print((char)STK_OK); } else { error++; SERIAL_PRG.print((char)STK_NOSYNC); } } void breply(uint8_t b) { if (CRC_EOP == getch()) { SERIAL_PRG.print((char)STK_INSYNC); SERIAL_PRG.print((char)b); SERIAL_PRG.print((char)STK_OK); } else { error++; SERIAL_PRG.print((char)STK_NOSYNC); } } void get_version(uint8_t c) { switch(c) { case 0x80: breply(HWVER); break; case 0x81: breply(SWMAJ); break; case 0x82: breply(SWMIN); break; case 0x93: breply('S'); // serial programmer break; default: breply(0); } } void set_parameters(void) { // call this after reading paramter packet into buff[] param.devicecode = buff[0]; param.revision = buff[1]; param.progtype = buff[2]; param.parmode = buff[3]; param.polling = buff[4]; param.selftimed = buff[5]; param.lockbytes = buff[6]; param.fusebytes = buff[7]; param.flashpoll = buff[8]; // ignore buff[9] (= buff[8]) // following are 16 bits (big endian) param.eeprompoll = beget16(&buff[10]); param.pagesize = beget16(&buff[12]); param.eepromsize = beget16(&buff[14]); // 32 bits flashsize (big endian) param.flashsize = beget32(&buff[16]); } void start_pmode(void) { pmode = 1; // reset target before driving SCK or MOSI digitalWrite(PIN_RESET, LOW); digitalWrite(PIN_SCK, LOW); digitalWrite(PIN_MOSI, HIGH); pinMode(PIN_MISO, INPUT); pinMode(PIN_RESET, OUTPUT); // PIN_RESET not always SS: Leonardo, Due... SPI.begin(); // now SS, MOSI and SCK are output // See datasheets: "SERIAL_PRG Programming Algorithm": delay(5); // choosen arbitrarilly // pulse RESET high after SCK is low digitalWrite(PIN_RESET, HIGH); delay(1); // must be minimum 2 CPU clock cycles digitalWrite(PIN_RESET, LOW); delay(50); // minimum 20 ms if (RESETDELAY) delay(RESETDELAY); spi_transaction(0xAC, 0x53, 0x00, 0x00); } void end_pmode(void) { SPI.end(); pinMode(PIN_MOSI, INPUT); pinMode(PIN_SCK, INPUT); pinMode(PIN_RESET, INPUT); pmode = 0; } void universal(void) { uint8_t ch; fill(4); ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]); breply(ch); } #define flash_write_cmd(hilo, addr, data) \ spi_transaction(0x40|((hilo)<<3), (addr)>>8 & 0xFF, (addr) & 0xFF, (data)) #define flash_read_cmd(hilo, addr) \ spi_transaction(0x20|((hilo)<<3), (addr)>>8 & 0xFF, (addr) & 0xFF, 0) void commit(uint16_t addr) { if (PROG_FLICKER) prog_lamp(LOW); spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0); if (PROG_FLICKER) { delay(PTIME); prog_lamp(HIGH); } } uint16_t current_page(uint16_t addr) { if (param.pagesize == 32) return here & 0xFFF0; if (param.pagesize == 64) return here & 0xFFE0; if (param.pagesize == 128) return here & 0xFFC0; if (param.pagesize == 256) return here & 0xFF80; return here; } void write_flash(unsigned length) { fill(length); if (CRC_EOP == getch()) { SERIAL_PRG.print((char) STK_INSYNC); SERIAL_PRG.print((char) write_flash_pages(length)); } else { error++; SERIAL_PRG.print((char) STK_NOSYNC); } } uint8_t write_flash_pages(unsigned length) { unsigned x = 0; uint16_t page = current_page(here); while (x < length) { if (page != current_page(here)) { commit(page); page = current_page(here); } flash_write_cmd(LOW, here, buff[x++]); flash_write_cmd(HIGH, here, buff[x++]); here++; } commit(page); return STK_OK; } #define EECHUNK (32) uint8_t write_eeprom(unsigned length) { // here is a word address, get the byte address uint16_t start = here << 1; if (length > param.eepromsize) { error++; return STK_FAILED; } while (length > EECHUNK) { write_eeprom_chunk(start, EECHUNK); start += EECHUNK; length -= EECHUNK; } write_eeprom_chunk(start, length); return STK_OK; } // write (length) bytes, (start) is a byte address uint8_t write_eeprom_chunk(uint16_t addr, unsigned length) { // this writes byte-by-byte, // page writing may be faster (4 bytes at a time) fill(length); prog_lamp(LOW); for (unsigned x = 0; x < length; x++, addr++) { spi_transaction(0xC0, (addr>>8) & 0xFF, addr & 0xFF, buff[x]); delay(45); } prog_lamp(HIGH); return STK_OK; } void program_page(void) { char result = (char) STK_FAILED; unsigned length = getch()<<8; length |= getch(); char memtype = getch(); // flash memory @here, (length) bytes if (memtype == 'F') { write_flash(length); return; } if (memtype == 'E') { result = (char)write_eeprom(length); if (CRC_EOP == getch()) { SERIAL_PRG.print((char) STK_INSYNC); SERIAL_PRG.print(result); } else { error++; SERIAL_PRG.print((char) STK_NOSYNC); } return; } SERIAL_PRG.print((char)STK_FAILED); } char flash_read_page(unsigned length) { for (unsigned x = 0; x < length; x+=2) { char ch; ch = flash_read_cmd(LOW, here); SERIAL_PRG.print(ch); ch = flash_read_cmd(HIGH, here); SERIAL_PRG.print(ch); here++; } return STK_OK; } char eeprom_read_page(unsigned length) { // here again we have a word address uint16_t addr = here << 1; for (unsigned x = 0; x < length; x++, addr++) { uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF); SERIAL_PRG.print((char) ee); } return STK_OK; } void read_page(void) { char result = (char)STK_FAILED; unsigned length = getch() << 8; length |= getch(); char memtype = getch(); if (CRC_EOP != getch()) { error++; SERIAL_PRG.print((char) STK_NOSYNC); return; } SERIAL_PRG.print((char) STK_INSYNC); if (memtype == 'F') result = flash_read_page(length); if (memtype == 'E') result = eeprom_read_page(length); SERIAL_PRG.print(result); } void read_signature(void) { if (CRC_EOP != getch()) { error++; SERIAL_PRG.print((char) STK_NOSYNC); return; } SERIAL_PRG.print((char) STK_INSYNC); char ch; ch = spi_transaction(0x30, 0x00, 0x00, 0x00); SERIAL_PRG.print(ch); ch = spi_transaction(0x30, 0x00, 0x01, 0x00); SERIAL_PRG.print(ch); ch = spi_transaction(0x30, 0x00, 0x02, 0x00); SERIAL_PRG.print(ch); SERIAL_PRG.print((char) STK_OK); } ////////////////////////////////////////// ////////////////////////////////////////// //////////////////////////////////// //////////////////////////////////// void avrisp(void) { uint8_t data, low, high; uint8_t ch = getch(); TRACE("> "); TRACELN((char) ch); switch (ch) { case '0': // signon error = 0; empty_reply(); break; case '1': if (getch() == CRC_EOP) { SERIAL_PRG.print((char) STK_INSYNC); SERIAL_PRG.print("AVR ISP"); SERIAL_PRG.print((char) STK_OK); } else { error++; SERIAL_PRG.print((char) STK_NOSYNC); } break; case 'A': get_version(getch()); break; case 'B': fill(20); set_parameters(); empty_reply(); break; case 'E': // extended parameters - ignore for now fill(5); empty_reply(); break; case 'P': if (pmode) { pulse(LED_ERR, 3); } else { start_pmode(); } empty_reply(); break; case 'U': // set address (word) here = getch(); here |= getch()<<8; empty_reply(); break; case 0x60: //STK_PROG_FLASH low = getch(); high = getch(); empty_reply(); break; case 0x61: //STK_PROG_DATA data = getch(); empty_reply(); break; case 0x64: //STK_PROG_PAGE program_page(); break; case 0x74: //STK_READ_PAGE 't' read_page(); break; case 'V': //0x56 universal(); break; case 'Q': //0x51 error=0; end_pmode(); empty_reply(); break; case 0x75: //STK_READ_SIGN 'u' read_signature(); break; // expecting a command, not CRC_EOP // this is how we can get back in sync case CRC_EOP: error++; SERIAL_PRG.print((char) STK_NOSYNC); break; // anything else we will return STK_UNKNOWN default: error++; if (CRC_EOP == getch()) SERIAL_PRG.print((char)STK_UNKNOWN); else SERIAL_PRG.print((char)STK_NOSYNC); } }