Il mio Fuse Rescue - Parte 2 :)

A e B sono gli ingressi della porta AND e O è l'uscita.
Messo per come è collegato al momento SDA->A VCC->B e O->PD6 o PD7.
Finché Vcc è zero cosa c'è su A non ha importanza.
Si, si quello avevi al momento e quello usi, pensavo al futuro.

Ciao.

Ma l'SDA del master deve anche ricevere! Serve un interruttore bidirezionale.

Guardo l'immagine del post #16. E non capisco da chi deve ricevere SDA che è sempre connesso al display.
Può essere che mi sfugge qualcosa come già accaduto, ma il micro sullo zif riceve solo bit da GPIO mica deve dialogare con il programmatore via i2c.

Vabbe, poi disegno qualcosa e vedi tu se può funzionare.

Ciao.

Ah! Dici che il display non risponde... Sicuro?
ChatGPT dice:

Direzione dei Dati

  • Bidirezionale: La linea SDA è bidirezionale, il che significa che sia il master che lo slave possono inviare dati su questa linea. Tuttavia, solo uno dei due comunica alla volta, secondo le regole del protocollo I2C.
  • Acknowledge: Dopo ogni byte di dati inviato, il ricevitore (che può essere sia il master che lo slave) deve inviare un bit di acknowledge (ACK) per confermare la ricezione del byte.

A domande precise ChatGPT dà belle risposte, eh!...

