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.
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:
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:

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. ![]()
Per come l'ho pensato, poi, mi permette di programmare i fuse secondo le configurazioni preimpostate o arbitrariamente impostandone i valori.
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! ![]()
(consolati, ho visto e fatto di peggio
)
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.