Arduino rettet verfuste ATtinys

von Ralf Beesner, DK5BU




Wenn ich mit 8-poligen ATtinys herumbastele, benutze ich gern die kleine Platine aus dem LP Mikrocontroller und flashe die Chips mit Burkhard Kainkas LPmicroISP.exe oder mit AVRDude (in der avrdude.conf habe ich einen Eintrag für die LP-Mikrocontroller-Hardware hinzugefügt).

Leider taugt die serielle Schnittstelle meines Notebooks nicht viel; statt +/-8 macht sie nur knapp +/-5 V, und gelegentlich führt das dazu, dass ein kritisches Bit "umkippt" und der Attiny dann so verfust wird, dass er nicht mehr per ISP- Programmierung zugänglich ist.

Man kann ihn dann zwar noch per High Voltage Programming retten (das ist ein weiterer Progammiermodus, der anders als SPI abläuft und durch das Anlegen von +12 V an den Reset-Pin eingeleitet wird; Details kann man im ATtiny13-Datenblatt nachschlagen). Der bekannteste High-Voltage-fähige Programmer ist das STK500, das etwa 80 Euro kostet. Es gibt auch einige andere HV-Programmer; die Hardware ist aber bei allen recht aufwendig und man muss schon zahlreiche Controller verfusen, damit sich die Anschaffung lohnt. Nach dem halben Dutzend ATtiny13, die ich schon verfust und weggeworfen habe, traf es nun zwei ATtiny45, die ja etwas teurer sind, und nun begann es mich doch zu ärgern.


Ich habe daher auf der LP-Mikrocontroller-Platine die beiden 100-kOhm-Widerstände endlich mal auf 22 kOhm verkleinert (wie es Burkhard mal für seinen PingPong-Programmer empfohlen hat), um die Übertragung stabiler zu machen.

Und ich habe im Internet einen einfachen HV-Programmierzusatz für den Arduino gefunden. Das zugehörige Programm ist kein vollwertiger HV-Programmer, sondern schreibt lediglich die Default-Fuses in den ATtiny, so dass er wieder per ISP zugänglich ist. Der Link lautet:
www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/

Damit konnte ich die beiden ATtiny45 retten. Einen verfusten ATtiny13 hatte ich nicht zur Hand, aber ein "heiler" Attiny, der auf 4,8 MHz (ohne Vorteilung) gefust war, wurde erfolgreich auf die Default-Werte (9,6 MHz; Teilerfaktor 8) zurückgesetzt.


Hier nun die Details:

Der zu rettende ATtiny wird über 4 Schutzwiderstände an die Arduino-Pins 8-12 (das sind die Ports PB0 ... PB4) gelegt. Der Reset-Pin des ATtiny wird über einen Vorwiderstand an extern zuzuführende +12 V gelegt. Ein Schalttransistor zieht den Reset gegen Masse, wenn an Pin 13 (PB5) des Arduino ein "high" liegt.Die Widerstände haben bis auf eine Ausnahme 1 kOhm; R5 hat 10 kOhm.

Mit dem beigefügten Programm habe ich mich nicht nicht näher beschäftigt, sondern nur die Default-Fusebytes für ATtiny25/45/85 durch die für den ATtiny13 ersetzt.


Ablauf der "Rettungsaktion":

Die Tochterplatine ("Shield") mit dem verfusten Attiny wird an den Arduino gesteckt, dann werden USB und die externen 12V angeschlossen (erst Masse, dann Plus, damit es keine unliebsamen Effekte gibt).

Die Arduino-Entwicklungsumgebung wird gestartet, die korrekte virtuelle serielle Schnittstelle gewählt und das Programm in den Arduino geflasht.

Danach wird das Terminal ("Serial Monitor") der Entwicklungsumgebung gestartet. Das Programm schreibt "Hello?" als Bereitschaftsmeldung, und wenn man ein Byte, z.B. eine "1", an den Arduiono sendet, liest er die vorhandenen Fusebytes aus, schreibt die Defaultwerte in den ATtiny und liest sie zur Kontrolle nochmals aus. Und schon ist der Attiny gerettet ...

 Download: hvprogT13.zip


// AVR High-voltage Serial Programmer
// Originally created by Paul Willoughby 03/20/2010
// http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
// Inspired by Jeff Keyzer http://mightyohm.com
// Serial Programming routines from ATtiny25/45/85 datasheet
// ATtiny25/45/85 default fuses replaced by ATtiny 13 default fuses


// Desired fuse configuration

// #define HFUSE 0xDF //this were the defaults for ATtiny 25/45/85
// #define LFUSE 0x62

#define HFUSE 0xFF // Defaults for ATtiny13
#define LFUSE 0x6A




#define RST 13 // Output to level shifter for !RESET from transistor to Pin 1
#define CLKOUT 12 // Connect to Serial Clock Input (SCI) Pin 2
#define DATAIN 11 // Connect to Serial Data Output (SDO) Pin 7
#define INSTOUT 10 // Connect to Serial Instruction Input (SII) Pin 6
#define DATAOUT 9 // Connect to Serial Data Input (SDI) Pin 5
#define VCC 8 // Connect to VCC Pin 8