Comunque non riesco a capire perché legge sempre FF FF FF... >:(

Ancora non riesco a leggere i fuse, nonostante i numerosi tentativi. Questo è il programma aggiornato all'ultima versione:

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.                
/*
-----------------------------------------------------------------------------
| GIANLUCA GIANGRECO 2023                                                   |
| Partendo da:                                                              |
|                                                                           |
|   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 ATmega  |
|   48/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/                         |
|                                                                           |
| e aggiungendo il display, il menu e la lettura dei fuse, dei lock bits e  |
| dei signature bytes, secondo il datasheet.                                |
|                                                                           |
|   CLOCK: 8MHz RC INTERNO per lasciare liberi PB6 e PB7                    |
|   (ATmega328P nuovo di fabbrica senza DIV8: E2, D9, FF).                  |
|   Con MiniCore si può comunque programmare tramite USB.                   |
-----------------------------------------------------------------------------
  *** EEPROM ***
 0: lfuse manuale
 1: hfuse manuale
 2: efuse manuale
*/
#include <U8g2lib.h>
#include <Wire.h>
#include <EEPROM.h>
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
uint32_t t_press_lunga; // millis() aggiornato finché è premuto il pulsante.
uint32_t t_clic; // millis() alla pressione del pulsante.
bool clic=false; // Pressione breve dell'encoder.
bool esce_da_impostazioni_manuali=false;
uint8_t prg=1; // Numero del programma selezionato.
uint8_t stato=1;
uint8_t inizio=1;
bool nessuna_risposta=LOW;

int8_t E; // Risultato della routine encoder(): 1, -1, 0.
uint8_t S; // Lettura dei due valori dell'encoder.
uint8_t So;// Lettura precedente dell'encoder.
int8_t X; // Usato in encoder() per evitare letture multiple.

uint8_t lfuse;
uint8_t hfuse;
uint8_t efuse;
uint8_t lbits;

uint8_t lfuse_letto; 
uint8_t hfuse_letto;
uint8_t efuse_letto;
uint8_t lbits_letto; // Lock bits.
uint8_t sbyte_1_letto; // Signature byte 1.
uint8_t sbyte_2_letto; // Signature byte 2.
uint8_t sbyte_3_letto; // Signature byte 3.

bool fare_programmazione=false;
#define ENC_PREMUTO !(digitalRead(A1)) 
bool ENC_PREMUTO_PREC=false;

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

/*
Pin  Pin
IC   Nano I/O Porta Segnale VaVerso Pin  -> al chip; <- dal chip.
 1    3       PC6   RS         -     -
 2    2   0   PD0   Data0|Rx  PB0   14
 3    1   1   PD1   Data1|Tx  PB1   15
 4    5   2   PD2   Data2     PB2   16
 5    6   3   PD3   Data3     PB3   17
 6    7   4   PD4   Data4     PB4   18
 8+22 4  GND   -    GND       GND   8+22
 9   25  XTL1|PB6   EncA      Encoder-A
10   26  XTL2|PB7   EncB      Encoder-B
11    8   5   PD5   Data5     PB5   19
12    9   6   PD6   Data6     PC0   23
13   10   7   PD7   Data7     PC1   24
14   11   8   PB0   Vcc       Vcc   7+20
15   12   9   PB1   BS1       PD4    6   -> Byte Select 1
16   13  10   PB2   WR/       PD3    5   -> WRite pulse/
17   14  11   PB3   OE/       PD2    4   -> Output Enable/
18   15  12   PB4   RDY       PD1    3   <- Ready
19   16  13   PB5   XA0       PD5   11   -> XTAL Action bit 0
23   19 14=A0 PC0   /RST|HVon RST    1
24   20 15=A1 PC1   Sw|Buz    100 Ohm in serie e poi *cicalino*, *pulsante dell'encoder* e *1k+100nF* in parallelo 
25   21 16=A2 PC2   BS2       PC2   24   -> Byte Select 2
26   22 17=A3 PC3   XTAL1     XTAL1  9
27   23 18=A4 PC4   SDA|XA1   PD6   12   -> XTAL Action bit 1
28   24 19=A5 PC5   SCL|PAGEL PD7   13   -> data Page Load
 -   27 Vcc
 -   28 RS
 -   29 GND
 -   30 Vin
*/

//     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 Bip()
  {
  pinMode(A1, OUTPUT);
  tone(A1, 4000, 100);
  delay(120);
  pinMode(A1, INPUT_PULLUP);
  }
void Biip()
  {
  pinMode(A1, OUTPUT);
  tone(A1, 2000, 500);
  delay(550);
  pinMode(A1, INPUT_PULLUP);
  }

void encoder()
  {
  //                    PD 76543210
      S=((PINB^0xFF)>>6 &0b00000011); // Inverto i bit, perché l'encoder chiude a massa, faccio scorrere a destra di 6 e considero solo PB6 e PB7 facendo &0b11.
  // Con "3-" occupa circa 8 byte in meno di memoria rispetto a "PINB^=xFF", ma è più brutto...
  // Il valore binario di S rappresenta A e B. Il centrale dell'encoder è a massa, quindi faccio complemento a 3 (11).  
  S^=S>>1; // Faccio XOR (^) fra S (gray) e il suo bit 1, facendolo scorrere a Dx: AB XOR A,
           // ottenendo un binario che per ogni scatto fa 0-1-2-3-0 oppure 0-3-2-1-0.
  E=0;
  if (S!=So && S==0) X=0;
  if (X==0)
    {
    if (So==1&&S==2)
      {E=1; X=1;
      }
    if (So==3&&S==2)
      {E=-1; X=1;
      }
    So=S;  
    }
  }



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 e Cicalino piezo (le funzioni Bip e Biip commutano automaticamente In/Out/In).
PORTB |= 0b11000000; // PB7 e PB6 INPUT_PULLUP: Encoder A e B.

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);

Wire.setClock(1000000); // Imposta la velocità dell'I2C a 1MHz anziché a 100kHz (default).
u8g2.begin();
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(1500);
while(ENC_PREMUTO); // Blocca la visualizzazione se desiderato.
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(33,63); u8g2.print(F("ESEGUI"));
u8g2.sendBuffer();
delay(700);
while(ENC_PREMUTO); // Blocca la visualizzazione se desiderato.
u8g2.clear();
u8g2.setFont(u8g2_font_ncenB08_tr);
visual_menu();
}



#define PRG_MAX 7

void loop()
{
encoder();
if(E) // Se è stato mosso l'encoder:
  {
  prg+=E; if(prg>PRG_MAX) prg=1; else if(prg<1) prg=PRG_MAX;
  visual_menu();
  }

if(clic && prg==7)
  {
  clic=false;    
  impostazioni_manuali();
  t_press_lunga=millis(); // Prende il tempo.
  while(ENC_PREMUTO) {if(millis()-t_press_lunga>2000) esegui(); ENC_PREMUTO_PREC=false;}
  t_press_lunga=millis(); // Prende il tempo.
  }

if(!ENC_PREMUTO)
  {
  if(ENC_PREMUTO_PREC)
    {
    ENC_PREMUTO_PREC=false;
    t_clic=millis();
    }
  if(millis()-t_clic<300 && millis()>300) clic=true;  
  t_press_lunga=millis(); 
  }
else ENC_PREMUTO_PREC=true;

if(millis()-t_press_lunga>2000) {esegui(); ENC_PREMUTO_PREC=false;} // Pressione lunga: esegue.

u8g2.setFont(u8g2_font_ncenB08_tr);
} // END loop.

