Topic permanente di programmazione newbie

Dopo una sana dormita di 10 ore stamattina mi sento in condizioni di poter riaffrontare la cosa, sia dal punto di vista teorico che da quello pratico, che poi, come dice giustamente Test, è quello che mi interessa di più: se non in grado di tirare fuori 1,2,....6 da quel valore memorizzato non mi serve altro, se devo farlo considerando che 6=1100 mi rompe un po' di più ma alla fine è la stessa cosa, l'importante è capire ed implementare il meccanismo, ma prima ho da affrontare due problemi più importanti:
1 - la frammentazione del mio firmware in void, mentre prima potevo fare una procedura unica perché l'informazione sul micro la davo io da menu e tutto il resto veniva da sé, ora devo prima riconoscere il micro in automatico, quindi devo eseguire gran parte del firmware per poi rieseguirlo quasi tutto in funzione del micro inserito, chiaro che non posso "ricopiare" un intero firmware togliendo poche cose, quindi ogni spezzone funzionante lo metto in una void e nel loop mi lascio un main semplice semplice che esegua le varie void all'occorrenza.
2 - Letta la signature devo confrontare i suoi tre byte con quelli prememorizzati, io direi di semplificare confrontanto solo secondo e terzo, il primo mi serve come controllo della validità della signature, deve essere infatti sempre 1E (=Atmel), sopno il secondo e terzo che cambiano in funzione della memoria e del modello del micro. A tale proposito ho imparato che:
1° byte = produttore = 1E = Atmel
2° byte = memoria del micro -> 91=2K, 92=4K, 93=8k, ecc.ecc. :wink:
3° byte =modello del micro ma in coppia col secondo byte -> 950F = 328P (da approfondire quando avrò la tabella completa da studiare)
scrivete gente, scrivete....
Allora la mia idea di confronto della signature è:
un ciclo for di due passi all'interno di un while (flag); logica:
fino a che il flag = 0
leggo il secondo byte del secondo array, se è uguale leggo il terzo, se è uguale fine.
se non è uguale (o primo o secondo byte) passo alla riga successiva dell'array
se arrivo a fine array gli dico : guagliò che tieni inta a capa? mietti o micro giusto XD
idee migliori? (di sicuro ne avete!)
a più tardi e grazie.

menniti:
leggo il secondo byte del secondo array, se è uguale leggo il terzo, se è uguale fine.
se non è uguale (o primo o secondo byte) passo alla riga successiva dell'array
se arrivo a fine array gli dico : guagliò che tieni inta a capa? mietti o micro giusto XD
idee migliori? (di sicuro ne avete!)
a più tardi e grazie.

Io procederei così, prima di tutto verifichi se il primo byte della signature è 0x1E, se è così continui in caso contrario il processo termina e visualizzi sul display un idoneo messaggio di errore.
Se il confronto sul primo byte da esito positivo inizializzi una for con tante iterazioni quanti sono i modelli di micro supportati, in due variabili locali unsigned int sommi i due byte rimanenti della signature e i due byte della riga matrice in uso, confronti tra loro i due valori, se sono identici hai finito e hai l'indice per accedere agli altri arry, se non trovi nessuna corrispondenza alla fine della for termini con un messaggio di modello non supportato.
Ti allego la relativa funzione così ci capiamo meglio, tiene presente che la sto scrivendo al volo direttamente sul forum senza provare nulla quindi è possibile che contenga qualche errore, quello che conta è il concetto.

/* funzione controllo signature invocata dopo il controllo del primo byte, 
    la invochi fornendo gli altri due byte della signature, ti ritorna il valore 
   dell'indice se viene trovata corrispondenza oppure 0xff in caso di errore.

*/

