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
#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);
}
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
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.
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.
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.
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.
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.
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:
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.
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.
@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.
I like the method in #9. It's the most straightforward and WYSIWYG. I think when the int temp is promoted to a float and divided by a float, the /256 is not computationally expensive because it just subtracts 8 from the exponent.
Also as pointed out in reply #13, the method is agnostic to precision.
@GolamMostafa: It is easy enough to test the conversions. If you assume that the high order byte is 0xFC and the low order byte is 0xC0, your code from message #12 will print 252.75. My code prints -3.25 which is correct.