// this sketch turns the Arduino into a AVRISP // using the following pins: // 10: slave reset // 11: MOSI // 12: MISO // 13: SCK // Put an LED (with resistor) on the following pins: // 8: Error - Lights up if something goes wrong (use red if that makes sense) // A0: Programming - In communication with the slave // 6: Heartbeat - shows the programmer is running (removed, see notes below) // Optional - Piezo speaker on pin A3 // // 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 // // March 2012 - William Phelps // modify to work with Arduino IDE 1.0 which has a shorter serial port receive buffer // getEOP() now gets entire request before avrisp() is called to process it // Serial.print((char) xxx) changed to Serial.write(xxx) // uint8_t changed to byte // added support for Piezo speaker // moved Pmode LED to A0 // removed "heartbeat" on pin 6, added short blip of ERROR LED instead // Why is it that PROG_FLASH and PROG_DATA don't actually do anything??? // Tested with Arduino IDE 22 and 1.0 // IDE 22 - 5148 bytes // IDE 1.0 - 5524 bytes! // SLOW SPEED CHIP ERASE AND FUSE BURNING // // Enable LOW_SPEED to allow you to erase chips that would fail otherwise, // for being running with a clock too slow for the programmer. // // This allowed me to recover several ATMega328 that had no boot loader and the // first instruction was to set the clock to the slowest speed. Usually this // kind of recovery requires high voltage programming, but this trick will do // just fine. // // How to proceed: // 1. Enable LOW_SPEED, and load it to the programmer. // 2. Erase and burn the fuses on the target uC. Example for ATMega328: // arduino-1.0.1/hardware/tools/avrdude -Carduino-1.0.1/hardware/tools/avrdude.conf -patmega328p -cstk500v1 -P /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A900cf1Q-if00-port0 -b19200 -e -Ulock:w:0x3F:m -Uefuse:w:0x05:m -Uhfuse:w:0xDA:m -Ulfuse:w:0xF7:m // 3. Comment LOW_SPEED and load it back to the programmer. // 4. Program the target uC as usual. Example: // arduino-1.0.1/hardware/tools/avrdude -Carduino-1.0.1/hardware/tools/avrdude.conf -patmega328p -cstk500v1 -P /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A900cf1Q-if00-port0 -b19200 -Uflash:w:firmware.hex:i // // Note 1: EXTRA_SPI_DELAY was added to let you slow down SPI even more. You can // play with the value if it does not work with the default. // Note 2: LOW_SPEED will alow you only to erase the chip and burn the fuses! It // will fail if you try to program the target uC this way! //#define LOW_SPEED #ifdef LOW_SPEED #define EXTRA_SPI_DELAY 125 #else #define EXTRA_SPI_DELAY 0 #endif #include "pins_arduino.h" // defines SS,MOSI,MISO,SCK #define RESET SS #define LED_ERR 8 #define LED_PMODE A0 //#define LED_HB 6 #define PIEZO A3 #define HWVER 2 #define SWMAJ 1 #define SWMIN 18 // STK Definitions const byte STK_OK = 0x10; const byte STK_FAILED = 0x11; const byte STK_UNKNOWN = 0x12; const byte STK_INSYNC = 0x14; const byte STK_NOSYNC = 0x15; const byte CRC_EOP = 0x20; //ok it is a space... const byte STK_GET_SYNC = 0x30; const byte STK_GET_SIGNON = 0x31; const byte STK_GET_PARM = 0x41; const byte STK_SET_PARM = 0x42; const byte STK_SET_PARM_EXT = 0x45; const byte STK_PMODE_START = 0x50; const byte STK_PMODE_END = 0x51; const byte STK_SET_ADDR = 0x55; const byte STK_UNIVERSAL = 0x56; const byte STK_PROG_FLASH = 0x60; const byte STK_PROG_DATA = 0x61; const byte STK_PROG_PAGE = 0x64; const byte STK_READ_PAGE = 0x74; const byte STK_READ_SIGN = 0x75; //// TONES ========================================== //// Start by defining the relationship between //// note, period, & frequency. //#define c 3830 // 261 Hz //#define d 3400 // 294 Hz //#define e 3038 // 329 Hz //#define f 2864 // 349 Hz //#define g 2550 // 392 Hz //#define a 2272 // 440 Hz //#define b 2028 // 493 Hz //#define C 1912 // 523 Hz //void pulse(int pin, int times); int error=0; int pmode=0; // address for reading and writing, set by STK_SET_ADDR command int _addr; byte _buffer[256]; // serial port buffer int pBuffer = 0; // buffer pointer int iBuffer = 0; // buffer index byte buff[256]; // temporary buffer boolean EOP_SEEN = false; void setup() { Serial.begin(19200); pinMode(PIEZO, OUTPUT); beep(1700, 40); EOP_SEEN = false; iBuffer = pBuffer = 0; pinMode(LED_PMODE, OUTPUT); pulse(LED_PMODE, 2); pinMode(LED_ERR, OUTPUT); pulse(LED_ERR, 2); // pinMode(LED_HB, OUTPUT); // pulse(LED_HB, 2); pinMode(9, OUTPUT); // setup high freq PWM on pin 9 (timer 1) // 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 } #define beget16(addr) (*addr * 256 + *(addr+1) ) typedef struct param { byte devicecode; byte revision; byte progtype; byte parmode; byte polling; byte selftimed; byte lockbytes; byte fusebytes; int flashpoll; int eeprompoll; int pagesize; int eepromsize; int flashsize; } parameter; parameter param; // this provides a heartbeat on pin 6, so you can tell the software is running. //byte hbval=128; //int8_t hbdelta=4; //void heartbeat() { //// if (hbval > 192) hbdelta = -hbdelta; //// if (hbval < 32) hbdelta = -hbdelta; // if (hbval > 250) hbdelta = -hbdelta; // if (hbval < 10) hbdelta = -hbdelta; // hbval += hbdelta; // analogWrite(LED_HB, hbval); // delay(20); //} void getEOP() { int minL = 0; byte avrch = 0; byte bl = 0; while (!EOP_SEEN) { while (Serial.available()>0) { byte ch = Serial.read(); _buffer[iBuffer] = ch; iBuffer = (++iBuffer)%256; // increment and wrap if (iBuffer == 1) avrch = ch; // save command if ((avrch == STK_PROG_PAGE) && (iBuffer==3)) { minL = 256*_buffer[1] + _buffer[2] + 4; } if ((iBuffer>minL) && (ch == CRC_EOP)) { EOP_SEEN = true; } } if (!EOP_SEEN) { // heartbeat(); // light the heartbeat LED if (bl == 100) { pulse(LED_ERR,1,10); // blink the red LED bl = 0; } bl++; delay(10); } } } // serialEvent not used so sketch would be compatible with older IDE versions //void serialEvent() { // int minL = 0; // byte avrch = 0; // while (Serial.available()>0) // { // byte ch = Serial.read(); // _buffer[iBuffer] = ch; // iBuffer = (++iBuffer)%256; // increment and wrap // if (iBuffer == 1) avrch = ch; // save command // if ((avrch == STK_PROG_PAGE) && (iBuffer==3)) { // minL = 256*_buffer[1] + _buffer[2] + 4; // } // if ((iBuffer>minL) && (ch == CRC_EOP)) { // EOP_SEEN = true; // } // } //} void loop(void) { // is pmode active? // if (pmode) digitalWrite(LED_PMODE, HIGH); // else digitalWrite(LED_PMODE, LOW); digitalWrite(LED_PMODE, LOW); // is there an error? if (error) digitalWrite(LED_ERR, HIGH); else digitalWrite(LED_ERR, LOW); getEOP(); // have we received a complete request? (ends with CRC_EOP) if (EOP_SEEN) { digitalWrite(LED_PMODE, HIGH); EOP_SEEN = false; avrisp(); iBuffer = pBuffer = 0; // restart buffer } } byte getch() { if (pBuffer == iBuffer) { // spin until data available ??? pulse(LED_ERR, 1); beep(1700, 20); error++; return -1; } byte ch = _buffer[pBuffer]; // get next char pBuffer = (++pBuffer)%256; // increment and wrap return ch; } void readbytes(int n) { for (int x = 0; x < n; x++) { buff[x] = getch(); } } //#define PTIME 20 void pulse(int pin, int times, int ptime) { do { digitalWrite(pin, HIGH); delay(ptime); digitalWrite(pin, LOW); delay(ptime); times--; } while (times > 0); } void pulse(int pin, int times) { pulse(pin, times, 50); } void spi_init() { byte x; SPCR = 0x53; #ifdef LOW_SPEED SPCR=SPCR|B00000011; #endif x=SPSR; x=SPDR; } void spi_wait() { do { } while (!(SPSR & (1 << SPIF))); } byte spi_send(byte b) { byte reply; #ifdef LOW_SPEED cli(); CLKPR=B10000000; CLKPR=B00000011; sei(); #endif SPDR=b; spi_wait(); reply = SPDR; #ifdef LOW_SPEED cli(); CLKPR=B10000000; CLKPR=B00000000; sei(); #endif return reply; } byte spi_transaction(byte a, byte b, byte c, byte d) { byte n; spi_send(a); n=spi_send(b); //if (n != a) error = -1; n=spi_send(c); return spi_send(d); } void replyOK() { // if (EOP_SEEN == true) { if (CRC_EOP == getch()) { // EOP should be next char Serial.write(STK_INSYNC); Serial.write(STK_OK); } else { pulse(LED_ERR, 2); Serial.write(STK_NOSYNC); error++; } } void breply(byte b) { if (CRC_EOP == getch()) { // EOP should be next char Serial.write(STK_INSYNC); Serial.write(b); Serial.write(STK_OK); } else { Serial.write(STK_NOSYNC); error++; } } void get_parameter(byte 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() { // 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]) //getch(); // discard second value // WARNING: not sure about the byte order of the following // 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 = buff[16] * 0x01000000 + buff[17] * 0x00010000 + buff[18] * 0x00000100 + buff[19]; } void start_pmode() { spi_init(); // following delays may not work on all targets... pinMode(RESET, OUTPUT); digitalWrite(RESET, HIGH); pinMode(SCK, OUTPUT); digitalWrite(SCK, LOW); delay(50+EXTRA_SPI_DELAY); digitalWrite(RESET, LOW); delay(50+EXTRA_SPI_DELAY); pinMode(MISO, INPUT); pinMode(MOSI, OUTPUT); spi_transaction(0xAC, 0x53, 0x00, 0x00); pmode = 1; } void end_pmode() { pinMode(MISO, INPUT); pinMode(MOSI, INPUT); pinMode(SCK, INPUT); pinMode(RESET, INPUT); pmode = 0; } void universal() { // int w; byte ch; // for (w = 0; w < 4; w++) { // buff[w] = getch(); // } readbytes(4); ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]); breply(ch); } void flash(byte hilo, int addr, byte data) { spi_transaction(0x40+8*hilo, addr>>8 & 0xFF, addr & 0xFF, data); } void commit(int addr) { spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0); } //#define _current_page(x) (here & 0xFFFFE0) int current_page(int addr) { if (param.pagesize == 32) return addr & 0xFFFFFFF0; if (param.pagesize == 64) return addr & 0xFFFFFFE0; if (param.pagesize == 128) return addr & 0xFFFFFFC0; if (param.pagesize == 256) return addr & 0xFFFFFF80; return addr; } byte write_flash(int length) { if (param.pagesize < 1) { return STK_FAILED; } //if (param.pagesize != 64) return STK_FAILED; int page = current_page(_addr); int x = 0; while (x < length) { if (page != current_page(_addr)) { commit(page); page = current_page(_addr); } flash(LOW, _addr, buff[x++]); flash(HIGH, _addr, buff[x++]); _addr++; } commit(page); return STK_OK; } byte write_eeprom(int length) { // here is a word address, so we use here*2 // this writes byte-by-byte, // page writing may be faster (4 bytes at a time) for (int x = 0; x < length; x++) { spi_transaction(0xC0, 0x00, _addr*2+x, buff[x]); delay(45); } return STK_OK; } void program_page() { byte result = STK_FAILED; int length = 256 * getch() + getch(); if (length > 256) { Serial.write(STK_FAILED); error++; return; } char memtype = (char)getch(); // for (int x = 0; x < length; x++) { // buff[x] = getch(); // } readbytes(length); if (CRC_EOP == getch()) { Serial.write(STK_INSYNC); switch (memtype) { case 'E': result = (byte)write_eeprom(length); break; case 'F': result = (byte)write_flash(length); break; } Serial.write(result); if (result != STK_OK) { error++; } } else { Serial.write(STK_NOSYNC); error++; } } byte flash_read(byte hilo, int addr) { return spi_transaction(0x20 + hilo * 8, (addr >> 8) & 0xFF, addr & 0xFF, 0); } char flash_read_page(int length) { for (int x = 0; x < length; x+=2) { byte low = flash_read(LOW, _addr); Serial.write( low); byte high = flash_read(HIGH, _addr); Serial.write( high); _addr++; } return STK_OK; } char eeprom_read_page(int length) { // here again we have a word address for (int x = 0; x < length; x++) { byte ee = spi_transaction(0xA0, 0x00, _addr*2+x, 0xFF); Serial.write( ee); } return STK_OK; } void read_page() { byte result = (byte)STK_FAILED; int length = 256 * getch() + getch(); char memtype = getch(); if (CRC_EOP != getch()) { Serial.write(STK_NOSYNC); return; } Serial.write(STK_INSYNC); if (memtype == 'F') result = flash_read_page(length); if (memtype == 'E') result = eeprom_read_page(length); Serial.write(result); return; } void read_signature() { if (CRC_EOP != getch()) { Serial.write(STK_NOSYNC); error++; return; } Serial.write(STK_INSYNC); byte high = spi_transaction(0x30, 0x00, 0x00, 0x00); Serial.write(high); byte middle = spi_transaction(0x30, 0x00, 0x01, 0x00); Serial.write(middle); byte low = spi_transaction(0x30, 0x00, 0x02, 0x00); Serial.write(low); Serial.write(STK_OK); } ////////////////////////////////////////// ////////////////////////////////////////// //////////////////////////////////// //////////////////////////////////// int avrisp() { byte data, low, high; byte avrch = getch(); switch (avrch) { case STK_GET_SYNC: // get in sync replyOK(); break; case STK_GET_SIGNON: // get sign on if (getch() == CRC_EOP) { Serial.write(STK_INSYNC); Serial.write("AVR ISP"); Serial.write(STK_OK); } break; case STK_GET_PARM: // 0x41 get_parameter(getch()); break; case STK_SET_PARM: // 0x42 readbytes(20); set_parameters(); replyOK(); break; case STK_SET_PARM_EXT: // extended parameters - ignore for now readbytes(5); replyOK(); break; case STK_PMODE_START: // 0x50 beep(2272, 20); start_pmode(); replyOK(); break; case STK_PMODE_END: //0x51 beep(1912, 50); error=0; end_pmode(); replyOK(); break; case STK_SET_ADDR: // 0x55 _addr = getch() + 256 * getch(); replyOK(); break; case STK_UNIVERSAL: //UNIVERSAL 0x56 universal(); break; case STK_PROG_FLASH: //STK_PROG_FLASH ??? low = getch(); high = getch(); replyOK(); break; case STK_PROG_DATA: //STK_PROG_DATA ??? data = getch(); replyOK(); break; case STK_PROG_PAGE: //STK_PROG_PAGE // beep(1912, 20); program_page(); break; case STK_READ_PAGE: //STK_READ_PAGE read_page(); break; case STK_READ_SIGN: //STK_READ_SIGN read_signature(); break; // expecting a command, not CRC_EOP // this is how we can get back in sync case CRC_EOP: Serial.write(STK_NOSYNC); break; // anything else we will return STK_UNKNOWN default: if (CRC_EOP == getch()) Serial.write(STK_UNKNOWN); else Serial.write(STK_NOSYNC); } } // beep without using PWM void beep(int tone, long duration){ long elapsed = 0; while (elapsed < (duration * 10000)) { digitalWrite(PIEZO, HIGH); delayMicroseconds(tone / 2); digitalWrite(PIEZO, LOW); delayMicroseconds(tone / 2); // Keep track of how long we pulsed elapsed += tone; } }