int inByte = 0; // incoming serial byte Computer
int inData = 0; // incoming serial byte AVR

void setup()
{
// Set up control lines for HV parallel programming
pinMode(VCC, OUTPUT);
pinMode(RST, OUTPUT);
pinMode(DATAOUT, OUTPUT);
pinMode(INSTOUT, OUTPUT);
pinMode(CLKOUT, OUTPUT);
pinMode(DATAIN, OUTPUT); // configured as input when in programming mode

// Initialize output pins as needed
digitalWrite(RST, HIGH); // Level shifter is inverting, this shuts off 12V

// start serial port at 9600 bps:
Serial.begin(9600);

establishContact(); // send a byte to establish contact until receiver responds

}


void loop()
{
// if we get a valid byte, run:
if (Serial.available() > 0) {
// get incoming byte:
inByte = Serial.read();
Serial.println(byte(inByte));
Serial.println("Entering programming Mode\n");

// Initialize pins to enter programming mode
pinMode(DATAIN, OUTPUT); //Temporary
digitalWrite(DATAOUT, LOW);
digitalWrite(INSTOUT, LOW);
digitalWrite(DATAIN, LOW);
digitalWrite(RST, HIGH); // Level shifter is inverting, this shuts off 12V

// Enter High-voltage Serial programming mode
digitalWrite(VCC, HIGH); // Apply VCC to start programming process
delayMicroseconds(20);
digitalWrite(RST, LOW); //Turn on 12v
delayMicroseconds(10);
pinMode(DATAIN, INPUT); //Release DATAIN
delayMicroseconds(300);

//Programming mode

readFuses();

//Write hfuse
Serial.println("Writing hfuse");
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x40, 0x4C);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, HFUSE, 0x2C);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x74);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7C);

//Write lfuse
Serial.println("Writing lfuse\n");
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x40, 0x4C);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, LFUSE, 0x2C);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x64);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6C);

readFuses();

Serial.println("Exiting programming Mode\n");
digitalWrite(CLKOUT, LOW);
digitalWrite(VCC, LOW);
digitalWrite(RST, HIGH); //Turn off 12v
}
}


void establishContact() {
while (Serial.available() <= 0) {
Serial.println("Hello?"); // send an initial string
delay(1000);
}
}

int shiftOut2(uint8_t dataPin, uint8_t dataPin1, uint8_t clockPin, uint8_t bitOrder, byte val, byte val1)
{
int i;
int inBits = 0;
//Wait until DATAIN goes high
while (!digitalRead(DATAIN));

//Start bit
digitalWrite(DATAOUT, LOW);
digitalWrite(INSTOUT, LOW);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);

for (i = 0; i < 8; i++) {

if (bitOrder == LSBFIRST) {
digitalWrite(dataPin, !!(val & (1 << i)));
digitalWrite(dataPin1, !!(val1 & (1 << i)));
}
else {
digitalWrite(dataPin, !!(val & (1 << (7 - i))));
digitalWrite(dataPin1, !!(val1 & (1 << (7 - i))));
}
inBits <<=1;
inBits |= digitalRead(DATAIN);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);

}


//End bits
digitalWrite(DATAOUT, LOW);
digitalWrite(INSTOUT, LOW);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);

return inBits;
}

void readFuses(){
//Read lfuse
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x68);
inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6C);
Serial.print("lfuse reads as ");
Serial.println(inData, HEX);

//Read hfuse
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7A);
inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x7E);
Serial.print("hfuse reads as ");
Serial.println(inData, HEX);

//Read efuse
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x04, 0x4C);
shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6A);
inData = shiftOut2(DATAOUT, INSTOUT, CLKOUT, MSBFIRST, 0x00, 0x6E);
Serial.print("efuse reads as ");
Serial.println(inData, HEX);
Serial.println();
}


Fuse-Retter im Steckboard-Aufbau



Aus aktuellem Anlass habe ich mir den Fuse-Retter von Fabian aufbauen lassen. Er hat es mit einem Steckboard gemacht und die 12-V-Versorgung vom Arduino verwendet. Man muss jetzt USB anschließen und zusätzlich ein 12-V-Steckernetzteil. Der Arduino erwartet ein Startkommando vom PC, dann wird gefust. Das Programm verwendet die Werte FF und 6A für Hi und Lo. Zurückgelesen wird allerdings FF und 62.  Warum das so ist konnte noch nicht geklärt werden. Mit dem STK500 kontrolliert, FF und 62 stimmt. Der Unterschied ist marginal und betrifft nur die Startup-Time nach Reset, 6A braucht 64 ms länger als F2.

Der aktuelle Anlass war übrigens der Sparrow. Die vielen Versuche haben zwei Controller ins Nirwana geschickt. Reset wurde offensichtlich abgeschaltet und als PB5 eingerichtet. Das ist leicht zu messen, denn man findet am Reset-Pin keine Spannung mehr. Das ist dann mit normaler  ISP-Programmierung nicht mehr zu retten, da hilft nur noch High Voltage. Mit dem Arduino-Fuseretter klappt das ohne Probleme.




zurück