Il mio Fuse Rescue :)

Ciao a tutti

Sto pensando di costruirne uno con un ATmega328P come programmatore, che controlli anche un LCD su cui mostrare le opzioni. Per fare ciò, vorrei mettere delle resistenze (220 ohm?) in serie a PD0...PD7 che inviano i dati a PB0...PC1 e collegare l'LCD su PD0...PD5.
Finita l'impostazione, inizia la programmazione, con il display che rimane collegato in lettura sui dati.
Le resistenze servono solo nel caso che il microcontrollore da reimpostare abbia attivo il clock interno e un programma caricato che attivi PB0...PC1.

Dovrebbe funzionare... Che ne pensate?

Grazie
Gianluca

Nota: il pulsante SEL non lo monterò.

/*  
 HVFuse - Use High Voltage Programming Mode to Set Fuses on ATmega328
 09/23/08  Jeff Keyzer  http://mightyohm.com                
 The HV programming routines are based on those described in the
 ATmega48/88/168 datasheet 2545M-AVR-09/07, pg. 290-297
 This program should work for other members of the AVR family, but has only
 been verified to work with the ATmega168.  If it works for you, please
 let me know!  http://mightyohm.com/blog/contact/
 */

// Desired fuse configuration
#define  HFUSE  0xD9   // Default for ATmega328, for others see   // ELECTROPEPPER CHANGED THIS LINES
#define  LFUSE  0x62   // http://www.engbedded.com/cgi-bin/fc.cgi //

/*
Pin I/O Port  Funzione
 2   0  PD0   Data0 | RS
 3   1  PD1   Data1 | EN  L
 4   2  PD2   Data2 | D4  C
 5   3  PD3   Data3 | D5  D
 6   4  PD4   Data4 | D6
11   5  PD5   Data5 | D7
12   6  PD6   Data6
13   7  PD7   Data7
14   8  PB0   VCC
15   9  PB1   BS1
16  10  PB2   WR
17  11  PB3   OE
18  12  PB4   RDY
19  13  PB5   XA0
23  14  A0 PC0   RST
24  15  A1 PC1   Pulsante
25  16  A2 PC2   BS2
26  17  A3 PC3   XTAL1
27  18  A4 PC4   XA1
28  19  A5 PC5   PAGEL
*/

// Pin Assignments
#define  VCC     8
#define  RDY     12     // RDY/!BSY signal from target
#define  OE      11
#define  WR      10
#define  BS1     9
#define  XA0     13
#define  XA1     18    // Analog inputs 0-5 can be addressed as
#define  PAGEL   19    // digital outputs 14-19
#define  RST     14    // Output to level shifter for !RESET
#define  BS2     16
#define  XTAL1   17

#define  PULSANTE  15    // Start button


const char *percorso=__FILE__; // Dalla macro __FILE__  prende il percorso del file in uso. Questa parte  di programma,
      char ver[12];            // però, si trova  nel file/cartella  dell'IDE c_setup, in cui non è scritta la versione
                               // del programma. La versione è scritta nel file principale, che ha lo stesso nome della
                               // cartella che contiene tutti i file del programma. Questa  riga, quindi, non può stare
                               // nel setup.
#include <LiquidCrystal.h>
LiquidCrystal lcd(0,1,2,3,4,5); // RS,EN,D4,D5,D6,D7

uint32_t t_premuto; // millis() alla pressione del pulsante.
uint8_t prg=0; // Numero del programma selezionato.
uint8_t lfuse;
uint8_t hfuse;
uint8_t xfuse;
uint8_t stato=0;


void setup()  // run once, when the sketch starts
{
pinMode(0, OUTPUT); // LCD RS
pinMode(1, OUTPUT); // LCD EN
pinMode(2, OUTPUT); // LCD D4
pinMode(3, OUTPUT); // LCD D5
pinMode(4, OUTPUT); // LCD D6
pinMode(5, OUTPUT); // LCD D7
pinMode(A1, INPUT_PULLUP);

pinMode(VCC, OUTPUT); 
pinMode(RDY, INPUT);
pinMode(OE, OUTPUT);
pinMode(WR, OUTPUT);
pinMode(BS1, OUTPUT);
pinMode(XA0, OUTPUT);
pinMode(XA1, OUTPUT);
pinMode(PAGEL, OUTPUT);
pinMode(RST, OUTPUT);    // Signal to level shifter for +12V !RESET.
digitalWrite(RST, HIGH); // Level shifter is inverting: this shuts off 12V.
pinMode(BS2, OUTPUT);
pinMode(XTAL1, OUTPUT);

lcd.print(F(" Fuse Rescue GG "));
lcd.setCursor(2,1); lcd.print("LF");
lcd.setCursor(7,1); lcd.print("HF");
lcd.setCursor(11,1); lcd.print("Ext");
delay(1000);
}


#define PREMUTO !(PINC&0b000000010) // Pulsante su PC1 (A1).

