Monday, August 17, 2020

ATTINY85 Unbrick DIY hardware


 

 ATTINY85 Unbrick  Arduino shield.

 

 

 

I have been using these ATTINY85 8bit AVR  capable MCUs in my projects for some time now because they are really easy to integrate on circuit boards and can run up to 16 MHz without the need to include an external crystal.  They  have 8k program storage which is quite a bit to work with.   


IDE example stats:

Sketch uses 944 bytes (11%) of program storage space. Maximum is 8192 bytes.
Global variables use 29 bytes (5%) of dynamic memory, leaving 483 bytes for local variables. Maximum is 512 bytes.


It is very often that I do not need a lot of IOs to do what I want  and need to get done. These devices also use very little power so they are ideal for a low power low IO count design. 

They are available through Digikey for example in low quantities for only about a dollar and 20 cents each. However when they arrive in the mail they are most often configured by manufacturer with internal fuses set to an internal rc oscillator that is 8 MHz with the divide by 8 fuse set "0" is a set in this case. The divide by 8 fuse effectively makes these devices  1 MHz parts which I find extremely slow.

 

 

Therefore I need to write the fuses to allow the device to run at 16 MHz. Note that 20 MHz is possible for these devices with external crystals but they are already extremely IO limited and so that additional 4 MHz of speed will cost you 2 of your total 6 available IOs.

Sometimes as you program the devices it is also possible for glitches to cause the devices to "brick" meaning that the fuses need to be re-written to acceptable levels for your particular configuration needs so that once in a while you will need to un-brick them by writing the fuses.

If you do not have a method to write the fuses there a number of people on the internet that have shown how this can be done by some code and DIY hardware.

 

Here is an example:

http://wildlab.org/index.php/2016/11/30/bricked-attiny85-no-problem/

 

 I found these useful but also found that they were kind of dodgy to use because they did not always work reliably.  One reason I think is that they require a Host Arduino to power up the DUT tiny85 using pin 8 of the host. I found that doing this in some cases is not very reliable and it can seem to power up the DUT to less than the 1.8v which is required by the ATTINY85 databook.for High Voltage programming. High voltage because it is greater than 5 volts (12 volts)


From ATMEL device Databook: p155

 

 

 Therefore I felt like a better more reliable option was needed.  So I added a pfet to the design to control the bringing of pwr from Arduino host board to ATTiny85 power pin. See video:

 

 

 So as I said in the video I have needed to change the state value of the D8 pin from the original code and I have changed some few other details.

 

Arduino code to load into UNO host device:

 
/*
AVR High-voltage Serial Fuse Reprogrammer by Paul Willoughby (03/20/2010) and modified by William Andersen
06/28/2020. http://www.rickety.us./2010/10/arduino-avr-hich-voltage-serial-programmer/
Fuse Calc:
http://www.engbedded.com/fusecalc/
 */
 
 #define RST 13 // Output to level shifter for !RESET from transistor
 #define SCI 12 // Target Clock Input
 #define SDO 11 // Target Data Output  ........................connected to tiny85 SCK/PB2  pin7
 #define SII 10 // Target Instruction Input ...................connected to tiny85 MISO/PB1 pin6
 #define SDI 9 // Target Data Input  ..........................connected to tiny85 MOSI/PB0 pin5
 #define VCC 8 // Target VCC
 #define TRG 7 // trigger signal for scope to debug communications... adding Will Andersen

 #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: 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
 
 byte checkIt = 0;
 byte cval;
 int menuselect;
 unsigned int sig = 0;
    
 void setup() {  
   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
   pinMode(TRG, OUTPUT);  // Adding  for debug SPI communications

   digitalWrite(TRG, LOW ); // will use positive pulese to trigger scope.
   digitalWrite(RST, HIGH ); // Level shifter is inverting, this shuts off 12V
   digitalWrite(VCC, HIGH); // Vcc off per modified circuit WA 7/11/20
   Serial.begin(19200);
   Serial.println("Code is modified by Rik. Visit riktronics.wordpress.com and electronics-lab.com for more projects");
   Serial.println("-------------------------------------------------------------------------------------------------");
   //Serial.println("Enter any character to start process..");

 //checkIt = Serial.available();
 //Serial.print("Serial.available returns: ");
 //Serial.println(cval,checkIt);
 //Serial.println("");
 //delay(1000);
 
 //if (Serial.available() )  {
 //  Serial.read();
 
   delay(100);
   
   pinMode(SDO, OUTPUT); // Set SDO to output
   //trigger();
   //for(;;) {
   digitalWrite(SDI, LOW);
   digitalWrite(SII, LOW);
   digitalWrite(SDO, LOW);
 
   digitalWrite(RST, HIGH); // 12v Off
   digitalWrite(VCC, HIGH); // Vcc off per modified circuit WA 7/11/
   digitalWrite(VCC, LOW); // Vcc ON per modified circuit WA 7/11/
   delayMicroseconds(20);
   digitalWrite(RST, LOW); // 12v On
   delayMicroseconds(10);
   
   pinMode(SDO, INPUT); // Set SDO to input
   //MCUCR |= bit (PUD);   
   delayMicroseconds(300);

     sig = readSignature();
    // Serial.println(sig, HEX);
   
   //sei();
   
   Serial.println("Reading signature from connected ATtiny......");
   Serial.println("Reading complete..");
   Serial.print("Signature is: ");
   Serial.println(sig, HEX);

   readFuses();
   // Do not want to write fuses at this point will perform that function in Loop if desired,
 
   if (sig == ATTINY13) {
     Serial.println("The ATtiny is detected as ATtiny13/ATtiny13A..");
     Serial.print("LFUSE: ");
     writeFuse(LFUSE, 0x6A);
     Serial.print("HFUSE: ");
     writeFuse(HFUSE, 0xFF);
     Serial.println("");
   } else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84 || sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85)  { {
     Serial.println("The ATtiny is detected as ");
        if(sig == ATTINY24) Serial.println("ATTINY24..");
        else if(sig == ATTINY44) Serial.println("ATTINY44..");
        else if(sig == ATTINY84) Serial.println("ATTINY84..");
     else if(sig == ATTINY25) Serial.println("ATTINY25..");
     else if(sig == ATTINY45) Serial.println("ATTINY45..");
     else if(sig == ATTINY85) Serial.println("ATTINY85..");
     writeFuse(LFUSE, 0xF1); // 62 = 1MHZ clk EZ = 8MHz F1 = 16 MHz internal RC  
     writeFuse(HFUSE, 0xDF);
     writeFuse(EFUSE, 0xFF);
   }}
 
 
   
   Serial.println("Fuses will be read again to check if it's changed successfully..");
   readFuses();
   digitalWrite(SCI, LOW);
   digitalWrite(VCC, HIGH); // Vcc Off active low pin per modifiedd circuit WA 7/11/20
   //Serial.println("VCC: Low");
   digitalWrite(RST, HIGH); // 12v Off
 }


 void loop() {
}


