@@ -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 ) ]
207215struct CalibrationCoefficients {
208216 par_t1 : f32 ,
@@ -222,6 +230,8 @@ struct CalibrationCoefficients {
222230}
223231
224232impl 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