The DS3231 RTC is a rather accurate RTC that has an internal temperature sensor that is used to calibrate the oscillator. The sensor is however also readable from external software. It has a 10 bit solution and uses two registers: 0x11 and 0x12. The information in the upper byte is stored in 2-complement notation. The lowerbyte is there for the fractional part and has a solution of 0.25 degrees Celsius.
Two’s complement notation means that with positive numbers it just follows the regular binary storage, but with negative numbers it does it a bit different. Knowing whether a number is negative or positive is indicated by the MSB in the Upper byte. If that is a ‘1’, then the number is negative.
Any reading of the registers therefore needs to include a check to see if the number is positive or negative. As the Lower byte only indicates the fraction with an accuracy of 0.25 degrees it only needs to count to 4 (0.0. 0.25, 0.50, 0.75), hence two bits are enough
So suppose we have retrieved the number:
0b0001100101 => +25.25°C. We can easily see it is 25.25°C because the top 8 bits are 00011001, which is 25, while the lower two bits 0b01, mean 1×0.25.
As the lower byte, only uses the top 2 bits, it may need to be rightshifted 6 positions for calculations. So how about negative numbers, say -18 degrees.
Well -18 is 0b11101110 (=238 in twos complement notatie).
We can see that the highest bit is a 1, indicating a negative number. In order to make a check, we do the following:
0b11101110 & 0b10000000 => 0b10000000 So we know it is negative
Then we need to convert the 2 complement notation
0b11101110 XOR 0b11111111 => 0b00010001 (=17) // first XOR it
17+1= 18 // and add a ‘1’
18*-1 = -18 // and then we turn it negative
So, how does that look in a program?
float getTemperature() { int temperatureCelsius; float fTemperatureCelsius; uint8_t UBYTE = readRegister(REG_TEMPM); //Two's complement form uint8_t LRBYTE = readRegister(REG_TEMPL); //Fractional part if (UBYTE & 0b10000000 !=0) //check if -ve number { UBYTE ^= 0b11111111; UBYTE += 0x1; fTemperatureCelsius = UBYTE + ((LRBYTE >> 6) * 0.25); fTemperatureCelsius = fTemperatureCelsius * -1; } else { fTemperatureCelsius = UBYTE + ((LRBYTE >> 6) * 0.25); } return (fTemperatureCelsius); }
Obvously this isnt a full program but just a function. You still need to define REG_TEMPM (0h11) and REG_TEMPL (0x12), and ‘readRegister’ is another function that just reads the specified registers (using the ‘Wire’library)
The datasheet does not specify if you should subtract the fractal if its a negative temperature. That makes more sense to me. So if temperature is -30 and fraction is 0.25 that means -30.25 and not -29.75.
Or will my way of doing it be wrong?
Good question and as a matter of fact, from doing some orientation on the internet, the judge is still out on the right way. As I understand LadyAda’s addition to the RTC lib, she adds the fraction to the two’s complement part, which means that say 0.25 is added to the negative temperature, making -30 into -29.75. But then again, other sources say that the fraction is part of the two’s complement, which could mean that you add the fraction, making 30 into 30.25 and then exercise the top bit, multiplying with -1 if that is set, making it -30.25. I must confess I am not sure
How should i read temperature between 0
Is it possible at all?
For example, if upper byte shows 0b00000000 and two lower bits is 0b01 , so the temperature is +0.25 or -0.25? because there’s no sing for number 0 in two’s complement encoding method.
Ben, i am not completely sure what you mean with ‘between 0’.
Anyway in the example you gavd the fraction is +0.25 as it is a positive number as indicated by the MSB.
Had the MSB of the upperbyte been a 1 it would have been -0.25
Sorry for bad text, i want to read temperature between 0 and -1 degrees : -0.25 , -0.5 or -0.75 degrees…
if i’m right, for reading “-0.25” we need to read “-0” in the register 11h and “0.25” in the register 12h.
but it confuses me because i think with the two’s complement method there’s no “-0”.in other hand, we can’t get 0b10000000 in 11h to recognize below zero because it represents of -128 instead of -0 .do you think we can get a 10bit data like 0b10000000001 to achieve -0.25 ?
0b10000000001 represents -0.25 it is a zero with 0.25 a d the top but set thus -0.25
I’d like to add my two cents to discussion since I too was having problems with reading negative temperatures and this post was the first result in google search (although it’s not correct).
The best way to understand the register values is to imagine all the 10 bits (8 from H and 2 from L) of T registers as a standard 10-bit binary register where every change of the value by 1 means change of temperature by 0.25°C. So if 0000000000 is 0.00°C and 0000000001 is 0.25°C, then decreasing from 0.00 to -0.25°C means 0000 0000 00 becoming 1111 1111 11. Further decreasing the temp in steps of 0.25°C means decrementing the register values by 1 to 1111 1111 10, 1111 1111 01, 1111 1111 00, 1111 1110 11, 1111 1110 10 etc.
This means that the statement in above comment [i]”0b10000000001 represents -0.25 it is a zero with 0.25 a d the top but set thus -0.25″[/i] is not true and the code in the post itself doesn’t yield correct values for temperatures below 0°C. I found this out yesterday when the temperature in my device dropped below freezing for the first time in months and I saw the temperature chart in the morning – instead of going from 0.00 to -0.25 the chart line jumped up to +1.75°C.
One simple correction must be made to the code; the decimal value must be added AFTER the upper byte is processed and reversed to negative value.
So:
fTemperatureCelsius = UBYTE + ((LRBYTE >> 6) * 0.25);
fTemperatureCelsius = fTemperatureCelsius * -1;
becomes
fTemperatureCelsius = UBYTE;
fTemperatureCelsius = fTemperatureCelsius * -1;
fTemperatureCelsius = fTemperatureCelsius + (LRBYTE >> 6) * 0.25;
I hope this helps someone.
Thank you for your contribution. I am gonna need to work through it but as you actually saw the jump happening i presume you are correct. Thank you
You’re welcome. By the way here’s the chart that made me realize what’s wrong: https://imgur.com/a/lb3bVDh
My code was a little bit different than yours (there was one more bug) – that’s why the whole line is in positive area, but the “decimal bug” is the same.
I made the correction to my code but according to the weather forecast there won’t be such cold in at least next 2 weeks in my area that would cause to drop the temperature below 0 inside the box with the device. So I need to wait to see the result for quite some time. Maybe till next winter 🙂
Tnx will check it out
Start to wonder if the adafruit library is correct then. I ll dive into it