void trigger() {
  digitalWrite(TRG, HIGH);
  digitalWrite(TRG,  LOW);
}

 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);
     delayMicroseconds(4);
     digitalWrite(SCI, HIGH);
     delayMicroseconds(2);
     digitalWrite(SCI, LOW);
    }
 return inBits >> 2;
 }

 void writeFuse (unsigned int fuse, byte val) {
   Serial.println("Writing correct fuse settings to ATtiny.......");
   shiftOut(0x40, 0x4C);
   shiftOut( val, 0x2C);
   shiftOut(0x00, (byte) (fuse >> 8));
   shiftOut(0x00, (byte) fuse);
   Serial.println("Writing complete..");
 }


 void readFuses () {
   byte val;
   Serial.println("Reading fuse settings from connected ATtiny.......");
   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);
   Serial.println("Reading complete..");
 }

 unsigned int readSignature() {
   unsigned int sig = 0;
   byte val;
   //trigger();
   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;
 }

 unsigned int readLockbits() {
   unsigned int LB = 0;
   //byte val;
   //trigger();
   for (int ii = 0; ii < 1; ii++) {
      shiftOut(0x04, 0x4C);
      shiftOut( 0x00, 0x38);
      LB = shiftOut(0x00, 0x7C);
  }
     return LB;
 }

 void eraseChip() {
    for (int ii = 0; ii < 1; ii++) {
      shiftOut(0x80, 0x4C);
      shiftOut(0x00, 0x64);
      shiftOut(0x00, 0x6C);
    }
 
 }

unsigned int readSignatureLV() {
   unsigned int sig = 0;
   //byte val;
   pinMode(SII, OUTPUT);
   pinMode(SDO, OUTPUT);
   delayMicroseconds(300);

   digitalWrite(SDO, LOW);
   digitalWrite( VCC,LOW);   // power up tiny86 chip through PNP device connected from power to PWR on attiny85
   digitalWrite( RST,HIGH);  // pulls 12v to 0v on reset pin through npn transistor
   delayMicroseconds(20);
   

//  digitalWrite(SDI,HIGH); // MOSI Per tBLE 20-10 PAGE 151  Program enable instruction
//  digitalWrite(SII,LOW);  // MISO (same)
//  digitalWrite(SDO, HIGH) // SCK (same)
    

   pinMode(SII, INPUT);
   pinMode(SDO, OUTPUT);
   delayMicroseconds(300);
   trigger();
   //done power down chip
   digitalWrite( VCC,HIGH);   // power down tiny85 chip through PNP device connected from power to PWR on attiny85
   digitalWrite( RST,LOW);  // pulls 12v to 12v on reset pin through npn transistor
   digitalWrite( RST,HIGH);  // pulls 12v to 0v on reset pin through npn transistor
   delayMicroseconds(20);
   return sig;
}

 

Hope you find this useful.  I will make this board kit available on ebay.com soon and make it available through seed studio as well.


Thanks as always let me know if you have any questions:


Will Andersen










1 comment:

  1. In the video I have been referring to the two transistors in this post as pfet and nfet.... they are neither. I am sorry for that verbal blunder. They are common BJTs the p-channel is a 2N2907 device and the n-channel transistor is a 2N2222. Also I have replaced the 1K resistors with zero ohm resistors where they communicate through the D8 through D13 pins of the Arduino UNO from host to shield.. These will not attenuate the signal coming from the host. There is still a 1k ohm resister from 12v input to attiny85 reset pin acting as a pull-up.

    ReplyDelete