Driver for the LSM303D digital compass. Permits access to all LSM303D registers by name, including multi-byte register sets, and has convenience methods for floating-point accelerometer and magnetometer reads scaled according to the device settings. Written from scratch for Mbed 6.0
Dependents: UoY-32C-lab8-exercise UoY-32C-lab5-lsm303d
Driver for the LSM303D digital compass
Permits access to all LSM303D registers by name, including multi-byte register sets. Convenience methods are included for sensor reads, including floating-point accelerometer and magnetometer reads scaled according to the device settings, and for the more common settings (data rates and scales).
I2C connection only. Written from scratch for Mbed 6.0.
The interface is subject to minor changes with each version. Deprecation warnings will be issued where possible.
Defaults
The class constructor checks that an LSM303D is present on the I2C bus, then enables all sensors. The accelerometer and magnetometer update rates are both set to 50Hz, and the scales are left at their device defaults of +-2g for the accelerometer and +-4gauss for the magnetometer.
Basic usage
Some usage examples
#include "lsm303d.h" LSM303D lsm303d(D14, D15); // SDA and SCL pins for I2C lsm303d.setAccelScale(LSM303D::AccelScale::scale_4g); // set +-4g range lsm303d.INT1_enable_DRDY_A(); // set DRDY_A bit in CTRL3 register (enable INT1 on accel data ready) short rawX = lsm303d.getAccelX(); // returns 16-bit signed result, unscaled float x, y, z; lsm303d.getAccelInto(x, y, z); // overwrites parameters, values in g float mX = lsm303d.getMagX(); // returns value in gauss
LSM303D.h
- Committer:
- ajp109
- Date:
- 2021-02-17
- Revision:
- 11:0bb2931123d0
- Parent:
- lsm303d.h@ 10:392fa35ca17c
File content as of revision 11:0bb2931123d0:
#include "mbed.h" typedef unsigned char u8; typedef signed short i16; typedef unsigned short u16; #pragma pack(push) #pragma pack(1) typedef struct { u8 x; u8 y; u8 z; } u8_3; typedef struct { i16 x; i16 y; i16 z; } i16_3; #pragma pack(pop) /** LSM303D class. * Communicates with LSM303D electronic compass over I<sup>2</sup>C. Accelerometer, * magnetometer and temperature sensor supported. Configurable sample rates * and full-scale ranges. All LSM303D config registers available for raw * reads and writes if needed. * * Example: * @code * #include "mbed.h" * #include "lsm303d.h" * * LSM303D lsm303d(SDA, SCL); * lsm303d.setAccelRate(LSM303D::AccelRate::rate_25Hz); * lsm303d.setAccelScale(LSM303D::AccelScale::scale_8g); * * int main() { * short x, y, z; * lsm303d.getRawAccelInto(x, y, z); // 16-bit signed, unscaled * float xf, yf, zf; * lsm303d.getAccelInto(xf, yf, zf); // floats, in g * } * @endcode */ class LSM303D { friend class AccelScaleOption; friend class MagScaleOption; unsigned char const addr_8bit_; unsigned char accel_scale_; unsigned char mag_scale_; I2C i2c_; protected: // These classes are protected (rather than private) for inlusion in docs. // Wouldn't be necessary if mbed online toolchain allowed local Doxyfiles. template <class T> class RWRegister; /** Read-only register class. * @tparam T The underlying data type of the register. * Provides an interface onto the read-only registers of the LSM303D. * Operator overloading means that it can be treated as a const variable of * type T for most purposes. Every read initiates an explicit * I<sup>2</sup>C transfer. */ template <class T> class Register { friend class LSM303D; friend class RWRegister<T>; private: unsigned char addr_; LSM303D &parent_; Register(LSM303D &parent, unsigned char addr) : parent_(parent), addr_(addr | (sizeof(T)>1 ? 0x80 : 0x00)) {} /// Explicit read method. /// \return The current value of the register. virtual T read() const { T result; parent_.i2c_.lock(); parent_.i2c_.write(parent_.addr_8bit_, (char *)&addr_, 1); parent_.i2c_.read(parent_.addr_8bit_, (char *)&result, sizeof(T)); parent_.i2c_.unlock(); return result; } /// Explicit cast to type T inline operator T() const { return read(); } }; /** Read-write register class. * @tparam T The underlying data type of the register. * Provides an interface onto the writable registers of the LSM303D. * Operator overloading means that it can be treated as a variable of type * T for most purposes. Assignment from int is also explicitly supported * to allow integer literals to be used without qualification or casting. * The register value is cached on first read and updated on write, so read * accesses do not use the I<sup>2</sup>C bus. */ template<class T> class RWRegister : public Register<T> { friend class LSM303D; using Register<T>::parent_; using Register<T>::addr_; private: mutable T cache_; mutable bool cached_; protected: RWRegister(LSM303D &parent, unsigned char addr) : Register<T>(parent, addr), cached_(false) {} /// Explicit write method. /// @param data The new value to write to the register. void write(T const &data) { char packet[sizeof(T)+1]; packet[0] = addr_; memcpy(&packet[1], &data, sizeof(T)); cache_ = data; cached_ = true; parent_.i2c_.lock(); parent_.i2c_.write(parent_.addr_8bit_, packet, sizeof(T)+1); parent_.i2c_.unlock(); } /// Explicit read method with cache. /// @return The current value of the register, from cache if available. virtual T read() const { if (!cached_) { cache_ = Register<T>::read(); cached_ = true; } return cache_; } /// Equivalent to write(). /// @param data The new value to write to the register. /// @return A reference to this object. inline RWRegister & operator= (T const &data) { write(data); return *this; } /// Equivalent to write(), with an explicit int parameter. /// @param data The new value to write to the register. /// @return A reference to this object. inline RWRegister & operator= (int const &data) { write(T(data)); return *this; } /// Or-assignment operator. /// @param data The value to OR with the register's existing contents. /// @return A reference to this object. inline RWRegister & operator|= (T const &data) { write(read() | data); return *this; } /// Or-assignment operator, with an explicit int parameter. /// @param data The value to OR with the register's existing contents. /// @return A reference to this object. inline RWRegister & operator|= (int const &data) { write(read() | T(data)); return *this; } /// And-assignment operator. /// @param data The value to AND with the register's existing contents. /// @return A reference to this object. inline RWRegister & operator&= (T const &data) { write(read() & data); return *this; } /// And-assignment operator, with an explicit int parameter. /// @param data The value to AND with the register's existing contents. /// @return A reference to this object. inline RWRegister & operator&= (int const &data) { write(read() & T(data)); return *this; } }; /** Class representing binary (on/off) options. * @tparam T The underlying data type of the register used to set the option. * Provides a user-friendly interface onto LSM303D options. */ template <class T> class Option { friend class LSM303D; protected: T const bits_; RWRegister<T> & reg_; Option(RWRegister<T> & reg, int bits) : reg_(reg), bits_(T(bits)) {} public: /// Set the option 'on'. inline void operator() () const { reg_ |= bits_; }; /// Set the option 'on' or 'off'. /// @param sr Option status: set to true for 'on', false for 'off'. inline void operator() (bool sr) const { if (sr) reg_ |= bits_; else reg_ &= ~bits_; }; }; const Register <i16_3> OUT_M; ///< Triple magnetometer register const Register <i16_3> OFFSET_M; ///< Triple magnetometer offset register const Register <u8_3> REFERENCE; ///< Triple reference register const Register <i16_3> OUT_A; ///< Triple accelerometer register public: const Register <i16> TEMP_OUT; ///< TEMP_OUT register, read-only const Register <u8> STATUS_M; ///< STATUS_M register, read-only const Register <i16> OUT_X_M; ///< OUT_X_M register, read-only const Register <i16> OUT_Y_M; ///< OUT_Y_M register, read-only const Register <i16> OUT_Z_M; ///< OUT_Z_M register, read-only const Register <u8> WHO_AM_I; ///< WHO_AM_I register, read-only const Register <u8> INT_SRC_M; ///< INT_SRC_M register, read-only const Register <i16> OFFSET_X_M; ///< OFFSET_X_M register, read-only const Register <i16> OFFSET_Y_M; ///< OFFSET_Y_M register, read-only const Register <i16> OFFSET_Z_M; ///< OFFSET_Z_M register, read-only const Register <u8> STATUS_A; ///< STATUS_A register, read-only const Register <i16> OUT_X_A; ///< OUT_X_A register, read-only const Register <i16> OUT_Y_A; ///< OUT_Y_A register, read-only const Register <i16> OUT_Z_A; ///< OUT_Z_A register, read-only const Register <u8> FIFO_SRC; ///< FIFO_SRC register, read-only const Register <u8> IG_SRC1; ///< IG_SRC1 register, read-only const Register <u8> IG_SRC2; ///< IG_SRC2 register, read-only const Register <u8> CLICK_SRC; ///< CLICK_SRC register, read-only RWRegister <u8> INT_CTRL_M; ///< INT_CTRL_M register, read-write RWRegister <u16> INT_THS_M; ///< INT_THS_M register, read-write RWRegister <u8> REFERENCE_X; ///< REFERENCE_X register, read-write RWRegister <u8> REFERENCE_Y; ///< REFERENCE_Y register, read-write RWRegister <u8> REFERENCE_Z; ///< REFERENCE_Z register, read-write RWRegister <u8> CTRL0; ///< CTRL0 register, read-write RWRegister <u8> CTRL1; ///< CTRL1 register, read-write RWRegister <u8> CTRL2; ///< CTRL2 register, read-write RWRegister <u8> CTRL3; ///< CTRL3 register, read-write RWRegister <u8> CTRL4; ///< CTRL4 register, read-write RWRegister <u8> CTRL5; ///< CTRL5 register, read-write RWRegister <u8> CTRL6; ///< CTRL6 register, read-write RWRegister <u8> CTRL7; ///< CTRL7 register, read-write RWRegister <u8> FIFO_CTRL; ///< FIFO_CTRL register, read-write RWRegister <u8> IG_CFG1; ///< IG_CFG1 register, read-write RWRegister <u8> IG_THS1; ///< IG_THS1 register, read-write RWRegister <u8> IG_DUR1; ///< IG_DUR1 register, read-write RWRegister <u8> IG_CFG2; ///< IG_CFG2 register, read-write RWRegister <u8> IG_THS2; ///< IG_THS2 register, read-write RWRegister <u8> IG_DUR2; ///< IG_DUR2 register, read-write RWRegister <u8> CLICK_CFG; ///< CLICK_CFG register, read-write RWRegister <u8> CLICK_THS; ///< CLICK_THS register, read-write RWRegister <u8> TIME_LIMIT; ///< TIME_LIMIT register, read-write RWRegister <u8> TIME_LATENCY;///< TIME_LATENCY register, read-write RWRegister <u8> TIME_WINDOW; ///< TIME_WINDOW register, read-write RWRegister <u8> Act_THS; ///< Act_THS register, read-write RWRegister <u8> Act_DUR; ///< Act_DUR register, read-write // CTRL1 /** AccelRate enum. * Provides friendly names for the accelerometer update rates * that are provided by the device. */ enum class AccelRate { powerdown = 0x00, ///< power-down mode rate_3_125Hz = 0x10, ///< 3.125Hz update rate rate_6_25Hz = 0x20, ///< 6.25Hz update rate rate_12_5Hz = 0x30, ///< 12.5Hz update rate rate_25Hz = 0x40, ///< 25Hz update rate rate_50Hz = 0x50, ///< 50Hz update rate rate_100Hz = 0x60, ///< 100Hz update rate rate_200Hz = 0x70, ///< 200Hz update rate rate_400Hz = 0x80, ///< 400Hz update rate rate_800Hz = 0x90, ///< 800Hz update rate rate_1600Hz = 0xA0 ///< 1600Hz update rate }; /** Sets the accelerometer update rate. * @param rate The new update rate. * Available values are: * - AccelRate::powerdown (power-down mode) * - AccelRate::rate_3_125Hz (3.125Hz) * - AccelRate::rate_6_25Hz (6.25Hz) * - AccelRate::rate_12_5Hz (12.5Hz) * - AccelRate::rate_25Hz (25Hz) * - AccelRate::rate_50Hz (50Hz) * - AccelRate::rate_100Hz (100Hz) * - AccelRate::rate_200Hz (200Hz) * - AccelRate::rate_400Hz (400Hz) * - AccelRate::rate_800Hz (800Hz) * - AccelRate::rate_1600Hz (1600Hz) */ void setAccelRate(AccelRate const &rate); const Option<u8> accel_enableX; ///< Enable or disable x-axis accelerometer const Option<u8> accel_enableY; ///< Enable or disable y-axis accelerometer const Option<u8> accel_enableZ; ///< Enable or disable z-axis accelerometer const Option<u8> accel_enableAll; ///< Enable or disable all accelerometers // CTRL2 /** AccelFilter enum. * Provides friendly names for the accelerometer antialiasing filter * frequencies that are provided by the device. */ enum class AccelFilter { filter_773Hz = 0x00, ///< 773Hz corner frequency filter_194Hz = 0x40, ///< 194Hz corner frequency filter_362Hz = 0x80, ///< 362Hz corner frequency filter_50Hz = 0xC0 ///< 50Hz corner frequency }; /** Sets the accelerometer antialiasing filter frequency. * @param freq The new frequency. * Available values are: * - AccelFilter::filter_773Hz * - AccelFilter::filter_194Hz * - AccelFilter::filter_362Hz * - AccelFilter::filter_50Hz */ void setAccelFilterFreq(AccelFilter const &freq); /** AccelScale enum. * Provides friendly names for the accelerometer scale ranges * that are provided by the device. */ enum class AccelScale { scale_2g = 0x00, ///< +-2g full scale scale_4g = 0x08, ///< +-4g full scale scale_6g = 0x10, ///< +-6g full scale scale_8g = 0x18, ///< +-8g full scale scale_16g = 0x20 ///< +-16g full scale }; /** Sets the accelerometer scale range. * @param scale The new scale. * Available values are: * - AccelScale::scale_2g (+-2g) * - AccelScale::scale_4g (+-4g) * - AccelScale::scale_6g (+-6g) * - AccelScale::scale_8g (+-8g) * - AccelScale::scale_16g (+-16g) */ void setAccelScale(AccelScale const &scale); // CTRL3 const Option<u8> INT1_enable_BOOT; ///< Enable or disable "boot" source for INT1 const Option<u8> INT1_enable_CLICK; ///< Enable or disable "click" source for INT1 const Option<u8> INT1_enable_IG1; ///< Enable or disable "inertial 1" source for INT1 const Option<u8> INT1_enable_IG2; ///< Enable or disable "inertial 2" source for INT1 const Option<u8> INT1_enable_IGM; ///< Enable or disable "magnetic" source for INT1 const Option<u8> INT1_enable_DRDY_A;///< Enable or disable "accelerometer data ready" source for INT1 const Option<u8> INT1_enable_DRDY_M;///< Enable or disable "magnetometer data ready" source for INT1 const Option<u8> INT1_enable_EMPTY; ///< Enable or disable "FIFO empty" source for INT1 // CTRL4 concerns INT2 which is not broken out on the Pimoroni board // CTRL5 const Option<u8> temp_enable; ///< Enable or disable temperature sensor const Option<u8> LIR1_enable; ///< Enable or disable latching interrupt requests for INT1 /** MagRes enum. * Provides friendly names for the magnetometer resolution options * that are provided by the device. */ enum class MagRes { high = 0x60, ///< High resolution low = 0x00, ///< Low resolution }; /** Sets the magnetometer resolution. * @param res The new resolution. * Available values are: * - MagRes::high (high resolution) * - MagRes::low (low resolution) */ void setMagRes(MagRes const &res); /** MagRate enum. * Provides friendly names for the magnetometer update rates * that are provided by the device. */ enum class MagRate { rate_3_125Hz = 0x00, ///< 3.125Hz update rate rate_6_25Hz = 0x04, ///< 6.25Hz update rate rate_12_5Hz = 0x08, ///< 12.5Hz update rate rate_25Hz = 0x0C, ///< 25Hz update rate rate_50Hz = 0x10, ///< 50Hz update rate rate_100Hz = 0x14 ///< 100Hz update rate }; /** Sets the magnetometer update rate. * @param rate The new update rate. * Available values are: * - MagRate::rate_3_125Hz (3.125Hz) * - MagRate::rate_6_25Hz (6.25Hz) * - MagRate::rate_12_5Hz (12.5Hz) * - MagRate::rate_25Hz (25Hz) * - MagRate::rate_50Hz (50Hz) * - MagRate::rate_100Hz (100Hz) * Note that the 100Hz update rate is available only if the accelerometer * update rate is set to 50Hz or higher or the accelerometer is set to * power-down mode (see setAccelRate()). Failing to meet these conditions * will lead to undefined behaviour. */ void setMagRate(MagRate const &rate); // CTRL6 /** MagScale enum. * Provides friendly names for the magnetometer scale ranges * that are provided by the device. */ enum class MagScale { scale_2G = 0x00, ///< +-2 Gauss scale_4G = 0x20, ///< +-4 Gauss scale_8G = 0x40, ///< +-8 Gauss scale_12G = 0x60 ///< +-12 Gauss }; /** Sets the magnetometer scale range. * @param scale The new scale. * Available values are: * - MagScale::scale_2G (+-2 Gauss) * - MagScale::scale_4G (+-4 Gauss) * - MagScale::scale_8G (+-8 Gauss) * - MagScale::scale_12G (+-16 Gauss) */ void setMagScale(MagScale const &scale); // CTRL7 /** MagScale enum. * Provides friendly names for the magnetometer modes that are supported * by the device. */ enum class MagMode { continuous = 0x00, ///< Continous conversion mode single = 0x01, ///< Single conversion mode powerdown = 0x02 ///< Magnetometer power-down mode }; /** Sets the magnetometer mode. * @param mode The new mode. * Available values are: * - MagMode::continuous (continuous conversion mode) * - MagMode::single (single-conversion mode) * - MagMode::powerdown (power-down mode) */ void setMagMode(MagMode const &mode); /** Constructor. * @param sda The pin to use for the I^^2^^C SDA signal. * @param scl The pin to use for the I^^2^^C SCL signal. * @param i2c_addr The 7-bit I<sup>2</sup>C address. Defaults to 0x1D, but * this can be overridden to 0x1E with a jumper setting on the device allowing * two LSM303D devices to coexist on a bus. */ LSM303D(PinName sda, PinName scl, unsigned char i2c_addr = 0x1D); /** Gets the raw (signed 16-bit integer) X-axis acceleration value. * @return The raw acceleration value. */ inline i16 getRawAccelX() { return OUT_X_A; }; /** Gets the raw (signed 16-bit integer) Y-axis acceleration value. * @return The raw acceleration value. */ inline i16 getRawAccelY() { return OUT_Y_A; }; /** Gets the raw (signed 16-bit integer) Z-axis acceleration value. * @return The raw acceleration value. */ inline i16 getRawAccelZ() { return OUT_Z_A; }; /** Gets the scaled (float) X-axis acceleration value in g. * @return The scaled acceleration value in g. */ inline float getAccelX() { return (float)OUT_X_A / accel_scale_ / 32768; }; /** Gets the scaled (float) Y-axis acceleration value in g. * @return The scaled acceleration value in g. */ inline float getAccelY() { return (float)OUT_Y_A / accel_scale_ / 32768; }; /** Gets the scaled (float) Z-axis acceleration value in g. * @return The scaled acceleration value in g. */ inline float getAccelZ() { return (float)OUT_Z_A / accel_scale_ / 32768; }; /** Reads the raw (signed 16-bit integer) acceleration values into the three * axis value containers provided. * @param x Container for the X value. * @param y Container for the Y value. * @param z Container for the Z value. */ void getRawAccelInto(short &x, short &y, short &z); /** Reads the scaled (float) acceleration values in g into the three * axis value containers provided. * @param x Container for the X value. * @param y Container for the Y value. * @param z Container for the Z value. */ void getAccelInto(float &x, float &y, float &z); /** Gets the raw (signed 16-bit integer) X-axis magnetometer value. * @return The raw magnetometer value. */ inline i16 getRawMagX() { return OUT_X_M; }; /** Gets the raw (signed 16-bit integer) Y-axis magnetometer value. * @return The raw magnetometer value. */ inline i16 getRawMagY() { return OUT_Y_M; }; /** Gets the raw (signed 16-bit integer) Z-axis magnetometer value. * @return The raw magnetometer value. */ inline i16 getRawMagZ() { return OUT_Z_M; }; /** Gets the scaled (float) X-axis magnetometer value in Gauss. * @return The scaled magnetometer value in Gauss. */ inline float getMagX() { return (float)OUT_X_M / mag_scale_ / 32768; }; /** Gets the scaled (float) Y-axis magnetometer value in Gauss. * @return The scaled magnetometer value in Gauss. */ inline float getMagY() { return (float)OUT_Y_M / mag_scale_ / 32768; }; /** Gets the scaled (float) Z-axis magnetometer value in Gauss. * @return The scaled magnetometer value in Gauss. */ inline float getMagZ() { return (float)OUT_Z_M / mag_scale_ / 32768; }; /** Reads the raw (signed 16-bit integer) magnetometer values into the three * axis value containers provided. * @param x Container for the X value. * @param y Container for the Y value. * @param z Container for the Z value. */ void getRawMagInto(short &x, short &y, short &z); /** Reads the scaled (float) magnetometer values in Gauss into the three * axis value containers provided. * @param x Container for the X value. * @param y Container for the Y value. * @param z Container for the Z value. */ void getMagInto(float &x, float &y, float &z); /** Reads the scaled (float) temperature values in ° C. * @return The current temperature. * Note that testing suggests this to be an inaccurate temperature sensor! * To read the raw temperature value, simply use the TEMP_OUT register directly. */ inline float getTemp() { return TEMP_OUT / 8.0 + 25; }; };