Introduction: How to Unlock Digispark ATtiny85 and Convert It to a Trinket

About: These Arduinos are driving me up the wall.

Digispark ATtiny85 boards come with the Micronucleus bootloader and with the reset pin disabled. When reset is disabled you get an additional digital pin, and you must upload sketches using the bootloader. You may not use ISP programming.

There are projects where I want to use Digispark and need to enable the reset pin for various reasons:

  • Reset Digispark via a signal from another device or via a button
  • Use ISP programming to upload sketches
  • Remove bootloader start up delay
  • Upgrade bootloader
  • Use a different bootloader, such as Trinket
  • Change fuses

This Instructable documents how I unlock the reset pin and program the Digispark with ISP programming to convert it to a Trinket. I have a clone Digispark, which works the same as the Kickstarter Digispark from Digistump.

In this Instructable, I use an Uno as the high voltage programmer, and later I use it as an ISP. You can also use a Nano or Pro Mini, which are simply small Unos.

Using this method you can continue to use the board as a Digispark, or use it as a plain ATtiny85 breakout board and program it via ISP, or use it as a Trinket. If you continue to use the board as a Digispark, the P5 pin has been converted from a regular I/O pin to a reset pin.

Since the Digispark is simply a small break out board for the ATtiny85 MCU, you can also use this Instructable to rescue fuses on a stand alone ATtiny85 MCU.

Step 1: Sketch for High Voltage Programming

A sketch for Uno is available here for unlocking ATtiny85:

http://www.rickety.us/wp-content/uploads/2010/03/hv_serial_prog.pde

Code is by Paul Willoughby and original article for unlocking bare ATtiny85 ICs here:

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

Sketch:

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

// Desired fuse configuration #define HFUSE 0xDF // Defaults for ATtiny25/45/85 #define LFUSE 0x62

#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("Enter a character to continue"); // 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(); }

Step 2: Wiring

I use an Uno and a breadboard with a 2N3904 transistor, 2 ea 1K resistors for the connections to the transistor, and 4 ea 100 ohm resistors for the programming connections between the Uno and Digispark. You may be able to use 1K resistors for the programming connections on a bare ATtiny85, but I found the assembled Digispark needs a lower value. It is probably because I need to overpower the onboard pull-up resistors during programming.

I use pieces of a paperclip for a battery holder and bend them to get a little spring tension.

I like to add all header pins to my Digispark, except VIN, so it fits on a breadboard and connects to the power rails and terminal strips.

Leave the 27A battery disconnected at first, and connect it after the Uno with HV programming sketch is attached to the computer via USB and powered up. This is because if the battery is connected before power is applied to the Uno, the digital pin 13 on the Uno will not have a high signal and switch the transistor, and the battery would then immediately begin sending 12V to the Digispark's pin 5.

When done programming, disconnect the battery first, then unplug the USB cable from the Uno.

Step 3: Running the HV Programming Sketch

When you connect the Uno via USB and connect the battery, open the Serial Monitor window. Set the Line Ending to Both NL & CR and baud rate to 9600. You will see the text "Enter a character to continue" scrolling through the window. On the send line type a character, such as "a", and click Send. You will then see the programmer go to work, and see some results:

Enter a character to continue
Enter a character to continue Enter a character to continue Enter a character to continue Enter a character to continue Enter a character to continue 97 Entering programming Mode

lfuse reads as FF hfuse reads as FF efuse reads as FF

Writing hfuse Writing lfuse

lfuse reads as FF hfuse reads as FF efuse reads as FF

Exiting programming Mode

13 Entering programming Mode

lfuse reads as F1 hfuse reads as 55 efuse reads as FE

Writing hfuse Writing lfuse

lfuse reads as 62 hfuse reads as DF efuse reads as FE

Exiting programming Mode

10 Entering programming Mode

lfuse reads as 62 hfuse reads as DF efuse reads as FE

Writing hfuse Writing lfuse

lfuse reads as 62 hfuse reads as DF efuse reads as FE

Exiting programming Mode

When it says the final "Exiting programming Mode" disconnect the battery, disconnect the Uno, and disconnect the Digispark from the breadboard.

Step 4: Programming Digispark Via ISP

You can now use ISP programming to program the ATtiny85 on the Digispark as you see fit. I use the Uno loaded with the example ArduinoISP sketch to do that.

You can continue to use the Micronucleus bootloader and the Digispark-specific version of Arduino IDE. Pin 5 is now a reset pin instead of a digital pin, so make adjustments in your sketches for that.

OR

You can install an ATtiny85 core in your Arduino IDE and program it using ISP just like a stand alone ATtiny85. The Digispark has been converted to an ATtiny85 breakout board.

OR

You can install the Trinket bootloader on it and use it as a Trinket. See Adafruit's website and tutorials on the Trinket.

Command line for avrdude

