Unsure if I am reading these 2's complement I2C registers correctly

Hey everyone, I am working on writing I2C code to interface with an SPL06-007 barometric altimeter, and right now I am getting stuck on it returning weird numbers. I think my issue is how I am handling the calibration coefficients. I think that at least one of them should be negative (otherwise the temperature equation does not make sense), but they are both returning positive numbers. The datasheet says they are 12-bit 2's complement values

I have tested the code and it does return a varying temperature reading in the registers, around 12,000 at ambient and when I put my finger on, it goes to 6,000. This leads me to believe that at least one of the calibration coefficients should be negative so that a smaller raw temperature value returns a larger corrected temperature value.

Any advice you can offer would be appreciated!

Here is my code:

#include <Wire.h>

byte val;

//Calibration register array

byte cal[18];

//Calibration Values
long c0 = 0;
long c1 = 0;
long c00 = 0;
long c10 = 0;
long c01 = 0;
long c11 = 0;
long c20 = 0;
long c21 = 0;
long c30 = 0;

byte PresArray[3];
byte TempArray[3];

long rawPres;
long rawTemp;

float Temp;
float Pres;

byte temporary;

void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);
  while(!Serial);
  Wire.begin();
  delay(500);

  //Read the calibration registers and store to variables for future use. 
  Wire.beginTransmission(0x77);
  Wire.write(0x10);
  Wire.endTransmission();
  Wire.requestFrom(0x77, 18);
  for (int i = 0; i < 17; i++){
    cal[i] = Wire.read();
    //Serial.println(cal[i], BIN);
  }

  
  //Create calibration value c0, made of the cal[0] and the first 4 bits of cal[1]
  c0 = (cal[0]<<8) + (cal[1]);
  c0 = c0 >> 4;

  //Create calibration value c1, made of the last 4 bits of cal[1] and all of cal[2]
  Serial.println(cal[1], BIN);
  Serial.println(cal[2], BIN);
  temporary = cal[1] << 4;
  temporary = temporary >> 4;
  c1 = temporary;
  c1 = c1 << 8;
  c1 = c1 + cal[2];
  Serial.println(c1, BIN);

  //Create calibration value c00, made of bytes cal[3], cal[4], and the first 4 bits of cal[5]
  c00 = (cal[3] << 16) + (cal[4] << 8) + (cal[5]);
  c00 >> 4;

  //Create calibration value c10, made of the last 4 bits of cal[5] and all of byte cal[6]
  temporary = cal[5] << 4;
  temporary >> 4;
  c10 = temporary;
  c10 << 8;
  c10 = c10 + cal[6];

  //I'll write the code to interpert the rest of the registers after this proof of concept works

  //Set the oversampling rate on temperature reads to 1 (i.e. no oversampling)
  //Change the entire register to 0
  Wire.beginTransmission(0x77);
  Wire.write(0x07);
  Wire.write(0x00);
  Wire.endTransmission();

  //Set register 9 to all 0
  Wire.beginTransmission(0x77);
  Wire.write(0x09);
  Wire.write(0x00);
  Wire.endTransmission();

  //Trigger a temperature reading
  //Read what is currently in the 0x09 register
  Wire.beginTransmission(0x77);
  Wire.write(0x08);
  Wire.endTransmission();
  Wire.requestFrom(0x77, 1);
  //Change the last 3 bits to "010" to trigger a temperature reading
  temporary = Wire.read();
  bitClear(temporary, 0);
  bitSet(temporary, 1); 
  bitClear(temporary, 2);
  //Write this new byte back to 0x09
  Wire.beginTransmission(0x77);
  Wire.write(0x08);
  Wire.write(temporary);
  Wire.endTransmission();

  //Wait a bit for a temperature measurement, should be no more than 2ms but let's be safe
  delay(50);

  //Read the raw temperature from the 0x03 to 0x05 registers
  Wire.beginTransmission(0x77);
  Wire.write(0x03);
  Wire.endTransmission();
  Wire.requestFrom(0x77, 3);
  for (int i = 0; i < 3; i++){
    TempArray[i] = Wire.read();
    //Serial.println(TempArray[i], BIN);
  }

  //Convert the bytes into a single  long, and check it for a sign
  rawTemp = (TempArray[0] << 16) + (TempArray[1] << 8) + (TempArray[2]);
  if (rawTemp & 0x00800000){
    rawTemp |= 0xFF000000 ;
  }
 
  //Convert the long to a celcius temperature using the conversion values
  Temp = c0 * 0.5 + c1 * (rawTemp/524288);

  //Print the numbers used in calculations
  Serial.println("Temp Calculations");
  Serial.println(c0, DEC);
  Serial.println(c1, DEC);
  Serial.println(rawTemp, DEC);
  //Print the results
  Serial.print("Temperature (C): ");
  Serial.println(Temp, DEC);

}

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

}

Is this the datasheet? https://datasheet.lcsc.com/szlcsc/1912111437_Goertek-SPL06-007_C233787.pdf

I think you need to handle the sign bit correct.

byte cal[18] = {0xff, 0x0f, 0xf0};

void setup() {
  Serial.begin(115200);

  int16_t c0 = ((int16_t) cal[0] << 8) + cal[1];
  c0 = c0 >> 4; // data type is signed. shifting should respect sign bit

  int16_t c1 = ((int16_t) cal[1] << 8) + cal[2];
  c1 = c1 & 0x0fff; // getting rid of c0 lsb's
  if(c1 & 0x0800) {
    // sign bit is set
    // extending it to 16 bit
    c1 = c1 | 0xf000;
  }

  Serial.print("c0: ");
  Serial.println(c0);
  Serial.print("c1: ");
  Serial.println(c1);
}

void loop() {
}

I would handle 12-bit 2's complemented formatted c0 data in the following way:

void setup() 
{
  Serial.begin(9600);
  //12-bitin 2's cmplement form
  unsigned int c0 = 0b101010101010; //0xAAAA example data
  unsigned restDecimal = 0;
  int decimal = -(bitRead(c0, 11) * 2048);// -2048
  for (int i = 10; i >= 0; i--)
  {
    restDecimal += bitRead(c0, i)*pow(2, i);  //680
  }
  Serial.println(decimal); //-2048
  Serial.println(restDecimal); //+680
  
  decimal = decimal + (int)restDecimal; //-1368
  Serial.print(decimal, DEC);  //-1368
}

void loop() 
{

}

I have also been working on some code to pull the pressure values from a SPL06-007. I have been able to successfully pull temperature values. My pressure value is off. I am thinking my coefficients are still off. I have not been able to find a working example to reference, so I am not sure what the expected values are for the coefficients.

ESP32_SPL06_V3.ino (10.6 KB)

I finally able to get some pressure readings out of my sensor that look accurate. I put my code up on github if you are interested.