From c9fefe8108c951a3ec5bc8261703f40fafcf8b3e Mon Sep 17 00:00:00 2001 From: Braidan Date: Fri, 2 Dec 2022 00:30:54 -0500 Subject: [PATCH 1/3] Began adding LIS3MDL content --- Arduino/LIS3MDL/LIS3MDL.cpp | 536 ++++++++++++++++++ Arduino/LIS3MDL/LIS3MDL.h | 289 ++++++++++ .../examples/L3G4200D_raw/L3G4200D_raw.ino | 88 +++ Arduino/LIS3MDL/library.json | 18 + 4 files changed, 931 insertions(+) create mode 100644 Arduino/LIS3MDL/LIS3MDL.cpp create mode 100644 Arduino/LIS3MDL/LIS3MDL.h create mode 100644 Arduino/LIS3MDL/examples/L3G4200D_raw/L3G4200D_raw.ino create mode 100644 Arduino/LIS3MDL/library.json diff --git a/Arduino/LIS3MDL/LIS3MDL.cpp b/Arduino/LIS3MDL/LIS3MDL.cpp new file mode 100644 index 00000000..72b9750b --- /dev/null +++ b/Arduino/LIS3MDL/LIS3MDL.cpp @@ -0,0 +1,536 @@ +/** + * @brief Based on ST Devices LIS3MDL datasheet, May 2017 + * 21 December 2022 by Braidan Duffy + * Updates should (hopefully) always be available at https://github.com/Legohead259/i2cdevlib + * + * CHANGELOG: + * 2022-12-01 - Initial Release + */ + +/** ============================================================================ + * I2Cdev device library code is placed under the MIT license + * Copyright (c) 2011 Braidan Duffy, Jeff Rowberg + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +================================================================================= */ + +#include "LIS3MDL.h" + +/* ============================================================================ + I2Cdev Class Quick Primer: + + The I2Cdev class provides simple methods for reading and writing from I2C + device registers without messing with the underlying TWI/I2C functions. You + just specify the device address, register address, and source or destination + data according to which action you are doing. Here is the list of relevant + function prototypes from the I2Cdev class (more info in the .cpp/.h files): + + static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); + static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); + + static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data); + static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data); + static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data); + static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data); + static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data); + static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data); + static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data); + static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data); + + Note that ALL OF THESE METHODS ARE STATIC. No I2Cdev object is needed; just + use the static class methods. + + Also note that the first two parameters of every one of these methods are + the same: "devAddr" and "regAddr". For every method, you need to specify the + target/slave address and the register address. + + If your device uses 8-bit registers, you will want to use the following: + readBit, readBits, readByte, readBytes + writeBit, writeBits, writeByte, writeBytes + + ...but if it uses 16-bit registers, you will want to use these instead: + readBitW, readBitsW, readWord, readWords + writeBitW, writeBitsW, writeWord, writeWords + + Here's a sample of how to use a few of the methods. Note that in each case, + the "buffer" variable is a uint8_t array or pointer, and the "value" variable + (in three of the write examples) is a uint8_t single byte. The multi-byte + write methods still require an array or pointer. + + READ 1 BIT FROM DEVICE 0x68, REGISTER 0x02, BIT POSITION 4 + bytesRead = I2Cdev::readBit(0x68, 0x02, 4, buffer); + + READ 3 BITS FROM DEVICE 0x68, REGISTER 0x02, BIT POSITION 4 + bytesRead = I2Cdev::readBits(0x68, 0x02, 4, 3, buffer); + + READ 1 BYTE FROM DEVICE 0x68, REGISTER 0x02 + bytesRead = I2Cdev::readByte(0x68, 0x02, buffer); + + READ 2 BYTES FROM DEVICE 0x68, REGISTER 0x02 (AND 0x03 FOR 2ND BYTE) + bytesRead = I2Cdev::readBytes(0x68, 0x02, 2, buffer); + + WRITE 1 BIT TO DEVICE 0x68, REGISTER 0x02, BIT POSITION 4 + status = I2Cdev::writeBit(0x68, 0x02, 4, value); + + WRITE 3 BITS TO DEVICE 0x68, REGISTER 0x02, BIT POSITION 4 + status = I2Cdev::writeBits(0x68, 0x02, 4, 3, value); + + WRITE 1 BYTE TO DEVICE 0x68, REGISTER 0x02 + status = I2Cdev::writeByte(0x68, 0x02, value); + + WRITE 2 BYTES TO DEVICE 0x68, REGISTER 0x02 (AND 0x03 FOR 2ND BYTE) + status = I2Cdev::writeBytes(0x68, 0x02, 2, buffer); + + The word-based methods are exactly the same, except they use 16-bit variables + instead of 8-bit ones. + + ============================================================================ */ + +/** + * @brief Default constructor, uses default I2C address. + * @see LIS3MDL_DEFAULT_ADDRESS + */ +LIS3MDL::LIS3MDL() { + devAddr = LIS3MDL_DEFAULT_ADDRESS; +} + +/** + * @brief Specific address constructor. + * @param address I2C address + * @see LIS3MDL_ADDRESS + */ +LIS3MDL::LIS3MDL(uint8_t address) { + devAddr = address; +} + +/** + * @brief Power on and prepare for general usage. + * All values are default + * @see LIS3MDL_RA_CTRL1 + * @see LIS3MDL_RA_CTRL2 + * @see LIS3MDL_RA_CTRL3 + * @see LIS3MDL_RA_CTRL4 + * @see LIS3MDL_RA_CTRL5 + */ +bool LIS3MDL::initialize() { + bool _success = false; + _success = _success && testConnection(); + _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL1, 0b00010000) && + _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL2, 0b00000000) && + _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL3, 0b00000011) && + _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL4, 0b00000000) && + _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL5, 0b00000000); + return _success; +} + +/** + * @brief Verify the I2C connection. + * Make sure the device is connected and responds as expected. + * @return True if connection is valid, false otherwise + */ +bool LIS3MDL::testConnection() { + return getDeviceID() == LIS3MDL_DEVICE_ID; +} + + +// ==================================== +// === WHO_AM_I register, read-only === +// ==================================== + + +/** + * @brief Query the device for it's WHOAMI ID + * + * @return The WHOAMI ID of the device (should be 0x1C or 0x1E) + * @see LIS3MDL_RA_WHO_AM_I + */ +uint8_t LIS3MDL::getDeviceID() { + I2Cdev::readByte(devAddr, LIS3MDL_RA_WHO_AM_I, buffer); + return buffer[0]; +} + + +// ============================== +// === CONTROL2 REGISTER, R/W === +// ============================== + + +/** + * @brief Enable the temperature sensor on the device + * + * @param en sensor enabled + */ +void LIS3MDL::enableTempSensor(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_TEMP_EN_BIT, en); +} + +/** + * @brief Get if the temperature sensor is enabled on the device + * + * @return true if the sensor is enabled, + * @return false otherwise + */ +bool LIS3MDL::getTempSensorEnable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_TEMP_EN_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Set the XY operating mode + * + * @param mode the new operating mode. Note: This must be one of the four modes (0x00-03) designated by the datasheet + * + * @see LIS3MDL_OM_LP_MODE + * @see LIS3MDL_OM_MP_MODE + * @see LIS3MDL_OM_HP_MODE + * @see LIS3MDL_OM_UHP_MODE + */ +void LIS3MDL::setXYOpMode(uint8_t mode) { + I2Cdev::writeBits(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_OM_BIT, LIS3MDL_OM_LENGTH, mode); +} + +/** + * @brief Returns the XY operating mode + * + * @return the current operating mode (0x00-03) + * + * @see LIS3MDL_OM_LP_MODE + * @see LIS3MDL_OM_MP_MODE + * @see LIS3MDL_OM_HP_MODE + * @see LIS3MDL_OM_UHP_MODE + */ +uint8_t LIS3MDL::getXYOpMode() { + I2Cdev::readBits(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_OM_BIT, LIS3MDL_OM_LENGTH, buffer); + return buffer[0]; +} + +/** + * @brief Sets the data rate of the device + * + * @param rate the new data rate. Note this must be one of eight rates (0x00-07) specified in the datasheet + * + * @see LIS3MDL_DATA_RATE_0_625_HZ + * @see LIS3MDL_DATA_RATE_1_25_HZ + * @see LIS3MDL_DATA_RATE_2_5_HZ + * @see LIS3MDL_DATA_RATE_5_HZ + * @see LIS3MDL_DATA_RATE_10_HZ + * @see LIS3MDL_DATA_RATE_20_HZ + * @see LIS3MDL_DATA_RATE_40_HZ + * @see LIS3MDL_DATA_RATE_80_HZ + */ +void LIS3MDL::setDataRate(uint8_t rate) { + I2Cdev::writeBits(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_DO_BIT, LIS3MDL_DO_LENGTH, rate); +} + +/** + * @brief Returns the current data rate + * + * @return the current data rate (0x00-07) + * + * @see LIS3MDL_DATA_RATE_0_625_HZ + * @see LIS3MDL_DATA_RATE_1_25_HZ + * @see LIS3MDL_DATA_RATE_2_5_HZ + * @see LIS3MDL_DATA_RATE_5_HZ + * @see LIS3MDL_DATA_RATE_10_HZ + * @see LIS3MDL_DATA_RATE_20_HZ + * @see LIS3MDL_DATA_RATE_40_HZ + * @see LIS3MDL_DATA_RATE_80_HZ + */ +uint8_t LIS3MDL::getDataRate() { + I2Cdev::readBits(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_DO_BIT, LIS3MDL_DO_LENGTH, buffer); + return buffer[0]; +} + +/** + * @brief Enable the FAST_ODR on the device + * + * @param en FAST_ODR enabled + */ +void LIS3MDL::enableFastODR(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_FAST_ODR_BIT, en); +} + +/** + * @brief Get if FAST_ODR is enabled on the device + * + * @return true if FAST_ODR is enabled, + * @return false otherwise + */ +bool LIS3MDL::getFastODREnable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_FAST_ODR_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Enable the SELF_TEST on the device + * + * @param en SELF_TEST enabled + */ +void LIS3MDL::enableSelfTest(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_ST_BIT, en); +} + +/** + * @brief Get if SELF_TEST is enabled on the device + * + * @return true if SELF_TEST is enabled, + * @return false otherwise + */ +bool LIS3MDL::getSelfTestEnable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL1, LIS3MDL_ST_BIT, buffer); + return buffer[0]; +} + + +// ============================== +// === CONTROL2 REGISTER, R/W === +// ============================== + + +/** + * @brief Sets the scale of the magnetometer + * + * @param rate the new scale. Note this must be one of four rates (0x00-03) specified in the datasheet + * + * @see LIS3MDL_FULL_SCALE_4_G + * @see LIS3MDL_FULL_SCALE_8_G + * @see LIS3MDL_FULL_SCALE_12_G + * @see LIS3MDL_FULL_SCALE_16_G + */ +void LIS3MDL::setFullScale(uint8_t scale) { + I2Cdev::writeBits(devAddr, LIS3MDL_RA_CTRL2, LIS3MDL_FS_BIT, LIS3MDL_FS_LENGTH, scale); +} + +/** + * @brief Returns the current scale + * + * @return the current scale (0x00-03) + * + * @see LIS3MDL_FULL_SCALE_4_G + * @see LIS3MDL_FULL_SCALE_8_G + * @see LIS3MDL_FULL_SCALE_12_G + * @see LIS3MDL_FULL_SCALE_16_G + */ +uint8_t LIS3MDL::getDataRate() { + I2Cdev::readBits(devAddr, LIS3MDL_RA_CTRL2, LIS3MDL_FS_BIT, LIS3MDL_FS_LENGTH, buffer); + return buffer[0]; +} + +/** + * @brief Write a single bit to the CTRL2 register, rebooting the device + * Note: This wipes all the memory content and restores defaults + */ +void LIS3MDL::reboot() { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL2, LIS3MDL_REBOOT_BIT, 1); +} + +/** + * @brief Write a single bit to the CTRL2 register, soft resetting the device + * Note: this resets configuration and user registers + */ +void LIS3MDL::reset() { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL2, LIS3MDL_SOFT_RST_BIT, 1); +} + + +// ============================== +// === CONTROL3 REGISTER, R/W === +// ============================== + + +/** + * @brief Enable low-power mode on the device + * + * @param en low-power enabled + */ +void LIS3MDL::enableLowPowerMode(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL3, LIS3MDL_LP_BIT, en); +} + +/** + * @brief Get if low-power mode is enabled on the device + * + * @return true if low-power mode is enabled, + * @return false otherwise + */ +bool LIS3MDL::getLowPowerModeEnable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL3, LIS3MDL_LP_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Sets the SPI interface mode + * + * @param mode the SPI interface. False (0) is the 4-wire interface + * True (1) is the 3-wire interface + */ +void LIS3MDL::setSPIInterfaceMode(bool mode) { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL3, LIS3MDL_SIM_BIT, mode); +} + +/** + * @brief Returns the current SPI interface mode + * + * @return the current mode (0x00-01). False (0) is the 4-wire interface + * True (1) is the 3-wire interface + */ +uint8_t LIS3MDL::getSPIInterfaceMode() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL3, LIS3MDL_SIM_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Sets the operating mode of the device + * + * @param rate the new operating mode. Note this must be one of three rates (0x00-02) specified in the datasheet + * + * @see LIS3MDL_MD_CC_MODE + * @see LIS3MDL_MD_SC_MODE + * @see LIS3MDL_MD_PD_MODE + */ +void LIS3MDL::setOperatingMode(uint8_t mode) { + I2Cdev::writeBits(devAddr, LIS3MDL_RA_CTRL3, LIS3MDL_MD_BIT, LIS3MDL_MD_LENGTH, mode); +} + +/** + * @brief Returns the current operating mode + * + * @return the current mode (0x00-02) + * + * @see LIS3MDL_MD_CC_MODE + * @see LIS3MDL_MD_SC_MODE + * @see LIS3MDL_MD_PD_MODE + */ +uint8_t LIS3MDL::getOperatingMode() { + I2Cdev::readBits(devAddr, LIS3MDL_RA_CTRL3, LIS3MDL_MD_BIT, LIS3MDL_MD_LENGTH, buffer); + return buffer[0]; +} + + +// ============================== +// === CONTROL4 REGISTER, R/W === +// ============================== + + +/** + * @brief Set the Z-axis operating mode + * + * @param mode the new operating mode. Note: This must be one of the four modes (0x00-03) designated by the datasheet + * + * @see LIS3MDL_OMZ_LP_MODE + * @see LIS3MDL_OMZ_MP_MODE + * @see LIS3MDL_OMZ_HP_MODE + * @see LIS3MDL_OMZ_UHP_MODE + */ +void LIS3MDL::setZOpMode(uint8_t mode) { + I2Cdev::writeBits(devAddr, LIS3MDL_RA_CTRL4, LIS3MDL_OMZ_BIT, LIS3MDL_OMZ_LENGTH, mode); +} + +/** + * @brief Returns the Z-axis operating mode + * + * @return the current operating mode (0x00-03) + * + * @see LIS3MDL_OMZ_LP_MODE + * @see LIS3MDL_OMZ_MP_MODE + * @see LIS3MDL_OMZ_HP_MODE + * @see LIS3MDL_OMZ_UHP_MODE + */ +uint8_t LIS3MDL::getZOpMode() { + I2Cdev::readBits(devAddr, LIS3MDL_RA_CTRL4, LIS3MDL_OMZ_BIT, LIS3MDL_OMZ_LENGTH, buffer); + return buffer[0]; +} + +/** + * @brief Sets the data endianness + * + * @param endianness The endianness of the data. False (0) is LSB first + * True (1) is MSB first + */ +void LIS3MDL::setEndianness(bool endianness) { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL4, LIS3MDL_BLE_BIT, endianness); +} + +/** + * @brief Returns the current data endianness + * + * @return the current endianness (0x00-01). False (0) is LSB first + * True (1) is MSB first + */ +uint8_t LIS3MDL::getEndianness() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL4, LIS3MDL_BLE_BIT, buffer); + return buffer[0]; +} + + +// =============================== +// === CONTROL5 REGISTERS, R/W === +// =============================== + + +/** + * @brief Enable FAST_READ on the device + * + * @param en FAST_READ enabled + */ +void LIS3MDL::enableFastRead(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL5, LIS3MDL_FAST_READ_BIT, en); +} + +/** + * @brief Get if FAST_READ is enabled on the device + * + * @return true if FAST_READ mode is enabled, + * @return false otherwise + */ +bool LIS3MDL::getFastReadEnable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL5, LIS3MDL_FAST_READ_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Enable Block Data Update on the device + * + * @param en Block Data Update enabled + */ +void LIS3MDL::enableBlockDataUpdate(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_RA_CTRL5, LIS3MDL_BDU_BIT, en); +} + +/** + * @brief Get if Block Data Update is enabled on the device + * + * @return true if Block Data Update mode is enabled, + * @return false otherwise + */ +bool LIS3MDL::getBlockDataUpdateEnable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL5, LIS3MDL_BDU_BIT, buffer); + return buffer[0]; +} + + +// =================================== +// === STATUS REGISTERS, READ-ONLY === +// =================================== \ No newline at end of file diff --git a/Arduino/LIS3MDL/LIS3MDL.h b/Arduino/LIS3MDL/LIS3MDL.h new file mode 100644 index 00000000..87ba1f87 --- /dev/null +++ b/Arduino/LIS3MDL/LIS3MDL.h @@ -0,0 +1,289 @@ +/** + * @brief Based on ST Devices LIS3MDL datasheet, May 2017 + * 21 December 2022 by Braidan Duffy + * Updates should (hopefully) always be available at https://github.com/Legohead259/i2cdevlib + * + * CHANGELOG: + * 2022-12-01 - Initial Release + */ + +/** ============================================================================ + * I2Cdev device library code is placed under the MIT license + * Copyright (c) 2011 Braidan Duffy, Jeff Rowberg + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +================================================================================= */ + +#ifndef LIS3MDL_H +#define LIS3MDL_H + +#include "I2Cdev.h" + + +// ======================== +// === DEVICE ADDRESSES === +// ======================== + + +#define LIS3MDL_ADDRESS_AD0_LOW 0x1C +#define LIS3MDL_ADDRESS_AD0_HIGH 0x1E +#define LIS3MDL_DEFAULT_ADDRESS 0x1C +#define LIS3MDL_DEVICE_ID 0x3D + + +// ======================== +// === REGISTER MAPPING === +// ======================== + + +#define LIS3MDL_RA_WHO_AM_I 0x0F +#define LIS3MDL_RA_CTRL1 0x20 +#define LIS3MDL_RA_CTRL2 0x21 +#define LIS3MDL_RA_CTRL3 0x22 +#define LIS3MDL_RA_CTRL4 0x23 +#define LIS3MDL_RA_CTRL5 0x24 +#define LIS3MDL_RA_STATUS 0x27 +#define LIS3MDL_OUT_X_L 0x28 +#define LIS3MDL_OUT_X_H 0x29 +#define LIS3MDL_OUT_Y_L 0x2A +#define LIS3MDL_OUT_Y_H 0x2B +#define LIS3MDL_OUT_Z_L 0x2C +#define LIS3MDL_OUT_Z_H 0x2D +#define LIS3MDL_OUT_TEMP_OUT_L 0x2E +#define LIS3MDL_OUT_TEMP_OUT_H 0x2F +#define LIS3MDL_INT_CFG 0x30 +#define LIS3MDL_INT_SRC 0x31 +#define LIS3MDL_OUT_THS_L 0x32 +#define LIS3MDL_OUT_THS_H 0x33 + + +// ================================ +// === REGISTER BIT DEFINITIONS === +// ================================ + + +// --- CTRL_REG1 --- +#define LIS3MDL_TEMP_EN_BIT 7 +#define LIS3MDL_OM_BIT 6 +#define LIS3MDL_OM_LENGTH 2 +#define LIS3MDL_DO_BIT 4 +#define LIS3MDL_DO_LENGTH 3 +#define LIS3MDL_FAST_ODR_BIT 1 +#define LIS3MDL_ST_BIT 0 + +// --- CTRL_REG2 --- +#define LIS3MDL_FS_BIT 6 +#define LIS3MDL_FS_LENGTH 2 +#define LIS3MDL_REBOOT_BIT 3 +#define LIS3MDL_SOFT_RST_BIT 2 + +// --- CTRL_REG3 --- +#define LIS3MDL_LP_BIT 5 +#define LIS3MDL_SIM_BIT 2 +#define LIS3MDL_MD_BIT 1 +#define LIS3MDL_MD_LENGTH 2 + +// --- CTRL_REG4 --- +#define LIS3MDL_OMZ_BIT 3 +#define LIS3MDL_OMZ_LENGTH 2 +#define LIS3MDL_BLE_BIT 1 + +// --- CTRL_REG5 --- +#define LIS3MDL_FAST_READ_BIT 7 +#define LIS3MDL_BDU_BIT 6 + +// --- STATUS_REG --- +#define LIS3MDL_ZYXOR_BIT 7 +#define LIS3MDL_ZOR_BIT 6 +#define LIS3MDL_YOR_BIT 5 +#define LIS3MDL_XOR_BIT 4 +#define LIS3MDL_ZYXDA_BIT 3 +#define LIS3MDL_ZDA_BIT 2 +#define LIS3MDL_YDA_BIT 1 +#define LIS3MDL_XDA_BIT 0 + +// --- INT_CFG --- +#define LIS3MDL_XIEN_BIT 7 +#define LIS3MDL_YIEN_BIT 6 +#define LIS3MDL_ZIEN_BIT 5 +#define LIS3MDL_IEA_BIT 2 +#define LIS3MDL_LIR_BIT 1 +#define LIS3MDL_IEN_BIT 0 + +// --- INT_SRC --- +#define LIS3MDL_PTH_X_BIT 7 +#define LIS3MDL_PTH_Y_BIT 6 +#define LIS3MDL_PTH_Z_BIT 5 +#define LIS3MDL_PTH_X_BIT 4 +#define LIS3MDL_PTH_Y_BIT 3 +#define LIS3MDL_PTH_Z_BIT 2 +#define LIS3MDL_MROI_BIT 1 +#define LIS3MDL_INT_BIT 0 + +// --- INT_THS_L --- +#define LIS3MDL_THS_L_BIT 7 +#define LIS3MDL_THS_L_LENGTH 8 + +// --- INT_THS_H --- +#define LIS3MDL_THS_H_BIT 6 +#define LIS3MDL_THS_H_LENGTH 7 + + +// ======================================= +// === PREDEFINED CONFIGURATION VALUES === +// ======================================= + + +// --- Table 21: X and Y axes operating mode selection --- +#define LIS3MDL_OM_LP_MODE 0x00 // Low-power mode +#define LIS3MDL_OM_MP_MODE 0x01 // Medium-performance mode +#define LIS3MDL_OM_HP_MODE 0x02 // High-performance mode +#define LIS3MDL_OM_UHP_MODE 0x03 // Ultra-high-performance mode + +// --- Table 22: Output data rate configuration +#define LIS3MDL_DATA_RATE_0_625_HZ 0x00 +#define LIS3MDL_DATA_RATE_1_25_HZ 0x01 +#define LIS3MDL_DATA_RATE_2_5_HZ 0x02 +#define LIS3MDL_DATA_RATE_5_HZ 0x03 +#define LIS3MDL_DATA_RATE_10_HZ 0x04 +#define LIS3MDL_DATA_RATE_20_HZ 0x05 +#define LIS3MDL_DATA_RATE_40_HZ 0x06 +#define LIS3MDL_DATA_RATE_80_HZ 0x07 + +// --- Table 25: Full-scale selection --- +#define LIS3MDL_FULL_SCALE_4_G 0x00 +#define LIS3MDL_FULL_SCALE_8_G 0x01 +#define LIS3MDL_FULL_SCALE_12_G 0x02 +#define LIS3MDL_FULL_SCALE_16_G 0x03 + +// --- Table 28: System operating mode selection --- +#define LIS3MDL_MD_CC_MODE 0x00 // Continuous-conversion mode +#define LIS3MDL_MD_SC_MODE 0x01 // Single-conversion mode (only valid for 0.625Hz to 80Hz) +#define LIS3MDL_MD_PD_MODE 0x02 // Power-down mode + +// --- Table 31: Z-axis operating mode selection --- +#define LIS3MDL_OMZ_LP_MODE 0x00 // Low-power mode +#define LIS3MDL_OMZ_MP_MODE 0x01 // Medium-performance mode +#define LIS3MDL_OMZ_HP_MODE 0x02 // High-performance mode +#define LIS3MDL_OMZ_UHP_MODE 0x03 // Ultra-high-performance mode + +class LIS3MDL { + public: + LIS3MDL(); + LIS3MDL(uint8_t address); + + bool initialize(); + bool testConnection(); +- + // WHO_AM_I register, read-only + uint8_t getDeviceID(); + + // CONTROL1 register, r/w + void enableTempSensor(bool en); + bool getTempSensorEnable(); + void setXYOpMode(uint8_t mode); + uint8_t getXYOpMode(); + void setDataRate(uint8_t rate); + uint8_t getDataRate(); + void enableFastODR(bool en); + bool getFastODREnable(); + void enableSelfTest(bool en); + bool getSelfTestEnable(); + + // CONTROL2 register, r/w + void setFullScale(uint8_t scale); + uint8_t getFullScale(); + void reboot(); + void reset(); + + // CONTROL3 register, r/w + void enableLowPowerMode(bool en); + bool getLowPowerModeEnable(); + void setSPIInterfaceMode(bool mode); + bool getSPIInterfaceMode(); + void setOperatingMode(uint8_t mode); + uint8_t getOperatingMode(); + + // CONTROL4 register, r/w + void setZOpMode(uint8_t mode); + uint8_t getZOpMode(); + void setEndianness(bool endianness); + bool getEndianness(); + + // CONTROL5 register, r/w + void enableFastRead(bool en); + bool getFastReadEnable(); + void enableBlockDataUpdate(bool en); + bool getBlockDataUpdateEnable(); + + // STATUS register, read-only + uint8_t getStatus(); + bool getXYZDataOverrun(); + bool getZDataOverrun(); + bool getYDataOverrun(); + bool getXDataOverrun(); + bool getXYZDataAvailable(); + bool getZDataAvailable(); + bool getYDataAvailable(); + bool getXDataAvailable(); + + // OUT_* register, read-only + void getMagneticField(uint16_t* x, uint16_t* y, uint16_t* z); + uint16_t getMagneticFieldX(); + uint16_t getMagneticFieldY(); + uint16_t getMagneticFieldZ(); + uint16_t getTemperature(); + + // INT_CFG register, r/w + void enableInterruptGenerationX(bool en); + bool getInterruptGenerationEnableX(); + void enableInterruptGenerationY(bool en); + bool getInterruptGenerationEnableY(); + void enableInterruptGenerationZ(bool en); + bool getInterruptGenerationEnableZ(); + void setActiveHighInt(bool high); + bool getActiveHighInt(); + void enableLatchIntRequest(bool en); + bool getLatchIntRequestEnable(); + void enableInterrupt(bool en); + bool getInterruptEnable(); + + // INT_SRC register, r/w + void setIntThresholdSide(bool sidedness); + bool getIntThresholdSize(); + void setIntThresholdSideX(bool sidedness); + bool getIntThresholdSizeX(); + void setIntThresholdSideY(bool sidedness); + bool getIntThresholdSizeY(); + void setIntThresholdSideZ(bool sidedness); + bool getIntThresholdSizeZ(); + bool getInternalMeasurementRangeOverflow(); + bool getInterruptTriggered(); + + // INT_THS registers, r/w + void setInterruptThreshold(uint16t ths); + uint16_t getInterruptThreshold(); + + private: + uint8_t devAddr; + uint8_t buffer[6]; + uint8_t status; +}; + +#endif /* LIS3MDL_H */ \ No newline at end of file diff --git a/Arduino/LIS3MDL/examples/L3G4200D_raw/L3G4200D_raw.ino b/Arduino/LIS3MDL/examples/L3G4200D_raw/L3G4200D_raw.ino new file mode 100644 index 00000000..02022822 --- /dev/null +++ b/Arduino/LIS3MDL/examples/L3G4200D_raw/L3G4200D_raw.ino @@ -0,0 +1,88 @@ +// I2C device class (I2Cdev) demonstration Arduino sketch for L3G4200D class +// 7/31/2013 by Jonathan Arnett +// Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib +// +// Changelog: +// 2011-07-31 - initial release + +/* ============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2011 Jonathan Arnett, Jeff Rowberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +=============================================== +*/ + +// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation +// is used in I2Cdev.h +#include + +// I2Cdev and L3G4200D must be installed as libraries, or else the .cpp/.h files +// for both classes must be in the include path of your project +#include +#include + +// default address is 105 +// specific I2C address may be passed here +L3G4200D gyro; + +int16_t avx, avy, avz; + +#define LED_PIN 13 // (Arduino is 13, Teensy is 6) +bool blinkState = false; + +void setup() { + // join I2C bus (I2Cdev library doesn't do this automatically) + Wire.begin(); + + // initialize serial communication + // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but + // it's really up to you depending on your project) + Serial.begin(9600); + + // initialize device + Serial.println("Initializing I2C devices..."); + gyro.initialize(); + + // verify connection + Serial.println("Testing device connections..."); + Serial.println(gyro.testConnection() ? "L3G4200D connection successful" : "L3G4200D connection failed"); + + // configure LED for output + pinMode(LED_PIN, OUTPUT); + + // data seems to be best when full scale is 2000 + gyro.setFullScale(2000); +} + +void loop() { + // read raw angular velocity measurements from device + gyro.getAngularVelocity(&avx, &avy, &avz); + + Serial.print("angular velocity:\t"); + Serial.print(avx); Serial.print("\t"); + Serial.print(avy); Serial.print("\t"); + Serial.println(avz); + + // blink LED to indicate activity + blinkState = !blinkState; + digitalWrite(LED_PIN, blinkState); +} + + diff --git a/Arduino/LIS3MDL/library.json b/Arduino/LIS3MDL/library.json new file mode 100644 index 00000000..b52b6f94 --- /dev/null +++ b/Arduino/LIS3MDL/library.json @@ -0,0 +1,18 @@ +{ + "name": "I2Cdevlib-LIS3MDL", + "version": "1.0.0", + "keywords": "magnetometer, sensor, i2cdevlib, i2c", + "description": "The LIS3MDL is a low-power three-axis magnetometer able to provide unprecedented stablility of zero rate level and sensitivity over temperature and time", + "include": "Arduino/LIS3MDL", + "repository": + { + "type": "git", + "url": "https://github.com/jrowberg/i2cdevlib.git" + }, + "dependencies": + { + "jrowberg/I2Cdevlib-Core": "*" + }, + "frameworks": "arduino", + "platforms": "*" +} From d751176571bc940b4e849fda2b00c96c47d0ea04 Mon Sep 17 00:00:00 2001 From: Braidan Date: Sun, 4 Dec 2022 21:40:46 -0500 Subject: [PATCH 2/3] Finished initial release --- Arduino/LIS3MDL/LIS3MDL.cpp | 582 ++++++++++++++++++++++++++++++------ Arduino/LIS3MDL/LIS3MDL.h | 34 +-- 2 files changed, 511 insertions(+), 105 deletions(-) diff --git a/Arduino/LIS3MDL/LIS3MDL.cpp b/Arduino/LIS3MDL/LIS3MDL.cpp index 72b9750b..e4904f3b 100644 --- a/Arduino/LIS3MDL/LIS3MDL.cpp +++ b/Arduino/LIS3MDL/LIS3MDL.cpp @@ -32,82 +32,6 @@ #include "LIS3MDL.h" -/* ============================================================================ - I2Cdev Class Quick Primer: - - The I2Cdev class provides simple methods for reading and writing from I2C - device registers without messing with the underlying TWI/I2C functions. You - just specify the device address, register address, and source or destination - data according to which action you are doing. Here is the list of relevant - function prototypes from the I2Cdev class (more info in the .cpp/.h files): - - static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); - static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); - - static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data); - static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data); - static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data); - static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data); - static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data); - static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data); - static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data); - static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data); - - Note that ALL OF THESE METHODS ARE STATIC. No I2Cdev object is needed; just - use the static class methods. - - Also note that the first two parameters of every one of these methods are - the same: "devAddr" and "regAddr". For every method, you need to specify the - target/slave address and the register address. - - If your device uses 8-bit registers, you will want to use the following: - readBit, readBits, readByte, readBytes - writeBit, writeBits, writeByte, writeBytes - - ...but if it uses 16-bit registers, you will want to use these instead: - readBitW, readBitsW, readWord, readWords - writeBitW, writeBitsW, writeWord, writeWords - - Here's a sample of how to use a few of the methods. Note that in each case, - the "buffer" variable is a uint8_t array or pointer, and the "value" variable - (in three of the write examples) is a uint8_t single byte. The multi-byte - write methods still require an array or pointer. - - READ 1 BIT FROM DEVICE 0x68, REGISTER 0x02, BIT POSITION 4 - bytesRead = I2Cdev::readBit(0x68, 0x02, 4, buffer); - - READ 3 BITS FROM DEVICE 0x68, REGISTER 0x02, BIT POSITION 4 - bytesRead = I2Cdev::readBits(0x68, 0x02, 4, 3, buffer); - - READ 1 BYTE FROM DEVICE 0x68, REGISTER 0x02 - bytesRead = I2Cdev::readByte(0x68, 0x02, buffer); - - READ 2 BYTES FROM DEVICE 0x68, REGISTER 0x02 (AND 0x03 FOR 2ND BYTE) - bytesRead = I2Cdev::readBytes(0x68, 0x02, 2, buffer); - - WRITE 1 BIT TO DEVICE 0x68, REGISTER 0x02, BIT POSITION 4 - status = I2Cdev::writeBit(0x68, 0x02, 4, value); - - WRITE 3 BITS TO DEVICE 0x68, REGISTER 0x02, BIT POSITION 4 - status = I2Cdev::writeBits(0x68, 0x02, 4, 3, value); - - WRITE 1 BYTE TO DEVICE 0x68, REGISTER 0x02 - status = I2Cdev::writeByte(0x68, 0x02, value); - - WRITE 2 BYTES TO DEVICE 0x68, REGISTER 0x02 (AND 0x03 FOR 2ND BYTE) - status = I2Cdev::writeBytes(0x68, 0x02, 2, buffer); - - The word-based methods are exactly the same, except they use 16-bit variables - instead of 8-bit ones. - - ============================================================================ */ - /** * @brief Default constructor, uses default I2C address. * @see LIS3MDL_DEFAULT_ADDRESS @@ -135,14 +59,12 @@ LIS3MDL::LIS3MDL(uint8_t address) { * @see LIS3MDL_RA_CTRL5 */ bool LIS3MDL::initialize() { - bool _success = false; - _success = _success && testConnection(); - _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL1, 0b00010000) && - _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL2, 0b00000000) && - _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL3, 0b00000011) && - _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL4, 0b00000000) && - _success = _success && I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL5, 0b00000000); - return _success; + return testConnection() && + I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL1, 0b00010000) && + I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL2, 0b00000000) && + I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL3, 0b00000011) && + I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL4, 0b00000000) && + I2Cdev::writeByte(devAddr, LIS3MDL_RA_CTRL5, 0b00000000); } /** @@ -333,7 +255,7 @@ void LIS3MDL::setFullScale(uint8_t scale) { * @see LIS3MDL_FULL_SCALE_12_G * @see LIS3MDL_FULL_SCALE_16_G */ -uint8_t LIS3MDL::getDataRate() { +uint8_t LIS3MDL::getFullScale() { I2Cdev::readBits(devAddr, LIS3MDL_RA_CTRL2, LIS3MDL_FS_BIT, LIS3MDL_FS_LENGTH, buffer); return buffer[0]; } @@ -396,7 +318,7 @@ void LIS3MDL::setSPIInterfaceMode(bool mode) { * @return the current mode (0x00-01). False (0) is the 4-wire interface * True (1) is the 3-wire interface */ -uint8_t LIS3MDL::getSPIInterfaceMode() { +bool LIS3MDL::getSPIInterfaceMode() { I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL3, LIS3MDL_SIM_BIT, buffer); return buffer[0]; } @@ -479,7 +401,7 @@ void LIS3MDL::setEndianness(bool endianness) { * @return the current endianness (0x00-01). False (0) is LSB first * True (1) is MSB first */ -uint8_t LIS3MDL::getEndianness() { +bool LIS3MDL::getEndianness() { I2Cdev::readBit(devAddr, LIS3MDL_RA_CTRL4, LIS3MDL_BLE_BIT, buffer); return buffer[0]; } @@ -533,4 +455,488 @@ bool LIS3MDL::getBlockDataUpdateEnable() { // =================================== // === STATUS REGISTERS, READ-ONLY === -// =================================== \ No newline at end of file +// =================================== + + +/** + * @brief Return the entire status register of the device + * + * @return The entire status register + * @see LIS3MDL_ZYXOR_BIT + * @see LIS3MDL_ZOR_BIT + * @see LIS3MDL_YOR_BIT + * @see LIS3MDL_XOR_BIT + * @see LIS3MDL_ZYXDA_BIT + * @see LIS3MDL_ZDA_BIT + * @see LIS3MDL_YDA_BIT + * @see LIS3MDL_XDA_BIT + */ +uint8_t LIS3MDL::getStatus() { + I2Cdev::readByte(devAddr, LIS3MDL_RA_STATUS, buffer); + return buffer[0]; +} + +/** + * @brief Get the status of the X-, Y-, Z-axis overrun + * + * @return The status of the X-, Y-, Z-axis overrun + */ +bool LIS3MDL::getXYZDataOverrun() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_STATUS, LIS3MDL_ZYXOR_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Get the status of the Z-axis overrun + * + * @return The status of the Z-axis overrun + */ +bool LIS3MDL::getZDataOverrun() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_STATUS, LIS3MDL_ZOR_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Get the status of the Y-axis overrun + * + * @return The status of the Y-axis overrun + */ +bool LIS3MDL::getYDataOverrun() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_STATUS, LIS3MDL_YOR_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Get the status of the X-axis overrun + * + * @return The status of the X-axis overrun + */ +bool LIS3MDL::getXDataOverrun() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_STATUS, LIS3MDL_XOR_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Get the status of the X-, Y-, Z-axis data available + * + * @return The status of the X-, Y-, Z-axis data available + */ +bool LIS3MDL::getXYZDataAvailable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_STATUS, LIS3MDL_ZYXDA_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Get the status of the Z-axis data available + * + * @return The status of the Z-axis data available + */ +bool LIS3MDL::getZDataAvailable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_STATUS, LIS3MDL_ZDA_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Get the status of the Y-axis data available + * + * @return The status of the Y-axis data available + */ +bool LIS3MDL::getYDataAvailable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_STATUS, LIS3MDL_YDA_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Get the status of the X-axis data available + * + * @return The status of the X-axis data available + */ +bool LIS3MDL::getXDataAvailable() { + I2Cdev::readBit(devAddr, LIS3MDL_RA_STATUS, LIS3MDL_XDA_BIT, buffer); + return buffer[0]; +} + + +// ================================== +// === OUT_* REGISTERS, READ-ONLY === +// ================================== + + +/** + * @brief Get the total magnetic field detected by the device + * + * @param x X-axis reading in mGauss + * @param y Y-axis reading in mGauss + * @param z Z-axis reading in mGauss + */ +void LIS3MDL::getMagneticField(uint16_t* x, uint16_t* y, uint16_t* z) { + *x = getMagneticFieldX(); + *y = getMagneticFieldY(); + *z = getMagneticFieldZ(); +} + +/** + * @brief Get the magnetic field for the X-axis + * + * @return The strength of the magnetic field in the X-axis in mGauss + */ +uint16_t LIS3MDL::getMagneticFieldX() { + I2Cdev::readBytes(devAddr, LIS3MDL_OUT_X_L, 2, buffer); + uint16_t _val = !getEndianness() ? (((int16_t) buffer[1]) << 8) | buffer[0] : (((int16_t) buffer[0]) << 8) | buffer[1]; + + switch (getFullScale()) { + case LIS3MDL_FULL_SCALE_4_G: + return _val / 6842 * 1000; + case LIS3MDL_FULL_SCALE_8_G: + return _val / 3421 * 1000; + case LIS3MDL_FULL_SCALE_12_G: + return _val / 2281 * 1000; + case LIS3MDL_FULL_SCALE_16_G: + return _val / 1711 * 1000; + default: + return _val / 6842 * 1000; + }; +} + +/** + * @brief Get the magnetic field for the Y-axis + * + * @return The strength of the magnetic field in the Y-axis in mGauss + */ +uint16_t LIS3MDL::getMagneticFieldY() { + I2Cdev::readBytes(devAddr, LIS3MDL_OUT_Y_L, 2, buffer); + uint16_t _val = !getEndianness() ? (((int16_t) buffer[1]) << 8) | buffer[0] : (((int16_t) buffer[0]) << 8) | buffer[1]; + + switch (getFullScale()) { + case LIS3MDL_FULL_SCALE_4_G: + return _val / 6842 * 1000; + case LIS3MDL_FULL_SCALE_8_G: + return _val / 3421 * 1000; + case LIS3MDL_FULL_SCALE_12_G: + return _val / 2281 * 1000; + case LIS3MDL_FULL_SCALE_16_G: + return _val / 1711 * 1000; + default: + return _val / 6842 * 1000; + }; +} + +/** + * @brief Get the magnetic field for the Z-axis + * + * @return The strength of the magnetic field in the Z-axis in mGauss + */ +uint16_t LIS3MDL::getMagneticFieldZ() { + I2Cdev::readBytes(devAddr, LIS3MDL_OUT_Z_L, 2, buffer); + uint16_t _val = !getEndianness() ? (((int16_t) buffer[1]) << 8) | buffer[0] : (((int16_t) buffer[0]) << 8) | buffer[1]; + + switch (getFullScale()) { + case LIS3MDL_FULL_SCALE_4_G: + return _val / 6842 * 1000; + case LIS3MDL_FULL_SCALE_8_G: + return _val / 3421 * 1000; + case LIS3MDL_FULL_SCALE_12_G: + return _val / 2281 * 1000; + case LIS3MDL_FULL_SCALE_16_G: + return _val / 1711 * 1000; + default: + return _val / 6842 * 1000; + }; +} + +/** + * @brief Get the device temperature as reported by the internal sensor + * + * @return The temperature in degrees celsius + */ +uint16_t LIS3MDL::getTemperature() { + I2Cdev::readBytes(devAddr, LIS3MDL_OUT_TEMP_OUT_L, 2, buffer); + uint16_t _val = !getEndianness() ? (((int16_t) buffer[1]) << 8) | buffer[0] : (((int16_t) buffer[0]) << 8) | buffer[1]; + + return _val / 8; +} + + +// ============================= +// === INT_CFG REGISTER, R/W === +// ============================= + + +/** + * @brief Enable interrupt generation for the X-axis on the device + * + * @param en Interrupt generation for the X-axis enabled + */ +void LIS3MDL::enableInterruptGenerationX(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_XIEN_BIT, en); +} + +/** + * @brief Get if interrupt generation for the X-axis is enabled on the device + * + * @return true if interrupt generation for the X-axis is enabled, + * @return false otherwise + */ +bool LIS3MDL::getInterruptGenerationEnableX() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_YIEN_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Enable interrupt generation for the Y-axis on the device + * + * @param en Interrupt generation for the Y-axis enabled + */ +void LIS3MDL::enableInterruptGenerationY(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_YIEN_BIT, en); +} + +/** + * @brief Get if interrupt generation for the Y-axis is enabled on the device + * + * @return true if interrupt generation for the Y-axis is enabled, + * @return false otherwise + */ +bool LIS3MDL::getInterruptGenerationEnableY() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_YIEN_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Enable interrupt generation for the Z-axis on the device + * + * @param en Interrupt generation for the Z-axis enabled + */ +void LIS3MDL::enableInterruptGenerationZ(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_ZIEN_BIT, en); +} + +/** + * @brief Get if interrupt generation for the Z-axis is enabled on the device + * + * @return true if interrupt generation for the Z-axis is enabled, + * @return false otherwise + */ +bool LIS3MDL::getInterruptGenerationEnableZ() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_ZIEN_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Sets the interrupt generation mode + * + * @param mode if the interrupt generation is active HIGH (true) or LOW (false) + */ +void LIS3MDL::setInterruptMode(bool mode) { + I2Cdev::writeBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_IEA_BIT, mode); +} + +/** + * @brief Returns the current interrupt generation mode + * + * @return the current interrupt generation mode (0x00-01). False (0) is active LOW + * True (1) is active HIGH + */ +bool LIS3MDL::getInterruptMode() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_IEA_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Enable interrupt latching. + * When enabled, the interrupt can only be cleared by reading the INT_SRC register + * + * @param en Interrupt latching enabled + */ +void LIS3MDL::enableLatchIntRequest(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_LIR_BIT, en); +} + +/** + * @brief Get if interrupt latch is enabled + * + * @return true if interrupt latch is enabled, + * @return false otherwise + */ +bool LIS3MDL::getLatchIntRequestEnable() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_LIR_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Enable interrupt. + * + * @param en Interrupt enabled + */ +void LIS3MDL::enableInterrupt(bool en) { + I2Cdev::writeBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_IEN_BIT, en); +} + +/** + * @brief Get if interrupt is enabled + * + * @return true if interrupt is enabled, + * @return false otherwise + */ +bool LIS3MDL::getInterruptEnable() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_CFG, LIS3MDL_IEN_BIT, buffer); + return buffer[0]; +} + + +// ============================= +// === INT_SRC REGISTER, R/W === +// ============================= + +/** + * @brief Returns the INT_SRC register to clear the interrupt + * This function only needs to be called when the interrupt latch is enabled and the interrupt has been triggered + * By reading the INT_SRC register, the interrupt will reset + * + * @return the INT_SRC register + */ +uint8_t LIS3MDL::clearInt() { + I2Cdev::readByte(devAddr, LIS3MDL_INT_SRC, buffer); + return buffer[0]; +} + +/** + * @brief Returns if the value of the X-, Y-, and Z-axis values is positive relative to the interrupt threshold + * True indicates the value is on the positive side. + * False indicates the value has not exceed the interrupt threshold or that it is on the negative side + */ +void LIS3MDL::getPosIntThreshold(bool *x, bool *y, bool *z) { + *x = getPosIntThresholdX(); + *y = getPosIntThresholdY(); + *z = getPosIntThresholdZ(); +} + +/** + * @brief Returns if the value of the X-axis relative to its interrupt threshold is positive. + * + * @return if the X-axis value is positive of its interrupt threshold. + * @return True indicates the value is on the positive side. + * @return False indicates the value has not exceed the interrupt threshold or that it is on the negative side + */ +bool LIS3MDL::getPosIntThresholdX() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_SRC, LIS3MDL_PTH_X_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Returns if the value of the Y-axis relative to its interrupt threshold is positive. + * + * @return if the Y-axis value is positive of its interrupt threshold. + * @return True indicates the value is on the positive side. + * @return False indicates the value has not exceed the interrupt threshold or that it is on the negative side + */ +bool LIS3MDL::getPosIntThresholdY() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_SRC, LIS3MDL_PTH_Y_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Returns if the value of the Z-axis relative to its interrupt threshold is positive. + * + * @return if the Z-axis value is positive of its interrupt threshold. + * @return True indicates the value is on the positive side. + * @return False indicates the value has not exceed the interrupt threshold or that it is on the negative side + */ +bool LIS3MDL::getPosIntThresholdZ() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_SRC, LIS3MDL_PTH_Z_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Returns if the value of the X-, Y-, and Z-axis values is negative relative to the interrupt threshold + * True indicates the value is on the negative side. + * False indicates the value has not exceed the interrupt threshold or that it is on the positive side + */ +void LIS3MDL::getNegIntThreshold(bool *x, bool *y, bool *z) { + *x = getPosIntThresholdX(); + *y = getPosIntThresholdY(); + *z = getPosIntThresholdZ(); +} + +/** + * @brief Returns if the value of the X-axis relative to its interrupt threshold is negative. + * + * @return if the X-axis value is negative of its interrupt threshold. + * @return True indicates the value is on the negative side. + * @return False indicates the value has not exceed the interrupt threshold or that it is on the positive side + */ +bool LIS3MDL::getNegIntThresholdX() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_SRC, LIS3MDL_NTH_X_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Returns if the value of the Y-axis relative to its interrupt threshold is negative. + * + * @return if the Y-axis value is negative of its interrupt threshold. + * @return True indicates the value is on the negative side. + * @return False indicates the value has not exceed the interrupt threshold or that it is on the positive side + */ +bool LIS3MDL::getNegIntThresholdY() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_SRC, LIS3MDL_NTH_Y_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Returns if the value of the Z-axis relative to its interrupt threshold is negative. + * + * @return if the Z-axis value is negative of its interrupt threshold. + * @return True indicates the value is on the negative side. + * @return False indicates the value has not exceed the interrupt threshold or that it is on the positive side + */ +bool LIS3MDL::getNegIntThresholdZ() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_SRC, LIS3MDL_NTH_Z_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Returns if the internal measurement range has overflowed on the magnetic field reading. + * + * @return True if the internal measurement range has overflowed, + * @return false otherwise + */ +bool LIS3MDL::getInternalMeasurementRangeOverflow() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_SRC, LIS3MDL_MROI_BIT, buffer); + return buffer[0]; +} + +/** + * @brief Returns if the interrupt has been triggered + * + * @return True if the interrupt has been triggered, + * @return false otherwise + */ +bool LIS3MDL::getInterruptTriggered() { + I2Cdev::readBit(devAddr, LIS3MDL_INT_SRC, LIS3MDL_INT_BIT, buffer); + return buffer[0]; +} + + +// ============================== +// === INT_THS REGISTERS, R/W === +// ============================== + + +/** + * @brief Set the interrupt threshold value for the device + * + * @param ths The threshold at which the interrupt will be triggered, in Gauss + */ +void LIS3MDL::setInterruptThreshold(uint16_t ths) { + I2Cdev::writeBytes(devAddr, LIS3MDL_OUT_THS_L, 2, ths); +} + +/** + * @brief Return the interrupt threshold value for the device + * + * @return the interrupt threshold value, in Gauss + */ +uint16_t LIS3MDL::getInterruptThreshold() { + I2Cdev::readBytes(devAddr, LIS3MDL_OUT_THS_L, 2, buffer); + return (((int16_t) buffer[1]) << 8) | buffer[0]; +} \ No newline at end of file diff --git a/Arduino/LIS3MDL/LIS3MDL.h b/Arduino/LIS3MDL/LIS3MDL.h index 87ba1f87..bccce450 100644 --- a/Arduino/LIS3MDL/LIS3MDL.h +++ b/Arduino/LIS3MDL/LIS3MDL.h @@ -130,9 +130,9 @@ #define LIS3MDL_PTH_X_BIT 7 #define LIS3MDL_PTH_Y_BIT 6 #define LIS3MDL_PTH_Z_BIT 5 -#define LIS3MDL_PTH_X_BIT 4 -#define LIS3MDL_PTH_Y_BIT 3 -#define LIS3MDL_PTH_Z_BIT 2 +#define LIS3MDL_NTH_X_BIT 4 +#define LIS3MDL_NTH_Y_BIT 3 +#define LIS3MDL_NTH_Z_BIT 2 #define LIS3MDL_MROI_BIT 1 #define LIS3MDL_INT_BIT 0 @@ -175,7 +175,7 @@ // --- Table 28: System operating mode selection --- #define LIS3MDL_MD_CC_MODE 0x00 // Continuous-conversion mode #define LIS3MDL_MD_SC_MODE 0x01 // Single-conversion mode (only valid for 0.625Hz to 80Hz) -#define LIS3MDL_MD_PD_MODE 0x02 // Power-down mode +#define LIS3MDL_MD_PD_MODE 0x02 // Power-down mode // --- Table 31: Z-axis operating mode selection --- #define LIS3MDL_OMZ_LP_MODE 0x00 // Low-power mode @@ -190,7 +190,7 @@ class LIS3MDL { bool initialize(); bool testConnection(); -- + // WHO_AM_I register, read-only uint8_t getDeviceID(); @@ -257,33 +257,33 @@ class LIS3MDL { bool getInterruptGenerationEnableY(); void enableInterruptGenerationZ(bool en); bool getInterruptGenerationEnableZ(); - void setActiveHighInt(bool high); - bool getActiveHighInt(); + void setInterruptMode(bool mode); + bool getInterruptMode(); void enableLatchIntRequest(bool en); bool getLatchIntRequestEnable(); void enableInterrupt(bool en); bool getInterruptEnable(); // INT_SRC register, r/w - void setIntThresholdSide(bool sidedness); - bool getIntThresholdSize(); - void setIntThresholdSideX(bool sidedness); - bool getIntThresholdSizeX(); - void setIntThresholdSideY(bool sidedness); - bool getIntThresholdSizeY(); - void setIntThresholdSideZ(bool sidedness); - bool getIntThresholdSizeZ(); + uint8_t clearInt(); + bool getPosIntThreshold(bool *x, bool *y, bool *z); + bool getPosIntThresholdX(); + bool getPosIntThresholdY(); + bool getPosIntThresholdZ(); + bool getNegIntThreshold(bool *x, bool *y, bool *z); + bool getNegIntThresholdX(); + bool getNegIntThresholdY(); + bool getNegIntThresholdZ(); bool getInternalMeasurementRangeOverflow(); bool getInterruptTriggered(); // INT_THS registers, r/w - void setInterruptThreshold(uint16t ths); + void setInterruptThreshold(uint16_t ths); uint16_t getInterruptThreshold(); private: uint8_t devAddr; uint8_t buffer[6]; - uint8_t status; }; #endif /* LIS3MDL_H */ \ No newline at end of file From dca7467d24fc307449230e3dbd3835a8adb76f3f Mon Sep 17 00:00:00 2001 From: Braidan Date: Sun, 4 Dec 2022 22:20:10 -0500 Subject: [PATCH 3/3] Added LIS3MDL arduino example --- .../LIS3MDL_raw.ino} | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) rename Arduino/LIS3MDL/examples/{L3G4200D_raw/L3G4200D_raw.ino => LIS3MDL_raw/LIS3MDL_raw.ino} (66%) diff --git a/Arduino/LIS3MDL/examples/L3G4200D_raw/L3G4200D_raw.ino b/Arduino/LIS3MDL/examples/LIS3MDL_raw/LIS3MDL_raw.ino similarity index 66% rename from Arduino/LIS3MDL/examples/L3G4200D_raw/L3G4200D_raw.ino rename to Arduino/LIS3MDL/examples/LIS3MDL_raw/LIS3MDL_raw.ino index 02022822..5f6b7571 100644 --- a/Arduino/LIS3MDL/examples/L3G4200D_raw/L3G4200D_raw.ino +++ b/Arduino/LIS3MDL/examples/LIS3MDL_raw/LIS3MDL_raw.ino @@ -36,53 +36,56 @@ THE SOFTWARE. // I2Cdev and L3G4200D must be installed as libraries, or else the .cpp/.h files // for both classes must be in the include path of your project #include -#include +#include // default address is 105 // specific I2C address may be passed here -L3G4200D gyro; +LIS3MDL mag; -int16_t avx, avy, avz; +int16_t magx, magy, magz; -#define LED_PIN 13 // (Arduino is 13, Teensy is 6) bool blinkState = false; void setup() { - // join I2C bus (I2Cdev library doesn't do this automatically) - Wire.begin(); + Wire.begin(SDA, SCL); - // initialize serial communication - // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but - // it's really up to you depending on your project) Serial.begin(9600); // initialize device - Serial.println("Initializing I2C devices..."); - gyro.initialize(); + Serial.print("Initializing I2C devices..."); + if (!mag.initialize()) { + Serial.println("Failed to find LIS3MDL!"); + } + Serial.println("done!"); // verify connection - Serial.println("Testing device connections..."); - Serial.println(gyro.testConnection() ? "L3G4200D connection successful" : "L3G4200D connection failed"); + Serial.print("Testing device connections..."); + Serial.println(mag.testConnection() ? "L3G4200D connection successful" : "L3G4200D connection failed"); // configure LED for output - pinMode(LED_PIN, OUTPUT); + pinMode(LED_BUILTIN, OUTPUT); - // data seems to be best when full scale is 2000 - gyro.setFullScale(2000); + // Configure device + mag.setXYOpMode(LIS3MDL_OM_HP_MODE); // Set XY-axes to High Performance mode + mag.setZOpMode(LIS3MDL_OMZ_HP_MODE); // Set Z-axis to High Performance mode + mag.setDataRate(LIS3MDL_DATA_RATE_80_HZ); // Set sample rate to 80 Hz + mag.setFullScale(LIS3MDL_FULL_SCALE_4_G); // Set scale to ±4 Gauss } void loop() { // read raw angular velocity measurements from device - gyro.getAngularVelocity(&avx, &avy, &avz); + mag.getMagneticField(&magx, &magy, &magz); - Serial.print("angular velocity:\t"); - Serial.print(avx); Serial.print("\t"); - Serial.print(avy); Serial.print("\t"); - Serial.println(avz); + Serial.print("Magnetic field \t"); + Serial.print("X: "); Serial.print(magx); Serial.print("\t"); + Serial.print("Y: "); Serial.print(magy); Serial.print("\t"); + Serial.print("Z: "); Serial.println(magz); // blink LED to indicate activity blinkState = !blinkState; digitalWrite(LED_PIN, blinkState); + + delay(1000/80); // Try to run every 1/80 of a second }