void esegui()
  {
  u8g2.clear();
  //u8g2.setFont(u8g2_font_cu9_tr); 
  u8g2.setFont(u8g2_font_fancypixels_tr); // *** Ha le dimensioni giuste! ***
  if(prg==1)
    {
    u8g2.setCursor(33,15); u8g2.print(F("LETTURA")); u8g2.sendBuffer();
    legge(); // Riscrive dopo la lettura, che ha bloccato l'OLED:
    u8g2.setCursor(33,15); u8g2.print(F("LETTURA")); u8g2.sendBuffer();
    Biip();
    u8g2.setCursor(13,31); u8g2.print(F(" *** FATTO! *** "));
    display_fuse_letti(); u8g2.sendBuffer(); Biip();
    delay(1000);
    }
  else
    {
    u8g2.setCursor(3,15); u8g2.print(F("PROGRAMMAZIONE")); u8g2.sendBuffer();
    programma();
    u8g2.setCursor(3,15); u8g2.print(F("PROGRAMMAZIONE")); // Riscrive dopo la programmazione, che ha bloccato l'OLED.
    Biip();
    if(nessuna_risposta) // Non è arrivato il RDY (Ready)!
      {
      u8g2.setCursor(0,36); u8g2.print(F("NESSUNA RISPOSTA!"));
      u8g2.setCursor(57,51); u8g2.print(F("! ! !")); u8g2.sendBuffer();
      delay(150);
      Biip();
      delay(150);
      Biip();
      nessuna_risposta=LOW;
      }
    else // La programmazione è stata completata.
      {
      u8g2.setCursor(13,31); u8g2.print(F(" *** FATTO! *** ")); u8g2.sendBuffer();
      Biip();
      }
    }
  delay(2000);
  digitalWrite(VCC, LOW);
  u8g2.setFont(u8g2_font_ncenB08_tr);
  visual_menu();
  return;
  }

void visual_menu()
  {
  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); 
  Bip();
  switch(prg)
    {
    case 1:      
      u8g2.setCursor(28,15); u8g2.print(F("*** LETTURA ***"));
      display_fuse_letti();
    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;
    
    case 7:      
      u8g2.setCursor(47,15); u8g2.print(F("Manuale"));
      lfuse=EEPROM.read(0);
      hfuse=EEPROM.read(1);
      efuse=EEPROM.read(2);
      display_fuse();
    break;
    } // Fine switch(prg)
  u8g2.sendBuffer();
  } // Fine visual_menu()

  
void display_fuse()
  {
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setDrawColor(0);
  // u8g2.drawBox ( 10, 53, 111, 10); // Con le 3 cancellazioni piccole mi sembra un po' più veloce...
  u8g2.drawBox ( 10, 53, 17, 10); // Cancella LF.
  u8g2.drawBox ( 57, 53, 17, 10); // Cancella HF.
  u8g2.drawBox (104, 53, 17, 10); // Cancella EF.
  u8g2.setDrawColor(1);
  u8g2.setCursor( 10, 63); u8g2.print(lfuse>>4, HEX); u8g2.setCursor( 19, 63); u8g2.print(lfuse&0x0F, HEX);
  u8g2.setCursor( 57, 63); u8g2.print(hfuse>>4, HEX); u8g2.setCursor( 66, 63); u8g2.print(hfuse&0x0F, HEX);
  u8g2.setCursor(104, 63); u8g2.print(efuse>>4, HEX); u8g2.setCursor(113, 63); u8g2.print(efuse&0x0F, HEX);
  }
  
void display_F(uint8_t x, uint8_t y, uint8_t nibble) // Cancella e riscrive una cifra del fuse.
  {                                  //  V  Se qui sotto non metto x-1, con i caratteri stretti rimane un puntino in basso a sinistra!
  u8g2.setDrawColor(0); u8g2.drawBox  ( x-1, y-10, 9, 10); // x e y del rettangolo che cancella sono a sinistra e IN ALTO.
  u8g2.setDrawColor(1); u8g2.setCursor( x, y); u8g2.print(nibble,HEX); // x e y di print sono a sinistra e IN BASSO!
  u8g2.sendBuffer();
  }

