Go Down

Topic: How to Present DS3231's Temp Signal as a float onto LCD? (Read 1 time) previous topic - next topic

GolamMostafa

Dec 15, 2017, 05:50 pm Last Edit: Dec 15, 2017, 06:18 pm by GolamMostafa
As a part of Lab Work, we are playing with the following setup (Fig-1) that involves UNO-LCD-DS3231-TWI Bus. Our current interest is to read the temperature signal of the built-in Temperature Sensor of the DS3231 RTC Chip and then present it onto the LCD.

Everything is fine except that we have difficulties to format the contents of the Temperature Registers of the RTC in order to present the Temperature value as float (XX.XX) onto the LCD. The fractional part shows: XX.1 or XX.2 or XX.3 instead of XX.25 or XX.50 or XX.75.

Let us remember that the Temperature Register (Upper, Fig-2) contains the integer part of the Temperature and the Bit-7, 6 of the Temperature Register (Lower, Fig-2) contains the fractional part of the Temperature.


Figure-1: UNO-LCD-DS3231-TWI Bus Setup


Figure-2: Temperature Registers of DS3231 RTC Chip

Code: [Select]
#include<Wire.h>
#define deviceAddress 0b1101000

#include<LiquidCrystal.h>
LiquidCrystal lcd(A0, A1, A2, A3, 12, 13);
byte x;
void setup()
{
  Wire.begin();       //TWI Bus is formed  
  lcd.begin(20, 4);
  lcd.setCursor(0, 0);
}

void loop()
{
  Wire.beginTransmission(deviceAddress);    //START, Roll Call
  Wire.write(0x0E);                         //Control Register Address set
  Wire.write(0x20);                         //Start Tempertaure Conversion; CONV bit is set High
  Wire.endTransmission();                   //Transfer above queued data, ACK, STOP
  
  do
  {
    Wire.requestFrom(deviceAddress, 1);     //to check end-of-conversion of TCXO algorithm
    x = Wire.read();                        //read content of Control Register
  }
  while (bitRead(x, 5) != LOW);             //chcek if CONV bit has assumed LL state
  
  
  //--- read Temperature Signal--------
  Wire.beginTransmission(deviceAddress);    //START, Roll Call
  Wire.write(0x11);                         //address of Temp Register (Uppler)
  Wire.endTransmission();                   //Transfer above queued data ACK, STOP

  Wire.requestFrom(deviceAddress, 2);       //Reading Temperature Data
  byte tempUpper = Wire.read();
  byte tempLower = Wire.read();
  
  lcd.print(tempUpper, DEC);        //LCD shows integer part: XX
  lcd.write(0x2E);                  //place point; LCD shows: XX.
  lcd.print(tempLower>>6, DEC);     //shows: 1 or 2 o 3; we want: 25 or 50 or 75
  delay(3000);                                 //3-sec sampling period
  lcd.setCursor(0,0);
}

cattledog

Code: [Select]
tempLower>>6

This is leaving the 2 bits  (7 and 6) with the fractional part of the temperature. They represent the two to the -1 and -2 powers.  The fractional part of the temperature the is therefore

0 0 = .00

0 1 = .25

1 0 = .50

1 1 = .75

You can use
Code: [Select]
lcd.print(25*(tempLower>>6))


GolamMostafa

#2
Dec 15, 2017, 06:59 pm Last Edit: Dec 15, 2017, 08:04 pm by GolamMostafa
@cattledog

Thanks with +1! Your formula works! However, there seems to be an unexpected erratic side effect --- the display time-to-time shows: .05! I have been investigating the source of this spurious signal.

cattledog

Quote
the display time-to-time shows: .05! I have been investigating the source of this spurious signal.
It's when you have .75 followed by 00 which is printing as 0. You will need to do some cursor managment to clear the full field length, or force printing to two digits.

GolamMostafa

@cattledog

Interesting observation!

BTW: I have fixed the integer part, and you have fixed the fractional part with regards to displaying the result with decimal point without doing any floating point math.

In fact, I have been thinking about the possibility of performing float math on the contents of Temperature Registers so that the formula lcd.print(temp, 2) could be used to show the temperature with 2-digit accuracy after the decimal point.

aarg

Untested:
Code: [Select]
  float temp = Wire.read(); //upper byte
  temp += (float)Wire.read()/256;  //lower byte
 
  lcd.print(temp, 2);
  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

GolamMostafa

#6
Dec 15, 2017, 08:02 pm Last Edit: Dec 15, 2017, 08:16 pm by GolamMostafa
@aarg