void loop()
{
t_premuto=millis(); // Prende il tempo.
while(PREMUTO) // Pressione lunga: programmazione.
  {
  if(millis()-t_premuto>3000)
    {
    lcd.setCursor(0,0); lcd.print(F("*PROGRAMMAZIONE*"));
    programma();
    lcd.setCursor(0,0); lcd.print(F(" *** FATTO! *** "));
    stato=0;
    }
  }
  
if    (!PREMUTO) stato=0; // Attesa.
else if(PREMUTO) stato=1; // Incrementa prg.


if(stato==1)
  {
  prg++;
  lcd.setCursor(0,0); lcd.print(prg); // Numero progressivo.
  lcd.setCursor(2,0); 
  switch(prg)
    {
    case 1:      
    lcd.print(F("328P 16MHZ BL "));
    lfuse=0xFF; hfuse=0xFD; xfuse=0xDF;
    display_fuse();
    break;

    case 2:      
    lcd.print(F("328P 16MHZ BL "));
    lfuse=0xFF; hfuse=0xFD; xfuse=0xDF;
    display_fuse();
    break;
    
    case 3:      
    lcd.print(F("328P 16MHZ BL "));
    lfuse=0xFF; hfuse=0xFD; xfuse=0xDF;
    display_fuse();
    break;
    
    case 4:      
    lcd.print(F("328P 16MHZ BL "));
    lfuse=0xFF; hfuse=0xFD; xfuse=0xDF;
    display_fuse();
    break;
    }
  stato=0;
  }
}

void display_fuse()
 {
 lcd.setCursor(0,1); 
 lcd.print(lfuse,HEX); lcd.print(hfuse,HEX); lcd.print(xfuse,HEX);
 }


void sendcmd(byte command)  // Send command to target AVR.
  {
  // Set controls for command mode:
  digitalWrite(XA1, HIGH);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  //DATA = B01000000;  // Command to load fuse bits
  PORTD = command;
  digitalWrite(XTAL1, HIGH);  // pulse XTAL to send command to target.
  delay(1);
  digitalWrite(XTAL1, LOW);
  //delay(1);
  }

void writefuse(byte fuse, boolean highbyte)  // write high or low fuse to AVR.
  {
  // if highbyte = true, then we program HFUSE, otherwise LFUSE.
  // Enable data loading:
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, HIGH);
  delay(1);
  // Write fuse:
  PORTD = fuse;  // set desired fuse value.
  digitalWrite(XTAL1, HIGH);
  delay(1);
  digitalWrite(XTAL1, LOW);
  if(highbyte == true) digitalWrite(BS1, HIGH);  // program HFUSE.
  else                 digitalWrite(BS1, LOW);
  digitalWrite(WR, LOW);
  delay(1);
  digitalWrite(WR, HIGH);
  delay(100);
  }

void programma()
  {
  // Set up control lines for HV parallel programming.
  PORTD = 0x00; // Clear digital pins 0-7.
  DDRD = 0xFF; // set digital pins 0-7 as outputs.
  
  // Initialize pins to enter programming mode:
  digitalWrite(PAGEL, LOW);
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  // Enter programming mode
  digitalWrite(VCC, HIGH); // Apply VCC to start programming process.
  digitalWrite(WR, HIGH);  // Now we can assert !OE and !WR.
  digitalWrite(OE, HIGH);
  delay(1);
  digitalWrite(RST, LOW);   // Apply 12V to !RESET thru level shifter.
  delay(1);
  // Now we're in programming mode until RST is set HIGH again.
  
  // Firstly we program HFUSE:
  sendcmd(B01000000);  // Send command to enable fuse programming mode
  writefuse(HFUSE, true);
  
  // Now we program LFUSE:
  sendcmd(B01000000);
  writefuse(LFUSE, false);
  
  delay(1000);  // wait a while to allow button to be released
  
  // Exit programming mode:
  digitalWrite(RST, HIGH);
  
  // Turn off all outputs:
  PORTD = 0x00;
  digitalWrite(OE, LOW);
  digitalWrite(WR, LOW);
  digitalWrite(PAGEL, LOW);
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  digitalWrite(VCC, LOW);
  } 

Ho come l'impressione che il display mostrerà cose strane.

Alla fine funge, li hai sbriccati?

Ciao.

Sì, può darsi, ma solo dopo aver fatto l'impostazione e programmato. Poi vedrò se basterà un lcd.clear() o sarà necessario spegnere e riaccendere...

