RTC DS3231 lettura registro interno della temperatura

Ciao a tutti :),
da un paio di giorni, mi sono messo a giocare con un RTC DS3231 (http://datasheets.maximintegrated.com/en/ds/DS3231.pdf), appurato che tutto sommato lo si può tranquillamente leggere e scrivere come fosse un DS1307, mi sono concentrato nella lettura del suo registro della temperatura, nel datasheet se ne parla a pag. 15.
Purtroppo per me restituisce i valori negativi in complemento a 2, mi sono andato a studiare come funziona la cosa ed avrei scritto lo sketch che vedete qui sotto.

#include <Wire.h>

byte secondi, minuti, ora, giornosett, giorno, mese;
int anno;
char* gset[] = {"Lunedi", "Martedi", "Mercoledi", "Giovedi", "Venerdi", "Sabato", "Domenica"};

byte decToBcd(byte val) // Da decimale a binario
{ return ( (val / 10 * 16) + (val % 10) );
}

byte bcdToDec(byte val) // Da binario a decimale
{ return ( (val / 16 * 10) + (val % 16) );
}

void LetturaDataOra()
{ Wire.beginTransmission(0x68);
  Wire.write((byte)0x00);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 7);
  //recupero i 7 byte relativi ai corrispondenti registri

  secondi = bcdToDec(Wire.read() & 0x7f);
  minuti = bcdToDec(Wire.read());
  ora = bcdToDec(Wire.read() & 0x3f);
  giornosett = bcdToDec(Wire.read());
  giorno = bcdToDec(Wire.read());
  mese = bcdToDec(Wire.read());
  anno = bcdToDec(Wire.read()) + 2000;
}

float Temp() {
  Wire.beginTransmission(0x68);
  Wire.write(0x11);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 2);
  int MSBtemp = Wire.read();
  byte LSBtemp = Wire.read();
  
  if (MSBtemp >> 7 == 1) //controllo il bit 7 per sapere se ho ricevuto un numero negativo
    MSBtemp = ~(~MSBtemp + 1 + 128) + 1;  //  se lo è eseguo la coversione del byte con complemento a 2
  
  float Temperature = (float) (MSBtemp) + (float) ((LSBtemp >> 6) * 0.25);
  return Temperature;
}

void stampa0 (int dato_tempo)
{ if (dato_tempo < 10)
    Serial.print(F("0"));
  Serial.print(dato_tempo);
}

void setup()
{
  //inizializzo la seriale
  Serial.begin(9600);

  //inizializzo la libreria
  Wire.begin();

  Wire.beginTransmission(0x68);	//il primo byte stabilisce il registro iniziale da scivere
  Wire.write((byte)0x00);
  Wire.write(decToBcd(1));
  Wire.write(decToBcd(41));
  Wire.write(decToBcd(10));
  Wire.write(decToBcd(2));
  Wire.write(decToBcd(8));
  Wire.write(decToBcd(4));
  Wire.write(decToBcd(14));
  Wire.endTransmission();
}


void loop()
{

  LetturaDataOra();

  Serial.print(gset[giornosett - 1]);
  Serial.print(" ");
  stampa0(giorno);
  Serial.print("/");
  stampa0(mese);
  Serial.print("/");
  stampa0(anno);
  Serial.print("  ");
  stampa0(ora);
  Serial.print(":");
  stampa0(minuti);
  Serial.print(":");
  stampa0(secondi);
  Serial.print("  Temperatura: ");
  Serial.print(Temp());
  Serial.println();

  delay(1000);
}

Nella pratica, ho fatto un po’ di prove in quest’altro sketch e sembra che la conversione dei binari a 7 bit funzioni, in alto nei commenti ho messo un paio di esempi da provare.

/* -25 = -00011001
          11100110 + byte negato
                 1
          --------
          11100111 complemento a 2 ovvero come dovrebbe essere il registro dell'RTC in caso di temperatura -25

   -40 = -00101000
          11010111 + byte negato
                 1
          --------
          11011000 complemento a 2 ovvero come dovrebbe essere il registro dell'RTC in caso di temperatura -25

*/

byte regtemp_RTCDS3231 = B11100111; // -25 complemento a due

void setup() {
  Serial.begin(9600);
  Serial.print("regRTCDS3231= ");
  Serial.print(regtemp_RTCDS3231, BIN); // giusto per essere sicuro che il byte sia memorizzato correttamente
  
  Serial.print("  Valore decimale = ");
  if (regtemp_RTCDS3231 >> 7 == 1)  { // se il bit 7 è 1 ho un valore negativo
    bitWrite(regtemp_RTCDS3231, 7, 0); // azzero il bit del segno
    int valore_dec = ~(~regtemp_RTCDS3231 + 1 + 128) + 1;
    Serial.print("  Valore decimale = ");
    Serial.print(valore_dec);
  }
  else
    Serial.print(regtemp_RTCDS3231);
}

