Gerando 12V
A primeira ideia que vem é usar uma fonte separada de 12V (como já fiz em algumas experiências com motor de passo). Para um programador standalone, eu pensaria em ter uma fonte de 12V e passar a saída por um regulador de 5V (7805, etc) para alimentar a parte lógica. Óbvio, simples, fácil mas (como diria Greg House) booooring. O que Wayne usa é um circuito simples que transforma 5V em 12V.
A teoria toda está descrita nas páginas dele. Resumindo, a ideia é termos três capacitores que podem ser dinamicamente configurados para ligação em paralelo ou série. Configurados em paralelo e ligados a 5V, cada um dos capacitores irá carregar até atingir os 5V. Mudando a configuração para série, obtemos 15V que podem ser usados para carregar um quarto capacitor. Enquanto estes 15V alimentam o resto do circuito, volta-se à configuração paralelo para recarregar os capacitores. Parece algo complicado? Na verdade bastam quatro diodos e dois sinais digitais para controlar isto. Este circuito é chamado "Dickson Charge Pump".
Ok, mas nós queremos 12V e não 15V. Para isto usamos o ADC do Arduino para medir a tensão de saída e controlar a carga dos capacitores conforme necessário. Como o ADC é limitado a 5V, usamos um divisor resistivo para fazer esta leitura. Por último, colocamos um transistor para forçar a descarga do capacitor de saída, desligando a alta tensão.
O Hardware do Nosso Programador
A figura abaixo é quase idêntica à da página do Wayne. Mudei apenas um dos resistores do divisor para o ADC, pois eu tinha resistores de 470K ao invés de 510K (isto requer alterar o valor de referência no software) Nos meus testes iniciais usei um 2N222 ao invés do 2N3904 e um capacitor eletrolítico de 1uF 25V ao invés do capacitor cerâmico multicamadas de 2,2uF que ele usou. Usei resistores de 1% para o divisor, provavelmente você não vai ter problemas com resistores de 5%; se quiser ficar mais seguro meça a tensão de saída da charge pump e altere o valor de referência no software.
O Software de Teste
O teste abaixo é uma adaptação do software feito pelo Wayne. Como o meu objetivo é fazer testes e não apenas recuperar ATtinys, implementei mais alguns comandos e permiti selecionar pela serial o comando a executar.
- #include <TimerOne.h>
- // Protótipo de programador serial de alta tensão para ATtiny
- // Adaptado do código de Wayne Holder:
- // https://sites.google.com/site/wayneholder/attiny-fuse-reset-with-12-volt-charge-pump
- #define LED 13
- // Conexões para programação
- #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
- // Comandos de leitura e escrita dos FUSES e LOCK
- #define HFUSE_RD 0x7A7E
- #define LFUSE_RD 0x686C
- #define EFUSE_RD 0x6A6E
- #define LOCK_RD 0x787C
- #define HFUSE_WR 0x747C
- #define LFUSE_WR 0x646C
- #define EFUSE_WR 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
- // Definições para acesso direto aos pinos do Charge Pump
- #define P1 0x04 // Pin D2
- #define P2 0x08 // Pin D3
- #define PWR 0x10 // Pin D4
- #define GND 0x20 // Pin D5
- #define REF 420 // valor do ADC para 12V
- // Variaveis para controle do Charge Pump
- volatile char phase = 0;
- volatile char onOff = 0;
- volatile char pwrOn = 0;
- // Rotina de controle do Charge Pump
- // Disparada pelo Timer 1
- 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;
- }
- }
- // Iniciação
- void setup() {
- pinMode(LED, OUTPUT);
- // Inicia os pinos de programação
- pinMode(VCC, OUTPUT);
- pinMode(SDI, OUTPUT);
- pinMode(SII, OUTPUT);
- pinMode(SCI, OUTPUT);
- pinMode(SDO, OUTPUT);
- // Inicia serial
- Serial.begin(57600);
- Serial.println("HVSP Prototipo");
- Serial.println("I - Le identificacao");
- Serial.println("F - Le os fuses");
- Serial.println("L - Le o lock");
- Serial.println("C - Chip Erase");
- Serial.println("R - Reinicia fuses");
- Serial.println("T - Trava");
- // Inicia ADC e Timer1 para o Charge Pump
- analogReference(DEFAULT);
- Timer1.initialize(500);
- Timer1.attachInterrupt(ticker);
- Serial.println("Pronto");
- }
- // Programa principal
- void loop() {
- int c;
- byte id[3];
- unsigned int sig;
- if (Serial.available() > 0) {
- c = Serial.read();
- // Entra no modo programação
- digitalWrite(LED, HIGH);
- pinMode(SDO, 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);
- // Trata o comando
- switch (c) {
- case 'I': case 'i':
- readSignature(id);
- Serial.print("Identificacao: ");
- Serial.print(id[0], HEX);
- Serial.print(' ');
- Serial.print(id[1], HEX);
- Serial.print(' ');
- Serial.println(id[2], HEX);
- break;
- case 'F': case 'f':
- Serial.print("LFUSE: ");
- Serial.println(readFuse(LFUSE_RD), HEX);
- Serial.print("HFUSE: ");
- Serial.println(readFuse(HFUSE_RD), HEX);
- Serial.print("EFUSE: ");
- Serial.println(readFuse(EFUSE_RD), HEX);
- break;
- case 'L': case 'l':
- Serial.print("LOCK: ");
- Serial.println(readFuse(LOCK_RD) & 3, HEX);
- break;
- case 'C': case 'c':
- Serial.println("Apagando...");
- chipErase();
- break;
- case 'R': case 'r':
- Serial.println("Reiniciando fuses...");
- sig = readSignature(id);
- if (sig == ATTINY13) {
- writeFuse(LFUSE_WR, 0x6A);
- writeFuse(HFUSE_WR, 0xFF);
- } else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84 ||
- sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85) {
- writeFuse(LFUSE_WR, 0x62);
- writeFuse(HFUSE_WR, 0xDF);
- writeFuse(EFUSE_WR, 0xFF);
- }
- break;
- case 'T': case 't':
- Serial.println("Travando...");
- writeLock (0);
- break;
- }
- // espera concluir operação
- while (!digitalRead(SDO))
- ;
- // sai do modo programação
- digitalWrite(SCI, LOW);
- digitalWrite(VCC, LOW); // desliga Vcc
- onOff = 0; // desliga 12V
- digitalWrite(LED, LOW);
- Serial.println("Pronto");
- }
- }
- // Envia instrução e dado e lê resposta
- byte shiftOut (byte dado, byte instr) {
- int inBits = 0;
- // Espera final da operação anterior
- while (!digitalRead(SDO))
- ;
- unsigned int dout = (unsigned int) dado << 2;
- unsigned int iout = (unsigned int) instr << 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;
- }
- // Le um fuse (ou lock)
- byte readFuse (unsigned int fuse)
- {
- shiftOut(0x04, 0x4C);
- shiftOut(0x00, (byte) (fuse >> 8));
- return shiftOut(0x00, (byte) fuse);
- }
- // Escreve em um fuse
- void writeFuse (unsigned int fuse, byte val) {
- shiftOut(0x40, 0x4C);
- shiftOut( val, 0x2C);
- shiftOut(0x00, (byte) (fuse >> 8));
- shiftOut(0x00, (byte) fuse);
- }
- // Le a identificação do chip
- unsigned int readSignature (byte *sig) {
- shiftOut(0x08, 0x4C);
- for (int ii = 0; ii < 3; ii++) {
- shiftOut( ii, 0x0C);
- shiftOut(0x00, 0x68);
- sig[ii] = shiftOut(0x00, 0x6C);
- }
- return (sig[1] << 8) | sig[2];
- }
- // Faz apagamento completo do chip
- void chipErase() {
- shiftOut(0x80, 0x4C);
- shiftOut(0x00, 0x64);
- shiftOut(0x00, 0x6C);
- }
- // Escreve no lock
- byte writeLock(byte val) {
- shiftOut(0x20, 0x4C);
- shiftOut(val, 0x2C);
- shiftOut(0x00, 0x64);
- shiftOut(0x00, 0x6C);
- }
Futuramente vou mostrar o projeto de um recuperador standalone de ATtinys.
Nenhum comentário:
Postar um comentário