Uno stava già da parte; adesso gli fa compagnia un altro dei tre arrivati dalla Cina... >:(

Non mi è molto chiaro quanto tempo il bootloader ritardi l'avvio... Nel magnetometro, a 16MHz, lo notavo; nello stroboscopio, anch'esso a 16MHz, invece, ho messo uno dei microcontrollori appena arrivati dalla Cina su cui ho caricato il bootloader (dopo aver bloccato il primo con AVR DUDESS), ma mi sembra di non notarlo...

Avendo il resettatore di fuse, potrei fare esperimenti a volontà... :smile:

Io invece del display userei la SerialCommand per farci una interfaccia comandi da seriale.

Non lo so, il bootloader è certamente utile, ma se hai un programmatore ISP affidabile non vedo la necessità di impegnare 2k di flash. In sostanza brasa tutto e vai di ISP.

PS: mi pare che tu lo hai un programmatore SPI serio o no.

Ciao.

Uso l'USBASP. Ho caricato il bootloader solo perché lo fa l'IDE senza rischi.
Con l'HV potrei programmare in un attimo i fuse dei microcontrollori nuovi senza bootloader e a 1MHz, a 8MHz o a 16MHz quarzati.

Prevedi di aggiungere anche altri zoccoli per i diversi formati, come nella versione di mightyohm ? ... e magari diverse opzioni selezionabili col pulsante ?

Bello sarebbe per me avere qui le mie "cosine" che mi permetterebbero di testare la funzione per leggere la signature in parallel mode.
Dalla signature si ricavano i fuse byte di default e li si visualizza chiedendo se si vuole scrivere le modifiche.

Comunque prima ci vorrebbe il circuito elevatore 5 to 12V.
Quello proposto da @Datman qui come ti pare?

Ciao.

Questo è il programma allo stato attuale:

const char *percorso=__FILE__; // Dalla macro __FILE__  prende il percorso del file in uso. Questa parte  di programma,
      char ver[12];            // però, si trova  nel file/cartella  dell'IDE c_setup, in cui non è scritta la versione
                               // del programma. La versione è scritta nel file principale, che ha lo stesso nome della
                               // cartella che contiene tutti i file del programma. Questa  riga, quindi, non può stare
                               // nel setup.
                               
 /*  
 https://github.com/lsahidin/HVSP-HVPP/blob/master/HVPP-Atmega.ino
 
 HVFuse - Use High Voltage Programming Mode to Set Fuses on ATmega328
 09/23/08  Jeff Keyzer  http://mightyohm.com                
 The HV programming routines are based on those described in the
 ATmega48/88/168 datasheet 2545M-AVR-09/07, pg. 290-297
 This program should work for other members of the AVR family, but has
 only been verified to work with the ATmega168.  If it works for you,
 please let me know!  http://mightyohm.com/blog/contact/
 https://mightyohm.com/blog/products/hv-rescue-shield-2-x/source-code/
 
 https://www.instructables.com/HV-Rescue-Simple/
 */
 
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
uint32_t t_premuto; // millis() alla pressione del pulsante.
uint8_t prg=0; // Numero del programma selezionato.
uint8_t lfuse;
uint8_t hfuse;
uint8_t efuse;
uint8_t stato=1;

// Desired fuse configuration
#define  HFUSE  0xD9   // Default for ATmega328, for others see   // ELECTROPEPPER CHANGED THIS LINES
#define  LFUSE  0x62   // http://www.engbedded.com/cgi-bin/fc.cgi //

/*
Pin I/O Porta Segnale
 2   0  PD0   Data0
 3   1  PD1   Data1
 4   2  PD2   Data2
 5   3  PD3   Data3
 6   4  PD4   Data4
11   5  PD5   Data5
12   6  PD6   Data6
13   7  PD7   Data7
14   8  PB0   VCC
15   9  PB1   BS1
16  10  PB2   WR
17  11  PB3   OE
18  12  PB4   RDY
19  13  PB5   XA0
23  14  A0 PC0   RST
24  15  A1 PC1   Pulsante
25  16  A2 PC2   BS2
26  17  A3 PC3   XTAL1
27  18  A4 PC4   XA1      SDA
28  19  A5 PC5   PAGEL    SCL
*/

//     Segnale  I/O   Porta 
#define  VCC     8  // PB0
#define  BS1     9  // PB1
#define  WR      10 // PB2
#define  OE      11 // PB3
#define  RDY     12 // PB4 RDY/!BSY signal from target
#define  XA0     13 // PB5 XA0
#define  RST     14 // PC0 A0  // Output to level shifter for !RESET
#define  BS2     16 // PC2 A2
#define  XTAL1   17 // PC3 A3
#define  XA1     18 // PC4 A4 SDA
#define  PAGEL   19 // PC5 A5 SCL


void setup()
{
char *ver_ext=strrchr(percorso,'v'); // Va a cercare l'ultima 'v' nel percorso.
byte n_car=strlen(ver_ext)-4; // Calcola la lunghezza escludendo .ino.
                              // (Perché in questo programma se metto 4 appare v0.1\a???...)
strncpy(ver, ver_ext, n_car); // Copia i primi n_car caratteri da ver_ext a ver.
ver[n_car]='\0'; // Mette il terminatore in fondo.

pinMode(A1, INPUT_PULLUP); // Pulsante.

pinMode(VCC, OUTPUT); 
pinMode(RDY, INPUT);
pinMode(OE, OUTPUT);
pinMode(WR, OUTPUT);
pinMode(BS1, OUTPUT);
pinMode(XA0, OUTPUT);
pinMode(XA1, OUTPUT);
pinMode(PAGEL, OUTPUT);
pinMode(RST, OUTPUT);    // Signal to level shifter for +12V !RESET.
digitalWrite(RST, HIGH); // Level shifter is inverting: this shuts off 12V.
pinMode(BS2, OUTPUT);
pinMode(XTAL1, OUTPUT);

u8g2.begin();
//u8g2.setFont(u8g2_font_6x10_tf);
//u8g2.setFontRefHeightExtendedText();
//u8g2.setDrawColor(1);
//u8g2.setFontPosTop();
//u8g2.setFontDirection(0);

u8g2.clearBuffer();  
u8g2.setFontMode(2);

u8g2.setFont(u8g2_font_cu12_tr);   
u8g2.setCursor(10,11); u8g2.print("Fuse Rescue GG");
u8g2.setCursor(7,25);  u8g2.print(F("per ATmega328P"));

u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.setCursor((128-6*strlen(ver))/2, 39); u8g2.print(ver);

u8g2.setFont(u8g2_font_cu12_tr); 

u8g2.setCursor(10,63); u8g2.print(F("  LF   HF   EF  "));
u8g2.sendBuffer();
delay(2000);
u8g2.clear();
u8g2.setCursor(0,15); u8g2.print(F("Pressione breve:"));
u8g2.setCursor(0,31); u8g2.print(F("   SELEZIONE    "));
u8g2.setCursor(0,47); u8g2.print(F("Pressione lunga:"));
u8g2.setCursor(0,63); u8g2.print(F(" PROGRAMMAZ. "));
u8g2.sendBuffer();
delay(2000);
u8g2.clear();
u8g2.setFont(u8g2_font_ncenB08_tr);
}


#define PREMUTO !(digitalRead(A1)) // Pulsante su PC1 (A1).

void loop()
{
if(!PREMUTO) t_premuto=millis(); // Prende il tempo.
if(millis()-t_premuto>2000) // Pressione lunga: programmazione.
  {
  u8g2.clear();
  //u8g2.setFont(u8g2_font_cu9_tr); 
  u8g2.setFont(u8g2_font_fancypixels_tr); // *** Ha le dimensini giuste! ***
  u8g2.setCursor(3,15);
  u8g2.print(F("PROGRAMMAZIONE")); u8g2.sendBuffer();
  programma();
  //u8g2.setFontMode(2);
  //u8g2.setFont(u8g2_font_cu12_tr);  
  delay(1000);
  u8g2.clear();
  u8g2.setCursor(13,15); u8g2.print(F(" *** FATTO! *** ")); u8g2.sendBuffer();
  delay(2000);
  if(prg>1)prg-=1; else prg=4;
  stato=1;
  return;
  }
  
u8g2.setFont(u8g2_font_ncenB08_tr);
if(stato==1 && !PREMUTO) // !PREMUTO serve per incrementare prg solo quando viene lasciato il
  {     // pulsante, impedendo che venga incrementato quando si preme a lungo per progammare.    
  prg++; if(prg>4) prg=1;
  u8g2.clear();
  u8g2.setCursor(0,15);
  if(prg<10) u8g2.print(' ');
  u8g2.print(prg); u8g2.sendBuffer(); // Numero progressivo.
  u8g2.setCursor(20,15); 
 // u8g2.setFont(u8g2_font_prospero_nbp_tr); 
  u8g2.setFont(u8g2_font_5x7_tr); 
  switch(prg)
    {
    case 1:      
    u8g2.print(F("ARDUINO DEFAULT"));
    u8g2.setCursor(0,23); 
    u8g2.print(F("16MHz  No eesave"));
    u8g2.setCursor(0,31); 
    u8g2.print(F("Con BOOTLOADER"));
    u8g2.setCursor(0,39); 
    u8g2.print(F("BOD 2,7V"));
    lfuse=0xFF; hfuse=0xDE; efuse=0xFD;
    display_fuse();
    break;

    case 2:      
//    u8g2.print(F(" ARDUINO EESAVE "));
//    u8g2.setCursor(0,1); 
//    u8g2.print(F("16MHz  x EESAVE "));
//    u8g2.setCursor(0,2); 
//    u8g2.print(F("  x BOOTLOADER  "));
//    u8g2.setCursor(0,3); 
//    u8g2.print(F("    BOD 2,7V    "));
    lfuse=0xFF; hfuse=0xD6; efuse=0xFD;
    display_fuse();
    break;
    
    case 3:      
//    u8g2.print(F(" 8MHz No EESAVE "));
//    u8g2.setCursor(0,1); 
//    u8g2.print(F(" 8MHz  - EESAVE "));
//    u8g2.setCursor(0,2); 
//    u8g2.print(F("  - bootloader  "));
//    u8g2.setCursor(0,3); 
//    u8g2.print(F("    BOD 2,7V    "));
    lfuse=0xFF; hfuse=0xDE; efuse=0xFD;
    display_fuse();
    break;
    
    case 4:      
//    u8g2.print(F("  8MHz  EESAVE  "));
//    u8g2.setCursor(0,1); 
//    u8g2.print(F(" 8MHz  x EESAVE "));
//    u8g2.setCursor(0,2); 
//    u8g2.print(F("  - bootloader  "));
//    u8g2.setCursor(0,3); 
//    u8g2.print(F("    BOD 2,7V    "));
    lfuse=0xFF; hfuse=0xD6; efuse=0xFD;
    display_fuse();
    break;

    
    }
  u8g2.sendBuffer();
  stato=2;
  }
if(stato==2 && !PREMUTO) stato=3; 
if(stato==3 && PREMUTO) stato=1;
}

void display_fuse()
  {
  //u8g2.setFont(u8g2_font_cu12_tr); 
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setCursor( 10,63); u8g2.print(lfuse,HEX);
  u8g2.setCursor( 57,63); u8g2.print(hfuse,HEX);
  u8g2.setCursor(104,63); u8g2.print(efuse,HEX);
  }


void sendcmd(byte command)  // Send command to target AVR.
  {
  // Set controls for command mode:
  digitalWrite(XA1, HIGH);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  //DATA = B01000000;  // Command to load fuse bits
  PORTD = command;
  digitalWrite(XTAL1, HIGH);  // pulse XTAL to send command to target.
  delay(1);
  digitalWrite(XTAL1, LOW);
  //delay(1);
  }

void writefuse(byte fuse, boolean highbyte)  // write high or low fuse to AVR.
  {
  // if highbyte = true, then we program HFUSE, otherwise LFUSE.
  // Enable data loading:
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, HIGH);
  delay(1);
  // Write fuse:
  PORTD = fuse;  // set desired fuse value.
  digitalWrite(XTAL1, HIGH);
  delay(1);
  digitalWrite(XTAL1, LOW);
  if(highbyte == true) digitalWrite(BS1, HIGH);  // program HFUSE.
  else                 digitalWrite(BS1, LOW);
  digitalWrite(WR, LOW);
  delay(1);
  digitalWrite(WR, HIGH);
  delay(100);
  }