unsigned char Controllo_signature(unsigned char byte2, unsigned char byte3)
{
char i;
unsigned int temp1, temp2;

temp1 = (unsigned inte) byte2 << 8 + byte1;

for (i=0;i<20;i++)
 {
  temp2 = (unsigned char) Signature[i] [1] <<8 + Signature[i] [2];
  if (temp1 == temp2) return i; // esce dalla funzione con il valore dell'indice
 }

return 0xff; // corrispondenza non trovata.
}

Piccola nota aggiuntiva, il codice è composta da funzioni e non da void, questo è solo un attributo che indica "nulla", ovvero la funzione non ritorna nessun valore, infatti nel nostro caso la funzione è identificata come "unsigned char Controllo_signature(unsigned char, unsigned char)", vuol dire che accetta in ingresso due valori compresi tra 0 e 255 e ritorna un valore compreso tra 0 e 255.

Testato:
leo tu hai ragione ed anche io preferisco ragionare in termini binari specialmente se si tratta di sfruttare un byte in termini di singoli flag. Pero lastrada del menny funziona ugualmente quindi se per lui e' piu facile ragionare in termini di byte singolo va bene cosi. Meglio avere uno sketch che capisci che uno migliore ma di cui non hai padronanza. Mio parere :slight_smile:

O sono io che non mi so spiegare oppure ho disimparato a leggere... ma la questione iniziale era usare un byte come contenitore di impostazioni aggiuntive, quindi flag. :stuck_out_tongue: Così infatti è stato detto e suggerito di usare il 4° byte. Usarlo in formato numerico porta ad un gran casino perché se il byte ha il valore, ad esempio, di 195 a me dice poco ma sapere che i suoi bit sono 11000011 mi fa capire che ho 4 flag ad 1 che mi indicheranno determinate opzioni o configurazioni. Proprio l'uso dei singoli bit permette di immagazzinare informazioni più "specifiche".
Se poi in quel 4° byte non ci deve stare altro che un valore da 0 a 6 allora è un altro discorso però è stato detto che era possibile usarlo anche per scopi futuri: strutturare il codice bene fin da subito in questo modo permette in seguito di non dover cambiare tutti i dati memorizzati e la logica del programma perché si è deciso di aggiungere dei dati a quel byte.

menniti:
Dopo una sana dormita di 10 ore stamattina mi sento in condizioni di poter riaffrontare la cosa, sia dal punto di vista teorico che da quello pratico, che poi, come dice giustamente Test, è quello che mi interessa di più: se non in grado di tirare fuori 1,2,....6 da quel valore memorizzato non mi serve altro, se devo farlo considerando che 6=1100 mi rompe un po' di più ma alla fine è la stessa cosa, l'importante è capire ed implementare il meccanismo, ma prima ho da affrontare due problemi più importanti:

Leggi sopra. Se vuoi metterci solo 0..6 usalo come vuoi, se in futuro pensi di usarlo per altri scopi, usalo nei singoli bit.

1 - la frammentazione del mio firmware in void, mentre prima potevo fare una procedura unica perché l'informazione sul micro la davo io da menu e tutto il resto veniva da sé, ora devo prima riconoscere il micro in automatico, quindi devo eseguire gran parte del firmware per poi rieseguirlo quasi tutto in funzione del micro inserito, chiaro che non posso "ricopiare" un intero firmware togliendo poche cose, quindi ogni spezzone funzionante lo metto in una void e nel loop mi lascio un main semplice semplice che esegua le varie void all'occorrenza.

Una piccola correzione. void indica al compilatore che una funzione restituisce un valore nullo, non è una parola chiave che indica al compilatore una funzione. Cioè, void non indica che si sta scrivendo una sub-routine ma il suo valore. Esempi:

void mario() {
  ... codice ...
}

Questo codice indica al compilatore che esiste una funzione o sub-routine, chiamala come vuoi, che non restituisce nessun valore. La dichiarazione di funzione è implicita, è come se esistesse una parola chiave "function" omessa.

Questo:

byte beppe() {
  byte dato;
  ... codice ...
  return dato;
}

