How To Unbrick Your ATtiny:
What to do when your ATtiny seems DEAD - it is probably not - find out
how to fix it here. Full walk though of how to save it!
How to Unbrick ATtiny:
You just tried to program your ATtiny and get the message...
"Device Signature is Invalid"
Arrrrggggghhh... Time to panic!!!!...
But hold on, don't bin the chip...
How To Unbrick it: The first thing is don't panic, because you
can accidentally brick an ATtiny during normal use - it just happens
sometimes especially on a breadboard - maybe the power fluctuated and erased the chip ID bytes - You can solve it here.
However, I am
assuming that you have not applied ridiculous voltages to any pins (or
released the magic smoke by putting the power supply backwards - in
which case it is not bricked - it is dead). Only the programming pin
(nRESET) can take 12V, the rest are 5V
tolerant.
On this page you can find out how to convert an Arduino programmer into a
high volt programmer by using a three extra components and a 12V
supply - and you need the High Volt Programmer to unbrick the ATtiny.
The actual message from the Arduino IDE will be something like:
avrdude: Device signature = 0x001fff
avrdude: Expected signature for ATtiny85 is 1E 93 0B
Double check chip, or use -F to override this check.
Note: An especially easy way to lose the device signature is to lift a chip from solderless breadboard while the power is on!
What is bricking?
Bricking is the inability of your programmer to communicate with the
chip. The most common scenarios are setting of an external crystal that
you don't have. When you do this the clock required for normal operation
is non existent so the normal SPI interface is unusable. Consequently
the Arduino IDE can not communicate with the chip i.e. it is bricked.
You can also brick it by accident when the device signature becomes
unreadable and you get a "device type not found" message from the IDE.
This seems to happen when there is a glitch during programming.
How is it bricked?
There are a few different ATtiny brick situations:
You entered a fuse setting that requires and external clock.
You entered a fuse setting that reduced the clock to 128kHz.
Device signature is lost.
Of the three, the last one is the most troublesome! You get the message from the Arduino IDE:
"Device signature is invalid"
...and you cannot proceed any further!
For this discussion I am using an ATtiny85, which at one point I had
thought totally lost since I had the dreaded "Device Signature Not
Found" message. It feels like you have done something wrong - probably not!
I did get it going again, but nearly considered giving up and buying a
new one, but carried on with quite a lot of internet searching for the
solution! If you get the message "invalid device signature" you can recover the chip using the method below.
How to Get ATtiny 128kHz Working
If you chose the internal watchdog timer as a clock source resulting in 128kHz operation, you might be surprised to end up with a bricked chip!
Digging around on the internet reveals very little information, so it
appears that most just give up and use 1MHZ clock. There are a few
posts alluding to the problem and all use the AVRdude and associated
tool chain to recover the situation.
You could spend a couple of hours installing the stuff and yes it
would get the chip going, but the Arduino IDE is extremely easy to use
so I decided to carry on searching.
The clue from avrdude posts is that the option -B250 allows
communication with the chip to resume and set the fuses. The B option is
for the bit rate - that is the problem, that the Arduino as ISP is now
sending data too fast for the slow 128kHz clock to cope with. So in fact
the ATtiny is not bricked, it is purely a speed programming problem and
therefore can be fixed in software.
Another clue is " a version of software on github " allows you to reset the fuses (but only the fuses) by using the Arduino IDE alone (with the LOW_SPEED definition active).
The really interesting thing is that the original ISP programmer (not high volt) accessed from the Arduino IDE:
Menu > File > Examples > 11.ArduinoISP >ArduinoISP
...has the following code (starting at line 45):
// 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 @ 1 MHz, is a reasonable default:
#define SPI_CLOCK (1000000/6)
So in fact the solution has been there all the time!! All you have to do is comment out the last line, and uncomment this one:
// #define
SPI_CLOCK
(128000/6)
You don't need to save the file as the current screen code is used as the
source to be compiled - when you next load it the programmer will
default to higher speed operation.
This action makes the programmer slow, so programs are going to take
longer to load but at least you can program the ATtiny in 128kHz mode.
Warning: The 128kHz clock makes the programming process slow but it works!
Since the sketch used is not saveable, as it is one of the fixed
examples, shutting down the IDE and re-loading it, will re-load the fast
version (or just edit it back).
Unbrick ATtiny Fuses
A note on fuse setting
When you use a library that lets you program different fuse values, it is possible to select an option that is
invalid for you e.g. you don't have the crystal. Here's the options
from the Spence Konde library for the ATtiny85.
Warning: The following fuses are programmed using the normal Arduino IDE. Program in the wrong setting, and you can't go back (easily).
It is easy to select the wrong fuse (or just try it for fun) and then
realise afterwards that you can't go back; to reprogram it requires an
operational clock when using the Arduino as AVRISP (or use the HVP shown below).
You can see that there are a lot of external crystal options to set
the clock source and all of these options are setting the internal (so
called) fuses in the chip - So on, start up, the chip is
going to use what you specified as the clock source!
If you program in one of these (external clock) options and don't
have the crystal, you can't use the Arduino IDE alone to recover it.
If you want to keep the chip going (and don't have a crystal available then use only internal or 'PLL, tweaked' clock options).
You will however think that the chip is bricked if you use the 128kHz (internal WDT) option - see here for the solution to this.
Three Unbrick ATtiny solutions:
Find the crystal to match the specified setting in the above table and use it.
Feed in an external clock (matching the programmed settings) to CLKI.
Use a high volt programmer for recovery (HVP).
I am not going over the first unbrick ATtiny two solutions here, as they are obvious
and they can not get rid of the dreaded "device signature not found"
situation,
whereas solution 3 can do so.
Note: All 'brick' problems are solved using a High Voltage Programmer.
Unbrick ATtiny Hardware
Convert (LVP) programmer to HVP
While using a normal Arduino IDE as an ISP programmer (or LVP Low Voltage Programmer) I had a look at
designs for a high voltage programmer and realised that there are
minimal extra connections required to turn the normal programmer into an
HVP programmer.
Al that really happens is that most connections are labelled
differently (i.e. they are used in a conceptually different way but are
still the same physical connections). This means if you use an Arduino
Uno (or Nano and probably others) you only need to add minimal components (3) and a 12V power supply:
ATtiny85 pinout
The ATtiny85 pinout is stated as:
..but for high volt programming the pin out changes to:
HVP programmer interface
This is taken from the ATmega328P datasheet (chapter 20.6).
Where these are the labels:
SDI - Serial Data Input.
SII - Serial Instruction Input.
SDO - Serial Instruction Output.
SCI - Serial Clock Input (min 220ns period).
The above labels do not appear on the main ATtiny85 pin out. When pin 1
(nRESET) is set at 12V then the HVP programming pins are used as above.
HVP circuit diagram
The circuit needed is from the writers of the HVP program that you can find here:
The following table assumes starting from the AVR
programmer pins (SPI interface - LVP) and assesses what needs to be changed to
get to the HVP programmer interface.
Attiny programmer (LVP)
Pin Usage
(LVP originally)
HVP Alt Name
Changes LVP to HVP
Arduino Uno pin
ATtiny85 chip pin [pin use]
13 - SCK
7 [PB2] - SCK - White
SDO
KEEP
12 - MISO
6 [PB1] - MISO - Purple
SII
KEEP
11 - MOSI
5 [PB0] - MOSI - Grey
SDI
KEEP
10 - RESET
1 [RESET] - RESET - Blue
RST
MODIFYTran-B=10 (via 1k), Tran-C = 12V (via 1k)
and to pin 1
(ATtiny85), Tran-E = GND.
GND
GND - Green
KEEP
VCC
VCC - Red
KEEP
5 - Digital I/O
2 [PB3] - CLOCK (HVP only) - Orange
SCI
NEW
The table shows that five connections remain the same, one
new connection is added (Uno pin 9), and a transistor is required to control the
high voltage applied to the reset pin (Uno pin 10).
Additional components required:
12V PSU.
2 off 1k.
1 off NPN transistor (general purpose).
Wire from pin 6 (Arduino) to pin2 (ATtiny85).
The biggest problem is the 12V PSU - I use a Lab bench supply but
you could use a simple 7812 device to drop down a laptop power supply.
Or use a boost circuit using a switch mode chip.
Wiring diagrams & Software
There are three wiring diagrams shown below.
Original LVP connections
The first shows the original connections used for LVP -
this is the SPI interface that can program a bootloader into an Arduino
chip.
HVP connections
The second shows adding a new connection (pin 9) and a transistor to control the 12V supply to the reset pin.
Easy transition back to LVP connections
This diagram temporarily moves connections back to allow LVP programming - you will probably need HVP again!
Note this wiring diagram is shown at the end of the page.
Original LVP diagram
Convert AVRISP to HVP Wiring
The following diagram shows you how to change an ATtiny low volt programmer into an HVP (High Volt Programmer) by adding a few components. You do this when you need to reset ATtiny fuses.
There are 2 more 1k resistors, a general purpose NPN transistor, a new wire connection and a 12V power supply.
pin 9 : Uno as AVRISP LED status op: heartbeat.
pin 8 : Uno as AVRISP LED status op: ERROR (Flash=Err in fuse resetter)
pin 7 : Uno as AVRISP LED status op: Prog active.
pin 6 : hvp button input start push to GND.
Note: The NPN transistor pins from left to right are EBC. This circuit is adapter from the high volt schematic here
- pins have changed (from the Arduino) and 4 resistors are removed
(pins 12~9 - they were only for protection not circuit function).
The transistor is an inverting 12V switch. Zero volts at the base
gives 12V at the collector and five volts at the base gives 0V at the
collector.
Detailed operation
(click to show)
The transistor's collector connects to pin 1 of ATtiny85 which also
connects to the 12V PSU via a 1k resistor. When inactive (0V at the base
of the transistor), the transistor is off and pin 1 (ATtiny85) is
pulled high to 12V through the 1k resistor.
When the transistor's base is pulled high (5V) current flows from
Collector to the Emitter, pulling down the pin 1 (ATtiny85) voltage to
0V via the transistor's emitter pin (left). The base resistor limits the
current into the base.
The HVP can therefore control the 12V signal at pin 1 (ATtiny85) to allow (12V) or disable (0V) HVP programming.
The base of the transistor is controlled by pin 13 of the Uno (yellow
wire) and is the same reset signal as for the Low Volt programmer
although the sense of the signal is inverted for the HVP programmer
because the transistor inverts the logic.
Software - HVP Fuses and more...
The following program checks the ATtiny signature and allows you to
perform various functions based on key input (one of them being an
ATtiny fuse reset):
/*
* HVProgrammer.cpp unbrick ATtiny
*/
#include <Arduino.h>
// AVR High-voltage Serial Fuse Reprogrammer
// Adapted from code and design by Paul Willoughby 03/20/2010
// http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/ - link not more valid
// https://www.electronics-lab.com/recover-bricked-attiny-using-arduino-as-high-voltage-programmer/
// Fuse Calc:
// http://www.engbedded.com/fusecalc/
// Restores the default fuse settings of the ATtiny and erases flash memory to restore
// lock bits to their default unlocked state
// Fuses can then easily be changed with the programmer you use for uploading your program.
// Modified for easy use with Nano board on a breadboard by Armin Joachimsmeyer - 3/2018
// - Added option to press button instead of sending character to start programming
// - Improved serial output information
// - After programming the internal LED blinks
// - Added timeout for reading data
// Modified to add Lock bits processing - bWildered1 6/2019
// - read and report lock bits status
// - device memory erase to restore lock bits to their default unlocked state
// JFM tronicsbench.com - unbrick ATtiny
// Change pin control for minimal change between LVP and HVP 28/9/21
// Stop too much screen info.
// Help is now on Query character '?'.
// Add user values for setting fuses fXX,XX,XX also fXX,XX (ATtiny13).
// Modified serial input for user command mode - i.e. not active all the time
//
#define VERSION "3.4.1"
//#define SERIAL_BAUDRATE 19200
#define SERIAL_BAUDRATE 115200
#define START_BUTTON_PIN 6 // connect a button to ground
#define READING_TIMEOUT_MILLIS 300 // for each shiftOut -> effective timeout is 4 times ore more this single timeout
/* Original pins
#define RST A4 // Output to level shifter for !RESET from transistor
#define SCI A5 // Target Clock Input
#define SDO 5 // Target Data Output
#define SII 4 // Target Instruction Input
#define SDI 3 // Target Data Input
#define VCC 2 // Target VCC
*/
/*
* Alternate pin layout
*/
/*
#define RST 13 // Output to level shifter for !RESET from transistor
#define SCI 12 // Target Clock Input
#define SDO 11 // Target Data Output
#define SII 10 // Target Instruction Input
#define SDI 9 // Target Data Input
#define VCC 8 // Target VCC
*/
/*
* Alternate pin layout JFM for LVP and HVP minimal changes on Arduino Uno
*/
#define RST 10 // Output to level shifter for !RESET from transistor
#define SCI 5 // Target Clock Input
#define SDO 13 // Target Data Output
#define SII 12 // Target Instruction Input
#define SDI 11 // Target Data Input
#define VCC 4 // Target VCC
// Address of the fuses
#define HFUSE 0x747C
#define LFUSE 0x646C
#define EFUSE 0x666E
// Define ATTiny series signatures
#define ATTINY13 0x9007 // L: 0x6A, H: 0xFF 8 pin
#define ATTINY24 0x910B // L: 0x62, H: 0xDF, E: 0xFF 14 pin
#define ATTINY25 0x9108 // L: 0x62, H: 0xDF, E: 0xFF 8 pin
#define ATTINY44 0x9207 // L: 0x62, H: 0xDF, E: 0xFF 14 pin
#define ATTINY45 0x9206 // L: 0x62, H: 0xDF, E: 0xFF 8 pin
#define ATTINY84 0x930C // L: 0x62, H: 0xDF, E: 0xFF 14 pin
#define ATTINY85 0x930B // L: 0x62, H: 0xDF, E: 0xFF 8 pin
#define DEVICE_UNKNOWN 0
#define DEVICE_ATTINY13 1
#define DEVICE_ATTINY24_TO_85 2
uint16_t readSignature();
uint8_t checkAndPrintSignature(uint16_t aSignature);
void writeFuse(uint16_t aFuseAddress, uint8_t aFuseValue);
void readFuses();
void eraseFlashAndLockBits();
bool readLockBits();
void printHelp(void) {
Serial.println();
Serial.println();
Serial.println(F("Enter 'r' to only read fuses and lock bits."));
Serial.println(F("Enter 'e' to erase flash and reset lock bits."));
Serial.println(F("Enter 'w' to write fuses to default."));
Serial.println(F("Enter 'f' Write User fuse fxx,xx,xx 3 hex bytes (can use 2 for ATtiny13).")); // JFM
Serial.println(F(" !!! 'w' will erase flash if lock bits are set, since otherwise fuses can not be overwritten!!!"));
Serial.println(F("Enter '?' to show this help"));
Serial.println();
}
void setup() {
Serial.begin(SERIAL_BAUDRATE);
#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL) || defined(ARDUINO_attiny3217)
delay(2000); // To be able to connect Serial monitor after reset or power up and before first printout
#endif
// Just to know which program is running on my Arduino
Serial.println(F("START " __FILE__ "\r\nVersion " VERSION " from " __DATE__));
delay(200);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(VCC, OUTPUT);
pinMode(RST, OUTPUT);
pinMode(SDI, OUTPUT);
pinMode(SII, OUTPUT);
pinMode(SCI, OUTPUT);
pinMode(SDO, OUTPUT); // Configured as input when in programming mode
digitalWrite(RST, HIGH); // Level shifter is inverting, this shuts off 12 volt
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
digitalWrite(LED_BUILTIN, LOW);
pinMode(START_BUTTON_PIN, INPUT_PULLUP);
printHelp();
}
void dump(char *ch, int n) {
int c = 0;
Serial.print("\n");
for (int i=0;i=32 ) Serial.print((*ch) ); else Serial.print('?'); //non print char
Serial.print(' ');
ch++;
if (c++==8) {Serial.print("\n"); c=0; }
}
Serial.print("\n");
}
/////////////////////////////////////////////
// returns 0 if no data else !=0 with data.
static char rxBuf[15];
byte rxSerialData(void) {
static char *p=rxBuf;
char ch='\0';
rxBuf[0]= '\0';
// Read command (also consumes trailing \n).
while (ch!='\n') { // Wait for end of input
while (ch!='\n' && Serial.available() > 0) {
ch = Serial.read();
if (p-rxBuf>=15) { // Keep p in the buffer,
rxBuf[14]='\0';
p=rxBuf;
}
*p++ = ch;
// Serial.print(">> "); Serial.println(ch!='\n' ? ch : '*');
// Fill buffer untill newline (exit in while condition).
if (ch=='\n') {
*--p='\0'; // erase \n
p=rxBuf;
}
} // Serial available
}
return rxBuf[0]; // Same as if (rxBuf[0]=='\0') return 0; else return 1;
}
void startProgramming(void) {
// signal start of programming
digitalWrite(LED_BUILTIN, HIGH);
pinMode(SDO, OUTPUT); // Set SDO to output
digitalWrite(SDI, LOW);
digitalWrite(SII, LOW);
digitalWrite(SDO, LOW);
//Switch on VCC and 12 volt
digitalWrite(RST, HIGH); // 12 V Off
digitalWrite(VCC, HIGH); // Vcc On
delayMicroseconds(20);
digitalWrite(RST, LOW); // 12 V On
delayMicroseconds(10);
pinMode(SDO, INPUT); // Set SDO to input
delayMicroseconds(300);
}
void endProgramming(void) {
// Switch off VCC and 12 volt
digitalWrite(SCI, LOW);
digitalWrite(VCC, LOW); // Vcc Off
digitalWrite(RST, HIGH); // 12v Off
}
void loop() {
char *buf=rxBuf, tReceivedChar;
while (! rxSerialData() ); // Wait for input.
tReceivedChar = tolower(buf[0]);
startProgramming();
uint16_t tSignature = readSignature();
uint8_t tDeviceType = checkAndPrintSignature(tSignature);
if (tDeviceType != DEVICE_UNKNOWN) {
readFuses();
bool tFusesAreLocked = readLockBits();
if (tFusesAreLocked && tReceivedChar != 'r') {
// should write fuses, but they are locked
Serial.println(F("Suppose to write fuses, but they are locked. -> Unlock them by performing an additional chip erase."));
}
if (tReceivedChar == '?') {
printHelp();
} else if (tFusesAreLocked || tReceivedChar == 'e') {
/*
* ERASE
*/
eraseFlashAndLockBits();
} else if (tReceivedChar == 'w' ) { // mod to only read if R (not any other char or r)
/*
* WRITE FUSES
*/
if (tDeviceType == DEVICE_ATTINY13) {
Serial.println(F("Write LFUSE: 0x6A"));
writeFuse(LFUSE, 0x6A);
Serial.println(F("Write HFUSE: 0xFF"));
writeFuse(HFUSE, 0xFF);
Serial.println();
} else if (tDeviceType == DEVICE_ATTINY24_TO_85) {
Serial.println(F("Write LFUSE: 0x62")); // Original code.
writeFuse(LFUSE, 0x62);
Serial.println(F("Write HFUSE: 0xDF"));
writeFuse(HFUSE, 0xDF);
Serial.println(F("Write EFUSE: 0xFF"));
writeFuse(EFUSE, 0xFF);
}
// JFM add user fuse writing 'fxx,xx,xx', 'fxx,xx' accommodates ATtiny13 (2 fuses).
} else if ( tReceivedChar == 'f' ) {
if ( ( strlen(buf)!=9 && (tDeviceType != DEVICE_ATTINY13) )||
( strlen(buf)!=6 && (tDeviceType == DEVICE_ATTINY13) )
) {
Serial.print(F("Malformed fuse data (format is fxx,xx,xx): "));
Serial.println(buf);
Serial.print(F("String length is: "));
Serial.println(strlen(buf));
} else { // Write user defined fuse values.
eraseFlashAndLockBits();
byte c=0;
int fuseAddr=-1;
const char *separator=",";
char *token = strtok(&buf[1],separator); // Ignore 'f'
while( token != NULL ) {
switch(c) {
case 0: fuseAddr = LFUSE; break;
case 1: fuseAddr = HFUSE; break;
case 2: // Majority are not ATtiny13; ATtiny13 only uses 2 fuses.
if (tDeviceType != DEVICE_ATTINY13)
fuseAddr = EFUSE; else fuseAddr = -1;
break;
default: fuseAddr = -1;
}
if (fuseAddr != -1) {
int val = (int)strtol(token, NULL, 16);
Serial.print(F("Write fuse "));
switch(c) {
case 0 : Serial.print(F("LFUSE"));break;
case 1 : Serial.print(F("HFUSE"));break;
case 2 : Serial.print(F("EFUSE"));break;
default: Serial.print(F("XXX"));
}
Serial.print(F(": "));Serial.print(val,HEX);
Serial.print('\n');
writeFuse(fuseAddr, val);
}
token = strtok(NULL, separator);
c++;
} // token != NULL
} // end // Write user defined fuse values.
} // receive char 'f'
} // device type is known
endProgramming();
digitalWrite(LED_BUILTIN, LOW);
if (tDeviceType == DEVICE_UNKNOWN) {
Serial.println(F("Try again."));
Serial.println(F("--------------."));
}
}
/*
* Prints message and returns device type
*/
uint8_t checkAndPrintSignature(uint16_t aSignature) {
uint8_t tReturnValue = DEVICE_ATTINY24_TO_85;
const char *tTypeString = "";
switch (aSignature) {
case ATTINY13:
tTypeString = "13/ATtiny13A.";
tReturnValue = DEVICE_ATTINY13;
break;
case ATTINY24:
tTypeString = "24.";
break;
case ATTINY44:
tTypeString = "44.";
break;
case ATTINY84:
tTypeString = "84.";
break;
case ATTINY25:
tTypeString = "25.";
break;
case ATTINY45:
tTypeString = "45.";
break;
case ATTINY85:
tTypeString = "85.";
break;
default:
tReturnValue = DEVICE_UNKNOWN;
break;
}
if (tReturnValue == DEVICE_UNKNOWN) {
Serial.println(F("No valid ATtiny signature detected!"));
} else {
Serial.print(F("The ATtiny is detected as ATtiny"));
Serial.println(tTypeString);
Serial.println();
}
return tReturnValue;
}
uint8_t shiftOut(uint8_t aValue, uint8_t aAddress) {
uint16_t tInBits = 0;
//Wait with timeout until SDO goes high
uint32_t tMillis = millis();
while (!digitalRead(SDO)) {
if (millis() > (tMillis + READING_TIMEOUT_MILLIS)) {
break;
}
}
uint16_t tSDIOut = (uint16_t) aValue << 2;
uint16_t tSIIOut = (uint16_t) aAddress << 2;
for (int8_t i = 10; i >= 0; i--) {
digitalWrite(SDI, !!(tSDIOut & (1 << i)));
digitalWrite(SII, !!(tSIIOut & (1 << i)));
tInBits <<= 1;
tInBits |= digitalRead(SDO);
digitalWrite(SCI, HIGH);
digitalWrite(SCI, LOW);
}
return tInBits >> 2;
Serial.print(F(" tInBits="));
Serial.println(tInBits);
}
void eraseFlashAndLockBits() {
Serial.println(F("Erasing flash and lock bits..."));
shiftOut(0x80, 0x4C);
shiftOut(0x00, 0x64);
shiftOut(0x00, 0x6C);
//Wait with timeout until SDO goes high
uint32_t tMillis = millis();
while (!digitalRead(SDO)) {
if (millis() > (tMillis + READING_TIMEOUT_MILLIS)) {
break;
}
}
Serial.println();
}
void writeFuse(uint16_t aFuseAddress, uint8_t aFuseValue) {
Serial.print(F("Writing fuse value "));
Serial.print(aFuseValue, HEX);
Serial.println(F(" to ATtiny..."));
shiftOut(0x40, 0x4C);
shiftOut(aFuseValue, 0x2C);
shiftOut(0x00, (uint8_t) (aFuseAddress >> 8));
shiftOut(0x00, (uint8_t) aFuseAddress);
Serial.println();
}
void readFuses() {
Serial.println(F("Reading fuse settings from ATtiny..."));
uint8_t tValue;
shiftOut(0x04, 0x4C); // LFuse
shiftOut(0x00, 0x68);
tValue = shiftOut(0x00, 0x6C);
Serial.print(F(" LFuse: "));
Serial.print(tValue, HEX);
shiftOut(0x04, 0x4C); // HFuse
shiftOut(0x00, 0x7A);
tValue = shiftOut(0x00, 0x7E);
Serial.print(F(", HFuse: "));
Serial.print(tValue, HEX);
shiftOut(0x04, 0x4C); // EFuse
shiftOut(0x00, 0x6A);
tValue = shiftOut(0x00, 0x6E);
Serial.print(F(", EFuse: "));
Serial.println(tValue, HEX);
Serial.println();
}
uint16_t readSignature() {
Serial.print(F("Reading signature from connected ATtiny..."));
uint16_t tSignature = 0;
uint8_t tValue;
for (uint8_t tIndex = 1; tIndex < 3; tIndex++) {
shiftOut(0x08, 0x4C);
shiftOut(tIndex, 0x0C);
shiftOut(0x00, 0x68);
tValue = shiftOut(0x00, 0x6C);
tSignature = (tSignature << 8) + tValue;
}
Serial.println(tSignature, HEX);
Serial.println();
return tSignature;
}
/*
* Returns true if fuses are locked
*/
bool readLockBits() {
bool tReturnValue = false;
Serial.println(F("Reading lock bits..."));
uint8_t tValue;
shiftOut(0x04, 0x4C); // Lock
shiftOut(0x00, 0x78);
tValue = shiftOut(0x00, 0x7C);
Serial.print(F(" Lock: "));
Serial.println(tValue, HEX);
Serial.print(F(" "));
// Mask lock bits
tValue &= 0x03;
//check and report LB1 and LB2 state
//0 is programmed
//tValue: x x x x x x LB2 LB1
if (tValue & 0x01) {
Serial.println(F("LB1 Not Programmed"));
} else {
Serial.println(F("LB1 Programmed"));
}
if (tValue & 0x02) {
Serial.println(F(" LB2 Not Programmed"));
} else {
Serial.println(F(" LB2 Programmed"));
}
if (tValue == 0x03) {
Serial.println(F("No memory lock features enabled."));
} else {
if (!(tValue & 0x01)) {
Serial.println(F("Further programming of the Flash and EEPROM is disabled in High-voltage and Serial Programming mode."));
Serial.println(F("The Fuse bits are locked in both Serial and High-voltage Programming mode. debugWire is disabled."));
tReturnValue = true;
}
if (!(tValue & 0x02)) {
Serial.println(F("Additionally verification is also disabled in High-voltage and Serial Programming mode."));
}
}
//Wait with timeout until SDO goes high
uint32_t tMillis = millis();
while (!digitalRead(SDO)) {
if (millis() > (tMillis + READING_TIMEOUT_MILLIS)) {
break;
}
}
Serial.println();
return tReturnValue;
}
[Sketch: hvp.ino]
Instructions for use are here (also available by pressing '?' in the program):
Enter 'r' to only read fuses and lock bits.
Enter 'e' to erase flash and reset lock bits.
Enter 'w' to write fuses to default. NEW > Enter 'f' Write User fuse fxx,xx,xx 3 hex bytes (can use 2 for ATtiny13).
!!! 'w' will erase flash if lock bits are set, since otherwise fuses can not be overwritten!!!
Enter '?' to show this help"
I added keyboard fuse settings input code, allowing you to enter your
own fuse values while the program is running. Use the following command
to set fuses:
f62,d7,ff
These are hex values for low fuse, high fuse and extra fuse. All other commands remain the same as the original.
Unbrick ATtiny Fuse Calculation
There's quite a few setting for fuses and they're all different
for each ATtiny, so using an ATtiny fuse calculator program to figure out the values is a good
idea:
Be warned that the 'tick' actually means programmed to zero (in line
with the data sheet) so don't think that the values are inverted. The
result at the end is a correct hex representation of the tick marks.
You can brick the chip by setting the fuses incorrectly but using an HVP programmer will let you unbrick the ATtiny chip.
Software - Alternative HVP program
If you really can't get the ATtiny to get going i.e. lost signature then use the following program by Uri Shaked.
It seems to have slightly better timing because a lot of analysis has
been done on the chip by analyzing the datasheet and observing logic
analyzer waveforms.
P.S. This program worked on one of my ATtiny85s to restore the signature
when other methods failed. You can use it to unbrick any ATtiny.
Program this sketch into the Aruduino Uno as usual.
Note: For Fuse resetter: pin 8 Flashes LED for error, LED off = ok.
// hvp-fuse-resetter
// https://blog.wokwi.com/removing-a-curse-from-attiny85-fuses/
// JFM mod pins for Uno - unbrick ATtiny // and LED JFM was (7) Change to 8 to match Uno AVRISP. // // AVR High-voltage Serial Fuse Reprogrammer with Chip Erase function
//
// Modified by Uri Shaked to add the Chip Erase function:
// https://blog.wokwi.com/removing-a-curse-from-attiny85-fuses
//
// Code taken from the following tutorial, under GPL 3+ license:
// https://www.hackster.io/sbinder/attiny85-powered-high-voltage-avr-programmer-3324e1
// Adapted from code and design by Paul Willoughby 03/20/2010
// http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
// and Wayne Holder
// https://sites.google.com/site/wayneholder/attiny-fuse-reset
//
// Fuse Calc:
// http://www.engbedded.com/fusecalc/
//#define LED 3 // Status indicator LED
//#define RST 4 // (13) Output to level shifter for !RESET from transistor
//#define SCI 3 // (12) Target Clock Input
//#define SDO 2 // (11) Target Data Output
//#define SII 1 // (10) Target Instruction Input
//#define SDI 0 // ( 9) Target Data Input
#define LED 8 // JFM was (7) Change to 8 to match Uno AVRISP
#define RST 10 // Output to level shifter for !RESET from transistor
#define SCI 5 // Target Clock Input
#define SDO 13 // Target Data Output
#define SII 12 // Target Instruction Input
#define SDI 11 // Target Data Input
#define VCC 4 // Use as power supply - keeps to same side of Uno. //JFM
#define HFUSE 0x747C
#define LFUSE 0x646C
#define EFUSE 0x666E
// ATTiny series signatures
#define ATTINY13 0x9007 // L: 0x6A, H: 0xFF 8 pin
#define ATTINY24 0x910B // L: 0x62, H: 0xDF, E: 0xFF 14 pin
#define ATTINY25 0x9108 // L: 0x62, H: 0xDF, E: 0xFF 8 pin
#define ATTINY44 0x9207 // L: 0x62, H: 0xDF, E: 0xFFF 14 pin
#define ATTINY45 0x9206 // L: 0x62, H: 0xDF, E: 0xFF 8 pin
#define ATTINY84 0x930C // L: 0x62, H: 0xDF, E: 0xFFF 14 pin
#define ATTINY85 0x930B // L: 0x62, H: 0xDF, E: 0xFF 8 pin
int error = 0;
byte FuseH = 0;
byte FuseL = 0;
byte FuseX = 0;
void setup() {
pinMode(VCC,OUTPUT); // JFM
digitalWrite(VCC,HIGH); // JFM power on.
pinMode(RST, OUTPUT);
digitalWrite(RST, HIGH); // Level shifter is inverting, this shuts off 12V
pinMode(SDI, OUTPUT);
pinMode(SII, OUTPUT);
pinMode(SCI, OUTPUT);
pinMode(SDO, OUTPUT); // Configured as input when in programming mode
digitalWrite(SDI, LOW);
digitalWrite(SII, LOW);
digitalWrite(SDO, LOW);
delayMicroseconds(30); // wait long enough for target chip to see rising edge
digitalWrite(RST, LOW); // 12v On
delayMicroseconds(10);
pinMode(SDO, INPUT); // Set SDO to input
delayMicroseconds(300);
unsigned int sig = readSignature();
if (sig == ATTINY13) {
chipErase();
writeFuse(LFUSE, 0x6A);
writeFuse(HFUSE, 0xFF);
readFuses(); // check to make sure fuses were set properly
if (FuseL != 0x6A || FuseH != 0xFF) {
error = 5; // fast flash if fuses don't match expected
}
} else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84 ||
sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85) {
chipErase();
writeFuse(LFUSE, 0x62);
writeFuse(HFUSE, 0xDF);
writeFuse(EFUSE, 0xFF);
readFuses(); // check to make sure fuses were set properly
if (FuseL != 0x62 || FuseH != 0xDF || FuseX != 0xFF) {
error = 5; // fast flash if fuses don't match expected
}
} else {
error = 1; // slow flash if device signature is invalid
}
digitalWrite(SCI, LOW);
digitalWrite(RST, HIGH); // 12v Off
digitalWrite(LED, LOW); // LED off for succerss
}
void loop() {
// Flash LED if there was an error
while (error > 0) {
int d = 500 / error;
digitalWrite(LED, HIGH);
delay(d);
digitalWrite(LED, LOW);
delay(d);
}
}
byte shiftOut (byte val1, byte val2) {
int inBits = 0;
//Wait until SDO goes high
while (!digitalRead(SDO))
;
unsigned int dout = (unsigned int) val1 << 2;
unsigned int iout = (unsigned int) val2 << 2;
for (int ii = 10; ii >= 0; ii--) {
digitalWrite(SDI, !!(dout & (1 << ii)));
digitalWrite(SII, !!(iout & (1 << ii)));
inBits <<= 1;
inBits |= digitalRead(SDO);
digitalWrite(SCI, HIGH);
digitalWrite(SCI, LOW);
}
return inBits >> 2;
}
void writeFuse (unsigned int fuse, byte val) {
shiftOut(0x40, 0x4C);
shiftOut( val, 0x2C);
shiftOut(0x00, (byte) (fuse >> 8));
shiftOut(0x00, (byte) fuse);
}
void readFuses () {
shiftOut(0x04, 0x4C); // LFuse
shiftOut(0x00, 0x68);
FuseL = shiftOut(0x00, 0x6C);
shiftOut(0x04, 0x4C); // HFuse
shiftOut(0x00, 0x7A);
FuseH = shiftOut(0x00, 0x7E);
shiftOut(0x04, 0x4C); // EFuse
shiftOut(0x00, 0x6A);
FuseX = shiftOut(0x00, 0x6E);
}
unsigned int readSignature () {
unsigned int sig = 0;
byte val;
for (int ii = 1; ii < 3; ii++) {
shiftOut(0x08, 0x4C);
shiftOut( ii, 0x0C);
shiftOut(0x00, 0x68);
val = shiftOut(0x00, 0x6C);
sig = (sig << 8) + val;
}
return sig;
}
// See table 20-16 in the datasheet
void chipErase () {
shiftOut(0b10000000, 0b01001100);
shiftOut(0b00000000, 0b01100100);
shiftOut(0b00000000, 0b01101100);
}
[Sketch: hvp-fuse-resetter.ino]
The original source is here
https://github.com/wokwi/attiny-hvsp-programmer. The code above is
modified to match other code pin out.
This returns the hardware to the AVRISP programmer state, while
leaving you the option of easily going back to the HVP programmer. The
wire from Arduino Uno pin 9 is unused by the AVRISP program (defaulting to an input in a program when unused).
You can now go back to programming your ATtiny using the method detailed here i.e. you have a working unbricked ATtiny and can now use it again!
How to get accurate DHT22 digital humidity sensor readings with an Arduino. Did you know it also measures temperature as Well? Find out why, in this page...
A PIR sensor lets your Arduino sense movement without contact. This tutorial covers PIR sensor basics, connecting one to an Arduino board and coding a motion detector.
Arduino Hall Effect Sensor: Add magnetic sensing superpowers to your Arduino projects with an easy-to-use hall effect sensor. With full code and layout...
Comments
Have your say about what you just read! Leave me a comment in the box below.
Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.