void programma()
  {
  // Set up control lines for HV parallel programming.
  PORTD = 0x00; // Clear digital pins 0-7.
  DDRD = 0xFF; // set digital pins 0-7 as outputs.
  
  // Initialize pins to enter programming mode:
  digitalWrite(PAGEL, LOW);
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  // Enter programming mode
  digitalWrite(VCC, HIGH); // Apply VCC to start programming process.
  digitalWrite(WR, HIGH);  // Now we can assert !OE and !WR.
  digitalWrite(OE, HIGH);
  delay(1);
  digitalWrite(RST, LOW);   // Apply 12V to !RESET thru level shifter.
  delay(1);
  // Now we're in programming mode until RST is set HIGH again.
  
  // Firstly we program HFUSE:
  sendcmd(B01000000);  // Send command to enable fuse programming mode
  writefuse(HFUSE, true);
  
  // Now we program LFUSE:
  sendcmd(B01000000);
  writefuse(LFUSE, false);
  
  delay(1000);  // wait a while to allow button to be released
  
  // Exit programming mode:
  digitalWrite(RST, HIGH);
  
  // Turn off all outputs:
  PORTD = 0x00;
  digitalWrite(OE, LOW);
  digitalWrite(WR, LOW);
  digitalWrite(PAGEL, LOW);
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  digitalWrite(VCC, LOW);
  }