void loop() {
  // put your main code here, to run repeatedly:

}

Nella teoria però non sono sicuro di aver fatto le cose a modino, a dirla tutta, a furia di fare tentativi non ricordo nemmeno più come ci sono arrivato :blush:, magari c’è un metodo molto più semplice, se fosse me lo potreste illustrare?

Chiaramente la prova pratica la farò stasera tenendo l’RTC nel freezer per un po’ e andandolo a leggere poi, ovviamente con le dovute cautele e cercando il più possibile di evitare corti dovuti alla condensa o altro.

Grazie in anticipo per il tempo che vorrete dedicarmi.

Ho fatto le prove di cui parlavo (RTC in freezer)
ed il codice, sembra funzionare, lo riporto perché avevo omesso un comando e sbagliato la dichiarazione di MSB:

#include <Wire.h>

byte secondi, minuti, ora, giornosett, giorno, mese;
int anno;
char* gset[] = {"Lunedi", "Martedi", "Mercoledi", "Giovedi", "Venerdi", "Sabato", "Domenica"};

byte decToBcd(byte val) // Da decimale a binario
{ return ( (val / 10 * 16) + (val % 10) );
}

byte bcdToDec(byte val) // Da binario a decimale
{ return ( (val / 16 * 10) + (val % 16) );
}

void LetturaDataOra()
{ Wire.beginTransmission(0x68);
  Wire.write((byte)0x00);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 7);
  //recupero i 7 byte relativi ai corrispondenti registri

  secondi = bcdToDec(Wire.read() & 0x7f);
  minuti = bcdToDec(Wire.read());
  ora = bcdToDec(Wire.read() & 0x3f);
  giornosett = bcdToDec(Wire.read());
  giorno = bcdToDec(Wire.read());
  mese = bcdToDec(Wire.read());
  anno = bcdToDec(Wire.read()) + 2000;
}

float Temp() {
  Wire.beginTransmission(0x68);
  Wire.write(0x11);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 2);
  int MSBtemp = Wire.read();
  byte LSBtemp = Wire.read();
  Serial.print("  "); Serial.print(MSBtemp, BIN); Serial.print("  ");

  if (MSBtemp >> 7 == 1) {//controllo il bit 7 per sapere se ho ricevuto un numero negativo
    bitWrite(MSBtemp, 7, 0);
    MSBtemp = ~(~MSBtemp + 1 + 128) + 1;  //  se lo è eseguo la coversione del byte con complemento a 2
  }

  float Temperature = (float) (MSBtemp) + (float) ((LSBtemp >> 6) * 0.25);
  return Temperature;
}

void stampa0 (int dato_tempo)
{ if (dato_tempo < 10)
    Serial.print(F("0"));
  Serial.print(dato_tempo);
}

void setup()
{
  //inizializzo la seriale
  Serial.begin(9600);

  //inizializzo la libreria
  Wire.begin();

  /*Wire.beginTransmission(0x68);	//il primo byte stabilisce il registro iniziale da scivere
  Wire.write((byte)0x00);
  Wire.write(decToBcd(1));
  Wire.write(decToBcd(41));
  Wire.write(decToBcd(10));
  Wire.write(decToBcd(2));
  Wire.write(decToBcd(8));
  Wire.write(decToBcd(4));
  Wire.write(decToBcd(14));
  Wire.endTransmission();*/
}


void loop()
{

  LetturaDataOra();

  Serial.print(gset[giornosett - 1]);
  Serial.print(" ");
  stampa0(giorno);
  Serial.print("/");
  stampa0(mese);
  Serial.print("/");
  stampa0(anno);
  Serial.print("  ");
  stampa0(ora);
  Serial.print(":");
  stampa0(minuti);
  Serial.print(":");
  stampa0(secondi);
  Serial.print("  Temperatura: ");
  Serial.print(Temp());
  Serial.println();

  delay(1000);
}

Però ripropongo la domanda, ho usato il metodo giusto, o c’è un metodo migliore di quello che ho usato io?

  if (MSBtemp >> 7 == 1) {//controllo il bit 7 per sapere se ho ricevuto un numero negativo
    bitWrite(MSBtemp, 7, 0);
    MSBtemp = ~(~MSBtemp + 1 + 128) + 1;  //  se lo è eseguo la coversione del byte con complemento a 2
  }

Grazie ancora per il vostro tempo.

