diff --git a/libraries/MSA300/examples/msa_basic/msa_basic.ino b/libraries/MSA300/examples/msa_basic/msa_basic.ino new file mode 100644 index 0000000..ae85518 --- /dev/null +++ b/libraries/MSA300/examples/msa_basic/msa_basic.ino @@ -0,0 +1,27 @@ +#include +#include + +acc_t data; +MSA300 msa; + +void setup() { + // put your setup code here, to run once: + Serial.begin(9600); + Wire.begin(); + msa.begin(); + + + +} + +void loop() { + // put your main code here, to run repeatedly: + data = msa.getAcceleration(); + Serial.print("Xa:"); + Serial.print(data.x); + Serial.print("Ya:"); + Serial.print(data.y); + Serial.print("Za:"); + Serial.println(data.z); + delay(100); +} diff --git a/libraries/MSA300/keywords.txt b/libraries/MSA300/keywords.txt new file mode 100644 index 0000000..3dde7d5 --- /dev/null +++ b/libraries/MSA300/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring Map For Wire +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### +acc_t KEYWORD1 +orient_t KEYWORD1 +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +getAcceleration KEYWORD2 +checkOrientation KEYWORD2 + + +####################################### +# Instances (KEYWORD2) +####################################### + +MSA300 KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/libraries/MSA300/library.properties b/libraries/MSA300/library.properties new file mode 100644 index 0000000..df0cbaa --- /dev/null +++ b/libraries/MSA300/library.properties @@ -0,0 +1,9 @@ +name=MSA300 +version=1.0.0 +author=Bigbits +maintainer=Bigbits +sentence= Arduino library for the MSA300 digital 14-bit accelerometer built into the Sipeed Maix Go. +paragraph=Arduino library for the MSA300 digital 14-bit accelerometer built into the Sipeed Maix Go. +category=Sensors +url=http://www.sipeed.com +architectures=k210 diff --git a/libraries/MSA300/src/MSA300.cpp b/libraries/MSA300/src/MSA300.cpp new file mode 100644 index 0000000..8d9ad8c --- /dev/null +++ b/libraries/MSA300/src/MSA300.cpp @@ -0,0 +1,939 @@ +/**************************************************************************/ +/*! + @file MSA300.cpp + + @mainpage MSA300 14-bit digital accelometer library + + @section author Author + Joel Lavikainen + + @section license License + BSD + + @section intro_sec Introduction + Based on Adafruit Accelerometer library code +*/ +/**************************************************************************/ + #include "Arduino.h" + +#include +#include + +#include "MSA300.h" + + + +/**************************************************************************/ +/*! + @brief Writes 8-bits to the specified destination register. + @param reg + Register address + @param value + Byte value to be written +*/ +/**************************************************************************/ +void MSA300::writeRegister(uint8_t reg, uint8_t value) +{ + Wire.beginTransmission(MSA300_I2C_ADDRESS); + Wire.write((uint8_t)reg); + Wire.write((uint8_t)(value)); + Wire.endTransmission(); +} + +/**************************************************************************/ +/*! + @brief Reads 8-bits from the specified register + @param reg + Address of register + @return Reply byte +*/ +/**************************************************************************/ +uint8_t MSA300::readRegister(uint8_t reg) +{ + Wire.beginTransmission(MSA300_I2C_ADDRESS); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(MSA300_I2C_ADDRESS, 1); + uint8_t data =Wire.read(); + return data; + +} + +/**************************************************************************/ +/*! + @brief Reads 16-bits from the specified register + @param reg + Register address + @return 16-bits of data +*/ +/**************************************************************************/ +int16_t MSA300::read16(uint8_t reg) +{ + Wire.beginTransmission(MSA300_I2C_ADDRESS); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(MSA300_I2C_ADDRESS, 2); + return (uint16_t)(((Wire.read()&0xff) | (uint16_t)(Wire.read()&0xff) << 8)); +} + +/**************************************************************************/ +/*! + @brief Read the part ID (can be used to check connection) + @return Part ID +*/ +/**************************************************************************/ +uint8_t MSA300::getPartID(void) +{ + // Check device ID register + return readRegister(MSA300_REG_PARTID); +} + +/**************************************************************************/ +/*! + @brief Gets the most recent X axis value + @return X-axis acceleration data +*/ +/**************************************************************************/ +int16_t MSA300::getX(void) +{ + int16_t temp = (readRegister(MSA300_REG_ACC_X_LSB) + (int8_t)(readRegister(MSA300_REG_ACC_X_MSB))*256)/4; + return temp; +} + +/**************************************************************************/ +/*! + @brief Gets the most recent Y axis value + @return Y-axis acceleration data +*/ +/**************************************************************************/ +int16_t MSA300::getY(void) +{ + int16_t temp = (readRegister(MSA300_REG_ACC_Y_LSB) + (int8_t)(readRegister(MSA300_REG_ACC_Y_MSB))*256)/4; + return temp; +} + +/**************************************************************************/ +/*! + @brief Gets the most recent Z axis value + @return Z-axis acceleration data +*/ +/**************************************************************************/ +int16_t MSA300::getZ(void) +{ + int16_t temp = (readRegister(MSA300_REG_ACC_Z_LSB) + (int8_t)(readRegister(MSA300_REG_ACC_Z_MSB))*256)/4; + return temp; +} + +/**************************************************************************/ +/*! + @brief Instantiates a new MSA300 class + @param sensorID + ID for identifying different sensors +*/ +/**************************************************************************/ +MSA300::MSA300(int32_t sensorID) +{ + _sensorID = sensorID; + _range = MSA300_RANGE_2_G; +} + + +/**************************************************************************/ +/*! + @brief Setups the HW (reads coefficients values, etc.) + @return True if connection was established, False if no MSA300 was + detected +*/ +/**************************************************************************/ +bool MSA300::begin() +{ + Wire.begin(); + + /* Check connection */ + uint8_t partid = getPartID(); + if (partid != 0x13) + { + /* No MSA300 detected ... return false */ + Serial.println(partid, HEX); + return false; + } + _multiplier = MSA300_MG2G_MULTIPLIER_4_G; + // Enable measurements + writeRegister(MSA300_REG_PWR_MODE_BW, 0x10); // Normal mode & 500 Hz Bandwidth + writeRegister(MSA300_REG_ODR, MSA300_DATARATE_1000_HZ); // Set Output Data Rate to 1000 Hz + writeRegister(MSA300_REG_RES_RANGE, 0x01); + writeRegister(MSA300_REG_INT_SET_0,0x00); + writeRegister(MSA300_REG_INT_SET_1,0x00); + writeRegister(MSA300_REG_INT_LATCH,0x81); + return true; +} + +/**************************************************************************/ +/*! + @brief Sets the g range for the accelerometer + @param range + Measurement range +*/ +/**************************************************************************/ +void MSA300::setRange(range_t range) +{ + /* Read the data format register to preserve bits */ + uint8_t format = readRegister(MSA300_REG_RES_RANGE); + + /* Update the range */ + format &= ~0x3; // clear range bits + format |= range; + + + /* Write the register back to the IC */ + writeRegister(MSA300_REG_RES_RANGE, format); + + /* Keep track of the current range (to avoid readbacks) */ + _range = range; + + /* Map correct conversion multiplier */ + switch(range) { + case MSA300_RANGE_16_G: + _multiplier = MSA300_MG2G_MULTIPLIER_16_G; + break; + case MSA300_RANGE_8_G: + _multiplier = MSA300_MG2G_MULTIPLIER_8_G; + break; + case MSA300_RANGE_4_G: + _multiplier = MSA300_MG2G_MULTIPLIER_4_G; + break; + case MSA300_RANGE_2_G: + _multiplier = MSA300_MG2G_MULTIPLIER_2_G; + } +} + +/**************************************************************************/ +/*! + @brief Get the g range for the accelerometer + @return Measurement range +*/ +/**************************************************************************/ +range_t MSA300::getRange(void) +{ + return (range_t)(readRegister(MSA300_REG_RES_RANGE) & 0x3); +} + +/**************************************************************************/ +/*! + @brief Sets the resolution for the accelerometer + @param resolution + Measurement resolution +*/ +/**************************************************************************/ +void MSA300::setResolution(res_t resolution) +{ + /* Read the data format register to preserve bits */ + uint8_t format = readRegister(MSA300_REG_RES_RANGE); + + /* Update the resolution */ + format &= ~0xC; // clear resolution bits + format |= resolution; + + + /* Write the register back to the IC */ + writeRegister(MSA300_REG_RES_RANGE, format); + + /* Keep track of the current resolution (to avoid readbacks) */ + _res = resolution; +} + +/**************************************************************************/ +/*! + @brief Get resolution for the accelerometer + @return Measurement resolution +*/ +/**************************************************************************/ +res_t MSA300::getResolution(void) +{ + return (res_t)(readRegister(MSA300_REG_RES_RANGE) & 0xC); +} + + +/**************************************************************************/ +/*! + @brief Sets the data rate for the MSA300 (controls power consumption) + @param dataRate + Output data rate +*/ +/**************************************************************************/ +void MSA300::setDataRate(dataRate_t dataRate) +{ + /* Note: The LOW_POWER bits are currently ignored and we always keep + the device in 'normal' mode */ + writeRegister(MSA300_REG_ODR, dataRate); +} + +/**************************************************************************/ +/*! + @brief Get the data rate for the MSA300 + @return Output data rate +*/ +/**************************************************************************/ +dataRate_t MSA300::getDataRate(void) +{ + return (dataRate_t)(readRegister(MSA300_REG_ODR) & 0x0F); +} + +/**************************************************************************/ +/*! + @brief Sets the operating mode for MSA300 + @param mode + Power mode +*/ +/**************************************************************************/ +void MSA300::setMode(pwrMode_t mode) +{ + /* Read the data format register to preserve bits */ + uint8_t format = readRegister(MSA300_REG_PWR_MODE_BW); + + /* Update the mode */ + format &= ~0xC0; // clear the mode bits + format |= mode; + + + /* Write the register back to the IC */ + writeRegister(MSA300_REG_PWR_MODE_BW, format); + + /* Keep track of the current mode (to avoid readbacks) */ + _mode = mode; +} + +/**************************************************************************/ +/*! + @brief Gets the data rate for the MSA300 + @return Power mode +*/ +/**************************************************************************/ +pwrMode_t MSA300::getMode(void) +{ + return (pwrMode_t)(readRegister(MSA300_REG_PWR_MODE_BW) & 0xC0); +} + +/**************************************************************************/ +/*! + @brief Reset all latched interrupts +*/ +/**************************************************************************/ +void MSA300::resetInterrupt(void) +{ + /* Read register to preserve bits */ + uint8_t reg = readRegister(MSA300_REG_INT_LATCH); + + /* Turn RESET_INT bit on */ + reg |= (1 << 7); + + /* Write the register back to the IC */ + writeRegister(MSA300_REG_INT_LATCH, reg); +} + +/**************************************************************************/ +/*! + @brief Clear all interrupt registers by setting them to default state. +*/ +/**************************************************************************/ +void MSA300::clearInterrupts(void) +{ + writeRegister(MSA300_REG_INT_SET_0, 0x00); + writeRegister(MSA300_REG_INT_SET_1, 0x00); + writeRegister(MSA300_REG_INT_MAP_0, 0x00); + writeRegister(MSA300_REG_INT_MAP_2_1, 0x00); + writeRegister(MSA300_REG_INT_MAP_2_2, 0x00); +} + +/**************************************************************************/ +/*! + @brief Check interrupt registers for all occured interrupts. Return + struct with booleans of all triggered interrupts. + @return Struct containing boolean status of interrupts. +*/ +/**************************************************************************/ +interrupt_t MSA300::checkInterrupts(void) +{ + interrupt_t interrupts; + uint8_t motionReg = readRegister(MSA300_REG_MOTION_INT); + uint8_t dataReg = readRegister(MSA300_REG_DATA_INT); + uint8_t tapReg = readRegister(MSA300_REG_TAP_ACTIVE_STATUS); + + interrupts.orientInt = (motionReg >> 6) & 1; + interrupts.sTapInt = (motionReg >> 5) & 1; + interrupts.dTapInt = (motionReg >> 4) & 1; + interrupts.activeInt = (motionReg >> 2) & 1; + interrupts.freefallInt = (motionReg >> 0) & 1; + interrupts.newDataInt = (dataReg >> 0) & 1; + + /* If there was active or tap interrupts, populate intStatus struct */ + if(interrupts.activeInt || interrupts.sTapInt || interrupts.dTapInt) { + interrupts.intStatus.tapSign = (tapReg >> 7) & 1; + interrupts.intStatus.tapFirstX = (tapReg >> 6) & 1; + interrupts.intStatus.tapFirstY = (tapReg >> 5) & 1; + interrupts.intStatus.tapFirstZ = (tapReg >> 4) & 1; + interrupts.intStatus.activeSign = (tapReg >> 3) & 1; + interrupts.intStatus.activeFirstX = (tapReg >> 2) & 1; + interrupts.intStatus.activeFirstY = (tapReg >> 1) & 1; + interrupts.intStatus.activeFirstZ = (tapReg >> 0) & 1; + } + + return interrupts; +} +/**************************************************************************/ +/*! + @brief Set interrupt latching mode + @param mode + Interrupt mode +*/ +/**************************************************************************/ +void MSA300::setInterruptLatch(intMode_t mode) +{ + /* Read register to preserve bits */ + uint8_t reg = readRegister(MSA300_REG_INT_LATCH); + + /* Update latching mode */ + reg &= ~0xF0; + reg |= mode; + + /* Write the register back to the IC */ + writeRegister(MSA300_REG_INT_LATCH, reg); +} + +/**************************************************************************/ +/*! + @brief Turn on active interrupt. Interrupt parameter corresponds to + interrupt pins. + @param axis + Axis to set active interrupt on + @param interrupt + Index of interrupt (1 or 2) +*/ +/**************************************************************************/ +void MSA300::enableActiveInterrupt(axis_t axis, uint8_t interrupt) +{ + uint8_t reg; + switch(interrupt) { + case 1: + reg = readRegister(MSA300_REG_INT_MAP_0); + reg |= (1 << 2); + writeRegister(MSA300_REG_INT_MAP_0, reg); + break; + case 2: + reg = readRegister(MSA300_REG_INT_MAP_2_1); + reg |= (1 << 2); + writeRegister(MSA300_REG_INT_MAP_2_1, reg); + break; + } + + reg = readRegister(MSA300_REG_INT_SET_0); + + switch(axis) { + case MSA300_AXIS_X: + reg |= (1 << 0); + break; + case MSA300_AXIS_Y: + reg |= (1 << 1); + break; + case MSA300_AXIS_Z: + reg |= (1 << 2); + break; + } + + writeRegister(MSA300_REG_INT_SET_0, reg); +} + +/**************************************************************************/ +/*! + @brief Toggle freefall interrupt. Interrupt parameter corresponds to + interrupt pins. + @param interrupt + Index of interrupt (1 or 2) +*/ +/**************************************************************************/ +void MSA300::enableFreefallInterrupt(uint8_t interrupt) +{ + uint8_t reg; + switch(interrupt) { + case 1: + reg = readRegister(MSA300_REG_INT_MAP_0); + reg |= (1 << 0); + writeRegister(MSA300_REG_INT_MAP_0, reg); + break; + case 2: + reg = readRegister(MSA300_REG_INT_MAP_2_1); + reg |= (1 << 0); + writeRegister(MSA300_REG_INT_MAP_2_1, reg); + break; + } + + reg = readRegister(MSA300_REG_INT_SET_1); + + reg |= (1 << 3); + + writeRegister(MSA300_REG_INT_SET_1, reg); +} + +/**************************************************************************/ +/*! + @brief Enable freefall interrupt. Interrupt parameter corresponds to + interrupt pins. + @param interrupt + Index of interrupt (1 or 2) +*/ +/**************************************************************************/ +void MSA300::enableOrientationInterrupt(uint8_t interrupt) +{ + uint8_t reg; + switch(interrupt) { + case 1: + reg = readRegister(MSA300_REG_INT_MAP_0); + reg |= (1 << 6); + writeRegister(MSA300_REG_INT_MAP_0, reg); + break; + case 2: + reg = readRegister(MSA300_REG_INT_MAP_2_1); + reg |= (1 << 6); + writeRegister(MSA300_REG_INT_MAP_2_1, reg); + break; + } + + reg = readRegister(MSA300_REG_INT_SET_0); + + reg |= (1 << 6); + + writeRegister(MSA300_REG_INT_SET_0, reg); +} + +/**************************************************************************/ +/*! + @brief Enable single tap interrupt. Interrupt parameter corresponds to + interrupt pins. + @param interrupt + Index of interrupt (1 or 2) +*/ +/**************************************************************************/ +void MSA300::enableSingleTapInterrupt(uint8_t interrupt) +{ + uint8_t reg; + switch(interrupt) { + case 1: + reg = readRegister(MSA300_REG_INT_MAP_0); + reg |= (1 << 5); + writeRegister(MSA300_REG_INT_MAP_0, reg); + break; + case 2: + reg = readRegister(MSA300_REG_INT_MAP_2_1); + reg |= (1 << 5); + writeRegister(MSA300_REG_INT_MAP_2_1, reg); + break; + } + + reg = readRegister(MSA300_REG_INT_SET_0); + + reg |= (1 << 5); + + writeRegister(MSA300_REG_INT_SET_0, reg); +} + +/**************************************************************************/ +/*! + @brief Enable double tap interrupt. Interrupt parameter corresponds to + interrupt pins. + @param interrupt + Index of interrupt (1 or 2) +*/ +/**************************************************************************/ +void MSA300::enableDoubleTapInterrupt(uint8_t interrupt) +{ + uint8_t reg; + switch(interrupt) { + case 1: + reg = readRegister(MSA300_REG_INT_MAP_0); + reg |= (1 << 4); + writeRegister(MSA300_REG_INT_MAP_0, reg); + break; + case 2: + reg = readRegister(MSA300_REG_INT_MAP_2_1); + reg |= (1 << 4); + writeRegister(MSA300_REG_INT_MAP_2_1, reg); + break; + } + + reg = readRegister(MSA300_REG_INT_SET_0); + + reg |= (1 << 4); + + writeRegister(MSA300_REG_INT_SET_0, reg); +} + +/**************************************************************************/ +/*! + @brief Enable new data interrupt. Interrupt parameter corresponds to + interrupt pins. + @param interrupt + Index of interrupt (1 or 2) +*/ +/**************************************************************************/ +void MSA300::enableNewDataInterrupt(uint8_t interrupt) +{ + uint8_t reg; + switch(interrupt) { + case 1: + reg = readRegister(MSA300_REG_INT_MAP_1); + reg |= (1 << 0); + writeRegister(MSA300_REG_INT_MAP_1, reg); + break; + case 2: + reg = readRegister(MSA300_REG_INT_MAP_1); + reg |= (1 << 7); + writeRegister(MSA300_REG_INT_MAP_1, reg); + break; + } + + reg = readRegister(MSA300_REG_INT_SET_1); + + reg |= (1 << 4); + + writeRegister(MSA300_REG_INT_SET_1, reg); +} + +/**************************************************************************/ +/*! + @brief Check orientation of the chip. + @return Orientation struct containing z and xy-orientations +*/ +/**************************************************************************/ +orient_t MSA300::checkOrientation(void) +{ + orient_t orientation; + uint8_t reg = readRegister(MSA300_REG_ORIENT_STATUS); + + orientation.z = (zOrient_t)((reg >> 6) & 1); + orientation.xy = (xyOrient_t)((reg >> 4) & 0x3); + + return orientation; +} + +/**************************************************************************/ +/*! + @brief Set offset compensation value for specific axis. Value can vary + from 0 to 998.4 mg. + Values outside the range will be clamped. + @param axis + Axis to set offset on + @param value + Offset value (0 to 998.4 mg) +*/ +/**************************************************************************/ +void MSA300::setOffset(axis_t axis, float value) +{ + uint8_t offset = value / 3.9f; + uint8_t reg; + + offset = clamp(offset, 0, 998.4f); + + switch(axis) { + case MSA300_AXIS_X: + reg = readRegister(MSA300_REG_OFFSET_COMP_X); + + reg &= ~0xFF; + reg |= offset; + + writeRegister(MSA300_REG_OFFSET_COMP_X, reg); + break; + + case MSA300_AXIS_Y: + reg = readRegister(MSA300_REG_OFFSET_COMP_Y); + + reg &= ~0xFF; + reg |= offset; + + writeRegister(MSA300_REG_OFFSET_COMP_Y, reg); + break; + + case MSA300_AXIS_Z: + reg = readRegister(MSA300_REG_OFFSET_COMP_Z); + + reg &= ~0xFF; + reg |= offset; + + writeRegister(MSA300_REG_OFFSET_COMP_Z, reg); + break; + } + +} + +/**************************************************************************/ +/*! + @brief Set threshold of tap interrupt. Value can vary from 0 to full + scale of each range. Values outside the range will be clamped. + @param value + Tap threshold value (0 to full scale) +*/ +/**************************************************************************/ +void MSA300::setTapThreshold(float value) +{ + float offset = 0; + switch(_range) { + case MSA300_RANGE_16_G: + offset = clamp(value / MSA300_MG2G_TAP_TH_16_G, 0, 16000.0); + + break; + case MSA300_RANGE_8_G: + offset = clamp(value / MSA300_MG2G_TAP_TH_8_G, 0, 8000.0); + + break; + case MSA300_RANGE_4_G: + offset = clamp(value / MSA300_MG2G_TAP_TH_4_G, 0, 4000.0); + + break; + case MSA300_RANGE_2_G: + offset = clamp(value / MSA300_MG2G_TAP_TH_2_G, 0, 2000.0); + + break; + } + + writeRegister(MSA300_REG_TAP_TH, offset); +} + +/**************************************************************************/ +/*! + @brief Set duration of tap interrupt. + @param duration + Second shock duration: According to tapDuration_t enum. + @param quiet + Quiet duration: 0 -> 30 ms + 1 -> 20 ms + @param shock + Shock duration: 0 -> 50 ms + 1 -> 70 ms +*/ +/**************************************************************************/ +void MSA300::setTapDuration(tapDuration_t duration, uint8_t quiet, uint8_t shock) +{ + uint8_t reg = 0; + + reg |= (quiet << 7); + reg |= (shock << 6); + reg |= duration; + + writeRegister(MSA300_REG_TAP_DUR, reg); +} + +/**************************************************************************/ +/*! + @brief Set threshold of active interrupt. Value can vary from 0 to full + scale of each range. Values outside the range will be clamped. + @param value + Active threshold value (0 to full scale) +*/ +/**************************************************************************/ +void MSA300::setActiveThreshold(float value) +{ + float offset = 0; + switch(_range) { + case MSA300_RANGE_16_G: + offset = clamp(value / MSA300_MG2G_ACTIVE_TH_16_G, 0, 16000.0); + break; + + case MSA300_RANGE_8_G: + offset = clamp(value / MSA300_MG2G_ACTIVE_TH_8_G, 0, 8000.0); + break; + + case MSA300_RANGE_4_G: + offset = clamp(value / MSA300_MG2G_ACTIVE_TH_4_G, 0, 4000.0); + break; + + case MSA300_RANGE_2_G: + offset = clamp(value / MSA300_MG2G_ACTIVE_TH_2_G, 0, 2000.0); + break; + } + + writeRegister(MSA300_REG_ACTIVE_TH, offset); +} + +/**************************************************************************/ +/*! + @brief Set duration of active interrupt. + Value can vary from 1 ms to 5 ms. + @param duration + Active interrupt duration (1 to 5 ms) +*/ +/**************************************************************************/ +void MSA300::setActiveDuration(uint8_t duration) +{ + uint8_t reg = 0; + uint8_t value = clamp(duration - 1, 0, 4); + reg |= value; + + writeRegister(MSA300_REG_ACTIVE_DUR, reg); +} + +/**************************************************************************/ +/*! + @brief Set duration of freefall interrupt. + Value can vary from 2 ms to 512 ms. + @param duration + Freefall interrupt duration (2 to 512 ms) +*/ +/**************************************************************************/ +void MSA300::setFreefallDuration(uint16_t duration) +{ + uint8_t reg = 0; + float dur_f; + duration = clamp(duration, 2, 512); + dur_f = clamp((float)(duration)/2.0f - 1, 0, 256); // avoid rounding the result in between + reg |= (uint8_t)dur_f; + + writeRegister(MSA300_REG_FREEFALL_DUR, reg); +} + +/**************************************************************************/ +/*! + @brief Set threshold of freefall interrupt. Value can vary from 0 to full + scale of each range. Values outside the range will be clamped. + @param value + Freefall interrupt threshold (0 to full scale) +*/ +/**************************************************************************/ +void MSA300::setFreefallThreshold(float value) +{ + float threshold = value / 7.81f; + switch(_range) { + case MSA300_RANGE_16_G: + threshold = clamp(threshold, 0, 16000.0); + break; + + case MSA300_RANGE_8_G: + threshold = clamp(threshold, 0, 8000.0); + break; + + case MSA300_RANGE_4_G: + threshold = clamp(threshold, 0, 4000.0); + break; + + case MSA300_RANGE_2_G: + threshold = clamp(threshold, 0, 2000.0); + break; + } + + writeRegister(MSA300_REG_FREEFALL_TH, threshold); +} + +/**************************************************************************/ +/*! + @brief Set hysteresis value and mode of freefall interrupt. + Value can vary from 0 to 500 mg in integers of 125mg. + Values outside the range will be clamped. + @param mode + Mode: 1 -> sum mode |acc_x| + |acc_y| + |acc_z| + 0 -> single mode + @param value + Freefall hysteresis value (0 to 500 mg in steps of 125 mg) +*/ +/**************************************************************************/ +void MSA300::setFreefallHysteresis(uint8_t mode, uint8_t value) +{ + uint8_t reg = 0; + uint8_t hysteresis = (uint8_t)clamp(value / 125, 0, 500); + + reg |= (mode << 3); + reg &= ~0x3; + reg |= hysteresis; + + writeRegister(MSA300_REG_FREEFALL_HY, reg); +} + +/**************************************************************************/ +/*! + @brief Swap polarity. + @param polarity + Polarity to be changed +*/ +/**************************************************************************/ +void MSA300::swapPolarity(pol_t polarity) +{ + uint8_t reg = readRegister(MSA300_REG_SWAP_POLARITY); + + reg ^= (1 << polarity); + + writeRegister(MSA300_REG_SWAP_POLARITY, reg); +} + +/**************************************************************************/ +/*! + @brief Set orientation mode + @param mode + Orientation mode +*/ +/**************************************************************************/ +void MSA300::setOrientMode(orientMode_t mode) +{ + uint8_t reg = readRegister(MSA300_REG_ORIENT_HY); + + reg &= ~0x3; + reg |= mode; + + writeRegister(MSA300_REG_ORIENT_HY, reg); +} + +/**************************************************************************/ +/*! + @brief Set orientation hysteresis. Value can vary from 0 to 500 mg. + @param value + Orientation hysteresis value (0 to 500 mg) +*/ +/**************************************************************************/ +void MSA300::setOrientHysteresis(float value) +{ + uint8_t reg = readRegister(MSA300_REG_ORIENT_HY); + + uint8_t hysteresis = (uint8_t)clamp(value / 62.5f, 0, 8); + + reg &= ~0x70; + reg |= hysteresis; + + writeRegister(MSA300_REG_ORIENT_HY, reg); +} + +/**************************************************************************/ +/*! + @brief Set z blocking. + @param mode + Orientation blocking mode + @param zBlockValue + Limit value of z-blocking +*/ +/**************************************************************************/ +void MSA300::setBlocking(orientBlockMode_t mode, float zBlockValue) +{ + uint8_t reg = readRegister(MSA300_REG_ORIENT_HY); + + reg &= ~0xC; + reg |= mode; + + writeRegister(MSA300_REG_ORIENT_HY, reg); + + uint8_t value = (uint8_t)clamp(zBlockValue / 62.5f, 0, 15); + + writeRegister(MSA300_REG_Z_BLOCK, value); +} + +/**************************************************************************/ +/*! + @brief Get the acceleration. + @return Acceleration struct containing accelarations of each axis in + m/s^2 +*/ +/**************************************************************************/ +acc_t MSA300::getAcceleration(void) +{ + acc_t acceleration; + + acceleration.x = getX() * _multiplier * GRAVITY; + acceleration.y = getY() * _multiplier * GRAVITY; + acceleration.z = getZ() * _multiplier * GRAVITY; + + return acceleration; +} diff --git a/libraries/MSA300/src/MSA300.h b/libraries/MSA300/src/MSA300.h new file mode 100644 index 0000000..cae05a2 --- /dev/null +++ b/libraries/MSA300/src/MSA300.h @@ -0,0 +1,337 @@ +/**************************************************************************/ +/*! + @file MSA300.h +*/ +/**************************************************************************/ + +#include "Arduino.h" + +#include + +/*========================================================================= + CONSTANTS + -----------------------------------------------------------------------*/ + /** */ + #define GRAVITY (9.80665F) ///< Gravity constant +/*=========================================================================*/ + + +/*========================================================================= + I2C ADDRESS/BITS + -----------------------------------------------------------------------*/ + #define MSA300_I2C_ADDRESS (0x26) ///< I2C write address. Assumes SDO pulled to GND, otherwise 0x4E + +/*=========================================================================*/ + +/*========================================================================= + REGISTERS + -----------------------------------------------------------------------*/ + /** MSA300 Registers */ + #define MSA_300_REG_SOFT_RESET (0x00) ///< Soft reset (R) + #define MSA300_REG_PARTID (0x01) ///< Part ID (R) + #define MSA300_REG_ACC_X_LSB (0x02) ///< X-acceleration LSB (R) + #define MSA300_REG_ACC_X_MSB (0x03) ///< X-acceleration MSB (R) + #define MSA300_REG_ACC_Y_LSB (0x04) ///< Y-acceleration LSB (R) + #define MSA300_REG_ACC_Y_MSB (0x05) ///< Y-acceleration MSB (R) + #define MSA300_REG_ACC_Z_LSB (0x06) ///< Z-acceleration LSB (R) + #define MSA300_REG_ACC_Z_MSB (0x07) ///< Z-acceleration MSB (R) + #define MSA300_REG_MOTION_INT (0x09) ///< Motion interrupt (R) + #define MSA300_REG_DATA_INT (0x0A) ///< Data interrupt (R) + #define MSA300_REG_TAP_ACTIVE_STATUS (0x0B) ///< Tap status (R) + #define MSA300_REG_ORIENT_STATUS (0x0C) ///< Orientation status (R) + #define MSA300_REG_RES_RANGE (0x0F) ///< Resolution/Range (R/W) + #define MSA300_REG_ODR (0x10) ///< Output Data Rate (R/W) + #define MSA300_REG_PWR_MODE_BW (0x11) ///< Power Mode/Bandwidth (R/W) + #define MSA300_REG_SWAP_POLARITY (0x12) ///< Swap Axis Polarity (R/W) + #define MSA300_REG_INT_SET_0 (0x16) ///< Interrupt Set 0 (R/W) + #define MSA300_REG_INT_SET_1 (0x17) ///< Interrupt Set 1 (R/W) + #define MSA300_REG_INT_MAP_0 (0x19) ///< Interrupt Map 0 (R/W) + #define MSA300_REG_INT_MAP_1 (0x1A) ///< Interrupt Map 1 (R/W) + #define MSA300_REG_INT_MAP_2_1 (0x1B) ///< Interrupt Map 2_1 (R/W) + #define MSA300_REG_INT_MAP_2_2 (0x20) ///< Interrupt Map 2_2 (R/W) + #define MSA300_REG_INT_LATCH (0x21) ///< Interrupt Latch (R/W) + #define MSA300_REG_FREEFALL_DUR (0x22) ///< Freefall Duration (R/W) + #define MSA300_REG_FREEFALL_TH (0x23) ///< Freefall Threshold (R/W) + #define MSA300_REG_FREEFALL_HY (0x24) ///< Freefall Hysteresis (R/W) + #define MSA300_REG_ACTIVE_DUR (0x27) ///< Active Duration (R/W) + #define MSA300_REG_ACTIVE_TH (0x28) ///< Active Threshold (R/W) + #define MSA300_REG_TAP_DUR (0x2A) ///< Tap Duration (R/W) + #define MSA300_REG_TAP_TH (0x2B) ///< Tap Threshold (R/W) + #define MSA300_REG_ORIENT_HY (0x2C) ///< Orientation Hysteresis (R/W) + #define MSA300_REG_Z_BLOCK (0x2D) ///< Z Blocking (R/W) + #define MSA300_REG_OFFSET_COMP_X (0x38) ///< X Offset Compensation (R/W) + #define MSA300_REG_OFFSET_COMP_Y (0x39) ///< Y Offset Compensation (R/W) + #define MSA300_REG_OFFSET_COMP_Z (0x3A) ///< Z Offset Compensation (R/W) + + +/*=========================================================================*/ + +/*========================================================================= + REGISTERS + -----------------------------------------------------------------------*/ + + /* Value conversion multipliers */ + #define MSA300_MG2G_MULTIPLIER_16_G (0.00195) ///< 1.95mg per lsb + #define MSA300_MG2G_MULTIPLIER_8_G (0.000976) ///< 0.976mg per lsb + #define MSA300_MG2G_MULTIPLIER_4_G (0.000488) ///< 0.488mg per lsb + #define MSA300_MG2G_MULTIPLIER_2_G (0.000244) ///< 0.244mg per lsb + + /* Tap interrupt threshold conversion multiplier */ + #define MSA300_MG2G_TAP_TH_16_G (0.5) ///< 500mg per lsb + #define MSA300_MG2G_TAP_TH_8_G (0.25) ///< 250mg per lsb + #define MSA300_MG2G_TAP_TH_4_G (0.125) ///< 125mg per lsb + #define MSA300_MG2G_TAP_TH_2_G (0.0625) ///< 62.5mg per lsb + + /* Active interrupt threshold conversion multiplier */ + #define MSA300_MG2G_ACTIVE_TH_16_G (0.03125) ///< 31.25mg per lsb + #define MSA300_MG2G_ACTIVE_TH_8_G (0.015625) ///< 15.625mg per lsb + #define MSA300_MG2G_ACTIVE_TH_4_G (0.00781) ///< 7.81mg per lsb + #define MSA300_MG2G_ACTIVE_TH_2_G (0.00391) ///< 3.91mg per lsb +/*=========================================================================*/ + +/** Datarate settings. Used with register 0x10 (MSA300_REG_ODR) to set datarate and with register 0x11 (MSA_REG_PWR_MODE_BW) to set Bandwidth */ +typedef enum +{ + MSA300_DATARATE_1000_HZ = 0b1111, ///< 500Hz Bandwidth, not available in low power mode + MSA300_DATARATE_500_HZ = 0b1001, ///< 250Hz Bandwidth, not available in low power mode + MSA300_DATARATE_250_HZ = 0b1000, ///< 125Hz Bandwidth + MSA300_DATARATE_125_HZ = 0b0111, ///< 62.5Hz Bandwidth + MSA300_DATARATE_62_5_HZ = 0b0110, ///< 31.25Hz Bandwidth + MSA300_DATARATE_31_25_HZ = 0b0101, ///< 15.63Hz Bandwidth + MSA300_DATARATE_15_63_HZ = 0b0100, ///< 7.81Hz Bandwidth + MSA300_DATARATE_7_81_HZ = 0b0011, ///< 3.9Hz Bandwidth + MSA300_DATARATE_3_9_HZ = 0b0010, ///< 1.95Hz Bandwidth + MSA300_DATARATE_1_95_HZ = 0b0001, ///< 0.975Hz Bandwidth, not available in normal mode + MSA300_DATARATE_1_HZ = 0b0000, ///< 0.5Hz Bandwidth, not available in normal mode +} dataRate_t; + +/** Range settings. Used with register 0x0F (MSA300_REG_RES_RANGE) to set g range */ +typedef enum +{ + MSA300_RANGE_16_G = 0b11, ///< +/- 16g + MSA300_RANGE_8_G = 0b10, ///< +/- 8g + MSA300_RANGE_4_G = 0b01, ///< +/- 4g + MSA300_RANGE_2_G = 0b00 ///< +/- 2g (default value) +} range_t; + +/** Resolution settings. */ +typedef enum +{ + MSA300_RES_14_BIT = 0b00, ///< 14 bit (default value) + MSA300_RES_12_BIT = 0b01, ///< 12 bit + MSA300_RES_8_BIT = 0b11 ///< 8 bit +} res_t; + +/** Power mode settings. */ +typedef enum +{ + MSA300_MODE_NORMAL = 0b00, ///< Normal operation mode + MSA300_MODE_LOW = 0b01, ///< Low power mode + MSA300_MODE_SUSPEND = 0b11 ///< Suspend/Shutdown mode +} pwrMode_t; + +/** Interrupt latch settings. */ +typedef enum +{ + MSA300_INT_NON_LATCHED = 0b0000, ///< Non Latched + MSA300_INT_LATCHED_250_MS = 0b0001, ///< 250ms Latched + MSA300_INT_LATCHED_500_MS = 0b0010, ///< 500ms Latched + MSA300_INT_LATCHED_1_S = 0b0011, ///< 1s Latched + MSA300_INT_LATCHED_2_S = 0b0100, ///< 2s Latched + MSA300_INT_LATCHED_4_S = 0b0101, ///< 4s Latched + MSA300_INT_LATCHED_8_S = 0b0110, ///< 8s Latched + MSA300_INT_LATCHED = 0b0111, ///< Permament Latched + MSA300_INT_LATCHED_1_MS = 0b1001, ///< 1ms Latched + MSA300_INT_LATCHED_2_MS = 0b1011, ///< 2ms Latched + MSA300_INT_LATCHED_25_MS = 0b1100, ///< 25ms Latched + MSA300_INT_LATCHED_50_MS = 0b1101, ///< 50ms Latched + MSA300_INT_LATCHED_100_MS = 0b1110 ///< 100ms Latched +} intMode_t; + +/** Axes. */ +typedef enum +{ + MSA300_AXIS_X = 0b00, ///< X axis + MSA300_AXIS_Y = 0b01, ///< Y axis + MSA300_AXIS_Z = 0b10 ///< Z axis +} axis_t; + +/** Tap duration settings. */ +typedef enum +{ + MSA300_TAP_DUR_50_MS = 0b000, ///< 50ms tap + MSA300_TAP_DUR_100_MS = 0b001, ///< 100ms tap + MSA300_TAP_DUR_150_MS = 0b010, ///< 150ms tap + MSA300_TAP_DUR_200_MS = 0b011, ///< 200ms tap + MSA300_TAP_DUR_250_MS = 0b100, ///< 250ms tap + MSA300_TAP_DUR_375_MS = 0b101, ///< 375ms tap + MSA300_TAP_DUR_500_MS = 0b110, ///< 500ms tap + MSA300_TAP_DUR_700_MS = 0b111 ///< 700ms tap +} tapDuration_t; + +/** Acceleration container */ +typedef struct +{ + float x; ///< X acceleration + float y; ///< Y acceleration + float z; ///< Z acceleration +} acc_t; + +/** Interrupt container. If active or tap interrupts are raised, populates intStatus with more info about interrupt. */ +typedef struct +{ + bool orientInt = false; ///< Orientation interrupt + bool sTapInt = false; ///< Single tap interrupt + bool dTapInt = false; ///< Double tap interrupt + bool activeInt = false; ///< Active interrupt + bool freefallInt = false; ///< Freefall interrupt + bool newDataInt = false; ///< New data interrupt + + /** Optional interrupt status container */ + union + { + /** Interrupt status container */ + struct + { + uint8_t tapSign; ///< Tap interrupt sign + uint8_t tapFirstX; ///< Tap triggered by x axis + uint8_t tapFirstY; ///< Tap triggered by y axis + uint8_t tapFirstZ; ///< Tap triggered by z axis + uint8_t activeSign; ///< Active interrupt sign + uint8_t activeFirstX; ///< Active interrupt triggered by x axis + uint8_t activeFirstY; ///< Active interrupt triggered by y axis + uint8_t activeFirstZ; ///< Active interrupt triggered by z axis + } intStatus; + + }; + +} interrupt_t; + +/** Z orientation */ +typedef enum +{ + ORIENT_UPWARD_LOOKING = 0b0, ///< Upward looking orientation + ORIENT_DOWNWARD_LOOKING = 0b1 ///< Downward looking orientation +} zOrient_t; + +/** XY orientation */ +typedef enum +{ + ORIENT_PORTRAIT_UPRIGHT = 0b00, ///< Portait upright orientation + ORIENT_PORTRAIT_UPSIDEDOWN = 0b01, ///< Portait upsidedown orientation + ORIENT_LANDSCAPE_LEFT = 0b10, ///< Landscape left orientation + ORIENT_LANDSCAPE_RIGHT = 0b11 ///< Landscape right orientation +} xyOrient_t; + +/** Orientation container */ +typedef struct +{ + zOrient_t z; ///< Z orientation container + xyOrient_t xy; ///< XY orientation container + +} orient_t; + +/** Polarity swap */ +typedef enum +{ + X_POLARITY = 3, ///< X polarity + Y_POLARITY = 2, ///< Y polarity + Z_POLARITY = 1, ///< Z polarity + X_Y_SWAP = 0 ///< XY polarity swap +} pol_t; +/** Orientation mode */ +typedef enum +{ + MODE_SYMMETRICAL = 0b00, ///< Symmetrical mode + MODE_HIGH_ASYMMETRICAL = 0b01, ///< High asymmetrical mode + MODE_LOW_ASYMMETRICAL = 0b10 ///< Low asymmetrical mode +} orientMode_t; + +/** Orientation blocking */ +typedef enum +{ + ORIENT_NO_BLOCKING = 0b00, ///< No blocking + ORIENT_Z_BLOCKING = 0b01, ///< Z blocking + ORIENT_Z_BLOCKING_0_2_G = 0b10 ///< Z blocking or slope in any axis > 0.2g +} orientBlockMode_t; + +/** Class for MSA300 */ +class MSA300{ + public: + MSA300(int32_t sensorID = -1); + + bool begin(void); + void setRange(range_t range); + range_t getRange(void); + void setResolution(res_t resolution); + res_t getResolution(void); + void setDataRate(dataRate_t dataRate); + dataRate_t getDataRate(void); + void setMode(pwrMode_t mode); + pwrMode_t getMode(void); + void setOffset(axis_t axis, float value); + void setTapThreshold(float value); + void setTapDuration(tapDuration_t duration, uint8_t quiet, uint8_t shock); + void setActiveThreshold(float value); + void setActiveDuration(uint8_t duration); + void setFreefallDuration(uint16_t duration); + void setFreefallThreshold(float value); + void setFreefallHysteresis(uint8_t mode, uint8_t value); + void swapPolarity(pol_t polarity); + void setOrientMode(orientMode_t mode); + void setOrientHysteresis(float value); + void setBlocking(orientBlockMode_t mode, float zBlockValue); + + void resetInterrupt(void); + void clearInterrupts(void); + interrupt_t checkInterrupts(void); + void setInterruptLatch(intMode_t mode); + void enableActiveInterrupt(axis_t axis, uint8_t interrupt); + void enableFreefallInterrupt(uint8_t interrupt); + void enableOrientationInterrupt(uint8_t interrupt); + void enableSingleTapInterrupt(uint8_t interrupt); + void enableDoubleTapInterrupt(uint8_t interrupt); + void enableNewDataInterrupt(uint8_t interrupt); + + acc_t getAcceleration(void); + orient_t checkOrientation(void); + + uint8_t getPartID(void); + void writeRegister(uint8_t reg, uint8_t value); + uint8_t readRegister(uint8_t reg); + int16_t read16(uint8_t reg); + + int16_t getX(void), getY(void), getZ(void); + private: + + + int32_t _sensorID; + range_t _range; + float _multiplier; + res_t _res; + pwrMode_t _mode; +}; + +/*! + @brief Generic function for clamping values. + @tparam T + Type of value to be clamped + @tparam value + Value to be clamped + @tparam min + Minimum limit value + @tparam max + Maximum limit value + @return Value if value inside limits, else min or max +*/ +template +T clamp(T value, T min, T max) +{ + if(value > max) { + value = max; + } else if(value < min) { + value = min; + } + + return value; +} \ No newline at end of file