4 Dec 2013

Arduino ISP Code to flash the Turnigy 9X

// 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:
// 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
//
// 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.
//
// 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
#include "pins_arduino.h"  // defines SS,MOSI,MISO,SCK
#define SCK 13
#define MISO 12
#define MOSI 11
#define RESET 10
#define LED_HB 9
#define LED_ERR 8
#define LED_PMODE 7
#define PROG_FLICKER true
#define HWVER 2
#define SWMAJ 1
#define SWMIN 18// 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(int pin, int times);
void setup() {
   
  Serial.begin(19200);
    pinMode(LED_PMODE, OUTPUT);
    pulse(LED_PMODE, 2);
    pinMode(LED_ERR, OUTPUT);
    pulse(LED_ERR, 2);
    pinMode(LED_HB, OUTPUT);
    pulse(LED_HB, 2);
}
int error=0;
int pmode=0;
// address for reading and writing, set by 'U' command
int here;
uint8_t buff[256]; // global block storage
#define beget16(addr) (*addr * 256 + *(addr+1) )
typedef 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;
    int flashpoll;
    int eeprompoll;
    int pagesize;
    int eepromsize;
    int flashsize;
}parameter;

parameter param;// this provides a heartbeat on pin 9, so you can tell the software is running.
uint8_t hbval=128;
int8_t hbdelta=8;
void heartbeat() {
    if (hbval > 192) hbdelta = -hbdelta;
    if (hbval < 32) hbdelta = -hbdelta;
    hbval += hbdelta;
    analogWrite(LED_HB, hbval);
    delay(40);
    }
   
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.available()) {
        avrisp();
    }
    }
   
uint8_t getch() {
    while(!Serial.available());
    return Serial.read();
}
void fill(int n) {
    for (int x = 0; x < n; x++) {
        buff[x] = getch();
    }
}

#define PTIME 30
void pulse(int pin, int times) {
    do {
        digitalWrite(pin, HIGH);
        delay(PTIME);
        digitalWrite(pin, LOW);
        delay(PTIME);
    }while (times--);
   
}
   
void prog_lamp(int state) {
    if(PROG_FLICKER)
      digitalWrite(LED_PMODE, state);
}
   
void spi_init() {
    uint8_t x;
    SPCR = 0x53;
    x=SPSR;
    x=SPDR;
}
   
void spi_wait() {
    do {
    }
    while (!(SPSR & (1 << SPIF)));
    }uint8_t spi_send(uint8_t b) {
    uint8_t reply;
    SPDR=b;
    spi_wait();
    reply = SPDR;
    return reply;
    }
   
  uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
    uint8_t n;
    spi_send(a);
    n=spi_send(b);
    //if (n != a) error = -1;
    n=spi_send(c);
    return spi_send(d);
    }
   
 void empty_reply() {
    if (CRC_EOP == getch()) {
        Serial.print((char)STK_INSYNC);
        Serial.print((char)STK_OK);
        } else {
        error++;
        Serial.print((char)STK_NOSYNC);
    }
}
void breply(uint8_t b) {
    if (CRC_EOP == getch()) {
        Serial.print((char)STK_INSYNC);
        Serial.print((char)b);
        Serial.print((char)STK_OK);
    }
    else {
        error++;
        Serial.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() {
    // 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 = 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);
    digitalWrite(RESET, LOW);
    delay(50);
    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;
    uint8_t ch;
   
    fill(4);
    ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]);
    breply(ch);
}

void flash(uint8_t hilo, int addr, uint8_t data) {
    spi_transaction(0x40+8*hilo,
    addr>>8 & 0xFF,
    addr & 0xFF,
    data);
}
void commit(int addr) {
    if (PROG_FLICKER) prog_lamp(LOW);
    spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0);
    if (PROG_FLICKER) {
        delay(PTIME);
        prog_lamp(HIGH);
    }
}

//#define _current_page(x) (here & 0xFFFFE0)
int current_page(int addr) {
    if (param.pagesize == 32) return here & 0xFFFFFFF0;
    if (param.pagesize == 64) return here & 0xFFFFFFE0;
    if (param.pagesize == 128) return here & 0xFFFFFFC0;
    if (param.pagesize == 256) return here & 0xFFFFFF80;
    return here;
}