/*
0.3   19/4/23 Provo a farlo non bloccante.
0.6   20/4/23 Non bloccante OK.
0.7   21/4/23 xfuse -> efuse.
              Decido di usare un LCD 16x4.
0.8   23/4/23 Passo dall'LCD, per il quale ero arrivato a pensare a
            un ingombrante 16x4, a un OLED da 1,3" I2C con SSH1106.
0.9   24/4/23 

*/

Da fare:

  • Verificare se la programmazione del 328P è uguale a quella del 168: giorni fa ho dato un'occhiata e la sequenza non mi è sembrata proprio uguale a quella descritta, però mi sembra che in origine il datasheet fosse unico per il 328P e per il 168... @Etemenanki, dai un'occhiata anche tu?
  • Fare una tabellina con i valori dei fuse per varie configurazioni
  • Montarlo e vedere se funziona.
  • Aggiungere le configurazioni di fuse (dopo la prima, i valori dei fuse sono copiati)
  • Aggiungere la possibilità di leggere i fuse.
    Si potrebbe fare:
    1: Lettura
    2... n: Programmazione:
    2: con i valori letti
    3: Arduino default
    4: ATmega328P default
    5: 1MHz
    ...

Sto aspettando lo zoccolo Textool dalla Cina.
Se volete collaborare, siete benvenuti! :slight_smile:
Potete anche ampliarlo per altri microcontrollori, anche con zoccoli aggiuntivi.

A proposito dei fuse:

Versione aggiornata (v0.10), con il menu predisposto anche per la lettura e la successiva scrittura dei valori letti:

const char *percorso=__FILE__; // Dalla macro __FILE__  prende il percorso del file in uso. Questa parte  di programma,
      char ver[12];            // però, si trova  nel file/cartella  dell'IDE c_setup, in cui non è scritta la versione
                               // del programma. La versione è scritta nel file principale, che ha lo stesso nome della
                               // cartella che contiene tutti i file del programma. Questa  riga, quindi, non può stare
                               // nel setup.
                               
 /*  
 https://github.com/lsahidin/HVSP-HVPP/blob/master/HVPP-Atmega.ino
 
 HVFuse - Use High Voltage Programming Mode to Set Fuses on ATmega328
 09/23/08  Jeff Keyzer  http://mightyohm.com                
 The HV programming routines are based on those described in the
 ATmega48/88/168 datasheet 2545M-AVR-09/07, pg. 290-297
 This program should work for other members of the AVR family, but has
 only been verified to work with the ATmega168.  If it works for you,
 please let me know!  http://mightyohm.com/blog/contact/
 https://mightyohm.com/blog/products/hv-rescue-shield-2-x/source-code/
 
 https://www.instructables.com/HV-Rescue-Simple/
 */
 
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
uint32_t t_premuto; // millis() alla pressione del pulsante.
uint8_t prg=0; // Numero del programma selezionato.
uint8_t lfuse;
uint8_t hfuse;
uint8_t efuse;
uint8_t lfuse_letto;
uint8_t hfuse_letto;
uint8_t efuse_letto;
uint8_t stato=1;
#define PREMUTO !(digitalRead(A1)) // Pulsante su PC1 (A1).
// Desired fuse configuration
#define  HFUSE  0xD9   // Default for ATmega328, for others see   // ELECTROPEPPER CHANGED THIS LINES
#define  LFUSE  0x62   // http://www.engbedded.com/cgi-bin/fc.cgi //

