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
댓글
댓글 쓰기