void write_flash(int length) {
    fill(length);
    if (CRC_EOP == getch()) {
        Serial.print((char) STK_INSYNC);
        Serial.print((char) write_flash_pages(length));
        } else {
        error++;
        Serial.print((char) STK_NOSYNC);
    }
}

uint8_t write_flash_pages(int length) {
    int x = 0;
    int page = current_page(here);
    while (x < length) {
        if (page != current_page(here)) {
            commit(page);
            page = current_page(here);
        }
        flash(LOW, here, buff[x++]);
        flash(HIGH, here, buff[x++]);
        here++;
    }
   
    commit(page);
   
    return STK_OK;
}

#define EECHUNK (32)
uint8_t write_eeprom(int length) {
    // here is a word address, get the byte address
    int start = here * 2;
    int remaining = length;
    if (length > param.eepromsize) {
        error++;
        return STK_FAILED;
    }
    while (remaining > EECHUNK) {
        write_eeprom_chunk(start, EECHUNK);
        start += EECHUNK;
        remaining -= EECHUNK;
    }
    write_eeprom_chunk(start, remaining);
    return STK_OK;
}
// write (length) bytes, (start) is a byte address
uint8_t write_eeprom_chunk(int start, int length) {
    // this writes byte-by-byte,
    // page writing may be faster (4 bytes at a time)
    fill(length);
    prog_lamp(LOW);
    for (int x = 0; x < length; x++) {
        int addr = start+x;
        spi_transaction(0xC0, (addr>>8) & 0xFF, addr & 0xFF, buff[x]);
        delay(45);
    }
    prog_lamp(HIGH);
    return STK_OK;
}

void program_page() {
    char result = (char) STK_FAILED;
    int length = 256 * getch() + 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.print((char) STK_INSYNC);
            Serial.print(result);
            } else {
            error++;
            Serial.print((char) STK_NOSYNC);
        }
        return;
    }
    Serial.print((char)STK_FAILED);
    return;
}

uint8_t flash_read(uint8_t 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) {
        uint8_t low = flash_read(LOW, here);
        Serial.print((char) low);
        uint8_t high = flash_read(HIGH, here);
        Serial.print((char) high);
        here++;
    }
    return STK_OK;
}

char eeprom_read_page(int length) {
    // here again we have a word address
    int start = here * 2;
    for (int x = 0; x < length; x++) {
        int addr = start + x;
        uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF);
        Serial.print((char) ee);
    }
    return STK_OK;
}

void read_page() {
    char result = (char)STK_FAILED;
    int length = 256 * getch() + getch();
    char memtype = getch();
    if (CRC_EOP != getch()) {
        error++;
        Serial.print((char) STK_NOSYNC);
        return;
    }
    Serial.print((char) STK_INSYNC);
    if (memtype == 'F') result = flash_read_page(length);
    if (memtype == 'E') result = eeprom_read_page(length);
    Serial.print(result);
    return;
}

void read_signature() {
    if (CRC_EOP != getch()) {
        error++;
        Serial.print((char) STK_NOSYNC);
        return;
    }
    Serial.print((char) STK_INSYNC);
    uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00);
    Serial.print((char) high);
    uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);
    Serial.print((char) middle);
    uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);
    Serial.print((char) low);
    Serial.print((char) STK_OK);
}

////////////////////////////////////'
////////////////////////////////////
int avrisp() {
    uint8_t data, low, high;
    uint8_t ch = getch();
    switch (ch) {
        case '0': // signon
        error = 0;
        empty_reply();
        break;
        case '1':
        if (getch() == CRC_EOP) {
            Serial.print((char) STK_INSYNC);
            Serial.print("AVR ISP");
            Serial.print((char) STK_OK);
        }
        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':
        start_pmode();
        empty_reply();
        break;
        case 'U': // set address (word)
        here = getch() + 256 * getch();
        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.print((char) STK_NOSYNC);
        break;
       
        // anything else we will return STK_UNKNOWN
        default:
        error++;
        if (CRC_EOP == getch())
        Serial.print((char)STK_UNKNOWN);
        else
        Serial.print((char)STK_NOSYNC);
    }
}