i2cdev_lsm303d/
lib.rs

1// Copyright 2017, Martin Deegan <mddeegan@gmail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/license/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option.  This file may not be copied, modified, or distributed
7// except according to those terms.
8
9// This module is implementing userland library to interact with
10// lsm303d accelerometer.
11
12#![allow(dead_code)]
13
14extern crate i2cdev;
15extern crate i2csensors;
16extern crate byteorder;
17
18use i2csensors::{Magnetometer,Accelerometer};
19use i2csensors::Vec3;
20use std::thread;
21use std::time::Duration;
22use std::error::Error;
23use i2cdev::core::I2CDevice;
24use byteorder::{ByteOrder, LittleEndian};
25#[cfg(any(target_os = "linux", target_os = "android"))]
26use i2cdev::linux::{LinuxI2CDevice,LinuxI2CError};
27
28pub const LSM303D_I2C_ADDR: u16 = 0x1E;
29
30const LSM303D_WHO_AM_I: u8 = 0x0F;
31const LSM303D_ID: u8 = 0b01001001;
32
33const LSM303D_INCREMENT_BIT: u8 = 0x80;
34const LSM303D_OUT_MAG: u8 = 0x08;
35const LSM303D_OUT_ACC: u8 = 0x28;
36
37const LSM303D_CTRL1: u8 = 0x20;
38const LSM303D_CTRL2: u8 = 0x21;
39const LSM303D_CTRL5: u8 = 0x24;
40const LSM303D_CTRL6: u8 = 0x25;
41const LSM303D_CTRL7: u8 = 0x26;
42
43#[derive(Debug, Copy, Clone)]
44pub enum LSM303DAccelerometerUpdateRate {
45    PowerDown = 0b0000,
46    Hz3_125 = 0b0001,
47    Hz6_25 = 0b0010,
48    Hz12_5 = 0b0011,
49    Hz25 = 0b0100,
50    Hz50 = 0b0101,
51    Hz100 = 0b0110,
52    Hz200 = 0b0111,
53    Hz400 = 0b1000,
54    Hz800 = 0b1001,
55    Hz1600 = 0b1010
56}
57
58#[derive(Debug, Copy, Clone)]
59pub enum LSM303DAccelerometerFilterBandwidth {
60    Hz773 = 0b00,
61    Hz194 = 0b01,
62    Hz362 = 0b10,
63    Hz50 = 0b11
64}
65
66#[derive(Debug, Copy, Clone)]
67pub enum LSM303DAccelerometerFS {
68    g2 = 0b000,
69    g4 = 0b001,
70    g6 = 0b010,
71    g8 = 0b011,
72    g16 = 0b100
73}
74
75#[derive(Debug, Copy, Clone)]
76pub enum LSM303DMagnetometerMode {
77    ContinuousConversion = 0b00,
78    SingleConversion = 0b01,
79    PowerDown = 0b10
80}
81
82#[derive(Debug, Copy, Clone)]
83pub enum LSM303DMagnetometerResolution {
84    Low = 0b00,
85    MediumLow = 0b01,
86    MediumHigh = 0b10,
87    High = 0b11
88}
89
90#[derive(Debug, Copy, Clone)]
91pub enum LSM303DMagnetometerUpdateRate {
92    Hz3_125 = 0b000,
93    Hz6_25 = 0b001,
94    Hz12_5 = 0b010,
95    Hz25 = 0b011,
96    Hz50 = 0b100,
97    Hz100 = 0b101
98}
99
100#[derive(Debug, Copy, Clone)]
101pub enum LSM303DMagnetometerFS {
102    gauss2 = 0b00,
103    gauss4 = 0b01,
104    gauss8 = 0b10,
105    gauss12 = 0b11
106}
107
108/// Use the [data sheet](http://www.st.com/content/ccc/resource/technical/document/datasheet/1c/9e/71/05/4e/b7/4d/d1/DM00057547.pdf/files/DM00057547.pdf/jcr:content/translations/en.DM00057547.pdf) to read in depth about settings
109#[derive(Debug, Copy, Clone)]
110pub struct LSM303DSettings {
111    /// Continuously update output registers or wait until read
112    pub continuous_update: bool,
113    //Accelerometer
114    /// Frequency that accelerometer measurements are made
115    pub accelerometer_data_rate: LSM303DAccelerometerUpdateRate,
116    pub accelerometer_anti_alias_filter_bandwidth: LSM303DAccelerometerFilterBandwidth,
117    /// Enable accelerometer z axis
118    pub azen: bool,
119    /// Enable accelerometer y axis
120    pub ayen: bool,
121    /// Enable accelerometer x axis
122    pub axen: bool,
123    /// The maximum/minimum (+-) reading of acceleration (Full range)
124    pub accelerometer_sensitivity: LSM303DAccelerometerFS,
125    //Magnetometer
126    pub magnetometer_resolution: LSM303DMagnetometerResolution,
127    /// Frequency that magnetometer measurements are made
128    pub magnetometer_data_rate: LSM303DMagnetometerUpdateRate,
129    pub magnetometer_low_power_mode: bool,
130    pub magnetometer_mode: LSM303DMagnetometerMode,
131    /// The maximum/minimum (+-) reading of magnetism (Full range)
132    pub magnetometer_sensitivity: LSM303DMagnetometerFS
133}
134
135/// Get Linux I2C device at L3GD20's default address
136#[cfg(any(target_os = "linux", target_os = "android"))]
137pub fn get_linux_lsm303d_i2c_device() -> Result<LinuxI2CDevice,LinuxI2CError> {
138    let device = try!(LinuxI2CDevice::new("/dev/i2c-1", LSM303D_I2C_ADDR));
139    Ok(device)
140}
141
142pub struct LSM303D<T: I2CDevice + Sized> {
143    accelerometer_magnetometer: T,
144    m_gain: f32,
145    a_gain: f32
146}
147
148impl<T> LSM303D<T>
149    where T: I2CDevice + Sized
150{
151    #[cfg(any(target_os = "linux", target_os = "android"))]
152    pub fn new(mut accel_mag: T, mut accel_mag_settings: LSM303DSettings) -> Result<LSM303D<T>, T::Error> {
153        assert!(try!(accel_mag.smbus_read_byte_data(LSM303D_WHO_AM_I)) == LSM303D_ID, "LSM303D ID didn't match for device at given I2C address.");
154
155        let mut ctrl_reg1: u8 = 0_u8 | ((accel_mag_settings.accelerometer_data_rate as u8) << 4);
156        if !accel_mag_settings.continuous_update {
157            ctrl_reg1 |= 0b00001000;
158        }
159        if accel_mag_settings.axen { ctrl_reg1 |= 0b001 };
160        if accel_mag_settings.ayen { ctrl_reg1 |= 0b010 };
161        if accel_mag_settings.azen { ctrl_reg1 |= 0b100 };
162        try!(accel_mag.smbus_write_byte_data(LSM303D_CTRL1, ctrl_reg1));
163
164        let mut ctrl_reg2: u8 = 0_u8 | ((accel_mag_settings.accelerometer_sensitivity as u8) << 3) | ((accel_mag_settings.accelerometer_anti_alias_filter_bandwidth as u8) << 6);
165        try!(accel_mag.smbus_write_byte_data(LSM303D_CTRL2, ctrl_reg2));
166
167        let mut ctrl_reg5: u8 = 0_u8 | ((accel_mag_settings.magnetometer_resolution as u8) << 5) | ((accel_mag_settings.magnetometer_data_rate as u8) << 2); //24
168        try!(accel_mag.smbus_write_byte_data(LSM303D_CTRL5, ctrl_reg5));
169
170        let mut ctrl_reg6: u8 = 0_u8 | ((accel_mag_settings.magnetometer_sensitivity as u8) << 5);
171        try!(accel_mag.smbus_write_byte_data(LSM303D_CTRL6, ctrl_reg6));
172
173        let mut ctrl_reg7: u8 = 0_u8 | (accel_mag_settings.magnetometer_mode as u8);
174        if accel_mag_settings.magnetometer_low_power_mode {
175            ctrl_reg7 |= 0b00000100;
176        }
177        try!(accel_mag.smbus_write_byte_data(LSM303D_CTRL7, ctrl_reg7));
178
179        let mut a_gain: f32;
180        let mut m_gain: f32;
181
182        match accel_mag_settings.magnetometer_sensitivity {
183            LSM303DMagnetometerFS::gauss2 => {
184                m_gain = 0.08;
185            },
186            LSM303DMagnetometerFS::gauss4 => {
187                m_gain = 0.160;
188            },
189            LSM303DMagnetometerFS::gauss8 => {
190                m_gain = 0.320;
191            },
192            LSM303DMagnetometerFS::gauss12 => {
193                m_gain = 0.479;
194            }
195        }
196
197        match accel_mag_settings.accelerometer_sensitivity {
198            LSM303DAccelerometerFS::g2 => {
199                a_gain = 0.061;
200            },
201            LSM303DAccelerometerFS::g4 => {
202                a_gain = 0.122;
203            },
204            LSM303DAccelerometerFS::g6 => {
205                a_gain = 0.183;
206            },
207            LSM303DAccelerometerFS::g8 => {
208                a_gain = 0.244;
209            },
210            LSM303DAccelerometerFS::g16 => {
211                a_gain = 0.732;
212            }
213        }
214
215        Ok(LSM303D {
216            accelerometer_magnetometer: accel_mag,
217            m_gain: m_gain / 1000.0,
218            a_gain: a_gain / 1000.0
219        })
220    }
221
222    #[cfg(any(target_os = "linux", target_os = "android"))]
223    fn magnetometer_read_raw(&mut self) -> Result<(i16, i16, i16), T::Error> {
224        let mut buf: [u8; 6] = [0;6];
225        try!(self.accelerometer_magnetometer.write(&[LSM303D_INCREMENT_BIT | LSM303D_OUT_MAG]));
226        try!(self.accelerometer_magnetometer.read(&mut buf));
227        let x_raw = LittleEndian::read_i16(&buf[0..2]);
228        let y_raw = LittleEndian::read_i16(&buf[2..4]);
229        let z_raw = LittleEndian::read_i16(&buf[4..6]);
230        Ok((x_raw, y_raw, z_raw))
231    }
232
233    #[cfg(any(target_os = "linux", target_os = "android"))]
234    fn accelerometer_read_raw(&mut self) -> Result<(i16, i16, i16), T::Error> {
235        let mut buf: [u8; 6] = [0;6];
236        try!(self.accelerometer_magnetometer.write(&[LSM303D_INCREMENT_BIT | LSM303D_OUT_ACC]));
237        try!(self.accelerometer_magnetometer.read(&mut buf));
238        let x_raw = LittleEndian::read_i16(&buf[0..2]);
239        let y_raw = LittleEndian::read_i16(&buf[2..4]);
240        let z_raw = LittleEndian::read_i16(&buf[4..6]);
241        Ok((x_raw, y_raw, z_raw))
242    }
243}
244
245impl<T> Magnetometer for LSM303D<T>
246    where T: I2CDevice + Sized
247{
248    type Error = T::Error;
249
250    #[cfg(not(any(target_os = "linux", target_os = "android")))]
251    fn magnetic_reading(&mut self) -> Result<Vec3, T::Error> {
252        Ok(Vec3::zeros())
253    }
254
255    /// Returns reading in gauss
256    #[cfg(any(target_os = "linux", target_os = "android"))]
257    fn magnetic_reading(&mut self) -> Result<Vec3, T::Error> {
258        let (x_raw, y_raw, z_raw) = try!(self.magnetometer_read_raw());
259        Ok(Vec3 {
260            x: x_raw as f32 * self.m_gain,
261            y: y_raw as f32 * self.m_gain,
262            z: z_raw as f32 * self.m_gain
263        })
264    }
265}
266
267impl<T> Accelerometer for LSM303D<T>
268    where T: I2CDevice + Sized
269{
270    type Error = T::Error;
271
272    #[cfg(not(any(target_os = "linux", target_os = "android")))]
273    fn acceleration_reading(&mut self) -> Result<Vec3, T::Error> {
274        Ok(Vec3::zeros())
275    }
276
277    /// Returns acceleration in gs
278    #[cfg(any(target_os = "linux", target_os = "android"))]
279    fn acceleration_reading(&mut self) -> Result<Vec3, T::Error> {
280        let (x_raw, y_raw, z_raw) = try!(self.accelerometer_read_raw());
281        Ok(Vec3 {
282            x: x_raw as f32 * self.a_gain,
283            y: y_raw as f32 * self.a_gain,
284            z: z_raw as f32 * self.a_gain
285        })
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    #[test]
292    fn it_works() {
293    }
294}