Riccardo

Il complemento a due è il modo usato dai compilatori. Hai provato semplicemente ad assegnare il risultato che leggi ad un char, che è un signed ad 8 bit?

A parte questo, il controllo dell'ottavo bit (non il settimo!), lo puoi fare così:
if (dato && 0b10000000) { .... }

Ciao Leo,
grazie per la risposta.
Il bit 7 partendo la numerazione da 0 è appunto l'ottavo bit, di fatto facendo

if (MSBtemp >> 7 == 1)

non sto leggendo proprio quello?
Forse sbaglio io, anzi sicuramene, più tardi verifico sia questo che l'assegnazione a char come mi hai suggerito.
Grazie ancora.

Ciao Riccardo.

Scusami, l'ora mi ha fatto leggere non so dove "settimo" :sweat_smile: :sweat_smile:
Sì, il bit n° 7 è l'ottavo bit.

Però con l'AND diretto come ho fatto io fai una sola operazione, mentre con lo shift e poi il test di uguaglianza fai compiere più operazioni per il test

Ciao Leo,
ho fatto le prove che mi hai suggerito ovvero con char e l’AND ed ecco le mie :cold_sweat: conclusioni:
char effettivamente è un signed, io non ci avrei mai pensato :blush:, questo mi ha permesso di fare confronti direttamente per >= 0 senza scomodare AND con i bit, per sapere se il dato è positivo o negativo, in più ho corretto anche un’altro mio errore, infatti in caso di parte intera negativa, la parte frazionaria per essere aggiunta va sottratta non sommata, così ho differenziato le operazioni, ecco il codice rielaboato:

#include <Wire.h>

byte secondi, minuti, ora, giornosett, giorno, mese;
int anno;
char* gset[] = {"Lunedi", "Martedi", "Mercoledi", "Giovedi", "Venerdi", "Sabato", "Domenica"};

byte decToBcd(byte val) // Da decimale a binario
{ return ( (val / 10 * 16) + (val % 10) );
}

byte bcdToDec(byte val) // Da binario a decimale
{ return ( (val / 16 * 10) + (val % 16) );
}

void LetturaDataOra()
{ Wire.beginTransmission(0x68);
  Wire.write((byte)0x00);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 7);
  //recupero i 7 byte relativi ai corrispondenti registri

  secondi = bcdToDec(Wire.read() & 0x7f);
  minuti = bcdToDec(Wire.read());
  ora = bcdToDec(Wire.read() & 0x3f);
  giornosett = bcdToDec(Wire.read());
  giorno = bcdToDec(Wire.read());
  mese = bcdToDec(Wire.read());
  anno = bcdToDec(Wire.read()) + 2000;
}

float Temp() {
  Wire.beginTransmission(0x68);
  Wire.write(0x11);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 2);
  char MSBtemp = Wire.read();
  char LSBtemp = Wire.read();
  
  if (MSBtemp >= 0) {
    float Temperature = (float) (MSBtemp) + (float) ((LSBtemp >> 6) * 0.25);
    return Temperature;
  }
  else  {  // se la parte intera della temp. è negativa lo è anche la parte frazionaria, quindi per aggiungerla la devo sottrarre.
    float Temperature = (float) (MSBtemp) - (float) ((LSBtemp >> 6) * 0.25);
    return Temperature;
  }
}

void stampa0 (int dato_tempo)
{ if (dato_tempo < 10)
    Serial.print(F("0"));
  Serial.print(dato_tempo);
}

void setup()
{
  //inizializzo la seriale
  Serial.begin(9600);

  //inizializzo la libreria
  Wire.begin();

  /*Wire.beginTransmission(0x68);	//il primo byte stabilisce il registro iniziale da scivere
  Wire.write((byte)0x00);
  Wire.write(decToBcd(1));
  Wire.write(decToBcd(41));
  Wire.write(decToBcd(10));
  Wire.write(decToBcd(2));
  Wire.write(decToBcd(8));
  Wire.write(decToBcd(4));
  Wire.write(decToBcd(14));
  Wire.endTransmission();*/
}


void loop()
{

  LetturaDataOra();

  Serial.print(gset[giornosett - 1]);
  Serial.print(" ");
  stampa0(giorno);
  Serial.print("/");
  stampa0(mese);
  Serial.print("/");
  stampa0(anno);
  Serial.print("  ");
  stampa0(ora);
  Serial.print(":");
  stampa0(minuti);
  Serial.print(":");
  stampa0(secondi);
  Serial.print("  Temperatura: ");
  Serial.print(Temp());
  Serial.println();

  delay(1000);
}