/*
Pin I/O Porta Segnale
 2   0  PD0   Data0
 3   1  PD1   Data1
 4   2  PD2   Data2
 5   3  PD3   Data3
 6   4  PD4   Data4
11   5  PD5   Data5
12   6  PD6   Data6
13   7  PD7   Data7
14   8  PB0   VCC
15   9  PB1   BS1
16  10  PB2   WR
17  11  PB3   OE
18  12  PB4   RDY
19  13  PB5   XA0
23  14  A0 PC0   RST
24  15  A1 PC1   Pulsante
25  16  A2 PC2   BS2
26  17  A3 PC3   XTAL1
27  18  A4 PC4   XA1      SDA
28  19  A5 PC5   PAGEL    SCL
*/

//     Segnale  I/O   Porta 
#define  VCC     8  // PB0
#define  BS1     9  // PB1
#define  WR      10 // PB2
#define  OE      11 // PB3
#define  RDY     12 // PB4 RDY/!BSY signal from target
#define  XA0     13 // PB5 XA0
#define  RST     14 // PC0 A0  // Output to level shifter for !RESET
#define  BS2     16 // PC2 A2
#define  XTAL1   17 // PC3 A3
#define  XA1     18 // PC4 A4 SDA
#define  PAGEL   19 // PC5 A5 SCL


void setup()
{
char *ver_ext=strrchr(percorso,'v'); // Va a cercare l'ultima 'v' nel percorso.
byte n_car=strlen(ver_ext)-4; // Calcola la lunghezza escludendo .ino.
                              // (Perché in questo programma se metto 4 appare v0.1\a???...)
strncpy(ver, ver_ext, n_car); // Copia i primi n_car caratteri da ver_ext a ver.
ver[n_car]='\0'; // Mette il terminatore in fondo.

pinMode(A1, INPUT_PULLUP); // Pulsante con 100nF in parallelo.

pinMode(VCC, OUTPUT); 
pinMode(RDY, INPUT);
pinMode(OE, OUTPUT);
pinMode(WR, OUTPUT);
pinMode(BS1, OUTPUT);
pinMode(XA0, OUTPUT);
pinMode(XA1, OUTPUT);
pinMode(PAGEL, OUTPUT);
pinMode(RST, OUTPUT);    // HIGH => RESET: +12V
pinMode(BS2, OUTPUT);
pinMode(XTAL1, OUTPUT);

u8g2.begin();
//u8g2.setFont(u8g2_font_6x10_tf);
//u8g2.setFontRefHeightExtendedText();
//u8g2.setDrawColor(1);
//u8g2.setFontPosTop();
//u8g2.setFontDirection(0);

u8g2.clearBuffer();  
u8g2.setFontMode(2);

u8g2.setFont(u8g2_font_cu12_tr);   
u8g2.setCursor(10,11); u8g2.print("Fuse Rescue GG");
u8g2.setCursor(7,25);  u8g2.print(F("per ATmega328P"));

u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.setCursor((128-6*strlen(ver))/2, 39); u8g2.print(ver);

u8g2.setFont(u8g2_font_cu12_tr); 
u8g2.setCursor(10,63); u8g2.print(F("  LF   HF   EF  "));
u8g2.sendBuffer();
delay(2000);
while(PREMUTO);
u8g2.clear();
u8g2.setCursor(11,15); u8g2.print(F("Pressione breve:"));
u8g2.setCursor(22,31); u8g2.print(F("SELEZIONE"));
u8g2.setCursor(11,47); u8g2.print(F("Pressione lunga:"));
u8g2.setCursor(15,63); u8g2.print(F("ESECUZIONE"));
u8g2.sendBuffer();
delay(2000);
while(PREMUTO);
u8g2.clear();
u8g2.setFont(u8g2_font_ncenB08_tr);
}


