1#![cfg_attr(not(test), no_std)]
24extern crate embedded_hal as hal;
25
26#[cfg(all(feature = "ms5611", feature = "ms5607"))]
28compile_error!("Cannot specify both ms5611 and ms5607 flags. Try setting default-features = false");
29
30use hal::blocking::delay::DelayMs;
31use hal::blocking::spi::{Transfer, Write};
32use hal::digital::v2::OutputPin;
33
34pub struct Ms5611<SPI, NCS> {
36 spi: SPI,
37 ncs: NCS,
38 coeffs: Coefficients,
39}
40
41impl<SPI, NCS, E> Ms5611<SPI, NCS>
42where
43 SPI: Transfer<u8, Error = E> + Write<u8, Error = E>,
44 NCS: OutputPin,
45{
46 pub fn new(spi: SPI, ncs: NCS, delay: &mut impl DelayMs<u8>) -> Result<Ms5611<SPI, NCS>, E> {
48 let mut ms5611 = Ms5611 {
49 spi,
50 ncs,
51 coeffs: Coefficients::default(),
52 };
53
54 ms5611.reset(delay)?;
55 ms5611.coeffs = ms5611.read_coefficients()?;
56 assert!(ms5611.coeffs.check_crc());
57
58 Ok(ms5611)
59 }
60
61 fn get_temperature_compensation(&self, raw_sample: Sample) -> (i64, i64, i64) {
64 let dt: i64 = ((raw_sample.temperature as i32)
65 - ((self.coeffs.get_data(CoefficientsAddr::COEFF_5) as i32) << 8))
66 as i64;
67
68 #[cfg(any(feature = "ms5611"))]
69 let (offset_sh, sens_sh) = ((16, 7), (15, 8));
70 #[cfg(any(feature = "ms5607"))]
71 let (offset_sh, sens_sh) = ((17, 6), (16, 7));
72
73 let off = ((self.coeffs.get_data(CoefficientsAddr::COEFF_2) as i64) << offset_sh.0)
74 + (((self.coeffs.get_data(CoefficientsAddr::COEFF_4) as i64) * dt) >> offset_sh.1);
75
76 let sens = ((self.coeffs.get_data(CoefficientsAddr::COEFF_1) as i64) << sens_sh.0)
77 + (((self.coeffs.get_data(CoefficientsAddr::COEFF_3) as i64) * dt) >> sens_sh.1);
78
79 (dt, sens, off)
80 }
81
82 pub fn get_compensated_sample(&mut self, osr: Oversampling, delay_source: &mut impl DelayMs<u8>) -> Result<Sample, E> {
84 let raw_sample = self.read_raw_sample(osr, delay_source)?;
85
86 let (dt, sens, off) = self.get_temperature_compensation(raw_sample);
88
89 let temperature = (2000i64
90 + ((dt as i64) * (self.coeffs.get_data(CoefficientsAddr::COEFF_6) as i64) >> 23))
91 as i32;
92 let pressure = (((raw_sample.pressure as i64) * sens >> 21) - off) >> 15;
93
94 let sample = Sample {
95 pressure: pressure as i32,
96 temperature: temperature as i32,
97 };
98 Ok(sample)
99 }
100
101 pub fn get_second_order_sample(&mut self, osr: Oversampling, delay_source: &mut impl DelayMs<u8>) -> Result<Sample, E> {
104 let raw_sample = self.read_raw_sample(osr, delay_source)?;
105
106 let (dt, mut sens, mut off) = self.get_temperature_compensation(raw_sample);
108
109 let mut temperature = (2000i64
110 + ((dt as i64) * (self.coeffs.get_data(CoefficientsAddr::COEFF_6) as i64) >> 23))
111 as i32;
112
113 let mut offsets = if temperature < 2000 {
115 (
116 ((dt as i64 * dt as i64) >> 31) as i32,
117 (5 * (temperature - 2000) * (temperature - 2000)) >> 1 as i32,
118 (5 * (temperature - 2000) * (temperature - 2000)) >> 2 as i32,
119 )
120 } else {
121 (0, 0, 0)
122 };
123
124 if temperature < -1500 {
126 offsets.0 = offsets.0 + 7 * (temperature + 1500) * (temperature + 1500);
127 offsets.1 = offsets.1 + 11 * ((temperature + 1500) * (temperature + 1500) >> 1);
128 }
129
130 off -= offsets.1 as i64;
131 sens -= offsets.2 as i64;
132
133 temperature = temperature - offsets.0;
134 let pressure = (((raw_sample.pressure as i64) * sens >> 21) - off) >> 15;
135
136 let sample = Sample {
137 pressure: pressure as i32,
138 temperature: temperature as i32,
139 };
140 Ok(sample)
141 }
142
143 fn send(&mut self, addr: u8) -> Result<(), E> {
144 let _ = self.ncs.set_low();
145 self.spi.write(&[addr])?;
146 let _ = self.ncs.set_high();
147 Ok(())
148 }
149
150 fn read_raw(&mut self, addr: u8) -> Result<u32, E> {
151 let mut buffer = [0; 4];
152 buffer[0] = addr;
153 let _ = self.ncs.set_low();
154 self.spi.transfer(&mut buffer)?;
155 let _ = self.ncs.set_high();
156
157 let r = ((buffer[1] as u32) << 16) | ((buffer[2] as u32) << 8) | (buffer[3] as u32);
158
159 Ok(r)
160 }
161
162 fn read_raw_u16(&mut self, addr: u8) -> Result<u16, E> {
163 let mut buffer = [0; 3];
164 buffer[0] = addr;
165 let _ = self.ncs.set_low();
166 self.spi.transfer(&mut buffer)?;
167 let _ = self.ncs.set_high();
168
169 let r = ((buffer[1] as u16) << 8) | (buffer[2] as u16);
170
171 Ok(r)
172 }
173
174 fn read_raw_sample(&mut self, osr: Oversampling, delay_source: &mut impl DelayMs<u8>) -> Result<Sample, E> {
175 self.send(Command::CONV_D1.address() + osr.offset())?;
177 delay_source.delay_ms(osr.delay());
178 let raw_pressure = self.read_raw(Command::ADC_READ.address())?;
179
180 self.send(Command::CONV_D2.address() + osr.offset())?;
182 delay_source.delay_ms(osr.delay());
183 let raw_temperature = self.read_raw(Command::ADC_READ.address())?;
184
185 let sample = Sample {
186 pressure: raw_pressure as i32,
187 temperature: raw_temperature as i32,
188 };
189
190 Ok(sample)
191 }
192
193 fn reset(&mut self, delay_source: &mut impl DelayMs<u8>) -> Result<(), E> {
194 self.send(Command::RESET.address())?;
195 delay_source.delay_ms(3);
196 Ok(())
197 }
198
199 fn read_coefficients(&mut self) -> Result<Coefficients, E> {
200 let mut buffer = [0x00u16; 8];
201
202 buffer[CoefficientsAddr::MANUFACTURER as usize >> 1] =
203 self.read_raw_u16(Command::prom_address(CoefficientsAddr::MANUFACTURER))?;
204 buffer[CoefficientsAddr::COEFF_1 as usize >> 1] =
205 self.read_raw_u16(Command::prom_address(CoefficientsAddr::COEFF_1))?;
206 buffer[CoefficientsAddr::COEFF_2 as usize >> 1] =
207 self.read_raw_u16(Command::prom_address(CoefficientsAddr::COEFF_2))?;
208 buffer[CoefficientsAddr::COEFF_3 as usize >> 1] =
209 self.read_raw_u16(Command::prom_address(CoefficientsAddr::COEFF_3))?;
210 buffer[CoefficientsAddr::COEFF_4 as usize >> 1] =
211 self.read_raw_u16(Command::prom_address(CoefficientsAddr::COEFF_4))?;
212 buffer[CoefficientsAddr::COEFF_5 as usize >> 1] =
213 self.read_raw_u16(Command::prom_address(CoefficientsAddr::COEFF_5))?;
214 buffer[CoefficientsAddr::COEFF_6 as usize >> 1] =
215 self.read_raw_u16(Command::prom_address(CoefficientsAddr::COEFF_6))?;
216 buffer[CoefficientsAddr::CRC as usize >> 1] =
217 self.read_raw_u16(Command::prom_address(CoefficientsAddr::CRC))?;
218
219 Ok(Coefficients { data: buffer })
220 }
221}
222
223#[derive(Debug, Clone, Copy, PartialEq)]
225pub struct Sample {
226 pub pressure: i32,
227 pub temperature: i32,
228}
229
230#[allow(non_camel_case_types)]
231#[derive(Clone, Copy)]
232enum Command {
233 RESET = 0x1E,
234 CONV_D1 = 0x40,
235 CONV_D2 = 0x50,
236 ADC_READ = 0x00,
237 PROM_BASE = 0xA0,
238}
239
240impl Command {
241 pub fn address(self) -> u8 {
242 self as u8
243 }
244
245 pub fn prom_address(offset: CoefficientsAddr) -> u8 {
246 Command::PROM_BASE.address() + offset as u8
247 }
248}
249
250
251#[allow(non_camel_case_types)]
254#[derive(Clone, Copy)]
255pub enum Oversampling {
256 OS_256,
257 OS_512,
258 OS_1024,
259 OS_2048,
260 OS_4096,
261}
262
263impl Oversampling {
264 pub fn offset(self) -> u8 {
265 match self {
266 Oversampling::OS_256 => 0,
267 Oversampling::OS_512 => 2,
268 Oversampling::OS_1024 => 4,
269 Oversampling::OS_2048 => 6,
270 Oversampling::OS_4096 => 8,
271 }
272 }
273
274 pub fn delay(self) -> u8 {
275 match self {
276 Oversampling::OS_256 => 1,
277 Oversampling::OS_512 => 2,
278 Oversampling::OS_1024 => 3,
279 Oversampling::OS_2048 => 5,
280 Oversampling::OS_4096 => 10,
281 }
282 }
283}
284
285#[derive(Debug, Default)]
287struct Coefficients {
288 data: [u16; 8],
289}
290
291#[allow(non_camel_case_types)]
292enum CoefficientsAddr {
293 MANUFACTURER = 0x0,
294 COEFF_1 = 0x2,
295 COEFF_2 = 0x4,
296 COEFF_3 = 0x6,
297 COEFF_4 = 0x8,
298 COEFF_5 = 0xA,
299 COEFF_6 = 0xC,
300 CRC = 0xE,
301}
302
303impl Coefficients {
304 fn get_data(&self, addr: CoefficientsAddr) -> u16 {
305 (self.data[addr as usize >> 1])
306 }
307
308 fn get_crc(&self) -> u8 {
309 ((self.get_data(CoefficientsAddr::CRC) & 0xF) as u8)
310 }
311
312 pub fn check_crc(&self) -> bool {
313 let mut crc: u16 = 0;
314 let data_crc = self.get_crc() as u16;
315 for item in self.data[..self.data.len() - 1].iter() {
316 crc = Self::crc_coefficient(crc, item);
317 }
318 crc = Self::crc_coefficient(crc, &(self.get_data(CoefficientsAddr::CRC) & 0xFF00));
319
320 crc = (crc >> 12) & 0xF;
321 (crc == data_crc)
322 }
323
324 fn crc_coefficient(crc: u16, coefficient: &u16) -> u16 {
325 let mut crc = crc;
326 crc ^= (coefficient >> 8) & 0xFFu16;
327 crc = Self::crc_round(crc);
328 crc ^= coefficient & 0xFF;
329 crc = Self::crc_round(crc);
330 (crc)
331 }
332
333 fn crc_round(crc: u16) -> u16 {
334 let mut crc = crc;
335 for _ in (1..9).rev() {
336 crc = if (crc & 0x8000) > 0 {
337 (crc << 1) ^ 0x3000
338 } else {
339 (crc << 1)
340 }
341 }
342 (crc)
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 extern crate embedded_hal_mock;
349
350 use self::embedded_hal_mock::delay::MockNoop;
351 use self::embedded_hal_mock::spi::{Mock as SpiMock, Transaction as SpiTransaction};
352 use super::*;
353
354 struct Pin;
355
356 impl hal::digital::v2::OutputPin for Pin {
357 type Error = u32;
358 fn set_low(&mut self) -> Result<(), Self::Error> { Ok(()) }
359 fn set_high(&mut self) -> Result<(), Self::Error> { Ok(()) }
360 }
361
362 #[test]
363 fn get_coeffs() {
364 let data = [
365 0x3132, 0x3334, 0x3536, 0x3738, 0x3940, 0x4142, 0x4344, 0x450b,
366 ];
367 let coeffs = Coefficients { data };
368
369 assert_eq!(coeffs.get_data(CoefficientsAddr::MANUFACTURER), 0x3132);
370 assert_eq!(coeffs.get_data(CoefficientsAddr::COEFF_1), 0x3334);
371 assert_eq!(coeffs.get_data(CoefficientsAddr::COEFF_2), 0x3536);
372 assert_eq!(coeffs.get_data(CoefficientsAddr::COEFF_3), 0x3738);
373 assert_eq!(coeffs.get_data(CoefficientsAddr::COEFF_4), 0x3940);
374 assert_eq!(coeffs.get_data(CoefficientsAddr::COEFF_5), 0x4142);
375 assert_eq!(coeffs.get_data(CoefficientsAddr::COEFF_6), 0x4344);
376 assert_eq!(coeffs.get_data(CoefficientsAddr::CRC), 0x450b);
377
378 assert_eq!(coeffs.get_crc(), 0xb);
379 }
380
381 #[test]
382 fn check_crc() {
383 let mut data = [
384 0x0024, 0xB3D8, 0xBD83, 0x6E00, 0x628A, 0x8063, 0x6ADB, 0x947B,
385 ];
386 let coeffs = Coefficients { data };
387 assert_eq!(coeffs.check_crc(), true);
388
389 data[7] = 0x460b;
390 let coeffs = Coefficients { data };
391 assert_eq!(coeffs.check_crc(), false);
392 }
393
394 #[test]
395 #[cfg(feature = "ms5611")]
396 fn read_compensated_samples_ms5611() {
397 let expectations = [
399 SpiTransaction::write(vec![0x1E]),
400 SpiTransaction::transfer(vec![0xA0, 0, 0], vec![0, 0x00, 0x00]),
401 SpiTransaction::transfer(vec![0xA2, 0, 0], vec![0, 0x9C, 0xBF]), SpiTransaction::transfer(vec![0xA4, 0, 0], vec![0, 0x90, 0x3C]), SpiTransaction::transfer(vec![0xA6, 0, 0], vec![0, 0x5B, 0x15]), SpiTransaction::transfer(vec![0xA8, 0, 0], vec![0, 0x5A, 0xF2]), SpiTransaction::transfer(vec![0xAA, 0, 0], vec![0, 0x82, 0xB8]), SpiTransaction::transfer(vec![0xAC, 0, 0], vec![0, 0x6E, 0x98]), SpiTransaction::transfer(vec![0xAE, 0, 0], vec![0, 0x00, 0x00]),
408 SpiTransaction::write(vec![0x46]), SpiTransaction::transfer(vec![0, 0, 0, 0], vec![0, 0x8A, 0xA2, 0x1A]), SpiTransaction::write(vec![0x56]), SpiTransaction::transfer(vec![0, 0, 0, 0], vec![0, 0x82, 0xC1, 0x3E]), SpiTransaction::write(vec![0x46]), SpiTransaction::transfer(vec![0, 0, 0, 0], vec![0, 0x8A, 0xA2, 0x1A]), SpiTransaction::write(vec![0x56]), SpiTransaction::transfer(vec![0, 0, 0, 0], vec![0, 0x82, 0xC1, 0x3E]), ];
417
418 let spi = SpiMock::new(&expectations);
419 let pin = Pin;
420 let mut delay_source = MockNoop::new();
421 let mut ms5611 = Ms5611::new(spi, pin, &mut delay_source).unwrap();
422 let sample1 = ms5611
423 .get_compensated_sample(Oversampling::OS_2048, &mut delay_source)
424 .unwrap();
425 let sample2 = ms5611
426 .get_second_order_sample(Oversampling::OS_2048, &mut delay_source)
427 .unwrap();
428
429 assert_eq!(
430 Sample {
431 pressure: 100009,
432 temperature: 2007
433 },
434 sample1
435 );
436 assert_eq!(sample1, sample2);
437 }
438
439 #[test]
440 #[cfg(feature = "ms5607")]
441 fn read_compensated_samples_ms5607() {
442 let expectations = [
444 SpiTransaction::write(vec![0x1E]),
445 SpiTransaction::transfer(vec![0xA0, 0, 0], vec![0, 0x00, 0x00]),
446 SpiTransaction::transfer(vec![0xA2, 0, 0], vec![0, 0xB5, 0x24]), SpiTransaction::transfer(vec![0xA4, 0, 0], vec![0, 0xAB, 0xCD]), SpiTransaction::transfer(vec![0xA6, 0, 0], vec![0, 0x71, 0x83]), SpiTransaction::transfer(vec![0xA8, 0, 0], vec![0, 0x6C, 0xC2]), SpiTransaction::transfer(vec![0xAA, 0, 0], vec![0, 0x7B, 0x41]), SpiTransaction::transfer(vec![0xAC, 0, 0], vec![0, 0x6E, 0x05]), SpiTransaction::transfer(vec![0xAE, 0, 0], vec![0, 0x00, 0x08]),
453 SpiTransaction::write(vec![0x46]), SpiTransaction::transfer(vec![0, 0, 0, 0], vec![0, 0x62, 0xA7, 0xA4]), SpiTransaction::write(vec![0x56]), SpiTransaction::transfer(vec![0, 0, 0, 0], vec![0, 0x7B, 0x41, 0x44]), SpiTransaction::write(vec![0x46]), SpiTransaction::transfer(vec![0, 0, 0, 0], vec![0, 0x62, 0xA7, 0xA4]), SpiTransaction::write(vec![0x56]), SpiTransaction::transfer(vec![0, 0, 0, 0], vec![0, 0x7B, 0x41, 0x44]), ];
462
463 let spi = SpiMock::new(&expectations);
464 let pin = Pin;
465 let mut delay_source = MockNoop::new();
466 let mut ms5611 = Ms5611::new(spi, pin, &mut delay_source).unwrap();
467 let sample1 = ms5611
468 .get_compensated_sample(Oversampling::OS_2048, &mut delay_source)
469 .unwrap();
470 let sample2 = ms5611
471 .get_second_order_sample(Oversampling::OS_2048, &mut delay_source)
472 .unwrap();
473
474 assert_eq!(
475 Sample {
476 pressure: 110002,
477 temperature: 2000
478 },
479 sample1
480 );
481 assert_eq!(sample1, sample2);
482 }
483}