Invece per l’AND con i char, ho riscontrato che fare:

char dato =   11100111 //-25
if (dato && 0b10000000)

oppure

char dato =   00000111 // 7
if (dato && 0b10000000)

è sempre vero e non capisco perché :(.

Come sempre grazie sinceramente per i suggerimenti ed il supporto.

Riccardo.

"Char" è un tipo di dato nativo del C, che contiene un carattere (char-acter) ma anche valori di tipo signed ad 8 bit. Quindi da -128 a +127.

Invece sul secondo problema dovrei vedere il codice completo perché hai sbagliato a scrivere.
Senza "0b" iniziale, quei numeri sono convertiti letteralmente, quindi quello sopra vale 11100111 e quello sotto 111.

Ecco il codice, un pò brutale ma spero renda l'idea.

boolean confronto = false;

char dato =  11100111; //-25 o contenuto reg. RTC quindi non posso mettere davanti ob


void setup() {
  Serial.begin(9600);
  if (dato && 0b10000000)
    confronto = true;

  Serial.print(confronto);
}

void loop() {
  // put your main code here, to run repeatedly:

}

se cambi il valore di dato mettendo un positivo vedrai che confronto è sempre vero, ma perché? :frowning:

Riccardo

Stai commettendo lo stesso errore che ti ho citato prima. Ossia che se non metti "0b" davanti ad un numero, il compilatore non lo interpreta come binario..
Nel tuo codice c'è:

char dato =  11100111;

E' da cambiare in:

char dato =  0b11100111;

Tu stai assegnando i numero 11.100.111 (undici milioni, centomila, centoundici) ad un char, che ovviamente non può contenerlo e lo tronca.

Si è come dice leo. Inoltre non ha notato && al posto di &.

if (dato && 0b10000000)

Deve diventare:

if (dato & 0b10000000)

Che è uguale a:

if ( dato & _BV(7) )
// stessa cosa, ma esplicita
if (dato & 0x80)

Preferisco esadecimale perché: dal 7 al bit 0, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01

Ciao.

Buongiorno,
grazie a tutti e due per le risposte,

leo72:
Stai commettendo lo stesso errore che ti ho citato prima. Ossia che se non metti "0b" davanti ad un numero, il compilatore non lo interpreta come binario..
Nel tuo codice c'è:

char dato =  11100111;

E' da cambiare in:

char dato =  0b11100111;

Tu stai assegnando i numero 11.100.111 (undici milioni, centomila, centoundici) ad un char, che ovviamente non può contenerlo e lo tronca.

Hai ragione, ma per me dato è ciò che arriva dall'RTC e non posso scrivere:
dato = 0b(wire.read());
oppure dato = 0bwire.read();
ancora meno funziona
if (0bdato && 0b10000000)
oppure if (0b(dato) && 0b10000000)
Tu non avresti mai fatto ste prove, ma io che sono un pivellino mi sono voluto togliere la sete col prosciutto XD :blush:
Il problema però sta nel tuo suggerimento dell'altra mattina, quando le braccia di Morfeo ancora non ti avevano completamente liberato XD XD XD XD XD XD

leo72:
Il complemento a due è il modo usato dai compilatori. Hai provato semplicemente ad assegnare il risultato che leggi ad un char, che è un signed ad 8 bit?

A parte questo, il controllo dell'ottavo bit (non il settimo!), lo puoi fare così:
if (dato && 0b10000000) { .... }

Infatti ha ragione MauroTec

MauroTec:
Si è come dice leo. Inoltre non ha notato && al posto di &.

if (dato && 0b10000000)

Deve diventare:

if (dato & 0b10000000)

Con una sola "&" funziona, anche senza mettere il prefisso "0b"

In ogni caso grazie di nuovo grazie a tutti e due per gli insegnamenti e la pazienza :slight_smile:

Riccardo.

Sì, mea culpa. Ho scritto && al posto di &.

riciweb:

MauroTec:
Deve diventare:

if (dato & 0b10000000)

Con una sola "&" funziona, anche senza mettere il prefisso "0b"

Rici, non serve mettere 0b davanti a "dato". Il compilatore lo considera già un numero.
Diverso è il discorso della rappresentazione binaria.
Scrivere 10000000 (diecimilioni) è diverso da scrivere 0b10000000 (duecentocinquantacinque).

leo72:
Sì, mea culpa. Ho scritto && al posto di &.

Ma daiiiii... ci mancherebbe scherzi :astonished:
Senza te e gli altri chissa dove sarei ancora!!!

@PaoloP
Grazie anche a te, ora ho le idee decisamente più chiare come avrai notato :slight_smile:

Riccardo.