ATTiny Fuse Reset with 12 Volt Charge Pump

This page shows how to build a more advanced version of the ATTiny Fuse Reset tool that uses a Charge Pump controlled by an Arduino interrupt routine to generate the 12 volts needed to program the fuses on the ATTiny chips.  The details on how how to reset the fuses and how to build a charge pump are described on the pages linked above, so I won't repeat those details here.  The schematic of the circuit needed is shown below:

I'm working on a PC Board that implements a simple Arduino Shield for this circuit.  I plan to make this design available via SparkFun's BatchPCB service, but I'll post more details when that design is ready.  When the PCB is ready, here are some suggested components you can use to build this circuit:

The Arduino program needed to drive this circuit is shown next (also available for download as an Arduino project at the bottom of the page.)

#include <TimerOne.h>

// AVR High-voltage Serial Fuse Reprogrammer with 12 Volt Charge Pump

// Adapted from code and design by Paul Willoughby 03/20/2010

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

//

// Fuse Calc:

//   http://www.engbedded.com/fusecalc/

#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

#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 Direct I/O pins for Charge Pump

#define P1  0x04  // Pin D2

#define P2  0x08  // Pin D3

#define PWR 0x10  // Pin D4

#define GND 0x20  // Pin D5

#define REF 404   // 12 volt reference

// Variables used by Charge pump

volatile char phase = 0;

volatile char onOff = 0;

volatile char pwrOn = 0;

void ticker () {

  if (onOff) {

    DDRD = P1 | P2 | PWR | GND;

    int volts = analogRead(A0);

    if (volts < REF) {

      if (phase) {

        PORTD = P1 | PWR;

      } else {

        PORTD = P2 | PWR;

      }

      phase ^= 1;

    } else {

      pwrOn = 1;

    }

  } else {

    pwrOn = 0;

    DDRD = GND;

    PORTD = GND;

  }

}

void setup() {

  pinMode(VCC, OUTPUT);

  pinMode(SDI, OUTPUT);

  pinMode(SII, OUTPUT);

  pinMode(SCI, OUTPUT);

  pinMode(SDO, OUTPUT);     // Configured as input when in programming mode

  Serial.begin(57600);

  // Setup timer interrupt for  charge pump

  analogReference(DEFAULT);

  Timer1.initialize(500);

  Timer1.attachInterrupt(ticker);

}

void loop() {

   if (Serial.available() > 0) {

    Serial.read();

    pinMode(SDO, OUTPUT);     // Set SDO to output

    digitalWrite(SDI, LOW);

    digitalWrite(SII, LOW);

    digitalWrite(SDO, LOW);

    onOff = 0;                // 12v Off

    digitalWrite(VCC, HIGH);  // Vcc On

    delayMicroseconds(20);

    onOff = 1;                // 12v On

    while (pwrOn == 0)

      ;

    delayMicroseconds(10);

    pinMode(SDO, INPUT);      // Set SDO to input

    delayMicroseconds(300);

    unsigned int sig = readSignature();

    Serial.print("Signature is: ");

    Serial.println(sig, HEX);

    readFuses();

    if (sig == ATTINY13) {

      writeFuse(LFUSE, 0x6A);

      writeFuse(HFUSE, 0xFF);

    } else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84 ||

               sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85) {

      writeFuse(LFUSE, 0x62);

      writeFuse(HFUSE, 0xDF);

      writeFuse(EFUSE, 0xFF);

    }

    readFuses();

    digitalWrite(SCI, LOW);

    digitalWrite(VCC, LOW);    // Vcc Off

    onOff = 0;                 // 12v Off

  }

}

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 () {

  byte val;

        shiftOut(0x04, 0x4C);  // LFuse

        shiftOut(0x00, 0x68);

  val = shiftOut(0x00, 0x6C);

  Serial.print("LFuse: ");

  Serial.print(val, HEX);

        shiftOut(0x04, 0x4C);  // HFuse

        shiftOut(0x00, 0x7A);

  val = shiftOut(0x00, 0x7E);

  Serial.print(", HFuse: ");

  Serial.print(val, HEX);

        shiftOut(0x04, 0x4C);  // EFuse

        shiftOut(0x00, 0x6A);

  val = shiftOut(0x00, 0x6E);

  Serial.print(", EFuse: ");

  Serial.println(val, HEX);

}

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;

}