Crea invece una funzione di nome beppe che restituisce un tipo di dati byte.
Quindi, non è corretto indicare una funzione con il termine "void". Ti faccio questa precisazione perché se nell'articolo scrivi che hai creato una void per fare una certa operazione, poi ricevi 100 lettere da programmatori C incacchiati che ti vogliono ardere in piazza tipo Savonarola :wink:

2 - Letta la signature devo confrontare i suoi tre byte con quelli prememorizzati, io direi di semplificare confrontanto solo secondo e terzo, il primo mi serve come controllo della validità della signature, deve essere infatti sempre 1E (=Atmel), sopno il secondo e terzo che cambiano in funzione della memoria e del modello del micro. A tale proposito ho imparato che:
1° byte = produttore = 1E = Atmel
2° byte = memoria del micro -> 91=2K, 92=4K, 93=8k, ecc.ecc. :wink:
3° byte =modello del micro ma in coppia col secondo byte -> 950F = 328P (da approfondire quando avrò la tabella completa da studiare)
scrivete gente, scrivete....
Allora la mia idea di confronto della signature è:
un ciclo for di due passi all'interno di un while (flag); logica:
fino a che il flag = 0
leggo il secondo byte del secondo array, se è uguale leggo il terzo, se è uguale fine.
se non è uguale (o primo o secondo byte) passo alla riga successiva dell'array
se arrivo a fine array gli dico : guagliò che tieni inta a capa? mietti o micro giusto XD
idee migliori? (di sicuro ne avete!)
a più tardi e grazie.

Basta un if con un triplo controllo all'interno di un for. Mettiamo che firma[..] contenga la firma digitale letta dal micro e archivioFirme[..][..] le firme memorizzate:

boolean trovato=false;
for (byte i=0; i<num_firme; i++) {
  if ((firma[0]==archivioFirme[i][0]) && (firma[1]==archivioFirme[i][1]) && (firma[2]==archivioFirme[i][2])) {
    .....codice per firma trovata....
    trovato=true;
    break;
  }
}
if (!trovato) {
  ....micro non riconosciuto...
}

"trovato" è false all'uscita del ciclo for se non è stato trovato nessun corrispondente modello nell'archivio

Leo, ma sei chiarissimo, solo che io nella mia beata ignoranza tento di rispiegare a parole mie e dico czz; grazie per il chiarimento void/funzione, ci sto proprio litigando ora. HELP!
Se metto in loop

byte read_signature1;
read_signature1=SIGN_S_read(0x00, SIGN_READ_INSTR4);

con questa funzione messa nella “zona” delle routine:
byte SIGN_S_read(byte data, byte instr) { // leggo un byte col protocollo HVSP
byte response = 0x00;
.
.
return response;
}

funziona correttamente.

Se sposto il comando di lettura in una sua funzione, così:

byte read_signature1;
sign_HVSP_read();

e quindi ora ho due funzioni:

void sign_HVSP_read(void) {
read_signature1=SIGN_S_read(0x00, SIGN_READ_INSTR4);
}

byte SIGN_S_read(byte data, byte instr) { // leggo un byte col protocollo HVSP
byte response = 0x00;
.
.
return response;
}

Mi dà questo errore:
HV_Programmer_MAIN.cpp: In function 'byte signt_SP_read()':
HV_Programmer_MAIN:489: error: 'read_signature1' was not declared in this scope

Perché? Devo risolvere con qualcosa del genere altrimenti non ho la possibilità di leggere la signature in modo sequenziale nelle tre diverse modalità.
Cioè io da sign_HVSP_read(); mi aspetto di rientrare in main avendo a disposizione il valore di signature1

Si chiama spazio dei nomi, ed indica la visibilità di una variabile. Se una variabile la dichiari in una funzione, non la puoi usare al di fuori di essa.
Per poterla usare in tutte le funzioni del tuo programma, devi dichiararla come variabile globale: per far ciò basta mettere la sua dichiarazione in cima allo sketch:

