Merge pull request #3500 from PeterVH/issue-3321

Improve ArduinoISP sketch
This commit is contained in:
Martino Facchin 2015-10-07 12:00:20 +02:00
commit 651ae04a19
1 changed files with 308 additions and 157 deletions

View File

@ -1,4 +1,4 @@
// ArduinoISP version 04m3
// ArduinoISP
// Copyright (c) 2008-2011 Randall Bohn
// If you require a license, see
// http://www.opensource.org/licenses/bsd-license.php
@ -6,50 +6,144 @@
// 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: 52
// Pin 10 is used to reset the target microcontroller.
//
// By default, the hardware SPI pins MISO, MOSI and SCK pins are used
// to communicate with the target. On all Arduinos, these pins can be found
// on the ICSP/SPI header:
//
// MISO °. . 5V (!) Avoid this pin on Due, Zero...
// SCK . . MOSI
// . . GND
//
// On some Arduinos (Uno,...), pins MOSI, MISO and SCK are the same pins
// as digital pin 11, 12 and 13, respectively. That is why many tutorials
// instruct you to hook up the target to these pins. If you find this wiring
// more practical, have a define USE_OLD_STYLE_WIRING. This will work even
// even when not using an Uno. (On an Uno this is not needed).
//
// Alternatively you can use any other digital pin by configuring software ('BitBanged')
// SPI and having appropriate defines for PIN_MOSI, PIN_MISO and PIN_SCK.
//
// IMPORTANT: When using an Arduino that is not 5V tolerant (Due, Zero, ...)
// as the programmer, make sure to not expose any of the programmer's pins to 5V.
// A simple way to accomplish this is to power the complete system (programmer
// and target) at 3V3.
//
// 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
#include "pins_arduino.h"
#define RESET SS
#include "Arduino.h"
#undef SERIAL
#define PROG_FLICKER true
// Configure SPI clock (in Hz).
// E.g. for an attiny @128 kHz: the datasheet states that both the high
// and low spi clock pulse must be > 2 cpu cycles, so take 3 cycles i.e.
// divide target f_cpu by 6:
// #define SPI_CLOCK (128000/6)
//
// A clock slow enough for an attiny85 @ 1MHz, is a reasonable default:
#define SPI_CLOCK (1000000/6)
// Select hardware or software SPI, depending on SPI clock.
// Currently only for AVR, for other archs (Due, Zero,...),
// hardware SPI is probably too fast anyway.
#if defined(ARDUINO_ARCH_AVR)
#if SPI_CLOCK > (F_CPU / 128)
#define USE_HARDWARE_SPI
#endif
#endif
// Configure which pins to use:
// The standard pin configuration.
#ifndef ARDUINO_HOODLOADER2
#define RESET 10 // Use pin 10 to reset the target rather than SS
#define LED_HB 9
#define LED_ERR 8
#define LED_PMODE 7
#define PROG_FLICKER true
// Uncomment following line to use the old Uno style wiring
// (using pin 11, 12 and 13 instead of the SPI header) on Leonardo, Due...
// #define USE_OLD_STYLE_WIRING
#ifdef USE_OLD_STYLE_WIRING
#define PIN_MOSI 11
#define PIN_MISO 12
#define PIN_SCK 13
#endif
// HOODLOADER2 means running sketches on the atmega16u2
// serial converter chips on Uno or Mega boards.
// We must use pins that are broken out:
#else
#define RESET 4
#define LED_HB 7
#define LED_ERR 6
#define LED_PMODE 5
#endif
// By default, use hardware SPI pins:
#ifndef PIN_MOSI
#define PIN_MOSI MOSI
#endif
#ifndef PIN_MISO
#define PIN_MISO MISO
#endif
#ifndef PIN_SCK
#define PIN_SCK SCK
#endif
// Force bitbanged SPI if not using the hardware SPI pins:
#if (PIN_MISO != MISO) || (PIN_MOSI != MOSI) || (PIN_SCK != SCK)
#undef USE_HARDWARE_SPI
#endif
// Configure the serial port to use.
//
// Prefer the USB virtual serial port (aka. native USB port), if the Arduino has one:
// - it does not autoreset (except for the magic baud rate of 1200).
// - it is more reliable because of USB handshaking.
//
// Leonardo and similar have an USB virtual serial port: 'Serial'.
// Due and Zero have an USB virtual serial port: 'SerialUSB'.
//
// On the Due and Zero, 'Serial' can be used too, provided you disable autoreset.
// To use 'Serial': #define SERIAL Serial
#ifdef SERIAL_PORT_USBVIRTUAL
#define SERIAL SERIAL_PORT_USBVIRTUAL
#else
#define SERIAL Serial
#endif
// Configure the baud rate:
#define BAUDRATE 19200
// #define BAUDRATE 115200
// #define BAUDRATE 1000000
#define HWVER 2
#define SWMAJ 1
@ -65,20 +159,80 @@
void pulse(int pin, int times);
#ifdef USE_HARDWARE_SPI
#include "SPI.h"
#else
#define SPI_MODE0 0x00
class SPISettings {
public:
// clock is in Hz
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) : clock(clock){
(void) bitOrder;
(void) dataMode;
};
private:
uint32_t clock;
friend class BitBangedSPI;
};
class BitBangedSPI {
public:
void begin() {
digitalWrite(PIN_SCK, LOW);
digitalWrite(PIN_MOSI, LOW);
pinMode(PIN_SCK, OUTPUT);
pinMode(PIN_MOSI, OUTPUT);
pinMode(PIN_MISO, INPUT);
}
void beginTransaction(SPISettings settings) {
pulseWidth = (500000 + settings.clock - 1) / settings.clock;
if (pulseWidth == 0)
pulseWidth = 1;
}
void end() {}
uint8_t transfer (uint8_t b) {
for (unsigned int i = 0; i < 8; ++i) {
digitalWrite(PIN_MOSI, (b & 0x80) ? HIGH : LOW);
digitalWrite(PIN_SCK, HIGH);
delayMicroseconds(pulseWidth);
b = (b << 1) | digitalRead(PIN_MISO);
digitalWrite(PIN_SCK, LOW); // slow pulse
delayMicroseconds(pulseWidth);
}
return b;
}
private:
unsigned long pulseWidth; // in microseconds
};
static BitBangedSPI SPI;
#endif
void setup() {
Serial.begin(19200);
SERIAL.begin(BAUDRATE);
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;
unsigned int here;
uint8_t buff[256]; // global block storage
#define beget16(addr) (*addr * 256 + *(addr+1) )
@ -91,11 +245,11 @@ typedef struct param {
uint8_t selftimed;
uint8_t lockbytes;
uint8_t fusebytes;
int flashpoll;
int eeprompoll;
int pagesize;
int eepromsize;
int flashsize;
uint8_t flashpoll;
uint16_t eeprompoll;
uint16_t pagesize;
uint16_t eepromsize;
uint32_t flashsize;
}
parameter;
@ -105,17 +259,22 @@ parameter param;
uint8_t hbval = 128;
int8_t hbdelta = 8;
void heartbeat() {
if (hbval > 192) {
hbdelta = -hbdelta;
}
if (hbval < 32) {
hbdelta = -hbdelta;
}
static unsigned long last_time = 0;
unsigned long now = millis();
if ((now - last_time) < 40)
return;
last_time = now;
if (hbval > 192) hbdelta = -hbdelta;
if (hbval < 32) hbdelta = -hbdelta;
hbval += hbdelta;
analogWrite(LED_HB, hbval);
delay(20);
}
static bool rst_active_high;
void reset_target(bool reset) {
digitalWrite(RESET, ((reset && rst_active_high) || (!reset && !rst_active_high)) ? HIGH : LOW);
}
void loop(void) {
// is pmode active?
@ -133,14 +292,14 @@ void loop(void) {
// light the heartbeat LED
heartbeat();
if (Serial.available()) {
if (SERIAL.available()) {
avrisp();
}
}
uint8_t getch() {
while (!Serial.available());
return Serial.read();
while (!SERIAL.available());
return SERIAL.read();
}
void fill(int n) {
for (int x = 0; x < n; x++) {
@ -164,53 +323,31 @@ void prog_lamp(int 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);
SPI.transfer(a);
SPI.transfer(b);
SPI.transfer(c);
return SPI.transfer(d);
}
void empty_reply() {
if (CRC_EOP == getch()) {
Serial.print((char)STK_INSYNC);
Serial.print((char)STK_OK);
SERIAL.print((char)STK_INSYNC);
SERIAL.print((char)STK_OK);
} else {
error++;
Serial.print((char)STK_NOSYNC);
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);
SERIAL.print((char)STK_INSYNC);
SERIAL.print((char)b);
SERIAL.print((char)STK_OK);
} else {
error++;
Serial.print((char)STK_NOSYNC);
SERIAL.print((char)STK_NOSYNC);
}
}
@ -256,34 +393,54 @@ void set_parameters() {
+ buff[18] * 0x00000100
+ buff[19];
// avr devices have active low reset, at89sx are active high
rst_active_high = (param.devicecode >= 0xe0);
}
void start_pmode() {
spi_init();
// following delays may not work on all targets...
// Reset target before driving PIN_SCK or PIN_MOSI
// SPI.begin() will configure SS as output,
// so SPI master mode is selected.
// We have defined RESET as pin 10,
// which for many arduino's is not the SS pin.
// So we have to configure RESET as output here,
// (reset_target() first sets the correct level)
reset_target(true);
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.begin();
SPI.beginTransaction(SPISettings(SPI_CLOCK, MSBFIRST, SPI_MODE0));
// See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
// Pulse RESET after PIN_SCK is low:
digitalWrite(PIN_SCK, LOW);
delay(20); // discharge PIN_SCK, value arbitrally chosen
reset_target(false);
// Pulse must be minimum 2 target CPU clock cycles
// so 100 usec is ok for CPU speeds above 20KHz
delayMicroseconds(100);
reset_target(true);
// Send the enable programming command:
delay(50); // datasheet: must be > 20 msec
spi_transaction(0xAC, 0x53, 0x00, 0x00);
pmode = 1;
}
void end_pmode() {
pinMode(MISO, INPUT);
pinMode(MOSI, INPUT);
pinMode(SCK, INPUT);
SPI.end();
// We're about to take the target out of reset
// so configure SPI pins as input
pinMode(PIN_MOSI, INPUT);
pinMode(PIN_SCK, INPUT);
reset_target(false);
pinMode(RESET, INPUT);
pmode = 0;
}
void universal() {
int w;
uint8_t ch;
fill(4);
@ -291,13 +448,13 @@ void universal() {
breply(ch);
}
void flash(uint8_t hilo, int addr, uint8_t data) {
void flash(uint8_t hilo, unsigned int addr, uint8_t data) {
spi_transaction(0x40 + 8 * hilo,
addr >> 8 & 0xFF,
addr & 0xFF,
data);
}
void commit(int addr) {
void commit(unsigned int addr) {
if (PROG_FLICKER) {
prog_lamp(LOW);
}
@ -308,8 +465,7 @@ void commit(int addr) {
}
}
//#define _current_page(x) (here & 0xFFFFE0)
int current_page(int addr) {
unsigned int current_page() {
if (param.pagesize == 32) {
return here & 0xFFFFFFF0;
}
@ -329,21 +485,21 @@ int current_page(int addr) {
void write_flash(int length) {
fill(length);
if (CRC_EOP == getch()) {
Serial.print((char) STK_INSYNC);
Serial.print((char) write_flash_pages(length));
SERIAL.print((char) STK_INSYNC);
SERIAL.print((char) write_flash_pages(length));
} else {
error++;
Serial.print((char) STK_NOSYNC);
SERIAL.print((char) STK_NOSYNC);
}
}
uint8_t write_flash_pages(int length) {
int x = 0;
int page = current_page(here);
unsigned int page = current_page();
while (x < length) {
if (page != current_page(here)) {
if (page != current_page()) {
commit(page);
page = current_page(here);
page = current_page();
}
flash(LOW, here, buff[x++]);
flash(HIGH, here, buff[x++]);
@ -356,10 +512,10 @@ uint8_t write_flash_pages(int length) {
}
#define EECHUNK (32)
uint8_t write_eeprom(int length) {
uint8_t write_eeprom(unsigned int length) {
// here is a word address, get the byte address
int start = here * 2;
int remaining = length;
unsigned int start = here * 2;
unsigned int remaining = length;
if (length > param.eepromsize) {
error++;
return STK_FAILED;
@ -373,13 +529,13 @@ uint8_t write_eeprom(int length) {
return STK_OK;
}
// write (length) bytes, (start) is a byte address
uint8_t write_eeprom_chunk(int start, int length) {
uint8_t write_eeprom_chunk(unsigned int start, unsigned 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;
for (unsigned int x = 0; x < length; x++) {
unsigned int addr = start + x;
spi_transaction(0xC0, (addr >> 8) & 0xFF, addr & 0xFF, buff[x]);
delay(45);
}
@ -389,7 +545,7 @@ uint8_t write_eeprom_chunk(int start, int length) {
void program_page() {
char result = (char) STK_FAILED;
int length = 256 * getch();
unsigned int length = 256 * getch();
length += getch();
char memtype = getch();
// flash memory @here, (length) bytes
@ -400,19 +556,19 @@ void program_page() {
if (memtype == 'E') {
result = (char)write_eeprom(length);
if (CRC_EOP == getch()) {
Serial.print((char) STK_INSYNC);
Serial.print(result);
SERIAL.print((char) STK_INSYNC);
SERIAL.print(result);
} else {
error++;
Serial.print((char) STK_NOSYNC);
SERIAL.print((char) STK_NOSYNC);
}
return;
}
Serial.print((char)STK_FAILED);
SERIAL.print((char)STK_FAILED);
return;
}
uint8_t flash_read(uint8_t hilo, int addr) {
uint8_t flash_read(uint8_t hilo, unsigned int addr) {
return spi_transaction(0x20 + hilo * 8,
(addr >> 8) & 0xFF,
addr & 0xFF,
@ -422,9 +578,9 @@ uint8_t flash_read(uint8_t hilo, int addr) {
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);
SERIAL.print((char) low);
uint8_t high = flash_read(HIGH, here);
Serial.print((char) high);
SERIAL.print((char) high);
here++;
}
return STK_OK;
@ -436,7 +592,7 @@ char eeprom_read_page(int length) {
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);
SERIAL.print((char) ee);
}
return STK_OK;
}
@ -448,34 +604,29 @@ void read_page() {
char memtype = getch();
if (CRC_EOP != getch()) {
error++;
Serial.print((char) STK_NOSYNC);
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;
SERIAL.print((char) STK_INSYNC);
if (memtype == 'F') result = flash_read_page(length);
if (memtype == 'E') result = eeprom_read_page(length);
SERIAL.print(result);
}
void read_signature() {
if (CRC_EOP != getch()) {
error++;
Serial.print((char) STK_NOSYNC);
SERIAL.print((char) STK_NOSYNC);
return;
}
Serial.print((char) STK_INSYNC);
SERIAL.print((char) STK_INSYNC);
uint8_t high = spi_transaction(0x30, 0x00, 0x00, 0x00);
Serial.print((char) high);
SERIAL.print((char) high);
uint8_t middle = spi_transaction(0x30, 0x00, 0x01, 0x00);
Serial.print((char) middle);
SERIAL.print((char) middle);
uint8_t low = spi_transaction(0x30, 0x00, 0x02, 0x00);
Serial.print((char) low);
Serial.print((char) STK_OK);
SERIAL.print((char) low);
SERIAL.print((char) STK_OK);
}
//////////////////////////////////////////
//////////////////////////////////////////
@ -483,8 +634,7 @@ void read_signature() {
////////////////////////////////////
////////////////////////////////////
int avrisp() {
uint8_t data, low, high;
void avrisp() {
uint8_t ch = getch();
switch (ch) {
case '0': // signon
@ -493,9 +643,13 @@ int avrisp() {
break;
case '1':
if (getch() == CRC_EOP) {
Serial.print((char) STK_INSYNC);
Serial.print("AVR ISP");
Serial.print((char) STK_OK);
SERIAL.print((char) STK_INSYNC);
SERIAL.print("AVR ISP");
SERIAL.print((char) STK_OK);
}
else {
error++;
SERIAL.print((char) STK_NOSYNC);
}
break;
case 'A':
@ -510,9 +664,9 @@ int avrisp() {
fill(5);
empty_reply();
break;
case 'P':
start_pmode();
if (!pmode)
start_pmode();
empty_reply();
break;
case 'U': // set address (word)
@ -522,12 +676,12 @@ int avrisp() {
break;
case 0x60: //STK_PROG_FLASH
low = getch();
high = getch();
getch(); // low addr
getch(); // high addr
empty_reply();
break;
case 0x61: //STK_PROG_DATA
data = getch();
getch(); // data
empty_reply();
break;
@ -556,19 +710,16 @@ int avrisp() {
// this is how we can get back in sync
case CRC_EOP:
error++;
Serial.print((char) STK_NOSYNC);
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);
}
if (CRC_EOP == getch())
SERIAL.print((char)STK_UNKNOWN);
else
SERIAL.print((char)STK_NOSYNC);
}
}