void display_fuse_letti() // Preferisco tenere le due funzioni separate...
  {
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setCursor( 10, 63); u8g2.print(lfuse_letto>>4, HEX); u8g2.setCursor( 19, 63); u8g2.print(lfuse_letto&0x0F, HEX);
  u8g2.setCursor( 57, 63); u8g2.print(hfuse_letto>>4, HEX); u8g2.setCursor( 66, 63); u8g2.print(hfuse_letto&0x0F, HEX);
  u8g2.setCursor(104, 63); u8g2.print(efuse_letto>>4, HEX); u8g2.setCursor(113, 63); u8g2.print(efuse_letto&0x0F, HEX);
  }

  
/*void display_fuse_letti() // MODIFICATO PER SIGNATURE BYTES - Preferisco tenere le due funzioni separate...
  {
  u8g2.setFont(u8g2_font_ncenB08_tr);
  u8g2.setCursor( 10, 63); u8g2.print(sbyte_1_letto>>4, HEX); u8g2.setCursor( 19, 63); u8g2.print(sbyte_1_letto&0x0F, HEX);
  u8g2.setCursor( 57, 63); u8g2.print(sbyte_2_letto>>4, HEX); u8g2.setCursor( 66, 63); u8g2.print(sbyte_2_letto&0x0F, HEX);
  u8g2.setCursor(104, 63); u8g2.print(sbyte_3_letto>>4, HEX); u8g2.setCursor(113, 63); u8g2.print(sbyte_3_letto&0x0F, HEX);
  }
*/

void impostazioni_manuali()
  {
  // *** LFUSE ***
  u8g2.setDrawColor(1); // Serve solo per far vedere se ritorna dalla fine, dopo l'ultima cancellazione!
  u8g2.setCursor(10,53); u8g2.print('v');
  u8g2.sendBuffer();
  while(ENC_PREMUTO); // Attende rilascio del pulsante.
  while(!ENC_PREMUTO)
    {
    encoder();
    if(E)
      {
      if      (E== 1 && lfuse<240) lfuse+=16;
      else if (E==-1 && lfuse>15 ) lfuse-=16;
      display_F(10, 63, lfuse>>4); // Scrive la prima cifra Hex di lfuse.
      u8g2.sendBuffer();
      }
    }
  while(ENC_PREMUTO); // Attende rilascio del pulsante.
  u8g2.setDrawColor(0); u8g2.setCursor(10,53); u8g2.print('v'); // Cancella.
  u8g2.setDrawColor(1); u8g2.setCursor(19,53); u8g2.print('v'); // Scrive.
  u8g2.sendBuffer();
  while(!ENC_PREMUTO)
    {
    encoder();
    if(E)
      {
      if      (E== 1 && (lfuse & 0x0F)<15) lfuse+=1;
      else if (E==-1 && (lfuse & 0x0F)>0 ) lfuse-=1;
      display_F(19, 63, lfuse&0x0F); // Scrive la seconda cifra Hex di lfuse.
      u8g2.sendBuffer();
      }
    }
  while(ENC_PREMUTO); // Attende rilascio del pulsante.

  // *** HFUSE ***
  u8g2.setDrawColor(0); u8g2.setCursor(19,53); u8g2.print('v'); // Cancella.
  u8g2.setDrawColor(1); u8g2.setCursor(57,53); u8g2.print('v'); // Scrive.
  u8g2.sendBuffer();
  while(!ENC_PREMUTO)
    {
    encoder();
    if(E)
      {
      if      (E== 1 && hfuse<240) hfuse+=16;
      else if (E==-1 && hfuse>15 ) hfuse-=16;
      display_F(57, 63, hfuse>>4); // Scrive la prima cifra Hex di hfuse.
      u8g2.sendBuffer();
      }
    }
  while(ENC_PREMUTO); // Attende rilascio del pulsante.
  u8g2.setDrawColor(0); u8g2.setCursor(57,53); u8g2.print('v'); // Cancella.
  u8g2.setDrawColor(1); u8g2.setCursor(66,53); u8g2.print('v'); // Scrive.
  u8g2.sendBuffer();
  while(!ENC_PREMUTO)
    {
    encoder();
    if(E)
      {
      if      (E== 1 && (hfuse & 0x0F)<15) hfuse+=1;
      else if (E==-1 && (hfuse & 0x0F)>0 ) hfuse-=1;
      display_F(66, 63, hfuse&0x0F); // Scrive la seconda cifra Hex di hfuse.
      u8g2.sendBuffer();
      }
    } 
  while(ENC_PREMUTO); // Attende rilascio del pulsante.

  // *** EFUSE ***
  u8g2.setDrawColor(0); u8g2.setCursor( 66,53); u8g2.print('v'); // Cancella.
  u8g2.setDrawColor(1); u8g2.setCursor(104,53); u8g2.print('v'); // Scrive.
  u8g2.sendBuffer();
  while(!ENC_PREMUTO)
    {
    encoder();
    if(E)
      {
      if      (E== 1 && efuse<240) efuse+=16;
      else if (E==-1 && efuse>15 ) efuse-=16;
      display_F(104, 63, efuse>>4); // Scrive la prima cifra Hex di efuse.
      u8g2.sendBuffer();
      }
    }
  while(ENC_PREMUTO); // Attende rilascio del pulsante.
  u8g2.setDrawColor(0); u8g2.setCursor(104,53); u8g2.print('v'); // Cancella.
  u8g2.setDrawColor(1); u8g2.setCursor(113,53); u8g2.print('v'); // Scrive.
  u8g2.sendBuffer();
  while(!ENC_PREMUTO)
    {
    encoder();
    if(E)
      {
      if      (E== 1 && (efuse & 0x0F)<15) efuse+=1;
      else if (E==-1 && (efuse & 0x0F)>0 ) efuse-=1;
      display_F(113, 63, efuse&0x0F); // Scrive la seconda cifra Hex di efuse.
      u8g2.sendBuffer();
      }
    }
  u8g2.setDrawColor(0); u8g2.setCursor(113,53); u8g2.print('v'); // Cancella.
  u8g2.sendBuffer();
  EEPROM.update(0, lfuse);
  EEPROM.update(1, hfuse);
  EEPROM.update(2, efuse);
  Bip(); delay(150); Bip();
  u8g2.setDrawColor(1); // Ripristina la scrittura in bianco.
  }