void loop()
{
if(!PREMUTO) t_premuto=millis(); // Prende il tempo.
if(millis()-t_premuto>2000) // Pressione lunga: esegue.
  {
  u8g2.clear();
  //u8g2.setFont(u8g2_font_cu9_tr); 
  u8g2.setFont(u8g2_font_fancypixels_tr); // *** Ha le dimensini giuste! ***
  u8g2.setCursor(3,15);
  if(prg==1)
    {
    u8g2.setCursor(33,15); u8g2.print(F("LETTURA")); u8g2.sendBuffer();
    legge();
    delay(1000);
    u8g2.clear();
    u8g2.setCursor(13,15); u8g2.print(F(" *** FATTO! *** ")); u8g2.sendBuffer();
    display_fuse_letti();
    delay(2000);
    }
  else
    {
    u8g2.print(F("PROGRAMMAZIONE")); u8g2.sendBuffer();
    programma();
    delay(1000);
    u8g2.clear();
    u8g2.setCursor(13,15); u8g2.print(F(" *** FATTO! *** ")); u8g2.sendBuffer();
    }
  delay(2000);
  if(prg>1)prg-=1; else prg=6;
  stato=1;
  return;
  }
  
u8g2.setFont(u8g2_font_ncenB08_tr);
if(stato==1 && !PREMUTO) // !PREMUTO serve per incrementare prg solo quando viene lasciato il
  {     // pulsante, impedendo che venga incrementato quando si preme a lungo per progammare.    
  prg++; if(prg>6) prg=1;
  u8g2.clear();
  u8g2.setCursor(0,15);
  if(prg<10) u8g2.print(' ');
  u8g2.print(prg); u8g2.sendBuffer(); // Numero progressivo.
  u8g2.setCursor(20,15); 
 // u8g2.setFont(u8g2_font_prospero_nbp_tr); 
  u8g2.setFont(u8g2_font_5x7_tr); 
  switch(prg)
    {
    case 1:      
      u8g2.setCursor(30,15); u8g2.print(F("*** LETTURA ***"));
    break;

    case 2:      
      u8g2.print(F("PRG con valori letti"));
      u8g2.setCursor(0,1); 
      lfuse=lfuse_letto; hfuse=hfuse_letto; efuse=efuse_letto;
      display_fuse();
    break;
    
    case 3:      
      u8g2.setCursor(30,15); u8g2.print(F("ARDUINO DEFAULT"));
      u8g2.setCursor(26,23); u8g2.print(F("16MHz  No eesave"));
      u8g2.setCursor(32,31); u8g2.print(F("Con BOOTLOADER"));
      u8g2.setCursor(48,39); u8g2.print(F("BOD 2,7V"));
      lfuse=0xFF; hfuse=0xDE; efuse=0xFD;
      display_fuse();
    break;

    case 4:      
      u8g2.setCursor(33,15); u8g2.print(F("ARDUINO EESAVE"));
      u8g2.setCursor(23,23); u8g2.print(F("16MHz  Con EESAVE"));
      u8g2.setCursor(32,31); u8g2.print(F("Con BOOTLOADER"));
      u8g2.setCursor(48,39); u8g2.print(F("BOD 2,7V"));
      lfuse=0xFF; hfuse=0xD6; efuse=0xFD;
      display_fuse();
    break;

    case 5:      
      u8g2.setCursor(38,15); u8g2.print(F("8MHz  EESAVE"));
      u8g2.setCursor(26,23); u8g2.print(F("8MHz  Con EESAVE"));
      u8g2.setCursor(36,31); u8g2.print(F("No bootloader"));
      u8g2.setCursor(48,39); u8g2.print(F("BOD 2,7V"));
      lfuse=0xFF; hfuse=0xD6; efuse=0xFD;
      display_fuse();
    break;
    
    case 6:      
      u8g2.setCursor(33,15); u8g2.print(F("8MHz No eesave"));
      u8g2.setCursor(33,23); u8g2.print(F("8MHz No eesave"));
      u8g2.setCursor(36,31); u8g2.print(F("No bootloader"));
      u8g2.setCursor(48,39); u8g2.print(F("BOD 2,7V"));
      lfuse=0xFF; hfuse=0xDE; efuse=0xFD;
      display_fuse();
    break;   
    }
  u8g2.sendBuffer();
  stato=2;
  }
if(stato==2 && !PREMUTO) stato=3; 
if(stato==3 && PREMUTO) stato=1;
}

void display_fuse()
  {
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setCursor( 10,63); u8g2.print(lfuse,HEX);
  u8g2.setCursor( 57,63); u8g2.print(hfuse,HEX);
  u8g2.setCursor(104,63); u8g2.print(efuse,HEX);
  }

void display_fuse_letti() // Preferisco tenere le due funzioni separate...
  {
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setCursor( 10,63); u8g2.print(lfuse_letto,HEX);
  u8g2.setCursor( 57,63); u8g2.print(hfuse_letto,HEX);
  u8g2.setCursor(104,63); u8g2.print(efuse_letto,HEX);
  }


void sendcmd(byte command)  // Send command to target AVR.
  {
  // Set controls for command mode:
  digitalWrite(XA1, HIGH);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  //DATA = B01000000;  // Command to load fuse bits
  PORTD = command;
  digitalWrite(XTAL1, HIGH);  // pulse XTAL to send command to target.
  delay(1);
  digitalWrite(XTAL1, LOW);
  //delay(1);
  }

void writefuse(byte fuse, boolean highbyte)  // write high or low fuse to AVR.
  {
  // if highbyte = true, then we program HFUSE, otherwise LFUSE.
  // Enable data loading:
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, HIGH);
  delay(1);
  // Write fuse:
  PORTD = fuse;  // set desired fuse value.
  digitalWrite(XTAL1, HIGH);
  delay(1);
  digitalWrite(XTAL1, LOW);
  if(highbyte == true) digitalWrite(BS1, HIGH);  // program HFUSE.
  else                 digitalWrite(BS1, LOW);
  digitalWrite(WR, LOW);
  delay(1);
  digitalWrite(WR, HIGH);
  delay(100);
  }

void programma()
  {
  // Set up control lines for HV parallel programming.
  PORTD = 0x00; // Clear digital pins 0-7.
  DDRD = 0xFF; // set digital pins 0-7 as outputs.
  
  // Initialize pins to enter programming mode:
  digitalWrite(PAGEL, LOW);
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  // Enter programming mode
  digitalWrite(VCC, HIGH); // Apply VCC to start programming process.
  digitalWrite(WR, HIGH);  // Now we can assert !OE and !WR.
  digitalWrite(OE, HIGH);
  delay(1);
  digitalWrite(RST, HIGH);   // Apply 12V to !RESET thru level shifter.
  delay(1);
  // Now we're in programming mode until RST is set HIGH again.
  
  // Firstly we program HFUSE:
  sendcmd(B01000000);  // Send command to enable fuse programming mode
  writefuse(HFUSE, true);
  
  // Now we program LFUSE:
  sendcmd(B01000000);
  writefuse(LFUSE, false);
  
  while(PREMUTO);  // Si assicura che il pulsante sia stato lasciato.
  
  // Exit programming mode:
  digitalWrite(RST, LOW);
  
  // Turn off all outputs:
  PORTD = 0x00;
  digitalWrite(OE, LOW);
  digitalWrite(WR, LOW);
  digitalWrite(PAGEL, LOW);
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  digitalWrite(VCC, LOW);
  }