byte read_signature1;
....
void setup() {
....
}

void loop() {
...
}

void sign_HVSP_read(void) {
read_signature1=SIGN_S_read(0x00, SIGN_READ_INSTR4);
}

menniti:
HV_Programmer_MAIN.cpp: In function 'byte signt_SP_read()':
HV_Programmer_MAIN:489: error: 'read_signature1' was not declared in this scope

Vuol dire che "read_signature1" non viene trovata come variabile locale o globale, se l'hai dichiarata nella loop è normale che non la trova, devi dichiararla esternamente come variabile globale se vuoi che sia disponibile per tutte le funzioni senza doverla passare esplicitamente.

astrobeed:

menniti:
HV_Programmer_MAIN.cpp: In function 'byte signt_SP_read()':
HV_Programmer_MAIN:489: error: 'read_signature1' was not declared in this scope

Vuol dire che "read_signature1" non viene trovata come variabile locale o globale, se l'hai dichiarata nella loop è normale che non la trova, devi dichiararla esternamente come variabile globale se vuoi che sia disponibile per tutte le funzioni senza doverla passare esplicitamente.

Infatti stavo provando e mettendola direttamente nella funzione come
byte read_signature1;
l'errore è sparito
In effetti era dichiarata in loop, allora mi basta spostarla nelle variabili globali per risolvere in modo anche più elegante.
Grazie e a dopo, oggi sarà dura con questa storia della main :grin:

Ora che funziona la prima parte ho fatto una scoperta interessante: la tecnica HVPP mi permette di leggere TUTTI i tipi di signature, anche quelle dei micro che normalmente programmo in HVSP, quindi come prima manovra leggo la signature a colpo sicuro, poi differenzio invece la lettura e la scrittura dei fuse :slight_smile:

EDIT: ma mi viene un dubbio e devo fare una controverifica...

menniti:
EDIT: ma mi viene un dubbio e devo fare una controverifica...

Nel frattempo guardati pure la soluzione che ti ho scritto qualche post sopra per il controllo della signature.

Scusami Astro, ho visto il lunghissimo post di risposta che il povero Leo si è massacrato a scrivere e non havevo proprio visto il tuo post, dove peraltro mi chiarivi già la questione funzione/void; la soluzione che mi prospetti è semplice ed efficace, la metto subito in atto.
Però non posso basarmi immediatamente sul controllo 1E, perché devi considerare che io "non so" a priori che micro ho messo e quindi la modalità che uso potrebbe non essere compatibile.
Intanto ho avuto conferma del sospetto che avevo, devo proprio iniziare a leggere con la modalità HVSP, in tal modo ho:
1E se il micro è HVSP compatibile
FF se non lo è ma se comunque c'è un micro
00 se non c'è alcun micro e ho premuto per errore
quindi la tua routine la eseguo solo quando riesco ad ottenere un 1E, fra poco devo andare all'UNI, per oggi mi sa che ho finito, però mi pare di essere sulla buona strada.... Grazie a tutti! XD

menniti:
quindi la tua routine la eseguo solo quando riesco ad ottenere un 1E, fra poco devo andare all'UNI, per oggi mi sa che ho finito, però mi pare di essere sulla buona strada.... Grazie a tutti! XD

Ovviamente la mia routine di controllo la esegui solo dopo aver ottenuto una signature valida.
Attenzione al fatto che 0x1e per il momento è vero che ti indica un micro AVR di Atmel, ma non è detto che in futuro rimane sempre solo questo valore, per come è organizzata la signature è possibile specificare solo 255 diversi modelli di micro più le loro varianti come quantità di memoria.

astrobeed:

menniti:
quindi la tua routine la eseguo solo quando riesco ad ottenere un 1E, fra poco devo andare all'UNI, per oggi mi sa che ho finito, però mi pare di essere sulla buona strada.... Grazie a tutti! XD

