How To Unbrick An ATtiny


How To Unbrick An ATtiny: The first thing is don't panic, because you can accidentally brick an ATtiny during normal use. 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).



On this page you can also find out how to convert an Arduino programmer into a high volt programmer.

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 fro 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:

  1. You entered a fuse setting that requires and external clock.
  2. You entered a fuse setting that reduced the clock to 128kHz.
  3. 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!

I was developing the fuse setting program and put some random numbers into the settings to test the software - then the chip died on me. However, once the device was recovered, re-testing the same settings I could not get it to fail again.

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 that this is the case is that 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 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 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: This makes the programming process slow.

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.

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).
Clock settings for ATtiny85

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.

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 - this means that on start up the chip is going to use what you specified as the clock source!

If you program in one of these options and don't have the crystal, you can not 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:

  1. Find the crystal to match the specified setting in the above table and use it.
  2. Feed in an external clock (matching the programmed settings) to CLKI.
  3. 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 AVRISP programmer to HVP

While using a normal Arduino IDE as an ISP 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:

Additional components required:

  • 12V PSU.

  • 2 off 1k.

  • 1 off NPN transistor (general purpose).

  • Wire from pin 9 (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.


The ATtiny85 pinout is stated as:

ATtiny85 pinout
..but for high volt programming the pin out changes to:


ATtiny85 HVP pinout
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.

Unbrick ATtiny AVRISP circuit diagram

The circuit needed is from the writers of the HVP program that you can find here:

    https://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer


rickety schematic arduino hvp
[Source: https://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/]

Wiring diagrams & Software

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.

avrisp attiny85 into high volt programmer

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 9 // Target Clock Input
 #define SDO 13 // Target Data Output
 #define SII 12 // Target Instruction Input
 #define SDI 11 // Target Data Input
 #define VCC 8 // 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" 

The original source is here:

https://github.com/ArminJo/ATtiny-HighVoltageProgrammer_FuseEraser/blob/master/HVProgrammer.ino

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:

https://www.engbedded.com/fusecalc/

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 : HVP If All Else Fails

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.

You can see more here: Removing a Curse from an ATtiny85.

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.

// hvp-fuse-resetter
// https://blog.wokwi.com/removing-a-curse-from-attiny85-fuses/
// JFM mod pins for Uno - unbrick ATtiny
//
// 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 7  // JFM optional
 #define RST 10 // Output to level shifter for !RESET from transistor
 #define SCI 9  // Target Clock Input
 #define SDO 13 // Target Data Output
 #define SII 12 // Target Instruction Input
 #define SDI 11 // Target Data Input
 #define VCC 8  // 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 it for the pin out.

Return to AVRISP Wiring

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 outlined here i.e. you have a working unbricked ATtiny and can now use it again!

To go back to the normal AVRISP programmer:

  1. Remove the 12 PSU connection,
  2. Move the transistor so it is inactive,
  3. Move the yellow programming wire back to pin 1.
attiny85 avrisp to hvp and back


New! Comments

Have your say about what you just read! Leave me a comment in the box below.



Privacy Policy | Contact | About Me

Site Map | Terms of Use