void disabilitaI2C()
  {
  TWCR&=~(1<<TWEN);
  pinMode(A4, OUTPUT);
  pinMode(A5, OUTPUT);
  }

void abilitaI2C()
  {
  TWCR|=(1<<TWEN);
  u8g2.begin();
  }
  
void enterProgrammingMode()
  {
  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.
  delay(500);
  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);
  }

void exitProgrammingMode()
  {
  digitalWrite(RST, LOW); // Esce dalla programmazione.
  PORTD = 0x00; // Porta a livello basso tutte le uscite.
  digitalWrite(OE, LOW);
  digitalWrite(WR, LOW);
  digitalWrite(PAGEL, LOW);
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  delay(500);
  digitalWrite(VCC, LOW);
  }



void loadAddressLow(uint8_t address)
  {
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  PORTD = address;
  digitalWrite(XTAL1, HIGH);
  delay(1);
  digitalWrite(XTAL1, LOW);
  }
  
void legge()
  {
  while(ENC_PREMUTO);  // Si assicura che il pulsante sia stato lasciato.
  //Biip();
  disabilitaI2C();
  // Set up control lines for HV parallel programming.
  PORTD = 0x00; // Clear digital pins 0-7.
  DDRD = 0x00; // set digital pins 0-7 as inputs.
  enterProgrammingMode();
  // Produce lfuse_letto, hfuse_letto, efuse_letto, lbits_letto e sbyte_letto:
  sendCmd(0b00000100);

  // Lettura degli lfuse:
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  digitalWrite( OE, LOW);
  delay(1);
  lfuse_letto = PIND;
  digitalWrite( OE, HIGH);
  
  // Lettura degli hfuse:
  digitalWrite(BS1, HIGH);
  digitalWrite(BS2, HIGH);
  digitalWrite( OE, LOW);
  delay(1);
  hfuse_letto = PIND;
  digitalWrite( OE, HIGH);
  
  // Lettura degli efuse:
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, HIGH);
  digitalWrite( OE, LOW);
  delay(1);
  efuse_letto = PIND;
  digitalWrite( OE, HIGH);
  
  // Lettura dei lockbits:
  digitalWrite(BS1, HIGH);
  digitalWrite(BS2, LOW);
  lbits_letto = PIND;
  digitalWrite( OE, HIGH);
  delay(1);
  
  // Lettura dei Signature Bytes:
  sendCmd(0b00001000);
  loadAddressLow(0x00);
  digitalWrite( OE, LOW);
  digitalWrite(BS1, LOW);
  sbyte_1_letto = PIND;
  digitalWrite( OE, HIGH);
  delay(1);
  loadAddressLow(0x01);
  digitalWrite( OE, LOW);
  digitalWrite(BS1, LOW);
  sbyte_2_letto = PIND;
  digitalWrite( OE, HIGH);
  delay(1);
  loadAddressLow(0x02);
  digitalWrite( OE, LOW);
  digitalWrite(BS1, LOW);
  sbyte_3_letto = PIND;
  digitalWrite( OE, HIGH);
  exitProgrammingMode();
  abilitaI2C();
  while(ENC_PREMUTO);  // Si assicura che il pulsante sia stato lasciato.
  }



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