Ovviamente la mia routine di controllo la esegui solo dopo aver ottenuto una signature valida.
Attenzione al fatto che 0x1e per il momento è vero che ti indica un micro AVR di Atmel, ma non è detto che in futuro rimane sempre solo questo valore, per come è organizzata la signature è possibile specificare solo 255 diversi modelli di micro più le loro varianti come quantità di memoria.

Sì, ci riflettevo su, d'altra parte che posso farci? posso mettere una define all'inizio del firmware col valore 1E, per semplificare la variazione, ma per aumentare il numero devono almeno raddoppiare i byte, posso tentare di "anticipare" la possibile applicazione di due byte con un define ATMEL 0xFF1E e trasformare ogni primo byte in un doppio byte, ma dici che vale la pena? però considera che io rilascio il firmware libero, e se ciò dovesse accadere sarei il primo ad apportare le dovute correzioni e ridarlo alla Rivista e comunque ognuno potrebbe fare la correzione.
Comunque accetto suggerimenti, ovviamente, della tua esperienza mi fido ciecamente.
Intanto la routine di check della signature ora è perfetta, mi dice perfino se il micro non è nello zoccolo o se è danneggiato (ho un atmega8 morto che avevo conservato per usi futuri.... :grin:)

menniti:
Sì, ci riflettevo su, d'altra parte che posso farci? posso mettere una define all'inizio del firmware col valore 1E, per semplificare la variazione, ma per aumentare il numero devono almeno raddoppiare i byte, posso tentare di "anticipare" la possibile applicazione di due byte con un define ATMEL 0xFF1E e

Ma no, semplicemente in futuro avranno come primo byte non solo 0x1e, ma un secondo valore che potrebbe essere 0x1f, questa non è una convenzione mondiale di tutti i produttori, è solo uno standard usato da Atmel, tutti gli altri fanno come gli pare.
Per esempio Microchip usa solo due byte per il device ID, l'equivalente della signature di Atmel, i vari bit indicano sia il modello esatto del micro che la revisione hardware, quest'ultima informazione, molto importante, è totalmente assente nella signature di Atmel.

@Menniti oltre alle possibilità che elencavi prima, hai pensato alla eventualità di mettere i micro al contrario?
in teoria dovrebbe rientrare nel caso "nessun micro" ma siamo sicuri che per qualche strana ragione non si legga qualcosa (di sicuramente errato) comunque?

astrobeed:

menniti:
Sì, ci riflettevo su, d'altra parte che posso farci? posso mettere una define all'inizio del firmware col valore 1E, per semplificare la variazione, ma per aumentare il numero devono almeno raddoppiare i byte, posso tentare di "anticipare" la possibile applicazione di due byte con un define ATMEL 0xFF1E e

Ma no, semplicemente in futuro avranno come primo byte non solo 0x1e, ma un secondo valore che potrebbe essere 0x1f, questa non è una convenzione mondiale di tutti i produttori, è solo uno standard usato da Atmel, tutti gli altri fanno come gli pare.
Per esempio Microchip usa solo due byte per il device ID, l'equivalente della signature di Atmel, i vari bit indicano sia il modello esatto del micro che la revisione hardware, quest'ultima informazione, molto importante, è totalmente assente nella signature di Atmel.

Pensavo fosse uno standard per tutti i micro, a questo punto lascio com'è, non avendo idea di cosa possa diventare, quando sarà se ne parlerà....

BrainBooster:
@Menniti oltre alle possibilità che elencavi prima, hai pensato alla eventualità di mettere i micro al contrario?
in teoria dovrebbe rientrare nel caso "nessun micro" ma siamo sicuri che per qualche strana ragione non si legga qualcosa (di sicuramente errato) comunque?

