/*
Title: HVRescue_Shield_More (renamed from HVRescue_Shield_Plus)
Description: Arduino sketch for use with the HV Rescue Shield
Based on: HVRescue_Shield_Plus by Dennis Tricker
Author: Originally Jeff Keyzer, Modified and extended by Dennis Tricker and More version by Peter Boxler (Sept. 2016)
Company: MightyOhm Engineering
Website: http://mightyohm.com
Contact: http://mightyohm.com/blog/contact/
This sketch assumes that the Arduino is equipped with an AVR HV Rescue Shield.
Schematics and other details are available at http://mightyohm.com/hvrescue2
The sketch uses High Voltage Programming Mode to set fuses on many Atmel AVR family 8-bit microcontrollers.
Version 2.0 adds support for High Voltage Serial Programming (HVSP) mode and 8-pin ATtiny devices, but remains
backwards compatible with the 1.x series hardware.
The HVPP routines are based on those described in the ATmega48/88/168 datasheet rev.
2545M-AVR-09/07, pg. 290-297 and the ATtiny2313 datasheet rev. 2543I-AVR-04/06 pg. 165-176.
The HVSP routines are based on the ATtiny25/45/85 and 13A datasheets (ATtiny25/45/85 2586M–AVR–07/10 pg. 159-165,
ATtiny13A 8126E-AVR-7/10 pg. 109-116).
These routines are compatible with many other members of the AVR family that are not listed here.
For a complete list of tested microcontrollers, see http://mightyohm.com/wiki/products:hvrescue:compatibility
Changelog:
**********
16/15/09 More 1.00 (based on Plus version) by Peter Boxler, Switzerland
Tested with HVRescue Shield 2 on an Ardunio UNO R3 with Arduino IDE 1.6.9 (Mac) <---
Added table (array) of AVR device definitions (incomplete, can easily be extended) and added more functions.
Manual selection of device type removed, sketch tries to establish device type:
finds either:
invalid AVR signature found (not 0x1E) eg. no device present
valid AVR signature found but no match to internal table
valid AVR signature found and matches entry in internal table
Added print AVR device type (after reading signature)
Loop function streamlined for better readability
Programming mode (HVSP/HVPP) is taken from internal device table
Additionl selections in function menu:
Function D write default fuses according to device found
Function H write fuses for internal 8 Mhz clock (no division by 8)
Function Q write fuses for external crystal 16 Mhz
Note: these functions are available only if device was recognized based on the signature.
removed non-interactive feature
12/1/11 Plus 1.00 Rewritten to simplify device access and extend functionality, by Dennis J Tricker
Added unified HVPP & SP low level code: target_xxxxxx functions & associated support routines (Load & Strobe)
Introduced choice menu function and get_value with error checking and automation
Fuse write now checked and indicated with OK or Error
Extended chip data output to include SIG and LOCK bits
Extended commands to allow Erase, FLASH & EEPROM Read
Added FLASH & EEPROM test page write: writes and checks pattern to ensure chip operational
Note: Address must be a start of page address ! See datasheet
EEPROM test writes four bytes 0xaa,0x55,0xa5,0x5a
FLASH Test writes "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPp" = 32 bytes= 16 words. Upper case is MSB.
EEPROM&FLASH Test may not write a whole page due to limits on Arduino RAM memory, only check bytes written
All output in hex, all input in hex: 0x prefix optional
IMPORTANT: The Erase and Test functions will clear / overwrite existing device data - You have been warned !
Tested with Arduino 22 & 1.0, ATtiny85/ATtiny2313/ATmega48
3/15/11 2.12
- New digital pin 0-7 (command&data) read/write routines for the Arduino Mega, since these lines are implemented
differently on the Mega than on the original Arduino.
3/8/11 2.11
- Analog inputs (used here as digital outputs) are now called by their new Arduino A0-A5 names.
2/2/11 2.1
- adjusted RESET and VCC edge timing to work with new board design and avoid signal contention on SDO
- fixed bug that prevented program from compiling in non-interactive mode
- modified non-interactive mode so that read/verify serial comms still occur, but fuse values aren't prompted
12/17/10 2.01
- added missing braces to if(mode == HVSP) that sets SDO pinmode to INPUT
- removed misleading comment about removing AVR when entering fuse values
- default mode changed back to ATMEGA
12/13/10 v2.0
- Added support for 8-pin parts that use HV Serial Programming (HVSP)
- New mode selection at startup determines which type of part is to be programmed
- Got rid of endSerial function, since Arduino now includes Serial.end (finally!)
- Added a wait for serial transmit to complete before burning fuses. Without this HFUSE burn would fail occasionally.
- Numerous other minor tweaks, removal of unnecessary delays, better commenting
9/24/10 v1.2a
- ATtiny2313 mode was being set by default. Changed default mode back to ATmega (see #define ATtiny).
8/16/10 v1.2
- Existing fuse settings are now shown before asking the user for new values
- Added OE strobe after entering programming mode to get ATtiny2313 to read first fuse correctly.
- Cleaned up code a bit
- Some minor tweaks to data direction register settings during setup, etc.
11/02/09 v1.1
- Removed endSerial call after reading back fuse bytes, was spewing garbage into
serial monitor
- Still occsionally get garbage when opening serial monitor, not sure what is causing this.
03/01/09 v1.0
- ATtiny2313 support, enable with ATtiny option
- 12V Step up converter enable is non-inverting, unlike previous level shifter circuit
- added interactive mode, asks for fuse values to burn, option to turn off
- added EFUSE support and option to enable
- button now has very simple debounce routine
09/24/08
- original release of sketch "HVFuse" to support first implementation on perfboard
- Details: http://mightyohm.com/blog/2008/09/arduino-based-avr-high-voltage-programmer/
Original Copyright 2008, 2009, 2010 Jeff Keyzer
Additions Copyright 2011 Dennis Tricker
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
// User defined settings
#define MEGA 0 // Set this to 1 if you are using an Arduino Mega (default = 0)
#define LEONARDO 1 // Set this to 1 if you are using an Arduino Leonardo (default = 0)
#define BAUD 9600 // Serial port rate at which to talk to PC
#define DEBUG 1 // set to 1 for debug output on serial
// Old type hardware with Transistor switching 12v
//#define OFF12v HIGH
//#define ON12v LOW
// New version 2.1 hardware with DC-DC convertor
#define OFF12v LOW
#define ON12v HIGH
// Internal definitions
#define DEBUG 0 // set to 1 to produce debug output
#define ASK 0 // Note: it is 0 not '0' for no auto selection
#define HVPP 1
#define TINY2313 2
#define HVSP 3
uint8_t device; // holds found device type (array index)
/*
* Struct to hold signature and default fuse bits of target to be programmed
* use Atmel AVR� Fuse Calculator: http://www.engbedded.com/fusecalc/
* Note: such a structure was first used by Peter Fleury in his ATtiny FuseRestore Project
* http://homepage.hispeed.ch/peterfleury/avr-hvsp-fuse-restore.html
*
* structure expanded and used here.
*/
typedef struct {
byte signature[3];
char *mcutype;
uint8_t programming_mode;
uint8_t fuseLowBits;
uint8_t fuseHighBits;
uint8_t fuseExtendedBits;
uint8_t HifuseLowBits;
uint8_t HifuseHighBits;
uint8_t HifuseExtendedBits;
uint8_t ExfuseLowBits;
uint8_t ExfuseHighBits;
uint8_t ExfuseExtendedBits;
}TargetCpuInfo_t;
// DEVICE TABLE
// Array of controller types, held in program memory (let us call it: device table)
// extend if needed, sequence does not matter. simply copy/paste an entry
// end adjust values
// NOTE: Array must have a max of 253 entries or else sketch fails !!
//
// To add more mcu's to the array do this: <-----------
// get the datasheet for the mcu
// calculate the fuses with a fuse calculator
// find out if mcu has extended fuses
// get the proper signature
// get the proper name of the mcu
// get the progamming mode: HVSP or HVPP
// copy one existing entry and paste it in.
// change the values in the new entry.
// voila, that is it
// run it
//
static const TargetCpuInfo_t PROGMEM targetCpu[] =
{
{ // ATtiny13
.signature = { 0x1E, 0x90, 0x07 },
.mcutype = "ATtiny13",
.programming_mode = HVSP,
.fuseLowBits = 0x6A,
.fuseHighBits = 0xFF,
.fuseExtendedBits = 0x00,
.HifuseLowBits = 0x7A,
.HifuseHighBits = 0xFF,
.HifuseExtendedBits = 0x00, // 0x00 means: no extended fuses written
.ExfuseLowBits = 0x78,
.ExfuseHighBits = 0xFF,
.ExfuseExtendedBits = 0x00,
},
{ // ATtiny24
.signature = { 0x1E, 0x91, 0x0B },
.mcutype = "ATtiny24",
.programming_mode = HVSP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xFF,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xFF,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xFF,
},
{ // ATtiny44
.signature = { 0x1E, 0x92, 0x07 },
.mcutype = "ATtiny44",
.programming_mode = HVSP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xFF,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xFF,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xFF,
},
{ // ATtiny84
.signature = { 0x1E, 0x93, 0x0C },
.mcutype = "ATtiny84",
.programming_mode = HVSP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xFF,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xFF,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xFF,
},
{ // ATtiny25
.signature = { 0x1E, 0x91, 0x08 },
.mcutype = "ATtiny25",
.programming_mode = HVSP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xFF,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xFF,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xFF,
},
{ // ATtiny45
.signature = { 0x1E, 0x92, 0x06 },
.mcutype = "ATtiny45",
.programming_mode = HVSP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xFF,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xFF,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xFF,
},
{ // ATtiny85
.signature = { 0x1E, 0x93, 0x0B },
.mcutype = "ATtiny85",
.programming_mode = HVSP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xFF,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xFF,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xFF,
},
{ // ATtiny2313A
.signature = { 0x1E, 0x91, 0x0A },
.mcutype = "ATtiny2313A",
.programming_mode = HVPP,
.fuseLowBits = 0x64,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0x00,
.HifuseLowBits = 0xE4,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0x00,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0x00,
},
{ // ATmega8A
.signature = { 0x1E, 0x93, 0x07 },
.mcutype = "ATmega8",
.programming_mode = HVPP,
.fuseLowBits = 0xE1,
.fuseHighBits = 0xD9,
.fuseExtendedBits = 0x00, // 0x00 means: no extended fuses written
.HifuseLowBits = 0xE4,
.HifuseHighBits = 0xD9,
.HifuseExtendedBits = 0x00, // 0x00 means: no extended fuses written
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xC9,
.ExfuseExtendedBits = 0x00, // 0x00 means: no extended fuses written
},
{ // ATmega48A
.signature = { 0x1E, 0x92, 0x05 },
.mcutype = "ATmega48",
.programming_mode = HVPP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xFF,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xFF,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xFF,
},
{ // ATmega88
.signature = { 0x1E, 0x93, 0x0A },
.mcutype = "ATmega88",
.programming_mode = HVPP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xF9,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xF9,
.ExfuseLowBits = 0xF7,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xF9,
},
{ // ATmega168
.signature = { 0x1E, 0x94, 0x06 },
.mcutype = "ATmega168",
.programming_mode = HVPP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xDF,
.fuseExtendedBits = 0xF9,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xDF,
.HifuseExtendedBits = 0xF9,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xDF,
.ExfuseExtendedBits = 0xF9,
},
{ // ATmega328P
.signature = { 0x1E, 0x95, 0x0F },
.mcutype = "ATmega328P",
.programming_mode = HVPP,
.fuseLowBits = 0x62,
.fuseHighBits = 0xD9,
.fuseExtendedBits = 0xFF,
.HifuseLowBits = 0xE2,
.HifuseHighBits = 0xD9,
.HifuseExtendedBits = 0xFF,
.ExfuseLowBits = 0xFF,
.ExfuseHighBits = 0xD9,
.ExfuseExtendedBits = 0xFF,
},
// mark end of list
{ { 0,0,0 }, 0, 0, 0 },
};
/*
Data line assignments
Fuse and command data for HVPP mode are sent using Arduino digital lines 0-7
Arduino Uno and other original-style form factor boards:
Digital Line 0-7 outputs = PORTD
Inputs = PIND
Data direction register = DDRD
Arduino Mega - much more complicated because digital lines 0-7 don't map directly to an AVR hardware port:
Digital Line AVR Signal Name
0 PE0 (PORTE)
1 PE1 (PORTE)
2 PE4 (PORTE)
3 PE5 (PORTE)
4 PG5 (PORTG)
5 PE3 (PORTE)
6 PH3 (PORTH)
7 PH4 (PORTH)
*/
// Pin Assignments (you shouldn't need to change these)
#define VCC 12
#define RDY 13 // RDY/!BSY signal from target
#define OE 11
#define WR 10
#define BS1 A2
#define XA0 8
#define XA1 A4
#define RST A0 // 12V Step up converter enable (12V_EN)
#define XTAL1 A3
#define BUTTON A1 // Run button
// Pin assignments for HVSP mode
#define SCI BS1
#define SDO RDY
#define SII XA0
#define SDI XA1
// Commands for HV prog mode, used for both PP and SP, comment shows usage
#define HV_CMD_CHIP_ERASE B10000000 // CMD *WR
#define HV_CMD_WRITE_FUSE B01000000 // CMD DATAL *WR
#define HV_CMD_WRITE_LOCK B00100000 // CMD DATAL *WR
#define HV_CMD_WRITE_FLASH B00010000 // CMD ({ADDRL DATAL DATAH *PG} ADDRH *WR) NOP
#define HV_CMD_WRITE_EEPROM B00010001 // CMD ADDRH {ADDRL DATAL *PG} *WR // HVSP shows {ADDRL ADDRH but other seems ok
#define HV_CMD_READ_SIG B00001000 // CMD ADDRL *RD
#define HV_CMD_READ_FUSE_LOCK B00000100 // CMD *RD
#define HV_CMD_READ_FLASH B00000010 // CMD ADDRH ADDRL *RD // HVSP shows L first but H seems only
#define HV_CMD_READ_EEPROM B00000011 // CMD ADDRH ADDRL *RD // HVSP shows L first but H seems only
// Load type bitmap for HV prog: (0 0 0 0 0) XA1 XA0 BS1
// Used as is for HVSP, HVPP decodes bits to setup ports
#define LOAD_ADDRESS B00000000
#define LOAD_ADDRESS_H B00000001
#define LOAD_DATA B00000010
#define LOAD_DATA_H B00000011
#define LOAD_COMMAND B00000100
#define POST_OP B00000110 // Only used for HVSP
// Post-op bitmap for HV Prog: (0 0 0) BS1 WE OE BS2 PAGEL
// Used as is for HVSP, HVPP decodes bits to setup ports
#define LFUSE_SEL_R B00001000
#define LFUSE_SEL_W B00000100
#define HFUSE_SEL_R B00011010
#define HFUSE_SEL_W B00010100
#define EFUSE_SEL_R B00001010
#define EFUSE_SEL_W B00000110
#define LOCK_SEL_R B00010000
#define SIG_SEL_R B00001000
#define LSB_R B00001000
#define MSB_R B00011000
#define SEL_WR B00000100
#define EE_PAGE_LATCH B00001101
#define FL_PAGE_LATCH B00011101
#define MASK_WE_OE B00001100 // Make Write and Output enable strobes inactive
#define MASK_BS1 B00010000
#define MASK_BS2 B00000010
#define MASK_WE B00001000
#define MASK_OE B00000100
#define MASK_PAGEL B00000001
// Serial instructions for HVSP mode
// Based on the ATtiny85 datasheet Table 20-16 pg. 163-165.
// After analysis serial instructions can be built from the parallel ones
enum result_types {
DONEDEFAULT, SUCCESS, FAILURE };
// Global variables
byte mode; // programming mode
byte efuse_present;
byte tbuffer[64];
byte test_ee[]={0xaa,0x55,0xa5,0x5a}; // Four bytes
byte test_fl[]="AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPp"; // 16 bytes, 8 words. Upper case is MSB
// The function menu (full)
const char* menu_ff[]={
"\n-->Select function:",
"F: Write Custom Fuses",
"D: Write Default Fuses",
"H: Write 8Mhz Fuses",
"Q: Write ext.Quarz Fuses",
"E: Erase",
"R: Read Flash",
"P: Read EEPROM",
"W: Test Flash",
"T: Test EEPROM",
"X: Remove MCU",
0}; // end
// The function menu (short)
// used if mcu device could not be found in internal device table
const char* menu_fs[]={
"\n-->Select function:",
"F: Write Custom Fuses",
"E: Erase",
"R: Read Flash",
"P: Read EEPROM",
"W: Test Flash",
"T: Test EEPROM",
"X: Remove MCU",
0}; // end
byte config[17]; // Used to hold data read from target
#define SIG1 0
#define SIG2 1
#define SIG3 2
#define LFUSE 3
#define HFUSE 4
#define EFUSE 5
#define LOCK 6
#define PGMODE 7
// These pin assignments change depending on which chip is being programmed,
// so they can't be set using #define
// There is probably a more elegant way to do this. Suggestions?
byte PAGEL = A5; // ATtiny2313: PAGEL = BS1
byte BS2 = 9; // ATtiny2313: BS2 = XA1
char no_device_present;
byte hfuse=0xff, lfuse=0xff, efuse=0xff; // desired fuse values from user
char cmd;
int start_addr=0;
//**************************************************************
void setup() { // run once, when the sketch starts
// Initialize most important hardware, the rest waits til after target type is known
pinMode(VCC, OUTPUT); // Target Supply
pinMode(RST, OUTPUT); // Control of DC-DC converter that generates +12V !RESET
digitalWrite(RST, OFF12v); // Turn off 12V, Reset=0
digitalWrite(VCC, LOW); // DJT: OK if no chip inserted
//digitalWrite(VCC, HIGH); // DJT: Alternative if chip inserted already
pinMode(BUTTON, INPUT); // User button to start programming
digitalWrite(BUTTON, HIGH); // turn on internal pullup resistor
Serial.begin(BAUD); // Open serial port, this works on the Mega also because we are using serial port 0
delay(200);
Serial.println("\n*** AVR HV Rescue More v1.0");
target_exit_program_mode(); // be on the safe side....
}
//*** let us Loop ****************************************************************
//********************************************************************************
void loop() { // run over and over again
int device,dev_hvsp,dev_hvpp,g,key, searchon=0;
byte function_result;
// try to read signature
// first using HVSP and -if not successful - using HVPP
// if valid AVR signature found (starting with 0x1E) try to find a match in the internal table
// if no AVR signature found give appropriate error message
// leave while loop only if valid AVR signature found
do { // loop until valid AVR signature found
Serial.print("\n-->Insert Device and press button");
Serial.print("\n will try to find valid AVR device...\n");
Serial.end();
PAGEL = A5; // ATtiny2313: PAGEL = BS1
BS2 = 9; // ATtiny2313: BS2 = XA1
// wait for button press, debounce
while(1) {
while (digitalRead(BUTTON) == HIGH); // wait here until button is pressed
delay(100); // simple debounce routine
if (digitalRead(BUTTON) == LOW) // if the button is still pressed, continue
break; // valid press was detected, continue on with rest of program
}
dev_hvsp=0; // set device type to zero
dev_hvpp=0; // set device type to zero
// Try to read signature with HVSP first
// Put target device into program mode
mode =HVSP;
target_enter_program_mode(mode);
// read and print Signature and Fuses, also establishes device type (variable device)
dev_hvsp=read_Chip_data(); // returns device index (in internal table) --> opens Serial !
device=dev_hvsp; // returns device type
if (dev_hvsp==255) { // no valid AVR signature found using HVSP
// now try HVPP
Serial.end();
mode=HVPP;
target_enter_program_mode(mode);
// read and print Signature and Fuses, also establishes device type (variable device)
dev_hvpp=read_Chip_data(); //
if (dev_hvpp==255) { // no valid AVR signature found using HVPP
// no valid device found, we gave our best and tried HVSP and HVPP
target_exit_program_mode(); // Place after serial io, so Vcc is still on and prevents corruption
// Serial.println("\n\n*** no device found, nothing to do");
}
else device=dev_hvpp;
}
// Open serial port again to print existing values
Serial.begin(BAUD);
if ((dev_hvsp==254) | (dev_hvpp==254)) { // check if one of them resultet in device 254
Serial.print("\n*** AVR device found but is not in table");
Serial.print("\n*** full functionality only if device is defined");
Serial.print("\n*** in internal device table, please extend table");
Serial.print("\n");
}
} while (device==255); // go back to square one... no device found as yet
// we have found a valid AVR device, program-mode is still set to method used in finding the signature
delay (50);
#if (DEBUG == 1)
// prepare defaultfuses, variable device holds device table index
// get default fuses for device
lfuse = pgm_read_byte(&targetCpu[device].fuseLowBits);
hfuse = pgm_read_byte(&targetCpu[device].fuseHighBits);
efuse = pgm_read_byte(&targetCpu[device].fuseExtendedBits);
Serial.println("Data");
Serial.println(lfuse,HEX);
Serial.println(hfuse,HEX);
Serial.println(efuse,HEX);
Serial.println(dev_hvsp);
Serial.println(dev_hvpp);
Serial.println("Dataend");
#endif
// Loop until user selects X on the menu (safely remove device)
// otherwise multiple functions can be run on the recognized device
do // yeah, just do it baby...
{
// display function menu and get function required
// - display full menu if device recognized
// - display short menu if device not found in array (no default fuses available)
// - 254 means: valid AVR signature but no match in table
// - can offer only limited set of functions for set fuses !
print_Chip_data(); // output chip data to serial
if (device==254) {
cmd=*get_choice(menu_fs,0); // display short menu
}
else {
cmd=*get_choice(menu_ff,0); // display full menu
}
if (cmd=='X' ) {
target_exit_program_mode(); // Place after serial io, so Vcc is still on and prevents corruption
Serial.println(" Remove device before continuing");
break;
}
// get additional data based on selected function
get_add_data();
// This business with TXC0 is required because Arduino doesn't give us a means to tell if a serial
// transmission is complete before we move on and do other things. If we don't wait for TXC0 to be reset,
// I found that sometimes the 1st fuse burn would fail. It turns out that DATA1 (which doubles as Arduino serial
// TX) was still toggling by the time the 1st XTAL strobe latches the fuse program command. Bad news.
//UCSR0A |= _BV(TXC0); // Reset serial transmit complete flag (need to do this manually because TX interrupts aren't used by Arduino)
//while(!(UCSR0A & _BV(TXC0))); // Wait for serial transmission to complete before burning fuses!
// The above will no longer work with Arduino 1.0 which buffers transmit data
Serial.flush(); // In Arduino 1.0 this will wait for transmission to finish (prior to that will empty receive buffer so we reply on the delay after serial end to ensure TX finished)
Serial.end(); // We're done with serial comms (for now) so disable UART
delay(200);
target_enter_program_mode(config[PGMODE]);
function_result = run_function(); // do the function
Serial.begin(BAUD); // open serial port
// Print result
Serial.write('\n');
if (function_result==DONEDEFAULT)
Serial.println(" Done"); // Non commital result where functions doesn't check (default)
if (function_result==SUCCESS)
Serial.println(" OK"); // Success
if (function_result==FAILURE)
Serial.println(" Fail"); // Failure
Serial.flush(); // In Arduino 1.0 this will wait for transmission to finish (prior to that will empty receive buffer so we reply on the delay after serial end to ensure TX finished)
Serial.end(); // We're done with serial comms (for now) so disable UART
delay(200);
target_exit_program_mode(); // Place after serial io, so Vcc is still on and prevents corruption
delay(50);
target_enter_program_mode(config[PGMODE]);
// read and print Signature and Fuses again , also establishes device type (variable device)
device=read_Chip_data(); // opens serial again
Serial.begin(BAUD); // Open serial port, this works on the Mega also because we are using serial port 0
} while (1); // loop until user selects X (remove device)
Serial.begin(BAUD); // Open serial port, this works on the Mega also because we are using serial port 0
}
// End of loop *******************************************************
//********************************************************************************
//**************************************************************
void target_dump(const char* title, byte command, int address, int len){
int i=0,n;
n=address;
do{
if (command==HV_CMD_READ_FLASH)
tbuffer[i++]=target_io(command, n, 0, MSB_R); //Byte buffer order MSB-LSB MSB-LSB .....
tbuffer[i++]=target_io(command, n++, 0, LSB_R);
}
while (i> i); // set corresponding bit of response to 1
}
}
return response;
}
#if (MEGA == 1) // functions specifically for the Arduino Mega
//**************************************************************
void mega_data_write(byte data) { // Write a byte to digital lines 0-7
// This is really ugly, thanks to the way that digital lines 0-7 are implemented on the Mega.
PORTE &= ~(_BV(PE0) | _BV(PE1) | _BV(PE4) | _BV(PE5) | _BV(PE3)); // clear bits associated with digital pins 0-1, 2-3, 5
PORTE |= (data & 0x03); // set lower 2 bits corresponding to digital pins 0-1
PORTE |= (data & 0x0C) << 2; // set PORTE bits 4-5, corresponding to digital pins 2-3
PORTE |= (data & 0x20) >> 2; // set PORTE bit 5, corresponding to digital pin 5
DDRE |= (_BV(PE0) | _BV(PE1) | _BV(PE4) | _BV(PE5) | _BV(PE3)); // set bits we are actually using to outputs
PORTG &= ~(_BV(PG5)); // clear bits associated with digital pins 4-5
PORTG |= (data & 0x10) << 1; // set PORTG bit 5, corresponding to digital pin 4
DDRG |= (_BV(PG5)); // set to output
PORTH &= ~(_BV(PH3) | _BV(PH4)); // clear bites associated with digital pins 6-7
PORTH |= (data & 0xC0) >> 3; // set PORTH bits 3-4, corresponding with digital pins 6-7
DDRH |= (_BV(PH3) | _BV(PH4)); // set bits to outputs
}
//**************************************************************
byte mega_data_read(void) { // Read a byte from digital lines 0-7
byte data = 0x00; // initialize to zero
data |= (PINE & 0x03); // set lower 2 bits
data |= (PINE & 0x30) >> 2; // set bits 3-4 from PINE bits 4-5
data |= (PINE & 0x08) << 2; // set bit 5 from PINE bit 3
data |= (PING & 0x20) >> 1; // set bit 4 from PING bit 5
data |= (PINH & 0x18) << 3; // set bits 6-7 from PINH bits 3-4
return data;
}
//**************************************************************
void mega_data_input(void) { // Set digital lines 0-7 to inputs and turn off pullups
PORTE &= ~(_BV(PE0) | _BV(PE1) | _BV(PE4) | _BV(PE5) | _BV(PE3)); // Mega digital pins 0-3, 5
DDRE &= ~(_BV(PE0) | _BV(PE1) | _BV(PE4) | _BV(PE5) | _BV(PE3)); // Set to input
PORTG &= ~(_BV(PG5)); // Mega digital pin 4
DDRG &= ~(_BV(PG5)); // Set to input
PORTH &= ~(_BV(PH3) | _BV(PH4)); // Mega digital pins 6-7
DDRH &= ~(_BV(PH3) | _BV(PH4)); // Set to input
}
#endif
#if (LEONARDO == 1) // functions specifically for the Arduino Leonardo
//**************************************************************
void leo_data_write(byte data) { // Write a byte to digital lines 0-7
// This is equally ugly on Leonardo, thanks to the way that digital lines 0-7 are implemented on the Leonardo.
PORTD &= ~(_BV(PD2) | _BV(PD3) | _BV(PD1) | _BV(PD0) | _BV(PD4) | _BV(PD7)); // clear bits associated with digital pins 0,1,2,3,4,7
PORTD |= (data & 0x03) << 2; // set pins 2-3 corresponding to bits 0,1
PORTD |= (data & 0x04) >> 1; // set PD1 = bit 2
PORTD |= (data & 0x08) >> 3; // set PD0 = bit 3
PORTD |= (data & 0x10); // hooray: bit 4 is PD4 :-)
PORTD |= (data & 0x40) << 1; // and PD7 = bit 6 :-S
DDRD |= (_BV(PD2) | _BV(PD3) | _BV(PD1) | _BV(PD0) | _BV(PD4) | _BV(PD7)); // set bits we are actually using to outputs
PORTC &= ~(_BV(PC6)); // clear bits associated with digital pin 6
PORTC |= (data & 0x20) << 1; // set PORTC bit 6, corresponding to digital pin 5
DDRC |= (_BV(PC6)); // set to output
PORTE &= ~(_BV(PE6) ); // clear bites associated with digital pin 7
PORTE |= (data & 0x80) >> 1; // set PORTE bit 6, corresponding with digital pins 7
DDRE |= (_BV(PE6) ); // set bit to outputs
}
//**************************************************************
byte leo_data_read(void) { // Read a byte from digital lines 0-7
byte data = 0x00; // initialize to zero
data |= (PIND & 0x0C) >> 2; // set bits 0-1 from PIND 2-3
data |= (PIND & 0x02) << 1; // set bit 2 from PIND bit 1
data |= (PIND & 0x01) << 3; // set bit 3 from PIND bit 0
data |= (PIND & 0x10); // set bit 4 from PIND 4
data |= (PINC & 0x40) >> 1; // 5 from PINC 6
data |= (PIND & 0x80) >> 1; // 6 from PIND 7
data |= (PINE & 0x40) << 1; // 7 from PINE 6
return data;
}
//**************************************************************
void leo_data_input(void) { // Set digital lines 0-7 to inputs and turn off pullups
PORTD &= ~(_BV(PD2) | _BV(PD3) | _BV(PD1) | _BV(PD0) | _BV(PD4) | _BV(PD7)); // clear bits associated with digital pins 0,1,2,3,4,7
DDRD &= ~(_BV(PD2) | _BV(PD3) | _BV(PD1) | _BV(PD0) | _BV(PD4) | _BV(PD7)); // set bits we are actually using to inputs
PORTC &= ~(_BV(PC6)); // clear bits associated with digital pin 6
DDRC &= ~(_BV(PC6)); // set to output
PORTE &= ~(_BV(PE6) ); // clear bites associated with digital pins 6-7
DDRE &= ~(_BV(PE6) ); // set bits to outputs
}
#endif
//**************************************************************
void sclk(void) { // send serial clock pulse, used by HVSP commands
// These delays are much longer than the minimum requirements, but we don't really care about speed.
delayMicroseconds(100);
digitalWrite(SCI, HIGH);
delayMicroseconds(100);
digitalWrite(SCI, LOW);
}
//**************************************************************
void strobe_xtal(void) { // strobe xtal (usually to latch data on the bus)
delay(1);
digitalWrite(XTAL1, HIGH); // pulse XTAL to send command to target
delay(1);
digitalWrite(XTAL1, LOW);
}
//**************************************************************
void data_as_input(void) { // Set the data port as an input
#if (MEGA) // Mega uses different ports for same pins, much more complicated setup. yuck!
mega_data_input();
#elif (LEONARDO)
leo_data_input();
#else // Set up data lines on original Arduino
PORTD = 0x00; // clear digital pins 0-7
DDRD = 0x00; // set digital pins 0-7 as inputs for now
#endif
}
//**************************************************************
void data_as_output(byte data){ // Set the data port as an output with data
#if (MEGA)
mega_data_write(data);
#elif (LEONARDO)
leo_data_write(data);
#else // Set up data lines on original Arduino
PORTD = data;
DDRD = 0xFF; // set DATA to outputs
#endif
}
//**************************************************************
byte data_read(void) { // Capture value on data port
byte value;
#if (MEGA)
value = mega_data_read();
#elif (LEONARDO)
value = leo_data_read();
#else // Set up data lines on original Arduino
value = PIND;
#endif
return value;
}
//**************************************************************
byte int2hilo(int n) // Converts an int to HIGH or LOW
{
if (n)
return HIGH;
return LOW;
}
//**************************************************************
byte b2hilo(byte n) // Converts a byte to HIGH or LOW
{
if (n)
return HIGH;
return LOW;
}
//**************************************************************
int hex2dec(byte c) { // converts one HEX character into a number
if (c >= '0' && c <= '9') {
return c - '0';
}
else if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
else if (c >= 'a' && c <= 'f') { // Added to allow lower case hex
return c - 'a' + 10;
}
return -1;
}
// Simple printf substitute
//**************************************************************
void printf(const char* format, byte* param){ // Format string usings % to print 2 digit hex from param array
char c;
while ((bool)(c=*format++)){
if (c=='%'){
if (*param<0x10)
Serial.print("0");
Serial.print(*param++, HEX);
}
else
Serial.write(c);
}
}
//**************************************************************
void print_fixed_hex(int value){ // Prints a hex value as two fixed digits
//Serial.print(" ");
if (value<0x10)
Serial.print("0");
Serial.print(value, HEX);
}
// Get hex value from the user, handles upper and lower case & errors. Optional 0x prefix ignored
//**************************************************************
int get_value(const char* prompt, int def ) { // def parameter not yet used, could provide default input
int incomingByte;
int n,m,v=0;
byte err;
// Prompt
Serial.print(prompt);
do {
delay(100);
while (Serial.available()) // Clear buffer of any input ie flush
Serial.read();
// Serial.flush(); // Not supported in Arduino 1.0
Serial.print(" ?");
// Wait for input
do
n=Serial.available();
while (n==0);
// Wait for input to finish (timeout) . Allow variable length input without terminator
do {
delay(100);
m=n;
n=Serial.available();
}
while (m!=n);
// Process characters recieved
err=0; //Assume no errors in input
while (!err && ((incomingByte = Serial.read()) != -1)){
if (incomingByte=='\n' )
break;
if (incomingByte=='\r' )
break;
if ((v==0) && (incomingByte=='0') && (Serial.peek()=='x'))
incomingByte = Serial.read(); // Ignore 0x prefix at start only
else if (hex2dec(incomingByte)==-1)
err=1;
else
v=v*16 + hex2dec(incomingByte);
}
}
while(err); // Repeat until no errors
Serial.println(v, HEX); // echo value back to the user
return v;
}
// Print a simply menu and get user selection of option
// options = Array of strings, [0] is the intro, others prompt with selection char first
//**************************************************************
const char* get_choice(const char* options[], char auto_cmd){
int i=0,j,k;
char c;
Serial.println(options[0]); // First item is Menu Title
while(options[++i]!=0){
Serial.write(' '); // Indent
Serial.println(options[i]); // Print each option
}
k=0; // Will be set to index of option selected
do {
while (Serial.available()) // Clear buffer of any imput ie flush
Serial.read();
// Serial.flush(); // Not supported in Arduino 1.0
if (k==0)
Serial.write('?'); // Prompt the user if no choice made yet
if (auto_cmd){
c=auto_cmd; // Use the auto input
auto_cmd=0; // but only first time
}
else {
while (Serial.available() == 0); // wait for a character to come in
c=toUpperCase(Serial.read());
}
// Search to see if entry valid
for (j=1;jCurrent Chip Data");
printf("\n Signature: % % %", &config[SIG1]);
Serial.print(" Device: ");
if (device<254) // AVR device found in table
Serial.print( (PGM_P)pgm_read_word(&targetCpu[device].mcutype));
else Serial.print("Device not defined");
if (efuse_present) //
printf("\n Fuses Lo/Hi/Ex: % % %", &config[LFUSE]);
else
printf("\n Fuses Lo/Hi: % %", &config[LFUSE]);
printf("\n Lock Bits: %", &config[LOCK]);
Serial.print("\n");
}
// run function according to function selected by user
//**************************************************************
int run_function() {
int i;
byte result=DONEDEFAULT; // Default response to 'done';
// Now run selected function
switch(cmd){
case 'E': // Chip Erase
target_io(HV_CMD_CHIP_ERASE,0,0,SEL_WR); // Perform a chip erase
break;
case 'F': // Write fuses with user values
case 'D': // Write default fuses
case 'H': // Write 8 Mhz fuses
case 'Q': // Write fuses ext. crystal
target_io(HV_CMD_WRITE_FUSE,0,lfuse,LFUSE_SEL_W);
target_io(HV_CMD_WRITE_FUSE,0,hfuse,HFUSE_SEL_W);
if (efuse_present)
target_io(HV_CMD_WRITE_FUSE, 0, efuse, EFUSE_SEL_W);
// Read back fuse contents to verify burn worked
config[LFUSE] = target_io(HV_CMD_READ_FUSE_LOCK,0,0,LFUSE_SEL_R);
config[HFUSE] = target_io(HV_CMD_READ_FUSE_LOCK,0,0,HFUSE_SEL_R);
if (efuse_present)
config[EFUSE] = target_io(HV_CMD_READ_FUSE_LOCK,0,0,EFUSE_SEL_R);
// Check result
if (config[LFUSE]!=lfuse || config[HFUSE]!=hfuse || ( efuse_present && (config[EFUSE]!=efuse)))
result=FAILURE;
else
result=SUCCESS;
break;
case 'R': // Read and print block of FLASH
for (i=0;i<64;i+=8)
target_dump("FLASH", HV_CMD_READ_FLASH, start_addr+i, 16); // Read 16 bytes = 8 words
break;
case 'P': // Read and print block of EEPROM
for (i=0;i<128;i+=16)
target_dump("EEPROM", HV_CMD_READ_EEPROM, start_addr+i, 16);
break;
case 'W': // Test write FLASH
target_page_write(HV_CMD_WRITE_FLASH, start_addr, test_fl, 16); // Write 16 bytes = 8 words, must be equal or less than device page size. Arduino ram limits prevent us writing a whole page for now
result=SUCCESS; // Assume it worked unless test show otherwise
for (i=0;i<8;i++){
if ((test_fl[i*2]*256+test_fl[i*2+1])!=(target_io(HV_CMD_READ_FLASH, start_addr+i, 0, MSB_R)*256+target_io(HV_CMD_READ_FLASH, start_addr+i, 0, LSB_R)))
result=FAILURE;
}
break;
case 'T': // Test write EEPROM
target_page_write(HV_CMD_WRITE_EEPROM, start_addr, test_ee, 4); // Use EEPROM page size of 4 (Correct for all AVR we are likely to see
result=SUCCESS; // Assume it worked unless test show otherwise
for (i=0;i<4;i++){
if (test_ee[i]!=target_io(HV_CMD_READ_EEPROM, start_addr+i, 0, LSB_R))
result=FAILURE;
}
break;
}
return (result);
}
// get additional data from user, according to function selected
//**************************************************************
void get_add_data(){
// Get Additional data, according to selected function
if (cmd=='R' || cmd=='P' || cmd=='T'|| cmd=='W')
start_addr=get_value("Address",0x00);
else if (cmd=='F'){
// Ask the user what fuse values should be burned to the target
lfuse = get_value("LFUSE value",0x62);
hfuse = get_value("HFUSE value",0xdf);
if (efuse_present)
efuse = get_value("EFUSE value",0xff);
}
else if (cmd=='D') {
// get default fuses for device
lfuse = pgm_read_byte(&targetCpu[device].fuseLowBits);
hfuse = pgm_read_byte(&targetCpu[device].fuseHighBits);
efuse = pgm_read_byte(&targetCpu[device].fuseExtendedBits);
#if (DEBUG == 1)
Serial.print("\n Write Default Fuses: ");
Serial.print(lfuse,HEX);
Serial.print(" ");
Serial.print(hfuse,HEX);
Serial.print(" ");
Serial.println(efuse,HEX);
#endif
}
else if (cmd=='H') {
// get 8 Mhz fuses for device
lfuse = pgm_read_byte(&targetCpu[device].HifuseLowBits);
hfuse = pgm_read_byte(&targetCpu[device].HifuseHighBits);
efuse = pgm_read_byte(&targetCpu[device].HifuseExtendedBits);
#if (DEBUG == 1)
Serial.print("\n Write 8MHz Fuses: ");
Serial.print(lfuse,HEX);
Serial.print(" ");
Serial.print(hfuse,HEX);
Serial.print(" ");
Serial.println(efuse,HEX);
#endif
}
else if (cmd=='Q') {
// get fuses for ext. crystal for device
lfuse = pgm_read_byte(&targetCpu[device].ExfuseLowBits);
hfuse = pgm_read_byte(&targetCpu[device].ExfuseHighBits);
efuse = pgm_read_byte(&targetCpu[device].ExfuseExtendedBits);
#if (DEBUG == 1)
Serial.print("\n Write ext. crystal Fuses: ");
Serial.print(lfuse,HEX);
Serial.print(" ");
Serial.print(hfuse,HEX);
Serial.print(" ");
Serial.println(efuse,HEX);
#endif
}
}
// ******************************************************************
// End of program
// ******************************************************************