Skip to content

Commit da1d253

Browse files
author
Adam Sasine
committed
Fixed a couple of incorrect equations in compensation calculations
1 parent 689ac1f commit da1d253

File tree

1 file changed

+48
-36
lines changed

1 file changed

+48
-36
lines changed

src/lib.rs

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ impl From<Address> for u8 {
203203
}
204204
}
205205

206+
/// Output from the BMP390 consists of ADC outputs.
207+
///
208+
/// These must be compensated using formulas from the datasheet to obtain the actual temperature and pressure values,
209+
/// using coefficients stored in non-volatile memory (NVM).
210+
///
211+
/// # Datasheet
212+
/// - Section 3.11 Output compensation.
213+
/// - Appendix A: Computation formulae reference implementation.
206214
#[derive(Debug, Clone, Copy, Format)]
207215
struct CalibrationCoefficients {
208216
par_t1: f32,
@@ -222,6 +230,8 @@ struct CalibrationCoefficients {
222230
}
223231

224232
impl CalibrationCoefficients {
233+
/// Read the calibration coefficients from the BMP390's NVM registers and convert them to into a set of
234+
/// floating-point calibration coefficients for the formulas implemented in the compensation functions.
225235
async fn try_from_i2c<I: I2c>(address: Address, i2c: &mut I) -> Result<Self, Error<I::Error>> {
226236
let mut calibration_coefficient_regs = [0; 21];
227237
i2c.write_read(
@@ -238,7 +248,8 @@ impl CalibrationCoefficients {
238248
/// Calculate the calibration coefficients from the raw register data in registers [`Register::NVM_PAR_T1_0`] to
239249
/// [`Register::NVM_PAR_P11`].
240250
///
241-
/// See: Datasheet Apendix A, Section 8.4
251+
/// # Datasheet
252+
/// Apendix A, Section 8.4
242253
fn from_registers(data: &[u8; 21]) -> Self {
243254
trace!("NVM_PAR: {=[u8]:#04x}", *data);
244255
let nvm_par_t1: u16 = (data[1] as u16) << 8 | data[0] as u16;
@@ -263,50 +274,54 @@ impl CalibrationCoefficients {
263274
par_p1: ((nvm_par_p1 as f32) - 16_384.0) / 1_048_576.0, // 2^14 / 2^20
264275
par_p2: ((nvm_par_p2 as f32) - 16_384.0) / 536_870_912.0, // 2^14 / 2^29
265276
par_p3: (nvm_par_p3 as f32) / 4_294_967_296.0, // 2^32
266-
par_p4: (nvm_par_p4 as f32) / 137_438_953_427.0, // 2^37
277+
par_p4: (nvm_par_p4 as f32) / 137_438_953_472.0, // 2^37
267278
par_p5: (nvm_par_p5 as f32) / 0.125, // 2^-3
268279
par_p6: (nvm_par_p6 as f32) / 64.0, // 2^6
269280
par_p7: (nvm_par_p7 as f32) / 256.0, // 2^8
270281
par_p8: (nvm_par_p8 as f32) / 32768.0, // 2^15
271-
par_p9: (nvm_par_p9 as f32) / 81_474_976_710_656.0, //2^48
272-
par_p10: (nvm_par_p10 as f32) / 81_474_976_710_656.0, // 2^48
282+
par_p9: (nvm_par_p9 as f32) / 281_474_976_710_656.0, //2^48
283+
par_p10: (nvm_par_p10 as f32) / 281_474_976_710_656.0, // 2^48
273284
par_p11: (nvm_par_p11 as f32) / 36_893_488_147_419_103_232.0, // 2^65
274285
}
275286
}
276287

277288
/// Compensate a temperature reading according to calibration coefficients.
278289
///
279-
/// See: Datasheet Apendix A, Section 8.5
280-
fn compensate_temperature(&self, temperature_uncompensated: i32) -> ThermodynamicTemperature {
290+
/// # Datasheet
291+
/// Apendix A, Section 8.5
292+
fn compensate_temperature(&self, temperature_uncompensated: u32) -> ThermodynamicTemperature {
293+
// This could be done in fewer expressions, but it's broken down for clarity and to match the datasheet
281294
let uncompensated = temperature_uncompensated as f32;
282-
let partial_1 = uncompensated - self.par_t1;
283-
let partial_2 = partial_1 * self.par_t2;
284-
let temperature = partial_2 + (partial_1 * partial_1) * self.par_t3;
295+
let partial_data1 = uncompensated - self.par_t1;
296+
let partial_data2 = partial_data1 * self.par_t2;
297+
let temperature = partial_data2 + (partial_data1 * partial_data1) * self.par_t3;
285298
ThermodynamicTemperature::new::<degree_celsius>(temperature)
286299
}
287300

288301
/// Compensate a pressure reading according to calibration coefficients.
289302
///
290-
/// See: Datasheet Apendix A, Section 8.6
291-
fn compensate_pressure(&self, temperature: ThermodynamicTemperature, pressure_uncompensated: i32) -> Pressure {
303+
/// # Datasheet
304+
/// Apendix A, Section 8.6
305+
fn compensate_pressure(&self, temperature: ThermodynamicTemperature, pressure_uncompensated: u32) -> Pressure {
306+
// This could be done in fewer expressions, but it's broken down for clarity and to match the datasheet
292307
let uncompensated = pressure_uncompensated as f32;
293308
let temperature = temperature.get::<degree_celsius>();
294-
let partial_1 = self.par_p6 * temperature;
295-
let partial_2 = self.par_p7 * temperature * temperature;
296-
let partial_3 = self.par_p8 * temperature * temperature * temperature;
297-
let partial_out1 = self.par_p5 + partial_1 + partial_2 + partial_3;
298-
299-
let partial_1 = self.par_p2 * temperature;
300-
let partial_2 = self.par_p3 * temperature * temperature;
301-
let partial_3 = self.par_p4 * temperature * temperature * temperature;
302-
let partial_out2 = uncompensated * (self.par_p1 + partial_1 + partial_2 + partial_3);
303-
304-
let partial_1 = uncompensated * uncompensated;
305-
let partial_2 = self.par_p9 + self.par_p10 * temperature;
306-
let partial_3 = partial_1 * partial_2;
307-
let partial_4 = partial_3 + temperature * temperature * temperature * temperature * self.par_p11;
308-
309-
let pressure = partial_out1 + partial_out2 + partial_4;
309+
let partial_data1 = self.par_p6 * temperature;
310+
let partial_data2 = self.par_p7 * temperature * temperature;
311+
let partial_data3 = self.par_p8 * temperature * temperature * temperature;
312+
let partial_out1 = self.par_p5 + partial_data1 + partial_data2 + partial_data3;
313+
314+
let partial_data1 = self.par_p2 * temperature;
315+
let partial_data2 = self.par_p3 * temperature * temperature;
316+
let partial_data3 = self.par_p4 * temperature * temperature * temperature;
317+
let partial_out2 = uncompensated * (self.par_p1 + partial_data1 + partial_data2 + partial_data3);
318+
319+
let partial_data1 = uncompensated * uncompensated;
320+
let partial_data2 = self.par_p9 + self.par_p10 * temperature;
321+
let partial_data3 = partial_data1 * partial_data2;
322+
let partial_data4 = partial_data3 + uncompensated * uncompensated * uncompensated * self.par_p11;
323+
324+
let pressure = partial_out1 + partial_out2 + partial_data4;
310325
Pressure::new::<pascal>(pressure)
311326
}
312327

@@ -475,11 +490,8 @@ where
475490
.map_err(Error::I2c)?;
476491

477492
// DATA_3 is the LSB, DATA_5 is the MSB
478-
let temperature = i32::from(read[0]) | i32::from(read[1]) << 8 | i32::from(read[2]) << 16;
479-
let temperature = self
480-
.coefficients
481-
.compensate_temperature(temperature);
482-
493+
let temperature = u32::from(read[0]) | u32::from(read[1]) << 8 | u32::from(read[2]) << 16;
494+
let temperature = self.coefficients.compensate_temperature(temperature);
483495
Ok(temperature)
484496
}
485497

@@ -502,10 +514,10 @@ where
502514
trace!("DATA = {=[u8]:#04x}", read);
503515

504516
// pressure is 0:2 (XLSB, LSB, MSB), temperature is 3:5 (XLSB, LSB, MSB)
505-
let temperature = i32::from(read[3]) | i32::from(read[4]) << 8 | i32::from(read[5]) << 16;
517+
let temperature = u32::from(read[3]) | u32::from(read[4]) << 8 | u32::from(read[5]) << 16;
506518
let temperature = self.coefficients.compensate_temperature(temperature);
507519

508-
let pressure = i32::from(read[0]) | i32::from(read[1]) << 8 | i32::from(read[2]) << 16;
520+
let pressure = u32::from(read[0]) | u32::from(read[1]) << 8 | u32::from(read[2]) << 16;
509521
let pressure = self.coefficients.compensate_pressure(temperature, pressure);
510522

511523
Ok(Measurement {
@@ -541,7 +553,7 @@ mod tests {
541553

542554
/// The [`Measurement::pressure`] value for [`PRESSURE_TEMPERATURE_BYTES`] when compensated by [`CalibrationCoefficients::default()`].
543555
fn expected_pressure() -> Pressure {
544-
Pressure::new::<pascal>(100_207.97)
556+
Pressure::new::<pascal>(98370.55)
545557
}
546558

547559
/// Bytes for the DATA registers (0x07 .. 0x09) for a temperature measurement.
@@ -553,7 +565,7 @@ mod tests {
553565
}
554566

555567
fn expected_altitude() -> Length {
556-
Length::new::<meter>(93.36266)
568+
Length::new::<meter>(248.78754)
557569
}
558570

559571
impl Default for CalibrationCoefficients {

0 commit comments

Comments
 (0)