Double thanks along with +1! Your codes work!

BTW: Should we cast float in the following statement of your codes as the content of the Temperature Register (Upper) is integer? We are now asking that the integer should be stored as floating point number in binary32 format.

Code: [Select]
float temp = Wire.read(); //upper byte
===> float temp = (float)Wire.read(); //upper byte

aarg

  ... with a transistor and a large sum of money to spend ...
Please don't PM me with technical questions. Post them in the forum.

cattledog

Quote
Your codes work!
Code: [Select]
float temp = Wire.read(); //upper byte
  temp += (float)Wire.read()/256;  //lower byte
  lcd.print(temp, 2);


Please explain this to me. The full value set of the lower byte are B11000000, B10000000, B01000000, and B00000000.  Dividing by 256 should bring you back to the 3,2,1,0 situation you originally had.

I would have thought

Code: [Select]
float temp = Wire.read(); //upper byte
  temp += (float).25*Wire.read()/256;  //lower byte
  lcd.print(temp, 2);


EDIT: alternatively
Code: [Select]
float temp = Wire.read(); //upper byte
  temp += (float)((Wire.read()/256)/4.0)) //lower byte
 
  lcd.print(temp, 2);




el_supremo

B11000000 = 0xC0 = 192 decimal. Dividing by 256. gives .75 which is correct.
I am not sure that storing the bytes into floats will work because they are meant to be part of a twos complement representation of the temperature. It won't handle negative temperatures correctly.
It would be safer to assemble the temperature into a signed 16-bit integer and then divide by 256. to convert to float:
Code: [Select]
 int16_t i_temp;  // NOT uint16_t
  
  i_temp = WHICH_WIRE.read() << 8;
  i_temp |= WHICH_WIRE.read();
  float temp = i_temp / 256.;


Pete
Don't send me technical questions via Private Message.

cattledog

Quote
B11000000 = 0xC0 = 192 decimal. Dividing by 256. gives .75 which is correct.
Thank's. I had a binary brain fart.

Quote
It would be safer to assemble the temperature into a signed 16-bit integer and then divide by 256. to convert to float:
Here's the way the Jarzebski library does it

Code: [Select]
float DS3231::readTemperature(void)
{
    uint8_t msb, lsb;

    Wire.beginTransmission(DS3231_ADDRESS);
    #if ARDUINO >= 100
        Wire.write(DS3231_REG_TEMPERATURE);
    #else
        Wire.send(DS3231_REG_TEMPERATURE);
    #endif
    Wire.endTransmission();

    Wire.requestFrom(DS3231_ADDRESS, 2);

    while(!Wire.available()) {};

    #if ARDUINO >= 100
    msb = Wire.read();
    lsb = Wire.read();
    #else
    msb = Wire.receive();
    lsb = Wire.receive();
    #endif

    return ((((short)msb << 8) | (short)lsb) >> 6) / 4.0f;
}

GolamMostafa

Quote
Please explain this to me.
I must remain sincere to my own ethics. I have received codes from @cattledog, and I have tested it in a real UNO Machine. I have received codes from @aarg, and I have tested the codes in the same machine. The codes are different in their styles; but, they have produced the same result what I have expected as OP. I have extended thanks to both with +1s.


GolamMostafa

Once, I could not do it. Now, I can do it in the following way being inspired by the Forum Members of this thread:

Code: [Select]
float tempUpper = (float)Wire.read();
 byte tempLower = Wire.read();

 float tempLowerx = (float) ((tempLower>>7)*0.5 + ((tempLower>>6)&0x01) *0.250);
 float temp = tempLowerx + tempUpper;
 lcd.print(temp, 2);


 

david_prentice

I have just skimmed through these messages.  I am not sure if you will get the negative temperatures correct.

Look at other datasheets to to see the representation of -0.25C

Personally I read the two bytes into a signed int.   Then divide by 256.0 to get the f-p result.
You use exactly the same method whether the sensor has 9 bits, 10 bits, ... 12 ... 16 bits of precision.

David.

el_supremo

@cattledog: That code does essentially what I wrote but with a somewhat convoluted way to combine the two bytes into a signed integer which is then divided to give a float result.

@GolamMostafa: You can choose to do it anyway you like but using floats like that will only work for non-negative temperatures. If you will never get temperatures below zero it isn't going to matter but in the long run it is best not to use buggy code.

Pete
Don't send me technical questions via Private Message.

Go Up