Topic permanente di programmazione newbie

Diciamo che la mia dichiarata in Topic fatale ignoranza non mi ci ha fatto capire nulla, al solo comando "byte" ero arrivato ma non capivo l'errore successivo, evidentemente quello dell'inversione, ed il fatto che la prima istruzione avesse 20, 20 non mi ha aiutato a capire. COmunque ieri sera ho messo a punto grosso modo l'impostazione del 4° byte dell'array signature, mi servono 6 valori per le possibili combinazioni di EFuse e Tipo di Programmazione; sono state fondamentali le prove hw che ho descritto nell'altro Topic. Grazie a tutti, oggi altra giornataccia, ci sentiamo appena possibile.

io cambierei l'hardware :) (battuta per iscrizione)

menniti: mi servono 6 valori per le possibili combinazioni di EFuse e Tipo di Programmazione;

Per gestire in modo semplice il quarto byte che fa da bit flag ti consiglio l'uso di una unione contenente una variabile unsigned char e una struttura bit fields di otto elementi. In pratica carichi sulla variabile unsigned char il quarto byte dopo di che puoi accedere direttamente ai singoli flag tramite nomi mnemonici, ti allego un semplice sketch di esempio dove viene creata la struttura che ti dico, i vari nomi li puoi cambiare come ti pare, e dopo aver caricato sulla variabile principale un valore binario vengono stampati sul serial monitor i singoli bit che lo costituiscono:

struct Bit_Fields {
        unsigned char dato1:1;
        unsigned char dato2:1;
        unsigned char dato3:1;
        unsigned char dato4:1;
        unsigned char dato5:1;
        unsigned char dato6:1;
        unsigned char dato7:1;
        unsigned char dato8:1;
                  };

union bit_fields {
       unsigned char AllBits;
       struct Bit_Fields BtF;
                 } AuxData; 


void setup() 
{ 

  Serial.begin(9600); 

  AuxData.AllBits = 0b11001100;

  Serial.print("Bit 1 = ");  
  Serial.println(AuxData.BtF.dato1, DEC); 

  Serial.print("Bit 2 = ");  
  Serial.println(AuxData.BtF.dato2, DEC); 

  Serial.print("Bit 3 = ");  
  Serial.println(AuxData.BtF.dato3, DEC); 

  Serial.print("Bit 4 = ");  
  Serial.println(AuxData.BtF.dato4, DEC); 

  Serial.print("Bit 5 = ");  
  Serial.println(AuxData.BtF.dato5, DEC); 

  Serial.print("Bit 6 = ");  
  Serial.println(AuxData.BtF.dato6, DEC); 

  Serial.print("Bit 7 = ");  
  Serial.println(AuxData.BtF.dato7, DEC); 

  Serial.print("Bit 8 = ");  
  Serial.println(AuxData.BtF.dato8, DEC); 

  Serial.print("AllBits ");  
  Serial.println(AuxData.AllBits, HEX); 

} 


void loop() 
{ 

}

:astonished: Forse se lo provo in uno sketch da solo ci capisco qualcosa, ciò che non riesco proprio ad immaginare è come collegare tutto ciò al 4° byte del terzo array :blush: comunque facciamo un passo per volta, stasera provo, una volta che mi sono fatta l'idea certa del funzionamento vedremo di capire come collegare un valore del 4° bytre al corrispondente valore di questa struttura. Grazie!

menniti: :astonished: Forse se lo provo in uno sketch da solo ci capisco qualcosa, ciò che non riesco proprio ad immaginare è come collegare tutto ciò al 4° byte del terzo array :blush:

Semplicemente caricando il suo valore in "AuxData.AllBits", p.e. "AuxData.AllBits = Fuses [11] [4]", cioè carichi il 4 byte dell'undicesimo modello di micro nella varabile AllBits, da questo momento in poi puoi accedere a i singoli flag con i loro nomi mnemonici, p.e. per verificare il terzo flag:

"if (AuxData.BtF.dato3 == 1) quellochedevifare();"

astrobeed:

menniti: :astonished: Forse se lo provo in uno sketch da solo ci capisco qualcosa, ciò che non riesco proprio ad immaginare è come collegare tutto ciò al 4° byte del terzo array :blush:

Semplicemente caricando il suo valore in "AuxData.AllBits", p.e. "AuxData.AllBits = Fuses [11] [4]", cioè carichi il 4 byte dell'undicesimo modello di micro nella varabile AllBits, da questo momento in poi puoi accedere a i singoli flag con i loro nomi mnemonici, p.e. per verificare il terzo flag:

