# Altimeter, the bit shift malfunction

I was getting wrong lectures from the pressure sensor. But it was obvious that there was nothing wrong with it, because the very same sensor was giving good results when attached to an Arduino UNO.

In the snippet below we can see how Adafruit implements the operations needed for obtaining actual pressure out of raw readings and calibration values.

[code lang=”c”]

int32_t Adafruit_BMP085::readPressure(void) {

int32_t UT, UP, B3, B5, B6, X1, X2, X3, p;

uint32_t B4, B7;

UT = readRawTemperature();

UP = readRawPressure();

// do temperature calculations

X1=(UT-(int32_t)(ac6))*((int32_t)(ac5))/pow(2,15);

X2=((int32_t)mc*pow(2,11))/(X1+(int32_t)md);

B5=X1 + X2;

// do pressure calcs

B6 = B5 – 4000;

X1 = ((int32_t)b2 * ( (B6 * B6)>>12 )) >> 11;

X2 = ((int32_t)ac2 * B6) >> 11;

X3 = X1 + X2;

B3 = ((((int32_t)ac1*4 + X3) << oversampling) + 2) / 4; X1 = ((int32_t)ac3 * B6) >> 13;

X2 = ((int32_t)b1 * ((B6 * B6) >> 12)) >> 16;

X3 = ((X1 + X2) + 2) >> 2;

B4 = ((uint32_t)ac4 * (uint32_t)(X3 + 32768)) >> 15;

B7 = ((uint32_t)UP – B3) * (uint32_t)( 50000UL >> oversampling );

if (B7 < 0x80000000) { p = (B7 * 2) / B4; } else { p = (B7 / B4) * 2; } X1 = (p >> 8) * (p >> 8);

X1 = (X1 * 3038) >> 16;

X2 = (-7357 * p) >> 16;

p = p + ((X1 + X2 + (int32_t)3791)>>4);

return p;

}

[/code]

The code makes extensive use of bit shifts; though the datasheet demonstrates those operations using multiplications and divisions in the form of y × 2^{right} and y / 2^{left}. That is equivalent to ‘<<‘ for the former and ‘>>’ for the latter; but only if the compiler has arithmetic shift instructions, otherwise, the sign of signed variables is lost. And that’s why I was getting wrong pressure values, because PICs just have *rotate* and *rotate through carry*, and don’t bother with arithmetic or logical shift instructions.

As I didn’t want to trouble myself with rotate through carry shifts, and as math operations like pow() waste a lot of memory —the PIC18LF14K50 has only 16 kB of program memory—, I just did the calculations by hand and placed the numbers as multipliers and divisors as we can seen just below.

[code lang=”c”]

long readPressure(void) {

long UT, UP, B3, B5, B6, X1, X2, X3, p;

unsigned long B4, B7;

UT = readRawTemperature();

UP = readRawPressure();

// do temperature calculations

X1 = (UT-(long)(ac6)) * ((long)(ac5)) / 32768;

X2 = ((long)mc*2048)/(X1 + (long)md);

B5 = X1 + X2;

// do pressure calculations

B6 = B5 – 4000;

X1 = ((long)b2 * ((B6 * B6) / 4096)) / 2048;

X2 = ((long)ac2 * B6) / 2048;

X3 = X1 + X2;

B3 = ((((long)ac1*4 + X3) << oversampling) + 2) / 4;

X1 = ((long)ac3 * B6) / 8192;

X2 = ((long)b1 * ((B6 * B6) / 4096)) / 65536;

X3 = ((X1 + X2) + 2) / 4;

B4 = ((unsigned long)ac4 * (unsigned long)(X3 + 32768)) / 32768;

B7 = ((unsigned long)UP – B3) * (unsigned long)(50000 >> oversampling);

if (B7 < 0x80000000) {

p = (B7 * 2) / B4;

} else {

p = (B7 / B4) * 2;

}

X1 = (p / 256) * (p / 256);

X1 = (X1 * 3038) / 65536;

X2 = (-7357 * p) / 65536;

p = p + ((X1 + X2 + (long)3791) / 16);

return p;

}

[/code]

Now the altimeter just works like a charm.