void writeFuse(byte valore, char tipo)  // Scrive (h)igh, (l)ow o (e)xtended fuse sul microcontrollore.
  {
  // Enable data loading:
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, HIGH);
  delay(1);
  // Write fuse:
  PORTD = valore;  // Carica il valore da programmare sulla porta D.
  digitalWrite(XTAL1, HIGH);
  delay(1);
  digitalWrite(XTAL1, LOW);
  if     (tipo == 'l') {digitalWrite(BS1, LOW);  digitalWrite(BS2, LOW); }
  else if(tipo == 'h') {digitalWrite(BS1, HIGH); digitalWrite(BS2, LOW); }
  else if(tipo == 'e') {digitalWrite(BS1, LOW);  digitalWrite(BS2, HIGH);}
  digitalWrite(WR, LOW); // Scrive abbassando per un attimo il WR/.
  delay(1);
  digitalWrite(WR, HIGH);
  uint32_t t_ready=millis();
  while(digitalRead(RDY) == LOW)// Attende il Ready.
    {
    if(millis()-t_ready>500) // Se non arriva entro 500ms, significa che il chip non risponde:
      {
      nessuna_risposta=HIGH;
      return;
      }
    }
  // Ripristina BS1 e BS2:
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  }

void programma()
  {
  while(ENC_PREMUTO);  // Si assicura che il pulsante sia stato lasciato.
  Biip();
  disabilitaI2C();
  // 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:
  enterProgrammingMode();
  // Now we're in programming mode until RST is set HIGH again.
  // Scriviamo lfuse, hfuse e efuse:
  sendCmd(0b01000000);  // Send command to enable fuse programming mode
  writeFuse(lfuse, 'l'); // Scrive lfuse.
  writeFuse(hfuse, 'h'); // Scrive hfuse.
  writeFuse(efuse, 'e'); // Scrive efuse.
  exitProgrammingMode();
  abilitaI2C();
  while(ENC_PREMUTO);  // Si assicura che il pulsante sia stato lasciato.
  }



