Hardware | 벽돌된 ATtiny85 를 복구해 보자

Arduino 를 직접 만들거나, 간단한 구동은 ATtiny85 를 사용하는 등, Microchip Technology 사의 chip 들을 많이 활용하고 있습니다.

* ATtiny85 를 사용해 보자 - 1
  - http://chocoball.tistory.com/entry/Hardware-ATtiny85-1

* ATtiny85 를 사용해 보자 - 2
  - http://chocoball.tistory.com/entry/Hardware-ATtiny85-2

문제는 bootloader 를 올릴 시, fuse setting 이 꼬일 경우, 물리적으로 멀쩡한데도 chip 이 벽돌 되는 현상이 발생합니다. 문제의 근원은 잘못된 Lock Bit 때문에 그러는데, 이 fuse 설정만 바꾸어 주면 되지만, 한번 Lock 이 발생하면 쉽게 되돌릴 수가 없습니다. ATmega 계열이나 ATtiny 계열을 자주 가지고 노는 사람이라면, 이 fuse 문제를 해결해주는 resetter 가 필요합니다.


1. High Voltage Resetter

문제가 되는 Lock bit 를 reset 하기 위해서는 12V 라는, 다소 높은 전압이 필요합니다. 사실 9V 에서도 가능 했으나, 스펙상으로는 12V 가 필요하다 합니다.

참고될 자료를 찾아 보던 중, 아래 사이트가 정리를 잘 해주고 있습니다.

* High Voltage programming/Unbricking for Attiny
  - https://arduinodiy.wordpress.com/2015/05/16/high-voltage-programmingunbricking-for-attiny/

Any standard NPN transistor will do, such as BC337 BC327 2N2222 2N2907 2N3904 2N3906 (these are the ones I currently have on order from AliExpress, what a coincidence!). I can only see one capacitor that I probably used for decoupling the power supply so a 10µF will do. You can get a great selection in a small plastic case from AliExpress or Banggood for just a few dollars that will see you right for years!

여기에 필요한 필수 부품은, transistor 가 되겠습니다. 설명에 의하면 NPN transistor 라면 어떤 것이든 문제 없을 것이라고 합니다. 다행히 예전에 구매해 둔 transistor 꾸러미를 가지고 있군요.

* Hardware | Transistor 구매
  - https://chocoball.tistory.com/entry/Hardware-Transistor-buying

굴러다니는 NPN 하나를 집어 들었습니다. S9018 이네요.

전기적인 특성을 살펴 보니, BC547 과 거의 비슷합니다.


2. Diagram

위의 회로도를 참고하여 빵판에 연결해 봤습니다.

보기에는 복잡해 보이지만, 하나 씩 연결하다 보면 연결이 됩니다. Arduino 및 소자 보호를 위해, 각 연결점에 저항이 들어가는 것이 조금 복잡합니다.


3. Source

Arduino 에 올라가는 source code 는 다음과 같습니다.


// 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/
// 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
#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: 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
 
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
  digitalWrite(RST, HIGH);  // Level shifter is inverting, this shuts off 12V
  Serial.begin(19200);
}
 
void loop() {
   if (Serial.available() > 0) {
    Serial.read();
    pinMode(SDO, OUTPUT);     // Set SDO to output
    digitalWrite(SDI, LOW);
    digitalWrite(SII, LOW);
    digitalWrite(SDO, LOW);
    digitalWrite(RST, HIGH);  // 12v Off
    digitalWrite(VCC, HIGH);  // Vcc On
    delayMicroseconds(20);
    digitalWrite(RST, LOW);   // 12v On
    delayMicroseconds(10);
    pinMode(SDO, INPUT);      // Set SDO to input
    delayMicroseconds(300);
    unsigned int sig = readSignature();
    Serial.print("Signature");
    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
    digitalWrite(RST, HIGH);   // 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;
}



4. Another Source

다른 버전의 소스 입니다. 

* Arduino AVR High-Voltage Serial Programmer
  - https://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/

프로그램의 구조적 파악을 위해, 사용해 보지 않았지만 기록을 위해 올려 놓습니다.


// AVR High-voltage Serial Programmer
// Originally created by Paul Willoughby 03/20/2010
// http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
// Inspired by Jeff Keyzer http://mightyohm.com
// Serial Programming routines from ATtiny25/45/85 datasheet
 
// Desired fuse configuration
#define  HFUSE  0xDF   // Defaults for ATtiny25/45/85
#define  LFUSE  0x62
// For Attiny13 use
// #define HFUSE 0xFF
// #define LFUSE 0x6A 
 
 
#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("lfuses 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("hfuses 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("efuses as ");
    Serial.println(inData, HEX);
    Serial.println();
}


너무 오래전에 사용했던 터라, 스샷을 찾을 수 없어 진행 과정은 없습니다. 다만, 잘 동작하니 그대로 따라하면 됩니다.


5. AVRDUDESS

Resetter 내용은 아니지만, bootloader 를 직접 올리거나, fuse setting 을 수정해야 할 경우가 있습니다.

Command Line 에서도 가능하나, Windows 에서는 directory 권한 문제로 실패하는 경우가 많습니다. 그래서, 따로 avrdude 를 구동해주는 어플이 존재합니다. 이름하여, AVRDUDESS.

* AVRDUDESS is a GUI for AVRDUDE, a tool for programming Atmel microcontrollers.


공개되어 있다 보니, 몇 가지 버전이 존재합니다.

* AVR Downloader/UploaDEr - Summary
  - http://savannah.nongnu.org/projects/avrdude

* AVRDUDESS – A GUI for AVRDUDE
  - http://blog.zakkemble.co.uk/avrdudess-a-gui-for-avrdude/

* GitHub (zkemble/AVRDUDESS)
  - https://github.com/zkemble/AVRDUDESS


FIN


댓글