"if (AuxData.BtF.dato3 == 1) quellochedevifare();"

Fantastico! questa tecnica mi evita di dover mettere un bordello di "if" per convertire gli 8 valori del 4° byte (anche se per ora ne uso solo 6) in una coppia di valori che rappresentano il tipo di programmazione e lo status dell'EFuse. Un approfondimento: ma io posso estrapolare dal byte due valori invece di uno? Spiego: 0x01 per me significa EFuse = "No" e mode = "HVSP" 0x02 EFuse="No" e mode = "HVPP" ecc. come faccio ad assegnare due variabili distinte con questa tecnica?

Io avrei fatto diversamente. Bastava usare la funzione predefinita di Arduino bitRead: Per accedere ad un singolo bit basta fare bitRead(dato, num_bit).

Quindi

for (byte i=0; i<8; i++) {
  Serial.print("Bit ");
  Serial.print(i, DEC);
  Serial.print(": ");
  Serial.println(bitRead(dato,i), DEC);
}

leo72: Io avrei fatto diversamente. Bastava usare la funzione predefinita di Arduino bitRead:

Corretto, però la BitRead in realtà è questo:

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)

Ogni volta che la vai ad usare in realtà viene eseguito uno shift della variabile e una mascheratura, inoltre anche se ti crei delle #define con nomi mnemonici dei vari campi assegnandoli al relativo bit, p.e. #define HVPP 3, quando vai scrivere il software il tutto diventa meno leggibile dell'uso diretto di un nome mnemonico. Non ultimo il modo con cui vengono trattati i dati dalle strutture e unioni è decisamente più efficace del fare uno shift e una maschera, in pratica il codice compilato risulta più compatto e impiega meno cicli macchina, che poi queste sono finezze da programmazione avanzata è vero, nel nostro caso non cambia nulla se per accedere al dato ci vogliono solo tre cicli macchina invece di cinque, però non è male se si impara ad usare tutti gli strumenti del C e unioni/strutture sono tra quelli più potenti che ci vengono messi a disposizione.

forse è meglio se chiarisco il mio intento; nel 4° byte memorizzo un valore da 0x01 a 0x06 in base alle 6 possibili combinazioni tra EFuse presente/non presente e modo di programmazione: HVPP, HVSP, HVP13. Quando identifico la signature estrapolo e mostro il nome del micro, ed io nella mia ignoranza avrei fatto una void con una Select Case tipo (non badare alla sintassi):

void datimicro() read.... 4 byte dell'array select case 4byte case 0x01: EFUSE = 0 // non presente mode = "HVSP" case 0x02: ecc... Così il seguito del programma procede in base a queste due variabili già previste.... Ciò che mi state consigliando fa la stessa cosa e migliora la tecnica di esecuzione o no?

menniti: forse è meglio se chiarisco il mio intento; nel 4° byte memorizzo un valore da 0x01 a 0x06 in base alle 6 possibili combinazioni tra EFuse presente/non

Quello che ti sto suggerendo io è di usare i singoli bit del byte come singoli flag, p.e. bit 0 eFuse, bit 1 HVPP, bit 2 HVSP, bit 3 HVP13, dopo di che li hai sotto forma di nomi mnemonici come variabili a booleane (1 bit) grazie all'unione, p.e. AuxData.BtF.eFUSE, i nomi della unione e struttura puoi metterli a tuo piacere, anche di una sola lettera, p.e. U.S.eFuse .

astrobeed:

menniti: forse è meglio se chiarisco il mio intento; nel 4° byte memorizzo un valore da 0x01 a 0x06 in base alle 6 possibili combinazioni tra EFuse presente/non

Quello che ti sto suggerendo io è di usare i singoli bit del byte come singoli flag, p.e. bit 0 eFuse, bit 1 HVPP, bit 2 HVSP, bit 3 HVP13, dopo di che li hai sotto forma di nomi mnemonici come variabili a booleane (1 bit) grazie all'unione, p.e. AuxData.BtF.eFUSE, i nomi della unione e struttura puoi metterli a tuo piacere, anche di una sola lettera, p.e. U.S.eFuse .

OK, quindi dovrei impostare il bit meno significativo (p.es.) per il mode e quello più significativo per l'EFuse? 0x01 EFuse=0 mode = HVPP 0x02 EFuse=0 mode = HVSP 0x13 EFuse=1 mode = HVP13 intendi questo?

astrobeed:

leo72: Io avrei fatto diversamente. Bastava usare la funzione predefinita di Arduino bitRead:

Corretto, però la BitRead in realtà è questo:

Sì, è vero. Però suggerisco un metodo più semplice da comprendere ed usare da uno la cui prima attività non è la programmazione. Usando la funzione predefinita hai al costo di un leggero aumento del peso computazionale una semplicità di gestione dei dati.

leo72: Sì, è vero. Però suggerisco un metodo più semplice da comprendere ed usare da uno la cui prima attività non è la programmazione. Usando la funzione predefinita hai al costo di un leggero aumento del peso computazionale una semplicità di gestione dei dati.

Per la come la vedo io è più semplice scrivere, e anche più leggibile, "if(U.S.eFuse == 1)" invece di " if(bitRead(dato,eFuse) == 1)".

Io ho intenzione di provare ogni suggerimento, questa per me deve essere anche occasione di studio, altrimenti torniamo alle pappe pronte, con l'HW e i datasheet, l'avete visto, me la cavo un po' di più, ma il sw... :~ La BitRead dovrei usarla due volte per leggere prima uno e poi l'altro bit oppure in questo caso uso solo il bit meno significativo, dando valori da 01 a 06?

Poi ciò che userò sarà quella con cui riuscirò a prendere più dimestichezza, devo comunque avere la padronanza totale del firmware

menniti: La BitRead dovrei usarla due volte per leggere prima uno e poi l'altro bit oppure in questo caso uso solo il bit meno significativo, dando valori da 01 a 06?

Con la bitRead leggi un solo bit alla volta. Se vuoi leggere più bit insieme potresti usare una bitmask. Ad esempio

if (dato && 0b11000000) { ... }

faresti una certa azione solo nel caso in cui i 2 bit più significativi di un byte fossero ad 1.

NO, mi conviene leggere solo il bit meno significativo con valore da 1 a 6. Quando finisco di lavorare provo le due tecniche, vediamo che ne esce :~

menniti: NO, mi conviene leggere solo il bit meno significativo con valore da 1 a 6.

Non ho capito.... il bit meno significativo è lo 0.

menniti: La BitRead dovrei usarla due volte per leggere prima uno e poi l'altro bit oppure in questo caso uso solo il bit meno significativo, dando valori da 01 a 06?

Uhm.. Ma te volevi memorizzare un numero da 1 a 6. Per fare ciò bastano 3 bit. 000=0 001=1 010=2 011=3 100=4 101=5 110=6 Quindi basta uno switch..case su questi 3 bit: switch (dato && 0b00000111) { case 0: ecc... }

In pratica usi una bitmask con valore 7 o, se preferisci, 0b00000111, così isoli solo i primi 3 bit e poi controlli agisci

No, Leo, mi sono espresso male, io uso, come suggerito da Astro un valore tipo 0x00 per memorizzare dei flag, vorrei usare l’ultima cifra per leggere solo quella, cioè 0x01, 0x02, 0x03 ecc., quindi faccio qualcosa del tipo
se = 1 EFUSE=0 e mode=HVPP
se = 2 EFUSE=0 e mode=HVSP
è più chiaro ora?
Sono appena rientrato, ora mi comincio a mettere al lavoro, se hai suggerimenti immediati spara! :stuck_out_tongue:

Ok. Ora è chiaro. Comunque ricorda che in un byte ci sono 8 bit per cui puoi avere fino a 8 flag differenti. Se ragioni in termini di "cifre" perdi dei bit per futuri usi. Pensando in termini di notazioni numeriche, in notazione esadecimale ogni cifra contiene un valore che prende 4 bit. Ragionando in termini di notazione binaria, invece, per memorizzare dei valori decimali da 0 a 6, usi solo 3 bit, salvando quelli dal 4°all'8°(quindi 5 flag) per futuri usi.

leo72: Ok. Ora è chiaro. Comunque ricorda che in un byte ci sono 8 bit per cui puoi avere fino a 8 flag differenti. Se ragioni in termini di "cifre" perdi dei bit per futuri usi. Pensando in termini di notazioni numeriche, in notazione esadecimale ogni cifra contiene un valore che prende 4 bit. Ragionando in termini di notazione binaria, invece, per memorizzare dei valori decimali da 0 a 6, usi solo 3 bit, salvando quelli dal 4°all'8°(quindi 5 flag) per futuri usi.

Scusa, ma usando un valore esadecimale io mi predispongo per 32 combinazioni, da 0x00 a 0xFF, giusto? usando da 0x01 a 0x06 non sto semplicemente usando i primi 6 bit preservandomi i successivi 26 oppure non c'ho capito una mazza?