/*
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(ENC_PREMUTO); al posto di delay(1000); in programma().
              Predispongo il menu per la lettura dei fuse e la successiva programmazione
            con gli stessi.
              Aggiusto le posizioni delle scritte nel menu.
0.10a 27/4/23 Tolgo il delay(2000) nel loop alla riga 18.
0.11  28/4/23 Ho aggiunto le funzioni  per la lettura dei fuse, dei lock bits e dei sig-
            nature bytes, secondo il datasheet.
0.12  28/4/23 Metto il pulsante su PB7, che rimane libero  usando  il clock RC interno a
            8MHz, e il cicalino su PC1 (A1).
              Le funzioni per la lettura non sono ancora inserite nel menu.
0.13  29/4/23 In programma() ho  sostituito  gli originali  LFUSE e HFUSE, che erano dei
            #define iniziali che ho eliminato, con lfuse e hfuse.
              In programma() ho eliminato il ripetuto sendcmd(B01000000), inutile secon-
            do il datasheet.
              Rinomino writefuse in writeFuse e sendcmd in sendCmd
              Modifico  il secondo parametro di writeFuse() usando 'l', 'h' e 'e' intro-
            ducendo gli efuse.
0.14  29/4/23 Metto dei  #define iniziali  per impostare  il pulsante su PB7 (ATmega328P 
            con oscillatore interno a 8MHz) e il cicalino su PC1/A1  oppure  il pulsante
            su A1 senza cicalino, utile per fare le prove su un Nano.
              Alla fine di  programma() e di  legge() ho dovuto aggiungere u8g2.begin(); 
            perché il display rimaneva bloccato.
0.14  29/4/23 Ho aggiunto i Bip() e Biip().
0.15  29/4/23 Ok.
0.16  29/4/23 Aggiungo l'encoder.
              Nelle funzioni  display_fuse e diaplay_fuse_letti metto lo zero non signi-
            ficativo per valori<16.
0.18  29/4/23 Ho spostato la visualizzazione del menu nella funzione  visual_menu(), ri-
            solvendo anche il problema della visualizzazione iniziale.
0.19  29/4/23 Su  suggerimento  di  Guglielmo gpb01, ho rinominato  premuto e PREMUTO in
            enc_premuto e ENC_PREMUTO.
0.21   4/5/23 Ho modificato  la  writeFuse per segnalare  che il RDY (Ready) non arriva,
            perché il chip non risponde (o non è presente).
0.22   9/5/23 Ho migliorato i commenti iniziali sui pin.
0.23_Attiny// Ho definito i pin da utilizzare anche per la  programmazione seriale degli
            ATtiny.
              La funzione per programmare gli ATtiny è prog_ATtiny() (INCOMPLETA)
0.24  15/5/24 Riparto dalla v0.22, sospendendo per ora gli ATTtiny. Ho cominciato a mon-
t7 OK       tarlo e voglio vedere se funziona, aggiungendo l'impostazione manuale di LF,
            HF ed EF alla voce 7 del menu.
              Ho modificato la lettura dell'encoder con  PINB^0xFF anziché 3-...: occupa
            circa 8 byte in più, ma è più bello.
              Ho spostato  #define PRG_MAX  in testa a b_loop_display e portato da 6 a 7
            per aggiungere l'impostazione manuale di LF, HF ed EF.
              Ho aggiunto l'impostazione manuale di LF, HF ed EF.
0.25  16/5/24 Aggiungo la memoria in EEPROM dei valori dell'impostazione manuale.
              Ho corretto il font del numero PRG  dopo LETTURA o PROGRAMMAZIONE, che non 
            veniva ripristinato dopo essere stato cambiato.
0.27  20/5/24 Dopo aver aggiunto  il CD4053B per scollegare SDA e SCL  dallo zoccolo ZIF
            finché non è richiesto l'accesso  per la lettura o la scrittura dei fuse, la
            programmazione funziona! :) 
              Ancora non funziona la lettura, ma vedo che manca la parte di accesso alla
            programamzione parallela!
0.28  20/5/24 Metto l'accesso alla programmazione, già presente nella scrittura, in una
            funzione che uso anche per la lettura.
*/

Qualcuno ha un'idea della ragione per cui la lettura non funziona?...
Se lascio lo ZIF vuoto, legge 3F 3F 3F, mentre con un ATmega328p inserito legge FF FF FF.
Ho provato a imporre 3 valori hex al posto di PIND e vengono visualizzati come se fossero stati letti, quindi il problema sta proprio nella lettura...
Ho provato a spostare i digitalWrite OE prima delle impostazioni di BS1 e BS2, ma senza risolvere il problema.

Questo è lo schema elettrico:

Mi sembra che nessuno qui abbia sperimentato sulla programmazione parallela. Provo a chiedere agli anglofoni:

Vi faccio sapere.
Grazie.

FUNZIONA!
Avevo solo dimenticato di impostare la porta D come uscita prima di mandare il primo comando, credendo di averlo fatto nel setup!

portDoutput();
sendCmd(0b00000100);
portDinput();
void portDinput()
  {
  PORTD = 0x00; // Clear digital pins 0-7.
  DDRD = 0x00; // set digital pins 0-7 as inputs.
  }
  
void portDoutput()
  {
  DDRD = 0xFF; // set digital pins 0-7 as outputs.
  PORTD = 0x00; // Clear digital pins 0-7.
  }

