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)