Your command line for avrdude may use different directory paths and different ports. To learn what the correct command line looks like for your computer, I think it is easiest to upload the ArduinoISP sample sketch to the Uno, set Tools, Programmer to Arduino as ISP, and use the Uno to burn the bootloader on another Arduino of yours, such as a Nano, Pro Mini. You can actually leave the Uno disconnected and attempt to burn a bootloader to an imaginary device if you want. If you have gone into the Arduino IDE preferences and checked show verbose output during upload, the command line for avrdude which attempted to burn the bootloader will be shown to you. All you need to do is change the last part of the line that gives the path to the bootloader, and replace it with the path to your bootloader, and add the fuse settings. And you may need to also change the -p option, which is where you specify the processor type.

Continue to use board as a Digispark and upgrade Digispark Micronucleus bootloader

I downloaded the latest Micronucleus bootloader from here: https://github.com/micronucleus/micronucleus/tree...

And loaded it onto the Digispark like this:

/Applications/Arduino.app/Contents/Java/hardware/tools/avr/bin/avrdude -C/Applications/Arduino.app/Contents/Java/hardware/tools/avr/etc/avrdude.conf -v -pattiny85 -cstk500v1 -P/dev/cu.usbmodem411 -b19200 -Uflash:w:t85_default_micronucleus.hex -U lfuse:w:0xF1:m -U hfuse:w:0xD5:m -U efuse:w:0xFE:m

Use board as a Trinket

I downloaded the Trinket bootloader from here:
https://github.com/adafruit/Adafruit-Trinket-Gemma...

And loaded it like this:

/Applications/Arduino.app/Contents/Java/hardware/tools/avr/bin/avrdude -C/Applications/Arduino.app/Contents/Java/hardware/tools/avr/etc/avrdude.conf -v -pattiny85 -cstk500v1 -P/dev/cu.usbmodem411 -b19200 -Uflash:w:trinketgemma_v1.hex -U lfuse:w:0xF1:m -U hfuse:w:0xD5:m -U efuse:w:0xFE:m

If it has the Trinket bootloader on it, it is now a Trinket. If you are new to Trinket, look at the Adafruit website and tutorials about Trinket to find out how to set it up your IDE for programming it. It is a little different than other Arduinos in that you do not need to select a port, but instead you set Programmer to USBtinyISP. Just like with Digispark, the Trinket does not use Serial Monitor the same way you might do with other Arduinos.

Step 5: Reset Disable Fuse

If I want to disable the reset pin again and get that additional digital pin 5, I would replace the

hfuse:w:0xD5:m

with

hfuse:w:0x55:m

Of course, that means I would need to use the high voltage programmer to unlock it again if I want to use ISP programming with it again. I use this command line to just set that fuse to disable the reset pin:

/Applications/Arduino.app/Contents/Java/hardware/tools/avr/bin/avrdude -C/Applications/Arduino.app/Contents/Java/hardware/tools/avr/etc/avrdude.conf -v -pattiny85 -cstk500v1 -P/dev/cu.usbmodem411 -b19200 -U hfuse:w:0x55:m

So, after I load a sketch via ISP that needs to use digital pin 5, I can set the high fuse to 0x55 to start using that pin and to lock the sketch in. Or, I can use that extra pin when I program it with the Trinket or Micronucleus bootloader. The Digispark and the Trinket both have equal ability to use pin 5 as a reset pin, or as a data pin, depending on the setting of the fuse. There is nothing special about the design of the Digispark to give it the extra data pin, it is simply a matter of deciding how you want to set the fuse. You can easily set the fuse either way depending on what you need for the project you are working on.

Step 6: Fuses

In the links section I give the link to a fuse calculator, which shows what the various fuses do. My preferred fuses for both Trinket and Digispark:

  • L:F1
  • H:D5
  • E:FE
  • H:55 for reset disable

There is a minor bug in avrdude. At the end of programming when it shows you the fuses it has set, it looks like this:

avrdude: safemode: lfuse reads as F1
avrdude: safemode: hfuse reads as D5
avrdude: safemode: efuse reads as FE
avrdude: safemode: Fuses OK (H:FE, E:D5, L:F1)

Note the summary line for Fuses OK shows H and E fuse values reversed.

Step 7: Transistors? We Don't Need No Stinking Transistors!

Here is the alternate wiring

Ivoh's comment reminded me about the way I provided high voltage to program EPROMs back in the day. The data sheet for the ATtiny85 talks about the timing and sequence between applying 5V to VCC and 12V to RESET, but that timing and sequence does not appear to be critical. Alternate method for the high voltage programming step:

Instead of switching the 12V that is going to the reset pin, just connect it by hand. Connect the 12V first, before you power VCC (you will send power to VCC by running the sketch, which powers up the Digispark or ATtiny85 from the Arduino's pin 8). It may take a couple of tries, because sometimes nothing happens after you see the message "Entering programming Mode" in the serial monitor. So, if you need to start over, just disconnect the USB cable from the Arduino and remove the 12V battery and close the serial monitor. Then connect the battery, followed by connecting the USB cable, then open serial monitor and when prompted, enter a character. The programming happens quickly, just as shown in the video of Step 3. Proceed to Step 4 for ISP programming.

Step 8: Links