BB, ti rispondo con una prova da fare: prepara uno schema per un tiny85 e poi mettilo al contrario, ma PRIMA guardati le piedinature ok? Non scherziamo, ci manca solo che devo calcolarmi come prevedere il chip a rovescia (il tiny85 scoppierebbe) o magari quello che lascia i primi o gli ultimi due pin fuori dallo zoccolo :drooling_face:
La logica che ho usato è chiara e lampante, sono previsti i casi 00 (chip assente), FF (modo di programmazione sbagliato), 1E (chip e modo corretti), qualsiasi altro valore verrà dato come chip spacciato, cosa possibile se lo metti al contario o con i pin sfasati. Non lo scriverei nell'articolo come possibilità nemmeno sotto tortura, al principiante puoi dire che deve far corrispondere la tacca del chip col pin 1 serigrafato accanto allo zoccolo, non puoi trattarlo come un deficiente :wink:

chiaro... come solo un (bravo) insegnante puo essere :wink:

BrainBooster:
chiaro... come solo un (bravo) insegnante puo essere :wink:

Beh, grazie, ma anche tu come "studente" non sei niente male XD

Giovani, sono rientrato (è una specie di minaccia :D), ho già litigato col code di Astro che lui giustamente aveva definito buttato al volo e non esente da errori; ora vediamo se facciamo pace da soli o se deve intervenire il suo papà :wink: a tra poco.......

Un problemino per la tabella dati: ho diversi elenchi con le signature ma nemmeno uno con i fusebit di default, non è che posso mettermi col FuseCalc a tirarli fuori tutti, sai che sbatts, per non dire che non è detto che combaci la mia idea con quella di Atmel, specialmente per i tipi poco noti.

Queste dovrebbe essere una ricerca per BB, il LINKBUSTER :grin:

hai qualcosa per leggere un'enciclopedia di avr in xml ? se si mandami un mp :wink:

menniti:
Giovani, sono rientrato (è una specie di minaccia :D), ho già litigato col code di Astro che lui giustamente aveva definito buttato al volo e non esente da errori; ora vediamo se facciamo pace da soli o se deve intervenire il suo papà :wink: a tra poco.......

Infatti ci stava qualche errore di battitura, un unsigned char al posto di un unsigned int e due coppie di parentesi mancanti, ti allego uno sketch completo funzionante (provato) che illustra il senso generale della cosa:

char Stringa[20] [20] = { {"ATmega328p"},
                          {"ATMEGA644"},
                          {"ATMEGA2560"}
                        };
                        
byte signature [20] [3] = { {0x1E,0x1C,0x1D},
                            {0x1E,0x1C,0x1E},
                            {0x1E,0x1C,0x1F}
                          };  

byte fuses [20] [4] = { {0xff,0xff,0xff,0xff},
                        {0xff,0xff,0xff,0xff},
                        {0xff,0xff,0xff,0xff}
                       };  
char modello[20];

void setup() 
{ 
  Serial.begin(9600); 
} 

void loop() 
{ 
 unsigned char indice;
 char modello[20],i;
 
 delay(500);
 indice = Controllo_signature(0x1c,0x1e);
 if (indice < 20)
  {
    
     Serial.print("Trovato : ");
     for(i=0;i<20;i++) modello[i] =  Stringa[indice] [i];
     Serial.println (modello);
  }  
 else Serial.print("Nessun micro trovato");
 
} 

/* funzione controllo signature invocata dopo il controllo del primo byte, 
    la invochi fornendo gli altri due byte della signature, ti ritorna il valore 
   dell'indice se viene trovata corrispondenza oppure 0xff in caso di errore.

*/

unsigned char Controllo_signature(unsigned char byte2, unsigned char byte3)
{
char i;
unsigned int temp1, temp2;

temp1 = (unsigned int)(byte2 << 8) + byte3;

for (i=0;i<20;i++)
 {
  temp2 = (unsigned int) (signature[i] [1] <<8) + signature[i] [2];
  if (temp1 == temp2) return i; // esce dalla funzione con il valore dell'indice
 }

return 0xff; // corrispondenza non trovata.
}