decToBcd e bcdToDec

Ciao a tutti, nel reference non trovo riferimenti a queste due funzioni, ho capito, cercando in giro, che trasformano un decimale in Binario BCD e viceversa, e ho trovato che questo è il codice:

uint8_t decToBcd( int val )
{
    return (uint8_t) ((val / 10 * 16) + (val % 10));
}

uint8_t bcdToDec( uint8_t val )
{
    return (uint8_t) ((val / 16 * 10) + (val % 16));
}

Non riesco però ad immaginarmi cosa fa di preciso, vado su un esempio pratico: quando setto l'ora attuale sul ds1307 e trovo quando segue:

byte second = 55;
Wire.write(decToBcd(second));

come viene inviato il numero?

Penso che sia come scritto nella funzione: 55/10*16 + 55 modulo 10 = 88 + 5 = 93

Per capire a che servono, devi capire cos'è la codifica BCD. https://it.wikipedia.org/wiki/Binary-coded_decimal

E' un sistema nato parecchi anni fa che serve a memorizzare e/o rappresentare numeri decimali in formato binario. Le cifre decimali vanno da 0 a 9, sono quindi 10 (sistema decimale). Per rappresentarle in un registro binario servono 4 bit (2^4 possono infatti memorizzare 16 valori diversi). In un byte, quindi, ci entrano 2 cifre.

Perché usare questo sistema al posto del classico uso dell'intero byte, che può tenere un numero da 0 a 255? In passato era comodo perché la codifica BCD poteva essere usata ad esempio in circuiti senza processore per pilotare direttamente display a 7 segmenti, ad esempio. Con l'avvento dei processori e controllori la codifica BCD ha perso importanza anche se ancora viene usata in alcuni casi.

Le funzioni che hai menzionato te servono a convertire da e per BCD un numero decimale perché il DS1307 ancora salva i dati in formato BCD internamente. Il vecchissimo 6502, se non ricordo male, aveva la possibilità di passare dall'aritmetica decimale a quella BCD.

Vediamo se ho capito come funziona:

La scrittura BCD usa quattro bit per rappresentare una cifra (tecnicamente potrebbe rappresentare 16 combinazioni, e qui abbiamo quello spreco di memoria di cui si parla). In un Byte posso quindi inserire due BCD, pertanto due cifre. Un Byte è in grado di portare numeri da 0 a 99 scritti con BCD. Corretto?

La funzione pertanto (mantenendo l'esempio fatto) divide il 55 per 10 e ottiene 5, abbiamo così il valore più significativo del numero che viene moltiplicato per 16 perché per 'scattare di posizione' il BCD deve passare a 16 posizioni successive, quindi aggiunge il valore del resto della prima divisione per occupare la posizione meno significativa delle due. (So che è poco chiaro come l'ho scritto, ma è come se si moltiplicasse il 5*10 per occupare la posizione delle decine e si aggiungesse poi il 5 nella posizione delle unità per creare il numero decimale). A questo punto la funzione che spedisce via I2C traduce quel numero ottenuto (apparentemente decimale -93-) in byte. Quindi il 9 occupa i primi quattro bit e il 3 occupa gli altri quattro.

Giusto? spero di non essere stato troppo contorto

mastraa: Vediamo se ho capito come funziona:

La scrittura BCD usa quattro bit per rappresentare una cifra (tecnicamente potrebbe rappresentare 16 combinazioni, e qui abbiamo quello spreco di memoria di cui si parla). In un Byte posso quindi inserire due BCD, pertanto due cifre. Un Byte è in grado di portare numeri da 0 a 99 scritti con BCD. Corretto?

Tutto esatto.

Adesso una correzione ai calcoli. Non mi ero accorto dell'errore di Michele. 55 in codifica BCD è 85, non 93. Questo perché la formula lavora con interi, ed in caso di numeri con decimali, questi vengono troncati. Per cui:

(uint8_t) (55 / 10 * 16) diventa (più leggibile, almeno si capisce la priorità delle operazioni) (uint8_t)((55 / 10) * 16 Da cui si ha 80 perché 55/10 = 5.5 => 5. E poi 5*16 = 80. 80 + (55 mod 10) = 80 + 5 = 85

Difatti la conferma si ha analizzando il byte nei singoli bit:

0101 0101

Il numero 5 in binario è infatti 101. In un nibble (la metà di un byte), esso è rappresentato da 0101. Due nibble con valore 5 insieme sono appunto 0101 0101 Se consideriamo questo byte nella sua completezza si ha 01010101 Che trasformato in decimale è proprio 85.

La funzione pertanto (mantenendo l'esempio fatto) divide il 55 per 10 e ottiene 5, abbiamo così il valore più significativo del numero che viene moltiplicato per 16 perché per 'scattare di posizione' il BCD deve passare a 16 posizioni successive, quindi aggiunge il valore del resto della prima divisione per occupare la posizione meno significativa delle due. (So che è poco chiaro come l'ho scritto, ma è come se si moltiplicasse il 5*10 per occupare la posizione delle decine e si aggiungesse poi il 5 nella posizione delle unità per creare il numero decimale). A questo punto la funzione che spedisce via I2C traduce quel numero ottenuto (apparentemente decimale -93-) in byte. Quindi il 9 occupa i primi quattro bit e il 3 occupa gli altri quattro.

Giusto? spero di non essere stato troppo contorto

Esatto. Il "trasporto" di un byte non influisce sull'informazione che contiene

Mi scuso per l'errore ma ero convinto che essendo le due operazioni in unica parentesi l'eventuale troncamento si facesse alla fine e non già nella fase intermedia della divisione :sweat_smile:

[quote author=Michele Menniti link=topic=259974.msg1838121#msg1838121 date=1407746906] Mi scuso per l'errore ma ero convinto che essendo le due operazioni in unica parentesi l'eventuale troncamento si facesse alla fine [/quote]

Il troncamento è dovuto sia alla dimensione del registro che al fatto dell'esecuzione calcolo tramite numeri interi, tutti gli eventuali decimali vanno perduti durante l'esecuzione delle singole operazioni. Se nell'espressione viene indicato un valore float viene tenuto conto dei decimali per tutto il calcolo e vengono troncati solo nel risultato finale se è di tipo intero. Da notare che le funzioni decToDcb e inverso sono poco ottimizzate, dato che si tratta di divisioni e moltiplicazione per 16 meglio usare una shift di quattro per eseguirle, idem per il %16 dove è meglio usare una schermatura AND (val | 0x0F) per azzerare gli uno del nible superiore.

Grazie a tutti!!!