SCL3300 sensor 3-axis inclinometer with angle output and digital SPI interface
Revision 0:e8ba98a758d0, committed 2022-08-15
- Comitter:
- sameera0824
- Date:
- Mon Aug 15 19:17:10 2022 +0000
- Commit message:
- Initial commit
Changed in this revision
SCL3300.cpp | Show annotated file Show diff for this revision Revisions of this file |
SCL3300.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r e8ba98a758d0 SCL3300.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SCL3300.cpp Mon Aug 15 19:17:10 2022 +0000 @@ -0,0 +1,469 @@ +/****************************************************************************** +SCL3300.cpp +SCL3300 Arduino Driver +David Armstrong +Version 3.3.0 - September 13, 2021 +https://github.com/DavidArmstrong/SCL3300 + +Resources: +Uses SPI.h for SPI operation + +Development environment specifics: +Arduino IDE 1.8.15 + +This code is released under the [MIT License](http://opensource.org/licenses/MIT). +Please review the LICENSE.md file included with this example. +Distributed as-is; no warranty is given. + +******************************************************************************/ + +// include this library's description file +#include "SCL3300.h" + +// Public Methods ////////////////////////////////////////////////////////// +// Set the sensor mode to the number provided as modeNum. +boolean SCL3300::setMode(int modeNum) { + // Set Sensor mode - If not called, the default is mode 4, as set in header file + // Only allowed values are: 1,2,3,4 + if (modeNum > 0 && modeNum < 5) { + scl3300_mode = modeNum; + if (!setFastRead) beginTransmission(); //Set up this SPI port/bus + transfer(modeCMD[scl3300_mode]); //Set mode on hardware + if (!setFastRead) endTransmission(); //Let go of SPI port/bus + if (crcerr || statuserr) { + reset(); //Reset chip to fix the error state + return false; //Let the caller know something went wrong + } else return true; // Valid value, and chip was set to that mode + } else + return false; // Invalid value +} + +// Current Version of begin() to initialize the library and the SCL3300 +boolean SCL3300::begin(void) { + //This is the updated Version 3 begin function + // Determine if we need to set up to use the default SPI interface, or some other one + if (_spiPort == nullptr) _spiPort = &SPI; + + //Wait the required 1 ms before initializing the SCL3300 inclinomenter + unsigned long startmillis = millis(); + while (millis() - startmillis < 1) ; + + initSPI(); // Initialize SPI Library + if (!setFastRead) beginTransmission(); //Set up this SPI port/bus + //Write SW Reset command + transfer(SwtchBnk0); + transfer(SWreset); + startmillis = millis(); + while (millis() - startmillis < 1) ; + //Set measurement mode + transfer(modeCMD[scl3300_mode]); //Set mode on hardware + //We're good, so Enable angle outputs + transfer(EnaAngOut); + //The first response after reset is undefined and shall be discarded + //wait 5 ms to stablize + startmillis = millis(); + while (millis() - startmillis < 100) ; + + //Read Status to clear the status summary + transfer(RdStatSum); + transfer(RdStatSum); //Again, due to off-response protocol used + transfer(RdStatSum); //And now we can get the real status + + //Read the WHOAMI register + transfer(RdWHOAMI); + //And again + transfer(RdWHOAMI); + if (!setFastRead) endTransmission(); //Let go of SPI port/bus + //We now wait until the end of begin() to report if an error occurred + if (crcerr || statuserr) return false; + // Once everything is initialized, return a known expected value + // The WHOAMI command should give an 8 bit value of 0xc1 + return (SCL3300_DATA == 0xc1); //Let the caller know if this worked +} + +// Set up the SPI communication with the SCL3300 with provided Chip Select pin number, and provided SPI port +boolean SCL3300::begin(SPIClass &spiPort, uint8_t csPin) { + scl3300_csPin = csPin; + _spiPort = &spiPort; //Grab the port the user wants us to use + return begin(); +} // begin + +// Set up the SPI communication with the SCL3300 with provided Chip Select pin number +boolean SCL3300::begin(uint8_t csPin) { + scl3300_csPin = csPin; + _spiPort = &SPI; // With this call, we do the default SPI interface + return begin(); +} // begin + +//Check to validate that the sensor is still reachable and ready to provide data +boolean SCL3300::isConnected() { + if (!setFastRead) beginTransmission(); //Set up this SPI port/bus + transfer(SwtchBnk0); + //Read the WHOAMI register + transfer(RdWHOAMI); + //And again + transfer(RdWHOAMI); + if (!setFastRead) endTransmission(); //Let go of SPI port/bus + if (crcerr || statuserr) return false; + // Once everything is initialized, return a known expected value + // The WHOAMI command should give an 8 bit value of 0xc1 + return (SCL3300_DATA == 0xc1); //Let the caller know if this worked +} + +//Read all the sensor data together to keep it consistent +//This is required according to the datasheet +boolean SCL3300::available(void) { + //Version 3 of this function + boolean errorflag = false; + //Read all Sensor Data, as per Datasheet requirements + if (!setFastRead) beginTransmission(); //Set up this SPI port/bus + transfer(SwtchBnk0); + transfer(RdAccX); + if (crcerr || statuserr) errorflag = true; + transfer(RdAccY); + if (crcerr || statuserr) errorflag = true; + sclData.AccX = SCL3300_DATA; + transfer(RdAccZ); + if (crcerr || statuserr) errorflag = true; + sclData.AccY = SCL3300_DATA; + transfer(RdSTO); + if (crcerr || statuserr) errorflag = true; + sclData.AccZ = SCL3300_DATA; + transfer(RdTemp); + if (crcerr || statuserr) errorflag = true; + sclData.STO = SCL3300_DATA; + transfer(RdAngX); + if (crcerr || statuserr) errorflag = true; + sclData.TEMP = SCL3300_DATA; + transfer(RdAngY); + if (crcerr || statuserr) errorflag = true; + sclData.AngX = SCL3300_DATA; + transfer(RdAngZ); + if (crcerr || statuserr) errorflag = true; + sclData.AngY = SCL3300_DATA; + transfer(RdStatSum); + if (crcerr || statuserr) errorflag = true; + sclData.AngZ = SCL3300_DATA; + transfer(RdWHOAMI); + if (crcerr || statuserr) errorflag = true; + sclData.StatusSum = SCL3300_DATA; + transfer(RdWHOAMI); + if (crcerr || statuserr) errorflag = true; + sclData.WHOAMI = SCL3300_DATA; + if (!setFastRead) endTransmission(); //Let go of SPI port/bus + if (errorflag) return false; //Inform caller that something went wrong + // The WHOAMI command should give an 8 bit value of 0xc1 + return (SCL3300_DATA == 0xc1); //Let the caller know this worked +} + +/* Set SCL3300 library into Fast Read Mode + * Warning: Using Fast Read Mode in the library works by keeping the + * SPI connection continuously open. This may or may not affect + * the behavior of other hardware interactions, depending on the + * sketch design. Fast Read Mode is considered an advanced use case, + * and not recommended for the beginner. +*/ +void SCL3300::setFastReadMode() { + setFastRead = true; + beginTransmission(); //Set up this SPI port/bus + begin(); //Re-init chip +} + +/* Stop Fast Read Mode + * Warning: Using Fast Read Mode in the library works by keeping the + * SPI connection continuously open. This may or may not affect + * the behavior of other hardware interactions, depending on the + * sketch design. Fast Read Mode is considered an advanced use case, + * and not recommended for the beginner. +*/ +void SCL3300::stopFastReadMode() { + setFastRead = false; + endTransmission(); //Close connection to SPI port/bus + begin(); //Re-init chip +} + +//Return the calculated X axis tilt angle in degrees +double SCL3300::getCalculatedAngleX() { + double tempX = angle(sclData.AngX); + if (tempX < 0.) tempX += 360.; + return tempX; +} + +//Return the calculated Y axis tilt angle in degrees +double SCL3300::getCalculatedAngleY() { + double tempY = angle(sclData.AngY); + if (tempY < 0.) tempY += 360.; + return tempY; +} + +//Return the calculated Z axis tilt angle in degrees +double SCL3300::getCalculatedAngleZ() { + double tempZ = angle(sclData.AngZ); + if (tempZ < 0.) tempZ += 360.; + return tempZ; +} + +//Return the calculated X axis offset tilt angle in degrees +double SCL3300::getTiltLevelOffsetAngleX() { + return angle(sclData.AngX); +} + +//Return the calculated Y axis offset tilt angle in degrees +double SCL3300::getTiltLevelOffsetAngleY() { + return angle(sclData.AngY); +} + +//Return the calculated Z axis offset tilt angle in degrees +double SCL3300::getTiltLevelOffsetAngleZ() { + return angle(sclData.AngZ); +} + +//Return the calculated X axis accelerometer value in units of 'g' +double SCL3300::getCalculatedAccelerometerX(void) { + return acceleration(sclData.AccX); +} + +//Return the calculated Y axis accelerometer value in units of 'g' +double SCL3300::getCalculatedAccelerometerY(void) { + return acceleration(sclData.AccY); +} + +//Return the calculated Z axis accelerometer value in units of 'g' +double SCL3300::getCalculatedAccelerometerZ(void) { + return acceleration(sclData.AccZ); +} + +//Return value of Error Flag 1 register +uint16_t SCL3300::getErrFlag1(void) { + if (!setFastRead) beginTransmission(); //Set up this SPI port/bus + transfer(SwtchBnk0); + transfer(RdErrFlg1); + transfer(RdErrFlg1); + if (!setFastRead) endTransmission(); //Let go of SPI port/bus + //Since we are fetching the Error Flag 1 value, we want to return what we got + //to the caller, regardless of whether or not there was an error + //if (crcerr || statuserr) return ((uint16_t)(SCL3300_CMD) & 0xff); //check CRC and RS bits + return SCL3300_DATA; +} + +//Return value of Error Flag 2 register +uint16_t SCL3300::getErrFlag2(void) { + if (!setFastRead) beginTransmission(); //Set up this SPI port/bus + transfer(SwtchBnk0); + transfer(RdErrFlg2); + transfer(RdErrFlg2); + if (!setFastRead) endTransmission(); //Let go of SPI port/bus + //Since we are fetching the Error Flag 2 value, we want to return what we got + //to the caller, regardless of whether or not there was an error + //if (crcerr || statuserr) return ((uint16_t)(SCL3300_CMD) & 0xff); //check CRC and RS bits + return SCL3300_DATA; +} + +// Read the sensor Serial Number as created by the manufacturer +unsigned long SCL3300::getSerialNumber(void) { + //Return Device Serial number + boolean errorflag = false; + unsigned long serialNum = 0; + if (!setFastRead) beginTransmission(); //Set up this SPI port/bus + transfer(SwtchBnk1); + if (crcerr || statuserr) errorflag = true; + transfer(RdSer1); + if (crcerr || statuserr) errorflag = true; + transfer(RdSer2); + serialNum = SCL3300_DATA; + if (crcerr || statuserr) errorflag = true; + transfer(SwtchBnk0); + serialNum = ((unsigned long)SCL3300_DATA << 16) | serialNum; + if (!setFastRead) endTransmission(); //Let go of SPI port/bus + //We wait until now to return an error code + //In this case we send a 0 since a real serial number will never be 0 + if (crcerr || statuserr || errorflag) return 0; + return serialNum; +} + +// Place the sensor in a Powered Down mode to save power +uint16_t SCL3300::powerDownMode(void) { + //Software power down of sensor + if (!setFastRead) beginTransmission(); //Set up this SPI port/bus + transfer(SwtchBnk0); + transfer(SetPwrDwn); + endTransmission(); //Let go of SPI port/bus + //Since an error is non-zero, we will return 0 if there was no error + if (crcerr || statuserr) return (uint16_t)(SCL3300_CMD & 0xff); //check CRC and RS bits + return 0; +} + +// Revive the sensor from a power down mode so we can start getting data again +uint16_t SCL3300::WakeMeUp(void) { + //Software Wake Up of sensor + beginTransmission(); //Set up this SPI port/bus + transfer(WakeUp); + if (!setFastRead) endTransmission(); //Let go of SPI port/bus + //Since an error is non-zero, we will return 0 if there was no error + if (crcerr || statuserr) return (uint16_t)(SCL3300_CMD & 0xff); //check CRC and RS bits + return 0; +} + +// Hardware reset of the sensor electronics +uint16_t SCL3300::reset(void) { + //Software reset of sensor + //beginTransmission(); //Set up this SPI port/bus + //transfer(SwtchBnk0); + //transfer(SWreset); + //endTransmission(); //Let go of SPI port/bus + //we have to call begin() to set up the SCL3300 to the same state as before it was reset + begin(); //Re-init chip + //Since an error is non-zero, we will return 0 if there was no error + if (crcerr || statuserr) return (uint16_t)(SCL3300_CMD & 0xff); //check CRC and RS bits + return 0; +} + +// Routine to get temperature in degrees Celsius +double SCL3300::getCalculatedTemperatureCelsius(void) { + // Return calculated temperature in degrees C + double Temperature = -273. + (sclData.TEMP / 18.9); + return Temperature; +} + +// Routine to get temperature in degrees Farenheit +double SCL3300::getCalculatedTemperatureFarenheit(void) { + // Return calculated temperature in degrees F + double Temperature = -273. + (sclData.TEMP / 18.9); + Temperature = (Temperature * 9./5.) + 32.; + return Temperature; +} + +//Convert raw angle value to degrees tilt +double SCL3300::angle(int16_t SCL3300_ANG) { //two's complement value expected + // Return Angle in degrees + double Angle = (SCL3300_ANG / 16384.) * 90.; // 16384 = 2^14 + return Angle; +} + + //Convert raw accelerometer value to g's of acceleration +double SCL3300::acceleration(int16_t SCL3300_ACC) { //two's complement value expected + // Return acceleration in g + if (scl3300_mode == 1) return (double)SCL3300_ACC / 6000.; + if (scl3300_mode == 2) return (double)SCL3300_ACC / 3000.; + if (scl3300_mode == 3) return (double)SCL3300_ACC / 12000.; + if (scl3300_mode == 4) return (double)SCL3300_ACC / 12000.; + return (double)SCL3300_ACC / 12000.; //Default should never be reached +} + +//private functions for serial transmission +// Begin SPI bus transmission to the device +void SCL3300::beginTransmission() { + _spiPort->beginTransaction(spiSettings); +} //beginTransmission + +// End SPI bus transmission to the device +void SCL3300::endTransmission() { + // take the chip/slave select high to de-select: + digitalWrite(scl3300_csPin, HIGH); + _spiPort->endTransaction(); + unsigned long startmillis = millis(); + while (millis() - startmillis < 1) ; //wait a bit +} //endTransmission + +//Initialize the Arduino SPI library for the SCL3300 hardware +void SCL3300::initSPI() { + //Initialize the Arduino SPI library for the SCL3300 hardware + _spiPort->begin(); + // Maximum SPI frequency is 2 MHz - 4 MHz to achieve the best performance + // initialize the chip select pin: + pinMode(scl3300_csPin, OUTPUT); + digitalWrite(scl3300_csPin, HIGH); + // Data is read and written MSb first. + // Data is captured on rising edge of clock (CPHA = 0) + // Data is propagated on the falling edge (MISO line) of the SCK. (CPOL = 0) +} + +// The following is taken directly from the Murata SCL3300 datasheet +// Calculate CRC for 24 MSB's of the 32 bit dword +// (8 LSB's are the CRC field and are not included in CRC calculation) +uint8_t SCL3300::CalculateCRC(uint32_t Data) +{ +uint8_t BitIndex; +uint8_t BitValue; +uint8_t SCL3300_CRC; + +SCL3300_CRC = 0xFF; +for (BitIndex = 31; BitIndex > 7; BitIndex--) { + BitValue = (uint8_t)((Data >> BitIndex) & 0x01); + SCL3300_CRC = CRC8(BitValue, SCL3300_CRC); +} +SCL3300_CRC = (uint8_t)~SCL3300_CRC; +return SCL3300_CRC; +} +uint8_t SCL3300::CRC8(uint8_t BitValue, uint8_t SCL3300_CRC) +{ + uint8_t Temp; + Temp = (uint8_t)(SCL3300_CRC & 0x80); + if (BitValue == 0x01) { + Temp ^= 0x80; + } + SCL3300_CRC <<= 1; + if (Temp > 0) { + SCL3300_CRC ^= 0x1D; + } + return SCL3300_CRC; +} + +// Routine to transfer a 32-bit integer to the SCL3300, and return the 32-bit data read +unsigned long SCL3300::transfer(unsigned long value) { + FourByte dataorig; + unsigned long startmicros; + + dataorig.bit32 = value; //Allow 32 bit value to be sent 8 bits at a time + #ifdef debug_scl3300 + Serial_SCL.print(dataorig.bit32, HEX); + Serial_SCL.print(" "); + for (int j = 3; j >= 0; j--) { + Serial_SCL.print(dataorig.bit8[j], HEX); + Serial_SCL.print(" "); + } + #endif + //Must allow at least 10 uSec between SPI transfers + //The datasheet shows the CS line must be high during this time + if (!setFastRead) startmicros = micros(); + //while ((micros() - startmicros < 10) && (micros() > 10)) ; + if (!setFastRead) while ((micros() - startmicros < 10)) ; + + digitalWrite(scl3300_csPin, LOW); //Now chip select can be enabled for the full 32 bit xfer + SCL3300_DATA = 0; + for (int i = 3; i >= 0; i--) { //Xfers are done MSB first + dataorig.bit8[i] = _spiPort->transfer(dataorig.bit8[i]); + } + SCL3300_DATA = dataorig.bit8[1] + (dataorig.bit8[2] << 8); + SCL3300_CRC = dataorig.bit8[0]; + SCL3300_CMD = dataorig.bit8[3]; + digitalWrite(scl3300_csPin, HIGH); //And we are done + #ifdef debug_scl3300 + for (int i = 3; i >= 0; i--) { + Serial_SCL.print(" "); + Serial_SCL.print(dataorig.bit8[i], HEX); + } + Serial_SCL.print(" "); + #endif + if (SCL3300_CRC == CalculateCRC(dataorig.bit32)) + crcerr = false; + else + crcerr = true; + //check RS bits + if ((SCL3300_CMD & 0x03) == 0x01) + statuserr = false; + else + statuserr = true; + #ifdef debug_scl3300 + Serial_SCL.print((SCL3300_CMD & 0x03)); + Serial_SCL.print(" "); + Serial_SCL.print(SCL3300_DATA, HEX); + Serial_SCL.print(" "); + Serial_SCL.print(SCL3300_CRC, HEX); + Serial_SCL.print(" "); + Serial_SCL.print(CalculateCRC(dataorig.bit32), HEX); + Serial_SCL.print(" "); + Serial_SCL.println(crcerr); + #endif + return dataorig.bit32; +}
diff -r 000000000000 -r e8ba98a758d0 SCL3300.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SCL3300.h Mon Aug 15 19:17:10 2022 +0000 @@ -0,0 +1,164 @@ +/****************************************************************************** +SCL3300.h +SCL3300 Arduino Library Header File +David Armstrong +Version 3.2.0 - September 3, 2021 +https://github.com/DavidArmstrong/SCL3300 + +Resources: +Uses SPI.h for SPI operation + +Development environment specifics: +Arduino IDE 1.8.9, 1.8.11, 1.8.12, 1.8.13, 1.8.15 + +This code is released under the [MIT License](http://opensource.org/licenses/MIT). +Please review the LICENSE.md file included with this example. +Distributed as-is; no warranty is given. + +This file prototypes the SCL3300 class, as implemented in SCL3300.cpp + +******************************************************************************/ + +// ensure this library description is only included once +#ifndef __SCL3300_h +#define __SCL3300_h + +// Uncomment the following line for debugging output +//#define debug_scl3300 + +// Need the following define for SAMD processors +#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL) + #define Serial_SCL SERIAL_PORT_USBVIRTUAL +#else + #define Serial_SCL Serial +#endif + +#include <stdint.h> + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include <SPI.h> // SPI library is used for...SPI. + +#ifndef SCL3300_SPI_CLOCK +#ifdef ARDUINO_ARCH_ESP32 +#define SCL3300_SPI_CLOCK 4000000 +#else +#define SCL3300_SPI_CLOCK 4000000 +#endif +#endif + +#ifndef SCL3300_SPI_MODE +#define SCL3300_SPI_MODE SPI_MODE0 +#endif + +//Define allowed commands to SCL3300 inclinometer +#define RdAccX 0x040000f7 +#define RdAccY 0x080000fd +#define RdAccZ 0x0c0000fb +#define RdSTO 0x100000e9 +#define EnaAngOut 0xb0001f6f +#define RdAngX 0x240000c7 +#define RdAngY 0x280000cd +#define RdAngZ 0x2c0000cb +#define RdTemp 0x140000ef +#define RdStatSum 0x180000e5 +#define RdErrFlg1 0x1c0000e3 +#define RdErrFlg2 0x200000c1 +#define RdCMD 0x340000df +#define ChgMode1 0xb400001f +#define ChgMode2 0xb4000102 +#define ChgMode3 0xb4000225 +#define ChgMode4 0xb4000338 +#define SetPwrDwn 0xb400046b +#define WakeUp 0xb400001f +#define SWreset 0xb4002098 +#define RdWHOAMI 0x40000091 +#define RdSer1 0x640000a7 +#define RdSer2 0x680000AD +#define RdCurBank 0x7c0000b3 +#define SwtchBnk0 0xfc000073 +#define SwtchBnk1 0xfc00016e + +// Structure to hold raw sensor data +// We need to populate all this every time we read a set of data +struct SCL3300data { + public: + int16_t AccX; + int16_t AccY; + int16_t AccZ; + int16_t STO; + int16_t TEMP; + int16_t AngX; + int16_t AngY; + int16_t AngZ; + uint16_t StatusSum; + uint16_t WHOAMI; +}; + +// SCL3300 library interface description +class SCL3300 { + // user-accessible "public" interface + public: + SPISettings spiSettings{SCL3300_SPI_CLOCK, MSBFIRST, SCL3300_SPI_MODE}; + + SCL3300data sclData; + boolean setMode(int mode); + boolean begin(void); + boolean begin(uint8_t csPin); + boolean begin(SPIClass &spiPort, uint8_t csPin); + //Functions to retrieve sensor data + boolean isConnected(); + boolean available(void); + void setFastReadMode(); + void stopFastReadMode(); + double getCalculatedAngleX(void); + double getCalculatedAngleY(void); + double getCalculatedAngleZ(void); + double getTiltLevelOffsetAngleX(void); + double getTiltLevelOffsetAngleY(void); + double getTiltLevelOffsetAngleZ(void); + double getCalculatedAccelerometerX(void); + double getCalculatedAccelerometerY(void); + double getCalculatedAccelerometerZ(void); + uint16_t getErrFlag1(void); + uint16_t getErrFlag2(void); + unsigned long getSerialNumber(void); + double getCalculatedTemperatureCelsius(void); + double getCalculatedTemperatureFarenheit(void); + double angle(int16_t SCL3300_ANG); //two's complement value expected + double acceleration(int16_t SCL3300_ACC); + bool crcerr, statuserr; + uint16_t powerDownMode(void); + uint16_t WakeMeUp(void); + uint16_t reset(void); + + // library-accessible "private" interface + private: + SPIClass *_spiPort = NULL; //The generic connection to user's chosen spi hardware + + uint8_t scl3300_csPin = 10; // Default SPI chip select pin + uint8_t scl3300_mode = 4; // Default inclinometer mode + uint8_t SCL3300_CMD, SCL3300_CRC; + uint16_t SCL3300_DATA; + double Temperature, X_angle, Y_angle, Z_angle; + bool setFastRead = false; + + void initSPI(); + void beginTransmission(); + void endTransmission(); + uint8_t CalculateCRC(uint32_t Data); + uint8_t CRC8(uint8_t BitValue, uint8_t SCL3300_CRC); + unsigned long transfer(unsigned long value); + + union FourByte { + unsigned long bit32; + unsigned int bit16[2]; + unsigned char bit8[4]; + }; + unsigned long modeCMD[5] = { 0, ChgMode1, ChgMode2, ChgMode3, ChgMode4 }; +}; +#endif