diff --git a/examples/Example1-I2C_Simple_measurement/Example1-I2C_Simple_measurement.ino b/examples/Example1-I2C_Simple_measurement/Example1-I2C_Simple_measurement.ino index 7af06d5..c2b7b06 100644 --- a/examples/Example1-I2C_Simple_measurement/Example1-I2C_Simple_measurement.ino +++ b/examples/Example1-I2C_Simple_measurement/Example1-I2C_Simple_measurement.ino @@ -52,17 +52,22 @@ void setup() void loop() { - unsigned int currentX = 0; - unsigned int currentY = 0; - unsigned int currentZ = 0; + uint32_t currentX = 0; + uint32_t currentY = 0; + uint32_t currentZ = 0; double normalizedX = 0; double normalizedY = 0; double normalizedZ = 0; + // This reads the X, Y and Z channels consecutively + // (Useful if you have one or more channels disabled) currentX = myMag.getMeasurementX(); currentY = myMag.getMeasurementY(); currentZ = myMag.getMeasurementZ(); + // Or, we could read all three simultaneously + //myMag.getMeasurementXYZ(¤tX, ¤tY, ¤tZ); + Serial.print("X axis raw value: "); Serial.print(currentX); Serial.print("\tY axis raw value: "); @@ -70,6 +75,8 @@ void loop() Serial.print("\tZ axis raw value: "); Serial.println(currentZ); + // The magnetic field values are 18-bit unsigned. The zero (mid) point is 2^17 (131072). + // Normalize each field to +/- 1.0 normalizedX = (double)currentX - 131072.0; normalizedX /= 131072.0; normalizedY = (double)currentY - 131072.0; @@ -77,8 +84,10 @@ void loop() normalizedZ = (double)currentZ - 131072.0; normalizedZ /= 131072.0; + // The magnetometer full scale is +/- 8 Gauss + // Multiply the normalized values by 8 to convert to Gauss Serial.print("X axis field (Gauss): "); - Serial.print(normalizedX * 8, 5); + Serial.print(normalizedX * 8, 5); // Print with 5 decimal places Serial.print("\tY axis field (Gauss): "); Serial.print(normalizedY * 8, 5); @@ -88,4 +97,4 @@ void loop() Serial.println(); delay(100); -} \ No newline at end of file +} diff --git a/examples/Example2-I2C_Digital_compass/Example2-I2C_Digital_compass.ino b/examples/Example2-I2C_Digital_compass/Example2-I2C_Digital_compass.ino index 7f286f8..b30d1e3 100644 --- a/examples/Example2-I2C_Digital_compass/Example2-I2C_Digital_compass.ino +++ b/examples/Example2-I2C_Digital_compass/Example2-I2C_Digital_compass.ino @@ -43,52 +43,39 @@ void setup() void loop() { - unsigned int rawValue = 0; - double heading = 0; + uint32_t rawValueX = 0; + uint32_t rawValueY = 0; + uint32_t rawValueZ = 0; double normalizedX = 0; double normalizedY = 0; double normalizedZ = 0; + double heading = 0; + + // Read all three channels simultaneously + myMag.getMeasurementXYZ(&rawValueX, &rawValueY, &rawValueZ); - rawValue = myMag.getMeasurementX(); - normalizedX = (double)rawValue - 131072.0; + // The magnetic field values are 18-bit unsigned. The zero (mid) point is 2^17 (131072). + // Normalize each field to +/- 1.0 + normalizedX = (double)rawValueX - 131072.0; normalizedX /= 131072.0; - rawValue = myMag.getMeasurementY(); - normalizedY = (double)rawValue - 131072.0; + normalizedY = (double)rawValueY - 131072.0; normalizedY /= 131072.0; - rawValue = myMag.getMeasurementZ(); - normalizedZ = (double)rawValue - 131072.0; + normalizedZ = (double)rawValueZ - 131072.0; normalizedZ /= 131072.0; // Magnetic north is oriented with the Y axis - if (normalizedY != 0) - { - if (normalizedX < 0) - { - if (normalizedY > 0) - heading = 57.2958 * atan(-normalizedX / normalizedY); // Quadrant 1 - else - heading = 57.2958 * atan(-normalizedX / normalizedY) + 180; // Quadrant 2 - } - else - { - if (normalizedY < 0) - heading = 57.2958 * atan(-normalizedX / normalizedY) + 180; // Quadrant 3 - else - heading = 360 - (57.2958 * atan(normalizedX / normalizedY)); // Quadrant 4 - } - } - else - { - // atan of an infinite number is 90 or 270 degrees depending on X value - if (normalizedX > 0) - heading = 270; - else - heading = 90; - } + // Convert the X and Y fields into heading using atan2 (Arc Tangent 2) + heading = atan2(normalizedX, 0 - normalizedY); + + // atan2 returns a value between +PI and -PI + // Convert to degrees + heading /= PI; + heading *= 180; + heading += 180; Serial.print("Heading: "); Serial.println(heading, 1); - delay(10); -} \ No newline at end of file + delay(100); +} diff --git a/examples/Example3-I2C_Continuous_measurement/Example3-I2C_Continuous_measurement.ino b/examples/Example3-I2C_Continuous_measurement/Example3-I2C_Continuous_measurement.ino index 5d63fed..bd60868 100644 --- a/examples/Example3-I2C_Continuous_measurement/Example3-I2C_Continuous_measurement.ino +++ b/examples/Example3-I2C_Continuous_measurement/Example3-I2C_Continuous_measurement.ino @@ -25,12 +25,14 @@ SFE_MMC5983MA myMag; int interruptPin = 2; -volatile bool newDataAvailable = false; -unsigned int rawValue = 0; -double heading = 0; +volatile bool newDataAvailable = true; +uint32_t rawValueX = 0; +uint32_t rawValueY = 0; +uint32_t rawValueZ = 0; double normalizedX = 0; double normalizedY = 0; double normalizedZ = 0; +double heading = 0; void setup() { @@ -39,8 +41,9 @@ void setup() Wire.begin(); + // Configure the interrupt pin for the "Measurement Done" interrupt pinMode(interruptPin, INPUT); - attachInterrupt(digitalPinToInterrupt(interruptPin), interruptRoutine, FALLING); + attachInterrupt(digitalPinToInterrupt(interruptPin), interruptRoutine, RISING); if (myMag.begin() == false) { @@ -53,14 +56,6 @@ void setup() Serial.println("MMC5983MA connected"); - Serial.print("Die temperature: "); - int celsius = myMag.getTemperature(); - Serial.print(celsius); - Serial.print("°C or "); - float fahrenheit = (celsius * 9.0f / 5.0f) + 32.0f; - Serial.print((int)fahrenheit); - Serial.println("°F"); - Serial.println("Setting filter bandwith to 100 Hz for continuous operation..."); myMag.setFilterBandwidth(100); Serial.print("Reading back filter bandwith: "); @@ -81,61 +76,54 @@ void setup() Serial.print("Reading back continuous mode: "); Serial.println(myMag.isContinuousModeEnabled() ? "enabled" : "disabled"); + Serial.println("Enabling interrupt..."); myMag.enableInterrupt(); + Serial.print("Reading back interrupt status: "); + Serial.println(myMag.isInterruptEnabled() ? "enabled" : "disabled"); + + // Set our interrupt flag, just in case we missed the rising edge + newDataAvailable = true; } void loop() { if (newDataAvailable == true) { - rawValue = myMag.getMeasurementX(); - normalizedX = (double)rawValue - 131072.0; + newDataAvailable = false; // Clear our interrupt flag + myMag.clearMeasDoneInterrupt(); // Clear the MMC5983 interrupt + + // Read all three channels simultaneously + // Note: we are calling readFieldsXYZ to read the fields, not getMeasurementXYZ + // The measurement is already complete, we do not need to start a new one + myMag.readFieldsXYZ(&rawValueX, &rawValueY, &rawValueZ); + + // The magnetic field values are 18-bit unsigned. The zero (mid) point is 2^17 (131072). + // Normalize each field to +/- 1.0 + normalizedX = (double)rawValueX - 131072.0; normalizedX /= 131072.0; - - rawValue = myMag.getMeasurementY(); - normalizedY = (double)rawValue - 131072.0; + + normalizedY = (double)rawValueY - 131072.0; normalizedY /= 131072.0; - - rawValue = myMag.getMeasurementZ(); - normalizedZ = (double)rawValue - 131072.0; + + normalizedZ = (double)rawValueZ - 131072.0; normalizedZ /= 131072.0; - + // Magnetic north is oriented with the Y axis - if (normalizedY != 0) - { - if (normalizedX < 0) - { - if (normalizedY > 0) - heading = 57.2958 * atan(-normalizedX / normalizedY); // Quadrant 1 - else - heading = 57.2958 * atan(-normalizedX / normalizedY) + 180; // Quadrant 2 - } - else - { - if (normalizedY < 0) - heading = 57.2958 * atan(-normalizedX / normalizedY) + 180; // Quadrant 3 - else - heading = 360 - (57.2958 * atan(normalizedX / normalizedY)); // Quadrant 4 - } - } - else - { - // atan of an infinite number is 90 or 270 degrees depending on X value - if (normalizedX > 0) - heading = 270; - else - heading = 90; - } - + // Convert the X and Y fields into heading using atan2 (Arc Tangent 2) + heading = atan2(normalizedX, 0 - normalizedY); + + // atan2 returns a value between +PI and -PI + // Convert to degrees + heading /= PI; + heading *= 180; + heading += 180; + Serial.print("Heading: "); Serial.println(heading, 1); - - newDataAvailable = false; - myMag.enableInterrupt(); } } void interruptRoutine() { newDataAvailable = true; -} \ No newline at end of file +} diff --git a/examples/Example4-SPI_Simple_measurement/Example4-SPI_Simple_measurement.ino b/examples/Example4-SPI_Simple_measurement/Example4-SPI_Simple_measurement.ino index 473c92b..a12c3f1 100644 --- a/examples/Example4-SPI_Simple_measurement/Example4-SPI_Simple_measurement.ino +++ b/examples/Example4-SPI_Simple_measurement/Example4-SPI_Simple_measurement.ino @@ -53,17 +53,22 @@ void setup() void loop() { - unsigned int currentX = 0; - unsigned int currentY = 0; - unsigned int currentZ = 0; + uint32_t currentX = 0; + uint32_t currentY = 0; + uint32_t currentZ = 0; double normalizedX = 0; double normalizedY = 0; double normalizedZ = 0; + // This reads the X, Y and Z channels consecutively + // (Useful if you have one or more channels disabled) currentX = myMag.getMeasurementX(); currentY = myMag.getMeasurementY(); currentZ = myMag.getMeasurementZ(); + // Or, we could read all three simultaneously + //myMag.getMeasurementXYZ(¤tX, ¤tY, ¤tZ); + Serial.print("X axis raw value: "); Serial.print(currentX); Serial.print("\tY axis raw value: "); @@ -71,6 +76,8 @@ void loop() Serial.print("\tZ axis raw value: "); Serial.println(currentZ); + // The magnetic field values are 18-bit unsigned. The zero (mid) point is 2^17 (131072). + // Normalize each field to +/- 1.0 normalizedX = (double)currentX - 131072.0; normalizedX /= 131072.0; normalizedY = (double)currentY - 131072.0; @@ -78,8 +85,10 @@ void loop() normalizedZ = (double)currentZ - 131072.0; normalizedZ /= 131072.0; + // The magnetometer full scale is +/- 8 Gauss + // Multiply the normalized values by 8 to convert to Gauss Serial.print("X axis field (Gauss): "); - Serial.print(normalizedX * 8, 5); + Serial.print(normalizedX * 8, 5); // Print with 5 decimal places Serial.print("\tY axis field (Gauss): "); Serial.print(normalizedY * 8, 5); @@ -89,4 +98,4 @@ void loop() Serial.println(); delay(100); -} \ No newline at end of file +} diff --git a/examples/Example5-SPI_Digital_compass/Example5-SPI_Digital_compass.ino b/examples/Example5-SPI_Digital_compass/Example5-SPI_Digital_compass.ino index c592b62..6ce949f 100644 --- a/examples/Example5-SPI_Digital_compass/Example5-SPI_Digital_compass.ino +++ b/examples/Example5-SPI_Digital_compass/Example5-SPI_Digital_compass.ino @@ -44,52 +44,39 @@ void setup() void loop() { - unsigned int rawValue = 0; - double heading = 0; + uint32_t rawValueX = 0; + uint32_t rawValueY = 0; + uint32_t rawValueZ = 0; double normalizedX = 0; double normalizedY = 0; double normalizedZ = 0; + double heading = 0; + + // Read all three channels simultaneously + myMag.getMeasurementXYZ(&rawValueX, &rawValueY, &rawValueZ); - rawValue = myMag.getMeasurementX(); - normalizedX = (double)rawValue - 131072.0; + // The magnetic field values are 18-bit unsigned. The zero (mid) point is 2^17 (131072). + // Normalize each field to +/- 1.0 + normalizedX = (double)rawValueX - 131072.0; normalizedX /= 131072.0; - rawValue = myMag.getMeasurementY(); - normalizedY = (double)rawValue - 131072.0; + normalizedY = (double)rawValueY - 131072.0; normalizedY /= 131072.0; - rawValue = myMag.getMeasurementZ(); - normalizedZ = (double)rawValue - 131072.0; + normalizedZ = (double)rawValueZ - 131072.0; normalizedZ /= 131072.0; // Magnetic north is oriented with the Y axis - if (normalizedY != 0) - { - if (normalizedX < 0) - { - if (normalizedY > 0) - heading = 57.2958 * atan(-normalizedX / normalizedY); // Quadrant 1 - else - heading = 57.2958 * atan(-normalizedX / normalizedY) + 180; // Quadrant 2 - } - else - { - if (normalizedY < 0) - heading = 57.2958 * atan(-normalizedX / normalizedY) + 180; // Quadrant 3 - else - heading = 360 - (57.2958 * atan(normalizedX / normalizedY)); // Quadrant 4 - } - } - else - { - // atan of an infinite number is 90 or 270 degrees depending on X value - if (normalizedX > 0) - heading = 270; - else - heading = 90; - } + // Convert the X and Y fields into heading using atan2 (Arc Tangent 2) + heading = atan2(normalizedX, 0 - normalizedY); + + // atan2 returns a value between +PI and -PI + // Convert to degrees + heading /= PI; + heading *= 180; + heading += 180; Serial.print("Heading: "); Serial.println(heading, 1); delay(100); -} \ No newline at end of file +} diff --git a/examples/Example6-SPI_Fast_Continuous_measurement/Example6-SPI_Fast_Continuous_measurement.ino b/examples/Example6-SPI_Fast_Continuous_measurement/Example6-SPI_Fast_Continuous_measurement.ino new file mode 100644 index 0000000..a21bbe5 --- /dev/null +++ b/examples/Example6-SPI_Fast_Continuous_measurement/Example6-SPI_Fast_Continuous_measurement.ino @@ -0,0 +1,129 @@ +/* + Compute magnetic heading over SPI from the MMC5983MA + By: Nathan Seidle and Ricardo Ramos + SparkFun Electronics + Date: April 14th, 2022 + License: SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT). + + Feel like supporting our work? Buy a board from SparkFun! + https://www.sparkfun.com/products/19034 + + This example demonstrates how to compute a magnetic heading from the sensor over SPI + + Hardware Connections: + Connect CIPO to MISO, COPI to MOSI, and SCK to SCK, on an Arduino. + Connect CS to pin 4 on an Arduino. +*/ + +#include + +#include //Click here to get the library: http://librarymanager/All#SparkFun_MMC5983MA + +SFE_MMC5983MA myMag; + +int csPin = 4; + +int interruptPin = 2; + +volatile bool newDataAvailable = true; +uint32_t rawValueX = 0; +uint32_t rawValueY = 0; +uint32_t rawValueZ = 0; +double normalizedX = 0; +double normalizedY = 0; +double normalizedZ = 0; +double heading = 0; + +void setup() +{ + Serial.begin(115200); + Serial.println("MMC5983MA Example"); + + SPI.begin(); + + // Configure the interrupt pin for the "Measurement Done" interrupt + pinMode(interruptPin, INPUT); + attachInterrupt(digitalPinToInterrupt(interruptPin), interruptRoutine, RISING); + + if (myMag.begin(csPin) == false) + { + Serial.println("MMC5983MA did not respond - check your wiring. Freezing."); + while (true) + ; + } + + myMag.softReset(); + + Serial.println("MMC5983MA connected"); + + Serial.println("Setting filter bandwith to 800 Hz for continuous operation..."); + myMag.setFilterBandwidth(800); + Serial.print("Reading back filter bandwith: "); + Serial.println(myMag.getFilterBandwith()); + + Serial.println("Setting continuous measurement frequency to 100 Hz..."); + myMag.setContinuousModeFrequency(100); + Serial.print("Reading back continuous measurement frequency: "); + Serial.println(myMag.getContinuousModeFrequency()); + + Serial.println("Enabling auto set/reset..."); + myMag.enableAutomaticSetReset(); + Serial.print("Reading back automatic set/reset: "); + Serial.println(myMag.isAutomaticSetResetEnabled() ? "enabled" : "disabled"); + + Serial.println("Enabling continuous mode..."); + myMag.enableContinuousMode(); + Serial.print("Reading back continuous mode: "); + Serial.println(myMag.isContinuousModeEnabled() ? "enabled" : "disabled"); + + Serial.println("Enabling interrupt..."); + myMag.enableInterrupt(); + Serial.print("Reading back interrupt status: "); + Serial.println(myMag.isInterruptEnabled() ? "enabled" : "disabled"); + + // Set our interrupt flag, just in case we missed the rising edge + newDataAvailable = true; +} + +void loop() +{ + if (newDataAvailable == true) + { + newDataAvailable = false; // Clear our interrupt flag + myMag.clearMeasDoneInterrupt(); // Clear the MMC5983 interrupt + + // Read all three channels simultaneously + // Note: we are calling readFieldsXYZ to read the fields, not getMeasurementXYZ + // The measurement is already complete, we do not need to start a new one + myMag.readFieldsXYZ(&rawValueX, &rawValueY, &rawValueZ); + + // The magnetic field values are 18-bit unsigned. The zero (mid) point is 2^17 (131072). + // Normalize each field to +/- 1.0 + normalizedX = (double)rawValueX - 131072.0; + normalizedX /= 131072.0; + + normalizedY = (double)rawValueY - 131072.0; + normalizedY /= 131072.0; + + normalizedZ = (double)rawValueZ - 131072.0; + normalizedZ /= 131072.0; + + // Magnetic north is oriented with the Y axis + // Convert the X and Y fields into heading using atan2 (Arc Tangent 2) + heading = atan2(normalizedX, 0 - normalizedY); + + // atan2 returns a value between +PI and -PI + // Convert to degrees + heading /= PI; + heading *= 180; + heading += 180; + + Serial.print("Heading: "); + Serial.println(heading, 1); + } +} + +void interruptRoutine() +{ + newDataAvailable = true; +} diff --git a/keywords.txt b/keywords.txt index d312c06..0dc421a 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,6 +13,7 @@ SFE_MMC5983MA KEYWORD1 ####################################### setErrorCallback KEYWORD2 +errorCodeString KEYWORD2 begin KEYWORD2 isConnected KEYWORD2 getTemperature KEYWORD2 @@ -55,8 +56,20 @@ isExtraCurrentAppliedNegToPos KEYWORD2 getMeasurementX KEYWORD2 getMeasurementY KEYWORD2 getMeasurementZ KEYWORD2 +getMeasurementXYZ KEYWORD2 +readFieldsXYZ KEYWORD2 +clearMeasDoneInterrupt KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### +NONE LITERAL1 +I2C_INITIALIZATION_ERROR LITERAL1 +SPI_INITIALIZATION_ERROR LITERAL1 +INVALID_DEVICE LITERAL1 +BUS_ERROR LITERAL1 +INVALID_FILTER_BANDWIDTH LITERAL1 +INVALID_CONTINUOUS_FREQUENCY LITERAL1 +INVALID_PERIODIC_SAMPLES LITERAL1 + diff --git a/library.properties b/library.properties index 4571e9c..ba91f6e 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=SparkFun MMC5983MA Magnetometer Arduino Library -version=1.0.2 +version=1.0.3 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=A I2C/SPI library for the MMC5983MA magnetic compass sensor. -paragraph=The Qwiic MMC5983MA Magnetometer is a very sensitive magnetometer capable of sensing down to 0.4mG enabling a heading accuracy of ±0.5°. This is a feature complete library supporting I2C and SPI. Output rates of 1000Hz, ±8G FSR, and 18-bit resolution make the MMC5983MA a phenomenal magnetic sensor. +paragraph=The Qwiic MMC5983MA Magnetometer is a very sensitive magnetometer capable of sensing down to 0.4mG enabling a heading accuracy of ±0.5°. This is a feature complete library supporting I2C and SPI. Output rates of 1000Hz, ±8G FSR, and 18-bit resolution make the MMC5983MA a phenomenal magnetic sensor. category=Sensors url=https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library architectures=* \ No newline at end of file diff --git a/src/SparkFun_MMC5983MA_Arduino_Library.cpp b/src/SparkFun_MMC5983MA_Arduino_Library.cpp index d8cf84a..d685e3c 100644 --- a/src/SparkFun_MMC5983MA_Arduino_Library.cpp +++ b/src/SparkFun_MMC5983MA_Arduino_Library.cpp @@ -15,7 +15,7 @@ #include "SparkFun_MMC5983MA_Arduino_Library.h" #include "SparkFun_MMC5983MA_Arduino_Library_Constants.h" -void SFE_MMC5983MA::setShadowBit(uint8_t registerAddress, const uint8_t bitMask) +bool SFE_MMC5983MA::setShadowBit(uint8_t registerAddress, const uint8_t bitMask) { uint8_t *shadowRegister = nullptr; @@ -53,11 +53,13 @@ void SFE_MMC5983MA::setShadowBit(uint8_t registerAddress, const uint8_t bitMask) if (shadowRegister) { *shadowRegister |= bitMask; - mmc_io.writeSingleByte(registerAddress, *shadowRegister); + return (mmc_io.writeSingleByte(registerAddress, *shadowRegister)); } + + return false; } -void SFE_MMC5983MA::clearShadowBit(uint8_t registerAddress, const uint8_t bitMask) +bool SFE_MMC5983MA::clearShadowBit(uint8_t registerAddress, const uint8_t bitMask) { uint8_t *shadowRegister = nullptr; @@ -95,8 +97,10 @@ void SFE_MMC5983MA::clearShadowBit(uint8_t registerAddress, const uint8_t bitMas if (shadowRegister) { *shadowRegister &= ~bitMask; - mmc_io.writeSingleByte(registerAddress, *shadowRegister); + return (mmc_io.writeSingleByte(registerAddress, *shadowRegister)); } + + return false; } bool SFE_MMC5983MA::isShadowBitSet(uint8_t registerAddress, const uint8_t bitMask) @@ -140,6 +144,40 @@ void SFE_MMC5983MA::setErrorCallback(void (*_errorCallback)(SF_MMC5983MA_ERROR e errorCallback = _errorCallback; } +const char *SFE_MMC5983MA::errorCodeString(SF_MMC5983MA_ERROR errorCode) +{ + switch (errorCode) + { + case SF_MMC5983MA_ERROR::NONE: + return "NONE"; + break; + case SF_MMC5983MA_ERROR::I2C_INITIALIZATION_ERROR: + return "I2C_INITIALIZATION_ERROR"; + break; + case SF_MMC5983MA_ERROR::SPI_INITIALIZATION_ERROR: + return "SPI_INITIALIZATION_ERROR"; + break; + case SF_MMC5983MA_ERROR::INVALID_DEVICE: + return "INVALID_DEVICE"; + break; + case SF_MMC5983MA_ERROR::BUS_ERROR: + return "BUS_ERROR"; + break; + case SF_MMC5983MA_ERROR::INVALID_FILTER_BANDWIDTH: + return "INVALID_FILTER_BANDWIDTH"; + break; + case SF_MMC5983MA_ERROR::INVALID_CONTINUOUS_FREQUENCY: + return "INVALID_CONTINUOUS_FREQUENCY"; + break; + case SF_MMC5983MA_ERROR::INVALID_PERIODIC_SAMPLES: + return "INVALID_PERIODIC_SAMPLES"; + break; + default: + return "UNDEFINED"; + break; + } +}; + bool SFE_MMC5983MA::begin(TwoWire &wirePort) { // Initializes I2C and check if device responds @@ -178,9 +216,14 @@ bool SFE_MMC5983MA::begin(uint8_t userCSPin, SPISettings userSettings, SPIClass bool SFE_MMC5983MA::isConnected() { // Poll device for its ID. - uint8_t response; - response = mmc_io.readSingleByte(PROD_ID_REG); + uint8_t response = 0; + bool success = mmc_io.readSingleByte(PROD_ID_REG, &response); + if (!success) + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::BUS_ERROR); + return false; + } if (response != PROD_ID) { SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::INVALID_DEVICE); @@ -193,17 +236,26 @@ int SFE_MMC5983MA::getTemperature() { // Send command to device. Since TM_T clears itself we don't need to // use the shadow register for this - we can send the command directly to the IC. - mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_T); + if (!mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_T)) + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::BUS_ERROR); + return -99; + } // Wait until measurement is completed do { // Wait a little so we won't flood MMC with requests - delay(5); + delay(1); } while (!mmc_io.isBitSet(STATUS_REG, MEAS_T_DONE)); // Get raw temperature value from the IC. - uint8_t result = mmc_io.readSingleByte(T_OUT_REG); + uint8_t result = 0; + if (!mmc_io.readSingleByte(T_OUT_REG, &result)) + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::BUS_ERROR); + return -99; + } // Convert it using the equation provided in the datasheet float temperature = -75.0f + (static_cast(result) * (200.0f / 255.0f)); @@ -212,28 +264,30 @@ int SFE_MMC5983MA::getTemperature() return static_cast(temperature); } -void SFE_MMC5983MA::softReset() +bool SFE_MMC5983MA::softReset() { // Since SW_RST bit clears itself we don't need to to through the shadow // register for this - we can send the command directly to the IC. - mmc_io.setRegisterBit(INT_CTRL_1_REG, SW_RST); + bool success = mmc_io.setRegisterBit(INT_CTRL_1_REG, SW_RST); // The reset time is 10 msec. but we'll wait 15 msec. just in case. delay(15); + + return success; } -void SFE_MMC5983MA::enableInterrupt() +bool SFE_MMC5983MA::enableInterrupt() { // This bit must be set through the shadow memory or we won't be // able to check if interrupts are enabled using isInterruptEnabled() - setShadowBit(INT_CTRL_0_REG, INT_MEAS_DONE_EN); + return (setShadowBit(INT_CTRL_0_REG, INT_MEAS_DONE_EN)); } -void SFE_MMC5983MA::disableInterrupt() +bool SFE_MMC5983MA::disableInterrupt() { // This bit must be cleared through the shadow memory or we won't be // able to check if interrupts are enabled using isInterruptEnabled() - clearShadowBit(INT_CTRL_0_REG, INT_MEAS_DONE_EN); + return (clearShadowBit(INT_CTRL_0_REG, INT_MEAS_DONE_EN)); } bool SFE_MMC5983MA::isInterruptEnabled() @@ -243,153 +297,167 @@ bool SFE_MMC5983MA::isInterruptEnabled() return isShadowBitSet(INT_CTRL_0_REG, INT_MEAS_DONE_EN); } -void SFE_MMC5983MA::enable3WireSPI() +bool SFE_MMC5983MA::enable3WireSPI() { // This bit must be set through the shadow memory or we won't be // able to check if SPI is enabled using isSPIEnabled() - setShadowBit(INT_CTRL_3_REG, SPI_3W); + return (setShadowBit(INT_CTRL_3_REG, SPI_3W)); } -void SFE_MMC5983MA::disable3WireSPI() +bool SFE_MMC5983MA::disable3WireSPI() { // This bit must be cleared through the shadow memory or we won't be // able to check if is is enabled using isSPIEnabled() - clearShadowBit(INT_CTRL_3_REG, SPI_3W); + return (clearShadowBit(INT_CTRL_3_REG, SPI_3W)); } bool SFE_MMC5983MA::is3WireSPIEnabled() { // Get the bit value from the shadow register since the IC does not // allow reading INT_CTRL_3_REG register. - return isShadowBitSet(INT_CTRL_3_REG, SPI_3W); + return (isShadowBitSet(INT_CTRL_3_REG, SPI_3W)); } -void SFE_MMC5983MA::performSetOperation() +bool SFE_MMC5983MA::performSetOperation() { // Since SET bit clears itself we don't need to to through the shadow // register for this - we can send the command directly to the IC. - mmc_io.setRegisterBit(INT_CTRL_0_REG, SET_OPERATION); + bool success = mmc_io.setRegisterBit(INT_CTRL_0_REG, SET_OPERATION); // Wait until bit clears itself. delay(1); + + return success; } -void SFE_MMC5983MA::performResetOperation() +bool SFE_MMC5983MA::performResetOperation() { // Since RESET bit clears itself we don't need to to through the shadow // register for this - we can send the command directly to the IC. - mmc_io.setRegisterBit(INT_CTRL_0_REG, RESET_OPERATION); + bool success = mmc_io.setRegisterBit(INT_CTRL_0_REG, RESET_OPERATION); // Wait until bit clears itself. delay(1); + + return success; } -void SFE_MMC5983MA::enableAutomaticSetReset() +bool SFE_MMC5983MA::enableAutomaticSetReset() { // This bit must be set through the shadow memory or we won't be // able to check if automatic set/reset is enabled using isAutomaticSetResetEnabled() - setShadowBit(INT_CTRL_0_REG, AUTO_SR_EN); + return (setShadowBit(INT_CTRL_0_REG, AUTO_SR_EN)); } -void SFE_MMC5983MA::disableAutomaticSetReset() +bool SFE_MMC5983MA::disableAutomaticSetReset() { // This bit must be cleared through the shadow memory or we won't be // able to check if automatic set/reset is enabled using isAutomaticSetResetEnabled() - clearShadowBit(INT_CTRL_0_REG, AUTO_SR_EN); + return (clearShadowBit(INT_CTRL_0_REG, AUTO_SR_EN)); } bool SFE_MMC5983MA::isAutomaticSetResetEnabled() { // Get the bit value from the shadow register since the IC does not // allow reading INT_CTRL_0_REG register. - return isShadowBitSet(INT_CTRL_0_REG, AUTO_SR_EN); + return (isShadowBitSet(INT_CTRL_0_REG, AUTO_SR_EN)); } -void SFE_MMC5983MA::enableXChannel() +bool SFE_MMC5983MA::enableXChannel() { // This bit must be cleared through the shadow memory or we won't be // able to check if the channel is enabled using isXChannelEnabled() // and since it's a inhibit bit it must be cleared so X channel will // be enabled. - clearShadowBit(INT_CTRL_1_REG, X_INHIBIT); + return (clearShadowBit(INT_CTRL_1_REG, X_INHIBIT)); } -void SFE_MMC5983MA::disableXChannel() +bool SFE_MMC5983MA::disableXChannel() { // This bit must be set through the shadow memory or we won't be // able to check if the channel is enabled using isXChannelEnabled() // and since it's a inhibit bit it must be set so X channel will // be disabled. - setShadowBit(INT_CTRL_1_REG, X_INHIBIT); + return (setShadowBit(INT_CTRL_1_REG, X_INHIBIT)); } bool SFE_MMC5983MA::isXChannelEnabled() { // Get the bit value from the shadow register since the IC does not // allow reading INT_CTRL_1_REG register. - return isShadowBitSet(INT_CTRL_1_REG, X_INHIBIT); + return (isShadowBitSet(INT_CTRL_1_REG, X_INHIBIT)); } -void SFE_MMC5983MA::enableYZChannels() +bool SFE_MMC5983MA::enableYZChannels() { // This bit must be cleared through the shadow memory or we won't be // able to check if channels are enabled using areYZChannelsEnabled() // and since it's a inhibit bit it must be cleared so X channel will // be enabled. - clearShadowBit(INT_CTRL_1_REG, YZ_INHIBIT); + return (clearShadowBit(INT_CTRL_1_REG, YZ_INHIBIT)); } -void SFE_MMC5983MA::disableYZChannels() +bool SFE_MMC5983MA::disableYZChannels() { // This bit must be set through the shadow memory or we won't be // able to check if channels are enabled using areYZChannelsEnabled() // and since it's a inhibit bit it must be cleared so X channel will // be disabled. - setShadowBit(INT_CTRL_1_REG, YZ_INHIBIT); + return (setShadowBit(INT_CTRL_1_REG, YZ_INHIBIT)); } bool SFE_MMC5983MA::areYZChannelsEnabled() { // Get the bit value from the shadow register since the IC does not // allow reading INT_CTRL_1_REG register. - return isShadowBitSet(INT_CTRL_1_REG, YZ_INHIBIT); + return (isShadowBitSet(INT_CTRL_1_REG, YZ_INHIBIT)); } -void SFE_MMC5983MA::setFilterBandwidth(uint16_t bandwidth) +bool SFE_MMC5983MA::setFilterBandwidth(uint16_t bandwidth) { // These must be set/cleared using the shadow memory since it can be read // using getFilterBandwith() + bool success; + switch (bandwidth) { case 800: { - setShadowBit(INT_CTRL_1_REG, BW0); - setShadowBit(INT_CTRL_1_REG, BW1); + success = setShadowBit(INT_CTRL_1_REG, BW0); + success &= setShadowBit(INT_CTRL_1_REG, BW1); } break; case 400: { - clearShadowBit(INT_CTRL_1_REG, BW0); - setShadowBit(INT_CTRL_1_REG, BW1); + success = clearShadowBit(INT_CTRL_1_REG, BW0); + success &= setShadowBit(INT_CTRL_1_REG, BW1); } break; case 200: { - setShadowBit(INT_CTRL_1_REG, BW0); - clearShadowBit(INT_CTRL_1_REG, BW1); + success = setShadowBit(INT_CTRL_1_REG, BW0); + success &= clearShadowBit(INT_CTRL_1_REG, BW1); } break; case 100: + { + success = clearShadowBit(INT_CTRL_1_REG, BW0); + success &= clearShadowBit(INT_CTRL_1_REG, BW1); + } + break; + default: { - clearShadowBit(INT_CTRL_1_REG, BW0); - clearShadowBit(INT_CTRL_1_REG, BW1); + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::INVALID_FILTER_BANDWIDTH); + success = false; } break; } + + return success; } uint16_t SFE_MMC5983MA::getFilterBandwith() @@ -422,106 +490,116 @@ uint16_t SFE_MMC5983MA::getFilterBandwith() return retVal; } -void SFE_MMC5983MA::enableContinuousMode() +bool SFE_MMC5983MA::enableContinuousMode() { // This bit must be set through the shadow memory or we won't be // able to check if continuous mode is enabled using isContinuousModeEnabled() - setShadowBit(INT_CTRL_2_REG, CMM_EN); + return (setShadowBit(INT_CTRL_2_REG, CMM_EN)); } -void SFE_MMC5983MA::disableContinuousMode() +bool SFE_MMC5983MA::disableContinuousMode() { // This bit must be cleared through the shadow memory or we won't be // able to check if continuous mode is enabled using isContinuousModeEnabled() - clearShadowBit(INT_CTRL_2_REG, CMM_EN); + return (clearShadowBit(INT_CTRL_2_REG, CMM_EN)); } bool SFE_MMC5983MA::isContinuousModeEnabled() { // Get the bit value from the shadow register since the IC does not // allow reading INT_CTRL_2_REG register. - return isShadowBitSet(INT_CTRL_2_REG, CMM_EN); + return (isShadowBitSet(INT_CTRL_2_REG, CMM_EN)); } -void SFE_MMC5983MA::setContinuousModeFrequency(uint16_t frequency) +bool SFE_MMC5983MA::setContinuousModeFrequency(uint16_t frequency) { // These must be set/cleared using the shadow memory since it can be read // using getContinuousModeFrequency() + bool success; + switch (frequency) { case 1: { // CM_FREQ[2:0] = 001 - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_2); - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_1); - setShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + success = clearShadowBit(INT_CTRL_2_REG, CM_FREQ_2); + success &= clearShadowBit(INT_CTRL_2_REG, CM_FREQ_1); + success &= setShadowBit(INT_CTRL_2_REG, CM_FREQ_0); } break; case 10: { // CM_FREQ[2:0] = 010 - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_2); - setShadowBit(INT_CTRL_2_REG, CM_FREQ_1); - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + success = clearShadowBit(INT_CTRL_2_REG, CM_FREQ_2); + success &= setShadowBit(INT_CTRL_2_REG, CM_FREQ_1); + success &= clearShadowBit(INT_CTRL_2_REG, CM_FREQ_0); } break; case 20: { // CM_FREQ[2:0] = 011 - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_2); - setShadowBit(INT_CTRL_2_REG, CM_FREQ_1); - setShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + success = clearShadowBit(INT_CTRL_2_REG, CM_FREQ_2); + success &= setShadowBit(INT_CTRL_2_REG, CM_FREQ_1); + success &= setShadowBit(INT_CTRL_2_REG, CM_FREQ_0); } break; case 50: { // CM_FREQ[2:0] = 100 - setShadowBit(INT_CTRL_2_REG, CM_FREQ_2); - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_1); - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + success = setShadowBit(INT_CTRL_2_REG, CM_FREQ_2); + success &= clearShadowBit(INT_CTRL_2_REG, CM_FREQ_1); + success &= clearShadowBit(INT_CTRL_2_REG, CM_FREQ_0); } break; case 100: { // CM_FREQ[2:0] = 101 - setShadowBit(INT_CTRL_2_REG, CM_FREQ_2); - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_1); - setShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + success = setShadowBit(INT_CTRL_2_REG, CM_FREQ_2); + success &= clearShadowBit(INT_CTRL_2_REG, CM_FREQ_1); + success &= setShadowBit(INT_CTRL_2_REG, CM_FREQ_0); } break; case 200: { // CM_FREQ[2:0] = 110 - setShadowBit(INT_CTRL_2_REG, CM_FREQ_2); - setShadowBit(INT_CTRL_2_REG, CM_FREQ_1); - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + success = setShadowBit(INT_CTRL_2_REG, CM_FREQ_2); + success &= setShadowBit(INT_CTRL_2_REG, CM_FREQ_1); + success &= clearShadowBit(INT_CTRL_2_REG, CM_FREQ_0); } break; case 1000: { // CM_FREQ[2:0] = 111 - setShadowBit(INT_CTRL_2_REG, CM_FREQ_2); - setShadowBit(INT_CTRL_2_REG, CM_FREQ_1); - setShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + success = setShadowBit(INT_CTRL_2_REG, CM_FREQ_2); + success &= setShadowBit(INT_CTRL_2_REG, CM_FREQ_1); + success &= setShadowBit(INT_CTRL_2_REG, CM_FREQ_0); } break; case 0: - default: { // CM_FREQ[2:0] = 000 - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_2); - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_1); - clearShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + success = clearShadowBit(INT_CTRL_2_REG, CM_FREQ_2); + success &= clearShadowBit(INT_CTRL_2_REG, CM_FREQ_1); + success &= clearShadowBit(INT_CTRL_2_REG, CM_FREQ_0); + } + break; + + default: + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::INVALID_CONTINUOUS_FREQUENCY); + success = false; } break; } + + return success; } uint16_t SFE_MMC5983MA::getContinuousModeFrequency() @@ -585,107 +663,117 @@ uint16_t SFE_MMC5983MA::getContinuousModeFrequency() return frequency; } -void SFE_MMC5983MA::enablePeriodicSet() +bool SFE_MMC5983MA::enablePeriodicSet() { // This bit must be set through the shadow memory or we won't be // able to check if periodic set is enabled using isContinuousModeEnabled() - setShadowBit(INT_CTRL_2_REG, EN_PRD_SET); + return (setShadowBit(INT_CTRL_2_REG, EN_PRD_SET)); } -void SFE_MMC5983MA::disablePeriodicSet() +bool SFE_MMC5983MA::disablePeriodicSet() { // This bit must be cleared through the shadow memory or we won't be // able to check if periodic set is enabled using isContinuousModeEnabled() - clearShadowBit(INT_CTRL_2_REG, EN_PRD_SET); + return (clearShadowBit(INT_CTRL_2_REG, EN_PRD_SET)); } bool SFE_MMC5983MA::isPeriodicSetEnabled() { // Get the bit value from the shadow register since the IC does not // allow reading INT_CTRL_2_REG register. - return isShadowBitSet(INT_CTRL_2_REG, EN_PRD_SET); + return (isShadowBitSet(INT_CTRL_2_REG, EN_PRD_SET)); } -void SFE_MMC5983MA::setPeriodicSetSamples(const uint16_t numberOfSamples) +bool SFE_MMC5983MA::setPeriodicSetSamples(const uint16_t numberOfSamples) { // We must use the shadow memory to do all bits manipulations but // we need to access the shadow memory directly, change bits and // write back at once. + bool success; + switch (numberOfSamples) { case 25: { // PRD_SET[2:0] = 001 - clearShadowBit(INT_CTRL_2_REG, PRD_SET_2); - clearShadowBit(INT_CTRL_2_REG, PRD_SET_1); - setShadowBit(INT_CTRL_2_REG, PRD_SET_0); + success = clearShadowBit(INT_CTRL_2_REG, PRD_SET_2); + success &= clearShadowBit(INT_CTRL_2_REG, PRD_SET_1); + success &= setShadowBit(INT_CTRL_2_REG, PRD_SET_0); } break; case 75: { // PRD_SET[2:0] = 010 - clearShadowBit(INT_CTRL_2_REG, PRD_SET_2); - setShadowBit(INT_CTRL_2_REG, PRD_SET_1); - clearShadowBit(INT_CTRL_2_REG, PRD_SET_0); + success = clearShadowBit(INT_CTRL_2_REG, PRD_SET_2); + success &= setShadowBit(INT_CTRL_2_REG, PRD_SET_1); + success &= clearShadowBit(INT_CTRL_2_REG, PRD_SET_0); } break; case 100: { // PRD_SET[2:0] = 011 - clearShadowBit(INT_CTRL_2_REG, PRD_SET_2); - setShadowBit(INT_CTRL_2_REG, PRD_SET_1); - setShadowBit(INT_CTRL_2_REG, PRD_SET_0); + success = clearShadowBit(INT_CTRL_2_REG, PRD_SET_2); + success &= setShadowBit(INT_CTRL_2_REG, PRD_SET_1); + success &= setShadowBit(INT_CTRL_2_REG, PRD_SET_0); } break; case 250: { // PRD_SET[2:0] = 100 - setShadowBit(INT_CTRL_2_REG, PRD_SET_2); - clearShadowBit(INT_CTRL_2_REG, PRD_SET_1); - clearShadowBit(INT_CTRL_2_REG, PRD_SET_0); + success = setShadowBit(INT_CTRL_2_REG, PRD_SET_2); + success &= clearShadowBit(INT_CTRL_2_REG, PRD_SET_1); + success &= clearShadowBit(INT_CTRL_2_REG, PRD_SET_0); } break; case 500: { // PRD_SET[2:0] = 101 - setShadowBit(INT_CTRL_2_REG, PRD_SET_2); - clearShadowBit(INT_CTRL_2_REG, PRD_SET_1); - setShadowBit(INT_CTRL_2_REG, PRD_SET_0); + success = setShadowBit(INT_CTRL_2_REG, PRD_SET_2); + success &= clearShadowBit(INT_CTRL_2_REG, PRD_SET_1); + success &= setShadowBit(INT_CTRL_2_REG, PRD_SET_0); } break; case 1000: { // PRD_SET[2:0] = 110 - setShadowBit(INT_CTRL_2_REG, PRD_SET_2); - setShadowBit(INT_CTRL_2_REG, PRD_SET_1); - clearShadowBit(INT_CTRL_2_REG, PRD_SET_0); + success = setShadowBit(INT_CTRL_2_REG, PRD_SET_2); + success &= setShadowBit(INT_CTRL_2_REG, PRD_SET_1); + success &= clearShadowBit(INT_CTRL_2_REG, PRD_SET_0); } break; case 2000: { // PRD_SET[2:0] = 111 - setShadowBit(INT_CTRL_2_REG, PRD_SET_2); - setShadowBit(INT_CTRL_2_REG, PRD_SET_1); - setShadowBit(INT_CTRL_2_REG, PRD_SET_0); + success = setShadowBit(INT_CTRL_2_REG, PRD_SET_2); + success &= setShadowBit(INT_CTRL_2_REG, PRD_SET_1); + success &= setShadowBit(INT_CTRL_2_REG, PRD_SET_0); } break; case 1: - default: { // PRD_SET[2:0] = 000 - clearShadowBit(INT_CTRL_2_REG, PRD_SET_2); - clearShadowBit(INT_CTRL_2_REG, PRD_SET_1); - clearShadowBit(INT_CTRL_2_REG, PRD_SET_0); + success = clearShadowBit(INT_CTRL_2_REG, PRD_SET_2); + success &= clearShadowBit(INT_CTRL_2_REG, PRD_SET_1); + success &= clearShadowBit(INT_CTRL_2_REG, PRD_SET_0); + } + break; + + default: + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::INVALID_PERIODIC_SAMPLES); + success = false; } break; } + + return success; } uint16_t SFE_MMC5983MA::getPeriodicSetSamples() @@ -750,150 +838,185 @@ uint16_t SFE_MMC5983MA::getPeriodicSetSamples() return period; } -void SFE_MMC5983MA::applyExtraCurrentPosToNeg() +bool SFE_MMC5983MA::applyExtraCurrentPosToNeg() { // This bit must be set through the shadow memory or we won't be // able to check if extra current is applied using isExtraCurrentAppliedPosToNeg() - setShadowBit(INT_CTRL_3_REG, ST_ENP); + return (setShadowBit(INT_CTRL_3_REG, ST_ENP)); } -void SFE_MMC5983MA::removeExtraCurrentPosToNeg() +bool SFE_MMC5983MA::removeExtraCurrentPosToNeg() { // This bit must be cleared through the shadow memory or we won't be // able to check if extra current is applied using isExtraCurrentAppliedPosToNeg() - clearShadowBit(INT_CTRL_3_REG, ST_ENP); + return (clearShadowBit(INT_CTRL_3_REG, ST_ENP)); } bool SFE_MMC5983MA::isExtraCurrentAppliedPosToNeg() { // Get the bit value from the shadow register since the IC does not // allow reading INT_CTRL_3_REG register. - return isShadowBitSet(INT_CTRL_3_REG, ST_ENP); + return (isShadowBitSet(INT_CTRL_3_REG, ST_ENP)); } -void SFE_MMC5983MA::applyExtracurrentNegToPos() +bool SFE_MMC5983MA::applyExtracurrentNegToPos() { // This bit must be set through the shadow memory or we won't be // able to check if extra current is applied using isExtraCurrentAppliedNegToPos() - setShadowBit(INT_CTRL_3_REG, ST_ENM); + return (setShadowBit(INT_CTRL_3_REG, ST_ENM)); } -void SFE_MMC5983MA::removeExtracurrentNegToPos() +bool SFE_MMC5983MA::removeExtracurrentNegToPos() { // This bit must be cleared through the shadow memory or we won't be // able to check if extra current is applied using isExtraCurrentAppliedNegToPos() - clearShadowBit(INT_CTRL_3_REG, ST_ENM); + return (clearShadowBit(INT_CTRL_3_REG, ST_ENM)); } bool SFE_MMC5983MA::isExtraCurrentAppliedNegToPos() { // Get the bit value from the shadow register since the IC does not // allow reading INT_CTRL_3_REG register. - return isShadowBitSet(INT_CTRL_3_REG, ST_ENM); + return (isShadowBitSet(INT_CTRL_3_REG, ST_ENM)); } uint32_t SFE_MMC5983MA::getMeasurementX() { // Send command to device. TM_M self clears so we can access it directly. - mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_M); + if (!mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_M)) + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::BUS_ERROR); + return 0; + } // Wait until measurement is completed do { // Wait a little so we won't flood MMC with requests - delay(5); + delay(1); } while (!mmc_io.isBitSet(STATUS_REG, MEAS_M_DONE)); - uint32_t temp = 0; uint32_t result = 0; - uint8_t buffer[7] = {0}; - - mmc_io.readMultipleBytes(X_OUT_0_REG, buffer, 7); + uint8_t buffer[2] = {0}; + uint8_t buffer2bit = 0; - temp = static_cast(buffer[X_OUT_0_REG]); - temp = temp << XYZ_0_SHIFT; - result |= temp; + mmc_io.readMultipleBytes(X_OUT_0_REG, buffer, 2); + mmc_io.readSingleByte(XYZ_OUT_2_REG, &buffer2bit); - temp = static_cast(buffer[X_OUT_1_REG]); - temp = temp << XYZ_1_SHIFT; - result |= temp; + result = buffer[0]; // out[17:10] + result = (result << 8) | buffer[1]; // out[9:2] + result = (result << 2) | (buffer2bit >> 6); // out[1:0] - temp = static_cast(buffer[XYZ_OUT_2_REG]); - temp &= X2_MASK; - temp = temp >> 6; - result |= temp; return result; } uint32_t SFE_MMC5983MA::getMeasurementY() { // Send command to device. TM_M self clears so we can access it directly. - mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_M); + if (!mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_M)) + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::BUS_ERROR); + return 0; + } // Wait until measurement is completed do { // Wait a little so we won't flood MMC with requests - delay(5); + delay(1); } while (!mmc_io.isBitSet(STATUS_REG, MEAS_M_DONE)); - uint32_t temp = 0; uint32_t result = 0; - uint8_t registerValue = 0; - - registerValue = (mmc_io.readSingleByte(Y_OUT_0_REG)); + uint8_t buffer[2] = {0}; + uint8_t buffer2bit = 0; - temp = static_cast(registerValue); - temp = temp << XYZ_0_SHIFT; - result |= temp; + mmc_io.readMultipleBytes(Y_OUT_0_REG, buffer, 2); + mmc_io.readSingleByte(XYZ_OUT_2_REG, &buffer2bit); - registerValue = (mmc_io.readSingleByte(Y_OUT_1_REG)); + result = buffer[0]; // out[17:10] + result = (result << 8) | buffer[1]; // out[9:2] + result = (result << 2) | ((buffer2bit >> 4) & 0x03); // out[1:0] - temp = static_cast(registerValue); - temp = temp << XYZ_1_SHIFT; - result |= temp; - - registerValue = (mmc_io.readSingleByte(XYZ_OUT_2_REG)); - temp = static_cast(registerValue); - temp &= Y2_MASK; - temp = temp >> 4; - result |= temp; return result; } uint32_t SFE_MMC5983MA::getMeasurementZ() { // Send command to device. TM_M self clears so we can access it directly. - mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_M); + if (!mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_M)) + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::BUS_ERROR); + return 0; + } // Wait until measurement is completed do { // Wait a little so we won't flood MMC with requests - delay(5); + delay(1); } while (!mmc_io.isBitSet(STATUS_REG, MEAS_M_DONE)); - uint32_t temp = 0; uint32_t result = 0; - uint8_t registerValue = 0; + uint8_t buffer[3] = {0}; - registerValue = (mmc_io.readSingleByte(Z_OUT_0_REG)); + mmc_io.readMultipleBytes(Z_OUT_0_REG, buffer, 3); - temp = static_cast(registerValue); - temp = temp << XYZ_0_SHIFT; - result |= temp; + result = buffer[0]; // out[17:10] + result = (result << 8) | buffer[1]; // out[9:2] + result = (result << 2) | ((buffer[2] >> 2) & 0x03); // out[1:0] - registerValue = (mmc_io.readSingleByte(Z_OUT_1_REG)); + return result; +} - temp = static_cast(registerValue); - temp = temp << XYZ_1_SHIFT; - result |= temp; +bool SFE_MMC5983MA::getMeasurementXYZ(uint32_t *x, uint32_t *y, uint32_t *z) +{ + // Send command to device. TM_M self clears so we can access it directly. + bool success = mmc_io.setRegisterBit(INT_CTRL_0_REG, TM_M); - registerValue = (mmc_io.readSingleByte(XYZ_OUT_2_REG)); + if (!success) + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::BUS_ERROR); + return false; + } - temp = static_cast(registerValue); - temp &= Z2_MASK; - temp = temp >> 2; - result |= temp; - return result; + // Wait until measurement is completed + do + { + // Wait a little so we won't flood MMC with requests + delay(1); + } while (!mmc_io.isBitSet(STATUS_REG, MEAS_M_DONE)); + + return (readFieldsXYZ(x, y, z)); +} + +bool SFE_MMC5983MA::readFieldsXYZ(uint32_t *x, uint32_t *y, uint32_t *z) +{ + uint8_t registerValues[7] = {0}; + + bool success = (mmc_io.readMultipleBytes(X_OUT_0_REG, registerValues, 7)); + + if (success) + { + *x = registerValues[0]; // Xout[17:10] + *x = (*x << 8) | registerValues[1]; // Xout[9:2] + *x = (*x << 2) | (registerValues[6] >> 6); // Xout[1:0] + *y = registerValues[2]; // Yout[17:10] + *y = (*y << 8) | registerValues[3]; // Yout[9:2] + *y = (*y << 2) | ((registerValues[6] >> 4) & 0x03); // Yout[1:0] + *z = registerValues[4]; // Zout[17:10] + *z = (*z << 8) | registerValues[5]; // Zout[9:2] + *z = (*z << 2) | ((registerValues[6] >> 2) & 0x03); // Zout[1:0] + } + else + { + SAFE_CALLBACK(errorCallback, SF_MMC5983MA_ERROR::BUS_ERROR); + } + + return success; } + +bool SFE_MMC5983MA::clearMeasDoneInterrupt(uint8_t measMask) +{ + measMask &= (MEAS_T_DONE | MEAS_M_DONE); // Ensure only the Meas_T_Done and Meas_M_Done interrupts can be cleared + return (mmc_io.setRegisterBit(STATUS_REG, measMask)); // Writing 1 into these bits will clear the corresponding interrupt +} \ No newline at end of file diff --git a/src/SparkFun_MMC5983MA_Arduino_Library.h b/src/SparkFun_MMC5983MA_Arduino_Library.h index 9b09330..a07b573 100644 --- a/src/SparkFun_MMC5983MA_Arduino_Library.h +++ b/src/SparkFun_MMC5983MA_Arduino_Library.h @@ -43,10 +43,10 @@ class SFE_MMC5983MA } memoryShadow; // Sets register bit(s) on memory shadows and then registers - void setShadowBit(uint8_t registerAddress, const uint8_t bitMask); + bool setShadowBit(uint8_t registerAddress, const uint8_t bitMask); // Clears register bit(s) on memory shadows and then registers - void clearShadowBit(uint8_t registerAddress, const uint8_t bitMask); + bool clearShadowBit(uint8_t registerAddress, const uint8_t bitMask); // Checks if a specific bit is set on a register memory shadow bool isShadowBitSet(uint8_t registerAddress, const uint8_t bitMask); @@ -61,6 +61,9 @@ class SFE_MMC5983MA // Sets the error callback function. void setErrorCallback(void (*errorCallback)(SF_MMC5983MA_ERROR errorCode)); + // Convert errorCode to text + const char *errorCodeString(SF_MMC5983MA_ERROR errorCode); + // Initializes MMC5983MA using I2C bool begin(TwoWire &wirePort = Wire); @@ -75,110 +78,110 @@ class SFE_MMC5983MA int getTemperature(); // Soft resets the device. - void softReset(); + bool softReset(); // Enables interrupt generation after measurement is completed. // Must be re-enabled after each measurement. - void enableInterrupt(); + bool enableInterrupt(); // Disables interrupt generation. - void disableInterrupt(); + bool disableInterrupt(); // Checks if interrupt generation is enabled. bool isInterruptEnabled(); // Enables 3 wire SPI interface - void enable3WireSPI(); + bool enable3WireSPI(); // Disables SPI interface - void disable3WireSPI(); + bool disable3WireSPI(); // Checks if SPI is enabled bool is3WireSPIEnabled(); // Performs SET operation - void performSetOperation(); + bool performSetOperation(); // Performs RESET operation - void performResetOperation(); + bool performResetOperation(); // Enables automatic SET/RESET - void enableAutomaticSetReset(); + bool enableAutomaticSetReset(); // Disables automatic SET/RESET - void disableAutomaticSetReset(); + bool disableAutomaticSetReset(); // Checks if automatic SET/RESET is enabled bool isAutomaticSetResetEnabled(); // Enables X channel output - void enableXChannel(); + bool enableXChannel(); // Disables X channel output - void disableXChannel(); + bool disableXChannel(); // Checks if X channel output is enabled bool isXChannelEnabled(); // Enables Y and Z channels outputs - void enableYZChannels(); + bool enableYZChannels(); // Disables Y and Z channels outputs - void disableYZChannels(); + bool disableYZChannels(); // Checks if YZ channels outputs are enabled bool areYZChannelsEnabled(); // Sets decimation filter bandwidth. Allowed values are 800, 400, 200 or 100. Defaults to 100 on invalid values. - void setFilterBandwidth(uint16_t bandwidth); + bool setFilterBandwidth(uint16_t bandwidth); // Gets current decimation filter bandwith. Values are in Hz. uint16_t getFilterBandwith(); // Enables continuous mode. Continuous mode frequency must be greater than 0. - void enableContinuousMode(); + bool enableContinuousMode(); // Disables continuous mode. - void disableContinuousMode(); + bool disableContinuousMode(); // Checks if continuous mode is enabled. bool isContinuousModeEnabled(); // Sets continuous mode frequency. Allowed values are 1000, 200, 100, 50, 20, 10, 1 and 0 (off). Defaults to 0 (off). - void setContinuousModeFrequency(uint16_t frequency); + bool setContinuousModeFrequency(uint16_t frequency); // Gets continuous mode frequency. uint16_t getContinuousModeFrequency(); // Enables periodic set - void enablePeriodicSet(); + bool enablePeriodicSet(); // Disables periodic set - void disablePeriodicSet(); + bool disablePeriodicSet(); // Checks if periodic set is enabled bool isPeriodicSetEnabled(); // Sets how often the chip will perform an automatic set operation. Allowed values are 1, 25, 75, 100, 250, 500, 1000, 2000. Defaults to 1. - void setPeriodicSetSamples(uint16_t numberOfSamples); + bool setPeriodicSetSamples(uint16_t numberOfSamples); // Gets how many times the chip is performing an automatic set operation. uint16_t getPeriodicSetSamples(); // Apply extra current from positive side to negative side of the coil. This feature can be used to check whether the sensor has been saturated. - void applyExtraCurrentPosToNeg(); + bool applyExtraCurrentPosToNeg(); // Remove extra current from positive side to negative side of the coil. - void removeExtraCurrentPosToNeg(); + bool removeExtraCurrentPosToNeg(); // Checks if extra current is applied from positive to negative side of coil. bool isExtraCurrentAppliedPosToNeg(); // Apply extra current from negative side to positive side of the coil. This feature can be used to check whether the sensor has been saturated. - void applyExtracurrentNegToPos(); + bool applyExtracurrentNegToPos(); // Remove extra current from negative side to positive side of the coil. - void removeExtracurrentNegToPos(); + bool removeExtracurrentNegToPos(); // Checks if extra current is applied from negative to positive side of coil. bool isExtraCurrentAppliedNegToPos(); @@ -191,6 +194,16 @@ class SFE_MMC5983MA // Get Z axis measurement uint32_t getMeasurementZ(); + + // Get X, Y and Z field strengths in a single measurement + bool getMeasurementXYZ(uint32_t *x, uint32_t *y, uint32_t *z); + + // Read and return the X, Y and Z field strengths + bool readFieldsXYZ(uint32_t *x, uint32_t *y, uint32_t *z); + + // Clear the Meas_T_Done and/or Meas_M_Done interrupts + // By default, clear both + bool clearMeasDoneInterrupt(uint8_t measMask = MEAS_T_DONE | MEAS_M_DONE); }; #endif \ No newline at end of file diff --git a/src/SparkFun_MMC5983MA_Arduino_Library_Constants.h b/src/SparkFun_MMC5983MA_Arduino_Library_Constants.h index bd49efd..d6c7e3d 100644 --- a/src/SparkFun_MMC5983MA_Arduino_Library_Constants.h +++ b/src/SparkFun_MMC5983MA_Arduino_Library_Constants.h @@ -85,7 +85,10 @@ enum class SF_MMC5983MA_ERROR I2C_INITIALIZATION_ERROR, SPI_INITIALIZATION_ERROR, INVALID_DEVICE, - + BUS_ERROR, + INVALID_FILTER_BANDWIDTH, + INVALID_CONTINUOUS_FREQUENCY, + INVALID_PERIODIC_SAMPLES }; #endif \ No newline at end of file diff --git a/src/SparkFun_MMC5983MA_IO.cpp b/src/SparkFun_MMC5983MA_IO.cpp index 7638ae0..5f512b7 100644 --- a/src/SparkFun_MMC5983MA_IO.cpp +++ b/src/SparkFun_MMC5983MA_IO.cpp @@ -60,7 +60,7 @@ bool SFE_MMC5983MA_IO::begin(const uint8_t csPin, SPISettings userSettings, SPIC bool SFE_MMC5983MA_IO::isConnected() { - bool result = false; + bool result; if (useSPI) { _spiPort->beginTransaction(_mmcSpiSettings); @@ -74,14 +74,20 @@ bool SFE_MMC5983MA_IO::isConnected() else { _i2cPort->beginTransmission(I2C_ADDR); - if (_i2cPort->endTransmission() == 0) - result = readSingleByte(PROD_ID_REG) == PROD_ID; + result = (_i2cPort->endTransmission() == 0); + if (result) + { + uint8_t id = 0; + result &= readSingleByte(PROD_ID_REG, &id); + result &= id == PROD_ID; + } } return result; } -void SFE_MMC5983MA_IO::writeMultipleBytes(const uint8_t registerAddress, uint8_t *const buffer, uint8_t const packetLength) +bool SFE_MMC5983MA_IO::writeMultipleBytes(const uint8_t registerAddress, uint8_t *const buffer, uint8_t const packetLength) { + bool success = true; if (useSPI) { _spiPort->beginTransaction(_mmcSpiSettings); @@ -97,13 +103,14 @@ void SFE_MMC5983MA_IO::writeMultipleBytes(const uint8_t registerAddress, uint8_t _i2cPort->write(registerAddress); for (uint8_t i = 0; i < packetLength; i++) _i2cPort->write(buffer[i]); - - _i2cPort->endTransmission(); + success = _i2cPort->endTransmission() == 0; } + return success; } -void SFE_MMC5983MA_IO::readMultipleBytes(const uint8_t registerAddress, uint8_t *const buffer, const uint8_t packetLength) +bool SFE_MMC5983MA_IO::readMultipleBytes(const uint8_t registerAddress, uint8_t *const buffer, const uint8_t packetLength) { + bool success = true; if (useSPI) { _spiPort->beginTransaction(_mmcSpiSettings); @@ -117,23 +124,25 @@ void SFE_MMC5983MA_IO::readMultipleBytes(const uint8_t registerAddress, uint8_t { _i2cPort->beginTransmission(I2C_ADDR); _i2cPort->write(registerAddress); - _i2cPort->endTransmission(); + success = _i2cPort->endTransmission() == 0; - _i2cPort->requestFrom(I2C_ADDR, packetLength); - for (uint8_t i = 0; (i < packetLength); i++) + uint8_t returned = _i2cPort->requestFrom(I2C_ADDR, packetLength); + for (uint8_t i = 0; (i < packetLength) && (i < returned); i++) buffer[i] = _i2cPort->read(); + success &= returned == packetLength; } + return success; } -uint8_t SFE_MMC5983MA_IO::readSingleByte(const uint8_t registerAddress) +bool SFE_MMC5983MA_IO::readSingleByte(const uint8_t registerAddress, uint8_t *buffer) { - uint8_t result = 0; + bool success = true; if (useSPI) { _spiPort->beginTransaction(_mmcSpiSettings); digitalWrite(_csPin, LOW); _spiPort->transfer(READ_REG(registerAddress)); - result = _spiPort->transfer(DUMMY); + *buffer = _spiPort->transfer(DUMMY); digitalWrite(_csPin, HIGH); _spiPort->endTransaction(); } @@ -141,15 +150,19 @@ uint8_t SFE_MMC5983MA_IO::readSingleByte(const uint8_t registerAddress) { _i2cPort->beginTransmission(I2C_ADDR); _i2cPort->write(registerAddress); - _i2cPort->endTransmission(); - _i2cPort->requestFrom(I2C_ADDR, 1U); - result = _i2cPort->read(); + success = _i2cPort->endTransmission() == 0; + + uint8_t returned = _i2cPort->requestFrom(I2C_ADDR, 1U); + if (returned == 1) + *buffer = _i2cPort->read(); + success &= returned == 1; } - return result; + return success; } -void SFE_MMC5983MA_IO::writeSingleByte(const uint8_t registerAddress, const uint8_t value) +bool SFE_MMC5983MA_IO::writeSingleByte(const uint8_t registerAddress, const uint8_t value) { + bool success = true; if (useSPI) { _spiPort->beginTransaction(_mmcSpiSettings); @@ -164,27 +177,33 @@ void SFE_MMC5983MA_IO::writeSingleByte(const uint8_t registerAddress, const uint _i2cPort->beginTransmission(I2C_ADDR); _i2cPort->write(registerAddress); _i2cPort->write(value); - _i2cPort->endTransmission(); + success = _i2cPort->endTransmission() == 0; } + return success; } -void SFE_MMC5983MA_IO::setRegisterBit(const uint8_t registerAddress, const uint8_t bitMask) +bool SFE_MMC5983MA_IO::setRegisterBit(const uint8_t registerAddress, const uint8_t bitMask) { - uint8_t value = readSingleByte(registerAddress); + uint8_t value = 0; + bool success = readSingleByte(registerAddress, &value); value |= bitMask; - writeSingleByte(registerAddress, value); + success &= writeSingleByte(registerAddress, value); + return success; } -void SFE_MMC5983MA_IO::clearRegisterBit(const uint8_t registerAddress, const uint8_t bitMask) +bool SFE_MMC5983MA_IO::clearRegisterBit(const uint8_t registerAddress, const uint8_t bitMask) { - uint8_t value = readSingleByte(registerAddress); + uint8_t value = 0; + bool success = readSingleByte(registerAddress, &value); value &= ~bitMask; - writeSingleByte(registerAddress, value); + success &= writeSingleByte(registerAddress, value); + return success; } bool SFE_MMC5983MA_IO::isBitSet(const uint8_t registerAddress, const uint8_t bitMask) { - uint8_t value = readSingleByte(registerAddress); + uint8_t value = 0; + readSingleByte(registerAddress, &value); return (value & bitMask); } diff --git a/src/SparkFun_MMC5983MA_IO.h b/src/SparkFun_MMC5983MA_IO.h index c32bacd..9e018ea 100644 --- a/src/SparkFun_MMC5983MA_IO.h +++ b/src/SparkFun_MMC5983MA_IO.h @@ -53,22 +53,22 @@ class SFE_MMC5983MA_IO bool isConnected(); // Read a single uint8_t from a register. - uint8_t readSingleByte(const uint8_t registerAddress); + bool readSingleByte(const uint8_t registerAddress, uint8_t *buffer); // Writes a single uint8_t into a register. - void writeSingleByte(const uint8_t registerAddress, const uint8_t value); + bool writeSingleByte(const uint8_t registerAddress, const uint8_t value); // Reads multiple bytes from a register into buffer uint8_t array. - void readMultipleBytes(const uint8_t registerAddress, uint8_t *const buffer, const uint8_t packetLength); + bool readMultipleBytes(const uint8_t registerAddress, uint8_t *const buffer, const uint8_t packetLength); // Writes multiple bytes to register from buffer uint8_t array. - void writeMultipleBytes(const uint8_t registerAddress, uint8_t *const buffer, const uint8_t packetLength); + bool writeMultipleBytes(const uint8_t registerAddress, uint8_t *const buffer, const uint8_t packetLength); // Sets a single bit in a specific register. Bit position ranges from 0 (lsb) to 7 (msb). - void setRegisterBit(const uint8_t registerAddress, const uint8_t bitMask); + bool setRegisterBit(const uint8_t registerAddress, const uint8_t bitMask); // Clears a single bit in a specific register. Bit position ranges from 0 (lsb) to 7 (msb). - void clearRegisterBit(const uint8_t registerAddress, const uint8_t bitMask); + bool clearRegisterBit(const uint8_t registerAddress, const uint8_t bitMask); // Returns true if a specific bit is set in a register. Bit position ranges from 0 (lsb) to 7 (msb). bool isBitSet(const uint8_t registerAddress, const uint8_t bitMask);