void legge()
{
// Produce lfuse_letto, hfuse_letto e efuse_letto.
}


/*
0.3   19/4/23 Provo a farlo non bloccante.
0.6   20/4/23 Non bloccante OK.
0.7   21/4/23 xfuse -> efuse.
              Decido di usare un LCD 16x4.
0.8   23/4/23 Passo dall'LCD, per il quale ero arrivato a pensare a un ingombrante 16x4,
            a un OLED da 1,3" I2C con SSH1106.
0.9   24/4/23 
0.10  27/4/23 Inverto il comendo del Reset 12V per adattarlo al mio schema elettrico con
            due transistor.
              Metto while(PREMUTO); al posto di delay(1000); in programma().
              Predispongo il menu per la lettura dei fuse e la successiva programmazione
            con gli stessi.
*/

Io l'altro giorno mi ero divertito ad immaginare una possibile configurazione con i tre zoccoli e dei connettori per poter montare sopra alla base una schedina aggiuntiva con altri zoccoli (tipo quelli per i TQFP e simili), l'elevatore avevo cercato di immaginarlo, dato che configurato come joulethief non ci sono molti modi per farlo, e ne era uscito quel casino qui :grin:

Credo che anche facendo tutto a componenti passanti ci possa stare nei 100x100

Da quello che ricordo il datasheet generale e' uguale per piu MCU, 48, 88, 168 e 328, poi nelle varie sezioni danno le specifiche dei modelli diversi ... avevo trovato tempo fa un documento che parlava di lettura e riscrittura dei fuse e lock bit con un programmatore HV, ma non lo trovo piu, provero' a cercare nel mio casino archivio per vedere se lo ritrovo.

EDIT: ho visto ora che hai cambiato il display, non funzionava con il 16x2 ?

In realtà mi sono reso conto che con solo 32 caratteri a disposizione non si riusciva a descrivere bene i parametri di ciascuna configurazione, perciò ho pensato a un 16x4, ma è gigantesco! Allora ho pensato di usare un OLED I2C da 1,3", di cui recentemente mi sono arrivati alcuni dalla Cina.

Il pull-up sul pulsante lo attivo internamente, mentre serve un 100nF in parallelo come antirimbalzo.

La commutazione dei 12V l'ho realizzata con due transistor, per evitare di dover tenere il convertitore non in uso con un carico maggiore di quando deve fornire i 12V. La tensione si abbasserebbe a 8,5V e poi dovrebbe rialzarsi (mi sembra che gli occorressero 15ms) al momento della programmazione.

Se non usi un quarzo ma l'oscillatore interno, puoi settare anche PB6 e PB7 come ingressi e metterci altri due pulsanti (oppure un'encoder)

1 Like

Sì, hai ragione: non ho indicato né il quarzo, né i due pin liberi. Giusto cercavo un pin per un cicalino! :slight_smile:
Con l'oscillatore RC a 8 MHz dovrebbe funzionare senza problemi...

L'encoder non mi sembra necessario, per come ho impostato il progetto:

  • pressione breve: incrementa;
  • pressione lunga: enter.

Eccolo, provvisoriamente realizzato con un Nano:

Modificato al volo, potrebbe andare ?

I tre connettori sotto sono per portare i segnali ad eventuali "shield" con zoccoli ZIF diversi (tqfp e simili) da montarci sopra solo se necessari, alimentzione o da jack o da USB, qualche filtro sulle alimentazioni ... vuoi aggiungere un cicalino su PB7 ?

Buongiorno, Ete

Sì, mi piace, anche se non ho verificato nel dettaglio i collegamenti ai chip da programmare.
Il cicalino possiamo prevederlo, attivo o passivo, su PB6 o PB7. I LED, ad alta efficienza, con 4k7...10k in serie, possono stare lì. Un LED sui +5V potrebbe essere utile ma, se il microcontrollore "master" funziona, visualizza subito le prime informazioni.

Ieri ho stampato le pagine del datasheet sulla programmazione parallela, con cui vorrei ricontrollare la sequenza di programmazione e scrivere quella di lettura dei fuse e della signature.

I collegamenti li ho fatti basandomi sul tuo schema a biro ... il led sul +5 potresti metterlo, ma non avrebbe molto senso, c'e' gia il display che ti dice se va o no, come dici anche tu ... cicalino aggiunto su PB7

Mi sto divertendo a vedere come uscirebbe uno stampato fatto tutto a componenti passanti in meno di 100x100mm, sembra starci tutto ed avanza pure spazio, anche se non molto.

Woww!!! :heart_eyes:

Per i componenti passanti: beh... Ormai su millefori uso esclusivamente resistenze e condensatori di bypass smd, ma anche spesso transistor (montati in diagonale) e integrati (lasciando solo i 4 bollini agli angoli)... Tra l'altro, ho fatto una grande scorta di resistenze smd, mentre i cassettini di quelle passanti sono in gran parte quasi vuoti e le uso solo per fare prove su breadboard o saldandole con i reofori interi.
:slight_smile:
La presa USB è quadrata, tipo B?