mpl3115/
lib.rs

1//! This is an embedded-hal device driver for the MPL3115A2 Altitude sensor.
2//! 
3//! It is accurate to around 0.3m.
4//! 
5//! There is also a temperature and pressure readings avaliable.
6//! 
7//! See the examples folder for a blocking implementation.
8//! 
9//! The pressure mode is likely wildly inaccurate and needs to be tested against a known good pressure sensor.
10
11#![no_std]
12
13mod reg;
14use reg::*;
15
16use core::fmt::Debug;
17
18use embedded_hal as hal;
19use hal::blocking::i2c::{Write, WriteRead};
20use cast::f32;
21
22#[derive(Debug)]
23pub enum Error<E> {
24    /// I²C bus error
25    I2c(E),
26    /// Failed to parse sensor data (Not yet used)
27    InvalidData,
28    /// Chip ID doesn't match expected value
29    UnsupportedChip,
30}
31
32/// Device Mode
33/// 
34/// Useful for "non" blocking measurements
35#[derive(Copy,Clone,PartialEq)]
36pub enum Mode {
37	Inactive,
38	Active,
39	TakingReading,
40}
41
42/// Pressure or Altitude Mode
43/// 
44/// Toggle as required
45#[derive(Copy,Clone,PartialEq)]
46pub enum PressureAlt {
47	Pressure,
48	Altitude,
49}
50
51/// `MPL3115A2` driver
52/// 
53/// Will start off deactivated and in the PressureAlt mode set
54pub struct MPL3115A2<I2C> {
55    /// The concrete I²C device implementation
56    i2c: I2C,
57
58    /// Mode (Inactive, Active, Taking Sample)
59	mode: Mode,
60	
61	/// Pressure or Altitude Mode
62	pa: PressureAlt,
63}
64
65/// Interrupt setting and status
66/*pub struct Int<'a, REG, I2C> {
67    dev: &'a mut MMA8452q<I2C>,
68    reg: PhantomData<REG>,
69}*/
70
71impl<I2C, E> MPL3115A2<I2C>
72where
73    I2C: WriteRead<Error = E> + Write<Error = E>,
74    E: Debug,
75{
76    /// Create a new `MPL3115A2` driver from the given `I2C` peripheral
77    pub fn new(i2c: I2C, pa: PressureAlt) -> Result<Self, Error<E>> {
78
79		//Create the device
80        let mut dev = Self {
81            i2c,
82            mode: Mode::Inactive,
83            pa: pa,
84        };
85
86        // Ensure we have the correct device ID
87        if dev.get_device_id()? != DEVICE_ID {
88            return Err(Error::UnsupportedChip);
89        }
90
91		//Enables the pressure and temp measurement event flags so that we can
92		//test against them. This is recommended in datasheet during setup.
93		//Enable all three pressure and temp event flags 
94		dev.write_reg(Register::PT_DATA_CFG,EVENT_FLAGS).map_err(Error::I2c)?; 
95
96		//Set the required PA mode
97		dev.change_reading_type(pa)?;
98
99        Ok(dev)
100    }
101
102    /// Destroy driver instance, return `I2C` bus instance
103    pub fn destroy(self) -> I2C {
104        self.i2c
105    }
106
107    /// Get the `WHO_AM_I` register
108    pub fn get_device_id(&mut self) -> Result<u8, Error<E>> {
109        self.read_reg(Register::WHO_AM_I).map_err(Error::I2c)
110    }
111
112    /// Activate the Device
113    pub fn activate(&mut self) -> Result<(), Error<E>> {
114		self.reg_set_bits(Register::CTRL_REG1, DEVICE_EN).map_err(Error::I2c)?;
115		self.mode = Mode::Active;
116        Ok(())
117	}
118
119	/// De-activate the Device
120	pub fn deactivate(&mut self) -> Result<(), Error<E>> {
121		self.reg_reset_bits(Register::CTRL_REG1, DEVICE_EN).map_err(Error::I2c)?;
122		self.mode = Mode::Inactive;
123        Ok(())
124	}
125
126	/// Change between altitude and pressure
127	pub fn change_reading_type(&mut self, pa: PressureAlt) -> Result<(), Error<E>> {
128
129		match pa {
130			PressureAlt::Altitude => {
131				self.reg_set_bits(Register::CTRL_REG1, ALT_EN).map_err(Error::I2c)?;
132			},
133			PressureAlt::Pressure => {
134				self.reg_reset_bits(Register::CTRL_REG1, ALT_EN).map_err(Error::I2c)?;
135			}
136		}
137	
138		self.pa = pa;
139        Ok(())
140	}
141
142	/// Get one (blocking) Pressure or Altitude value
143	pub fn take_one_pa_reading(&mut self) -> Result<f32, Error<E>> {
144		//Trigger a one shot reading
145		self.start_reading()?;
146
147		//Wait for PDR bit, indicates we have new pressure data
148		while ! self.check_pa_reading()? {
149
150		}
151
152		//Get the data
153		self.get_pa_reading()
154	}
155
156	/// Get one (blocking) Temperature value
157	pub fn take_one_temp_reading(&mut self) -> Result<f32, Error<E>> {
158		//Trigger a one shot reading
159		self.start_reading()?;
160
161		//Wait for TDR bit, indicates we have new temperature data
162		while ! self.check_temp_reading()? {
163
164		}
165
166		//Get the data
167		self.get_temp_reading()
168	}
169
170	/// Clear then set the OST bit which causes the sensor to immediately take another reading
171	/// Needed to sample faster than 1Hz
172	pub fn start_reading(&mut self) -> Result<(), Error<E>> {
173
174		self.reg_reset_bits(Register::CTRL_REG1, ONE_SHOT).map_err(Error::I2c)?;
175		self.reg_set_bits(Register::CTRL_REG1, ONE_SHOT).map_err(Error::I2c)?;
176	  
177		//We are now waiting for data
178		self.mode = Mode::TakingReading;
179        Ok(())
180	}
181
182	/// Check the PDR bit for new data
183	pub fn check_pa_reading(&mut self) -> Result<bool, Error<E>> {
184		let status_reg = self.read_reg(Register::STATUS).map_err(Error::I2c)?;
185		Ok(status_reg & PDR != 0)
186	}
187
188	/// Check the TDR bit for new data
189	pub fn check_temp_reading(&mut self) -> Result<bool, Error<E>> {
190		let status_reg = self.read_reg(Register::STATUS).map_err(Error::I2c)?;
191
192		Ok(status_reg & TDR != 0)
193	}
194
195	/// Get and process the pressure or altitude data
196	pub fn get_pa_reading(&mut self) -> Result<f32, Error<E>> {
197
198		// Read pressure registers
199		let mut buf = [0u8; 3];
200		self.read_regs(Register::OUT_P_MSB, &mut buf).map_err(Error::I2c)?;
201
202		//Change the device back to active
203		self.mode = Mode::Active;
204
205		// The least significant bytes l_altitude and l_temp are 4-bit,
206		// fractional values, so you must cast the calulation in (float),
207		// shift the value over 4 spots to the right and divide by 16 (since 
208		// there are 16 values in 4-bits). 
209		match self.pa {
210			PressureAlt::Altitude => {
211				
212				let lsb = buf[2]>>4;
213				let tempcsb = f32(lsb)/16.0;
214				let int_buf = [buf[0], buf[1]];
215
216				let altitude = f32( i16::from_be_bytes(int_buf)) + tempcsb;
217			
218				Ok(altitude)
219			},
220			PressureAlt::Pressure => {
221				//Reads the current pressure in Pa
222				// Pressure comes back as a left shifted 20 bit number
223				let int_buf = [0u8, buf[0], buf[1], buf[2]];
224				let mut pressure_whole: u32 = u32::from_be_bytes(int_buf);
225				pressure_whole >>= 6; //Pressure is an 18 bit number with 2 bits of decimal. Get rid of decimal portion.
226		
227				buf[2] &= 0b0011_0000; //Bits 5/4 represent the fractional component
228				buf[2] >>= 4; //Get it right aligned
229				let pressure_decimal = f32(buf[2])/4.0; //Turn it into fraction
230		
231				let pressure = f32(pressure_whole) + pressure_decimal;
232		
233				Ok(pressure)
234		
235			}
236
237		}
238	}
239
240
241	///Get and process the temperature data
242	pub fn get_temp_reading(&mut self) -> Result<f32, Error<E>> {
243
244		// Read temperature registers
245		let mut buf = [0u8; 2];
246		self.read_regs(Register::OUT_T_MSB, &mut buf).map_err(Error::I2c)?;
247
248		//Change the device back to active
249		self.mode = Mode::Active;
250
251		//Negative temperature fix by D.D.G.
252		//let mut foo: u16 = 0;
253		let mut neg_sign = false;
254
255		//Check for 2s compliment
256		if buf[0] > 0x7F
257		{
258			let mut foo = u16::from_be_bytes(buf);
259			foo = !foo + 1;  //2’s complement
260			buf = foo.to_be_bytes();
261			buf[1] = buf[1] & 0xF0; 
262			neg_sign = true;
263		}
264
265		// The least significant bytes l_altitude and l_temp are 4-bit,
266		// fractional values, so you must cast the calulation in (float),
267		// shift the value over 4 spots to the right and divide by 16 (since 
268		// there are 16 values in 4-bits). 
269		let templsb = f32(buf[1]>>4)/16.0; //temp, fraction of a degree
270
271		let mut temperature = f32(buf[0]) + templsb;
272
273		if neg_sign {
274			temperature = 0.0 - temperature;
275		}
276
277		Ok(temperature)
278	}
279
280
281	/// Set the number of samples the device makes before saving the data
282	/// Call with a rate from 0 to 7. Datasheet calls for 128 but you can set it from 1 to 128 samples. 
283	/// The higher the oversample rate the greater the time between data samples.
284	///
285	/// Example Times:
286	/// * 0 = 8ms
287    /// * 3 = 30ms
288    /// * 7 = 380ms
289	pub fn set_oversample_rate(&mut self, mut sample_rate: u8)  -> Result<(), Error<E>> {
290		if sample_rate > 7 {
291			sample_rate = 7; //OS cannot be larger than 0b.0111
292		}
293		sample_rate <<= 3; //Align it for the CTRL_REG1 register
294		
295		let mut temp_setting = self.read_reg(Register::CTRL_REG1).map_err(Error::I2c)?; //Read current settings
296		temp_setting &= 0b1100_0111; //Clear out old OS bits
297		temp_setting |= sample_rate; //Mask in new OS bits
298		self.write_reg(Register::CTRL_REG1,temp_setting).map_err(Error::I2c)
299	}
300
301	#[inline]
302	fn read_reg(&mut self, reg: Register) -> Result<u8, E> {
303		let mut buf = [0u8];
304		self.i2c.write_read(I2C_SAD, &[reg.addr()], &mut buf)?;
305		Ok(buf[0])
306	}
307
308	#[inline]
309	fn read_regs(&mut self, reg: Register, buffer: &mut [u8]) -> Result<(), E> {
310		self.i2c
311			.write_read(I2C_SAD, &[reg.addr()], buffer)
312	}
313
314	#[inline]
315	fn write_reg(&mut self, reg: Register, val: u8) -> Result<(), E> {
316		self.i2c.write(I2C_SAD, &[reg.addr(), val])
317	}
318
319	#[inline]
320	fn modify_reg<F>(&mut self, reg: Register, f: F) -> Result<(), E>
321	where
322		F: FnOnce(u8) -> u8,
323	{
324		let r = self.read_reg(reg)?;
325		self.write_reg(reg, f(r))?;
326		Ok(())
327	}
328
329	#[inline]
330	fn reg_set_bits(&mut self, reg: Register, bits: u8) -> Result<(), E> {
331		self.modify_reg(reg, |v| v | bits)
332	}
333
334	#[inline]
335	fn reg_reset_bits(&mut self, reg: Register, bits: u8) -> Result<(), E> {
336		self.modify_reg(reg, |v| v & !bits)
337	}
338
339	#[inline]
340	fn reg_xset_bits(&mut self, reg: Register, bits: u8, set: bool) -> Result<(), E> {
341		if set {
342			self.reg_set_bits(reg, bits)
343		} else {
344			self.reg_reset_bits(reg, bits)
345		}
346	}
347}
348
349
350
351