void loadAddressLow(uint8_t address)
  {
  digitalWrite(XA1, LOW);
  digitalWrite(XA0, LOW);
  digitalWrite(BS1, LOW);
  PORTD = address;
  digitalWrite(XTAL1, HIGH);
  delay(1);
  digitalWrite(XTAL1, LOW);
  }
  
void legge()
  {
  while(ENC_PREMUTO);  // Si assicura che il pulsante sia stato lasciato.
  Biip();
  disabilitaI2C();
  enterProgrammingMode();
  // Produce lfuse_letto, hfuse_letto, efuse_letto, lbits_letto e sbyte_letto:
  portDoutput();
  sendCmd(0b00000100);
  portDinput();
  // Lettura degli lfuse:
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, LOW);
  digitalWrite( OE, LOW);
  lfuse_letto = PIND;
  digitalWrite( OE, HIGH);
  
  // Lettura degli hfuse:
  digitalWrite(BS1, HIGH);
  digitalWrite(BS2, HIGH);
  digitalWrite( OE, LOW);
  hfuse_letto = PIND;
  digitalWrite( OE, HIGH);
  
  // Lettura degli efuse:
  digitalWrite(BS1, LOW);
  digitalWrite(BS2, HIGH);
  digitalWrite( OE, LOW);
  efuse_letto = PIND;
  digitalWrite( OE, HIGH);

  // Lettura dei lockbits:
  digitalWrite(BS1, HIGH);
  digitalWrite(BS2, LOW);
  digitalWrite( OE, LOW);
  lbits_letto = PIND;
  digitalWrite( OE, HIGH);
  
  // Lettura dei Signature Bytes:
  portDoutput();
  sendCmd(0b00001000);
  loadAddressLow(0x00);
  portDinput();
  digitalWrite(BS1, LOW);
  digitalWrite( OE, LOW);
  sbyte_1_letto = PIND;
  digitalWrite( OE, HIGH);
  
  portDoutput();
  loadAddressLow(0x01);
  portDinput();
  digitalWrite( OE, LOW);
  digitalWrite(BS1, LOW);
  sbyte_2_letto = PIND;
  digitalWrite( OE, HIGH);

  portDoutput();
  loadAddressLow(0x02);
  portDinput();
  digitalWrite( OE, LOW);
  digitalWrite(BS1, LOW);
  sbyte_3_letto = PIND;
  digitalWrite( OE, HIGH);
  
  exitProgrammingMode();
  abilitaI2C();
  while(ENC_PREMUTO);  // Si assicura che il pulsante sia stato lasciato.
  }

Interessante... Per non perderlo, lo metto qui sotto:

fbb0a7563d174ec8496885ad52e549f81f8c876c

Si, ma serve un reset hardware per avviare il bootloader, quindi se manca il reset serve togliere e ridare l'alimentazione.

Comunque penso che a te serva più per ripristinare i fuse riabilitando il reset.

Ciao.

Su un 328p avevo messo a 1 lo SPIEN, disattivandolo.
Mi incuriosiva costruirlo e mi fa stare tranquillo quando modifico i fuse. :slight_smile:
Per come l'ho pensato, poi, mi permette di programmare i fuse secondo le configurazioni preimpostate o arbitrariamente impostandone i valori.

A proposito dei Lock bits:

https://circuitdigest.com/forums/general/how-lock-code-arduino

Ecco un video del Fuse Rescue finalmente funzionante! :smile:

Ora c'è anche il Chip Erase.

Complimenti.

Grazie!
Però è vergognoso essere rimasto fermo per 2 settimane solo perché non avevo impostato la porta D come uscita! :smile:

:joy: (consolati, ho visto e fatto di peggio :roll_eyes:)

Lo schema e' quello del post 25 o hai fatto altre modifiche ?

È quello del 25, con il 4053 sull'I2C. È l'unica modifica che ho fatto.

Mi incuriosisce un po' l'OE... Se è veramente un OE, dovrebbe funzionare anche lasciandolo sempre attivo, a livello basso. Diverso sarebbe se fosse un latch.

Intendi il pin 6 del 4053 ? ... e' un inhibit, se lasci a zero funziona, se metti ad 1 vengono disabilitate tutte le uscite (in pratica, non importa quale sara' lo stato di qualsiasi ingresso, tutte le uscite saranno OFF).

O intendevi altro ?

EDIT: cosi ?