MPU 9255 sketcha
parent
5e380911f7
commit
9e0a0c5c89
|
@ -0,0 +1,127 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "I2Cdev.h"
|
||||||
|
#include "RTIMULib/RTIMUSettings.h"
|
||||||
|
#include "RTIMULib/RTIMU.h"
|
||||||
|
#include "RTIMULib/RTFusionRTQF.h"
|
||||||
|
#include "CalLib/CalLib.h"
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
RTIMU *imu; // the IMU object
|
||||||
|
RTFusionRTQF fusion; // the fusion object
|
||||||
|
RTIMUSettings settings; // the settings object
|
||||||
|
|
||||||
|
// DISPLAY_INTERVAL sets the rate at which results are displayed
|
||||||
|
|
||||||
|
#define DISPLAY_INTERVAL 100 // interval between pose displays
|
||||||
|
|
||||||
|
// SERIAL_PORT_SPEED defines the speed to use for the debug serial port
|
||||||
|
|
||||||
|
#define SERIAL_PORT_SPEED 115200
|
||||||
|
|
||||||
|
unsigned long lastDisplay;
|
||||||
|
unsigned long lastRate;
|
||||||
|
int sampleCount;
|
||||||
|
RTQuaternion gravity;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
int errcode;
|
||||||
|
|
||||||
|
Serial.begin(SERIAL_PORT_SPEED);
|
||||||
|
Wire.begin();
|
||||||
|
imu = RTIMU::createIMU(&settings); // create the imu object
|
||||||
|
|
||||||
|
Serial.print("ArduinoIMU starting using device "); Serial.println(imu->IMUName());
|
||||||
|
if ((errcode = imu->IMUInit()) < 0) {
|
||||||
|
Serial.print("Failed to init IMU: "); Serial.println(errcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imu->getCalibrationValid())
|
||||||
|
Serial.println("Using compass calibration");
|
||||||
|
else
|
||||||
|
Serial.println("No valid compass calibration data");
|
||||||
|
|
||||||
|
lastDisplay = lastRate = millis();
|
||||||
|
sampleCount = 0;
|
||||||
|
|
||||||
|
gravity.setScalar(0);
|
||||||
|
gravity.setX(0);
|
||||||
|
gravity.setY(0);
|
||||||
|
gravity.setZ(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
unsigned long now = millis();
|
||||||
|
unsigned long delta;
|
||||||
|
RTVector3 realAccel;
|
||||||
|
RTQuaternion rotatedGravity;
|
||||||
|
RTQuaternion fusedConjugate;
|
||||||
|
RTQuaternion qTemp;
|
||||||
|
int loopCount = 0;
|
||||||
|
|
||||||
|
while (imu->IMURead()) { // get the latest data if ready yet
|
||||||
|
// this flushes remaining data in case we are falling behind
|
||||||
|
if (++loopCount >= 10)
|
||||||
|
continue;
|
||||||
|
fusion.newIMUData(imu->getGyro(), imu->getAccel(), imu->getCompass(), imu->getTimestamp());
|
||||||
|
|
||||||
|
// do gravity rotation and subtraction
|
||||||
|
|
||||||
|
// create the conjugate of the pose
|
||||||
|
|
||||||
|
fusedConjugate = fusion.getFusionQPose().conjugate();
|
||||||
|
|
||||||
|
// now do the rotation - takes two steps with qTemp as the intermediate variable
|
||||||
|
|
||||||
|
qTemp = gravity * fusion.getFusionQPose();
|
||||||
|
rotatedGravity = fusedConjugate * qTemp;
|
||||||
|
|
||||||
|
// now adjust the measured accel and change the signs to make sense
|
||||||
|
|
||||||
|
realAccel.setX(-(imu->getAccel().x() - rotatedGravity.x()));
|
||||||
|
realAccel.setY(-(imu->getAccel().y() - rotatedGravity.y()));
|
||||||
|
realAccel.setZ(-(imu->getAccel().z() - rotatedGravity.z()));
|
||||||
|
|
||||||
|
sampleCount++;
|
||||||
|
if ((delta = now - lastRate) >= 1000) {
|
||||||
|
Serial.print("Sample rate: "); Serial.print(sampleCount);
|
||||||
|
if (!imu->IMUGyroBiasValid())
|
||||||
|
Serial.println(", calculating gyro bias");
|
||||||
|
else
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
sampleCount = 0;
|
||||||
|
lastRate = now;
|
||||||
|
}
|
||||||
|
if ((now - lastDisplay) >= DISPLAY_INTERVAL) {
|
||||||
|
lastDisplay = now;
|
||||||
|
|
||||||
|
RTMath::display("Accel:", realAccel);
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "CalLib.h"
|
||||||
|
#ifdef __SAM3X8E__
|
||||||
|
|
||||||
|
// Due version
|
||||||
|
|
||||||
|
#include "DueFlash.h"
|
||||||
|
|
||||||
|
DueFlash flash;
|
||||||
|
|
||||||
|
void calLibErase(byte device)
|
||||||
|
{
|
||||||
|
uint32_t data = 0;
|
||||||
|
|
||||||
|
flash.write(CALLIB_START + sizeof(CALLIB_DATA) * device, &data, 1); // just destroy the valid byte
|
||||||
|
}
|
||||||
|
|
||||||
|
void calLibWrite(byte device, CALLIB_DATA *calData)
|
||||||
|
{
|
||||||
|
calData->validL = CALLIB_DATA_VALID_LOW;
|
||||||
|
calData->validH = CALLIB_DATA_VALID_HIGH;
|
||||||
|
|
||||||
|
flash.write(CALLIB_START + sizeof(CALLIB_DATA) * device, (uint32_t *)calData, sizeof(CALLIB_DATA) / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean calLibRead(byte device, CALLIB_DATA *calData)
|
||||||
|
{
|
||||||
|
memcpy(calData, CALLIB_START + sizeof(CALLIB_DATA) * device, sizeof(CALLIB_DATA));
|
||||||
|
return calData->valid == CALLIB_DATA_VALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// AVR version
|
||||||
|
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
void calLibErase(byte device)
|
||||||
|
{
|
||||||
|
EEPROM.write(sizeof(CALLIB_DATA) * device, 0); // just destroy the valid byte
|
||||||
|
}
|
||||||
|
|
||||||
|
void calLibWrite(byte device, CALLIB_DATA *calData)
|
||||||
|
{
|
||||||
|
byte *ptr = (byte *)calData;
|
||||||
|
byte length = sizeof(CALLIB_DATA);
|
||||||
|
int eeprom = sizeof(CALLIB_DATA) * device;
|
||||||
|
|
||||||
|
calData->validL = CALLIB_DATA_VALID_LOW;
|
||||||
|
calData->validH = CALLIB_DATA_VALID_HIGH;
|
||||||
|
|
||||||
|
for (byte i = 0; i < length; i++)
|
||||||
|
EEPROM.write(eeprom + i, *ptr++);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean calLibRead(byte device, CALLIB_DATA *calData)
|
||||||
|
{
|
||||||
|
byte *ptr = (byte *)calData;
|
||||||
|
byte length = sizeof(CALLIB_DATA);
|
||||||
|
int eeprom = sizeof(CALLIB_DATA) * device;
|
||||||
|
|
||||||
|
calData->magValid = false;
|
||||||
|
|
||||||
|
if ((EEPROM.read(eeprom) != CALLIB_DATA_VALID_LOW) ||
|
||||||
|
(EEPROM.read(eeprom + 1) != CALLIB_DATA_VALID_HIGH)) {
|
||||||
|
return false; // invalid data
|
||||||
|
}
|
||||||
|
|
||||||
|
for (byte i = 0; i < length; i++)
|
||||||
|
*ptr++ = EEPROM.read(eeprom + i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,58 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _CALLIB_H_
|
||||||
|
#define _CALLIB_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define CALLIB_DATA_VALID_LOW 0xfc // pattern to detect valid config - low byte
|
||||||
|
#define CALLIB_DATA_VALID_HIGH 0x15 // pattern to detect valid config - high byte
|
||||||
|
|
||||||
|
#ifdef __SAM3X8E__
|
||||||
|
#define CALLIB_START ((uint32_t *)(IFLASH1_ADDR + IFLASH1_SIZE - IFLASH1_PAGE_SIZE))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char validL; // should contain the valid pattern if a good config
|
||||||
|
unsigned char validH; // should contain the valid pattern if a good config
|
||||||
|
unsigned char magValid; // true if data valid
|
||||||
|
unsigned char pad;
|
||||||
|
float magMin[3]; // min values
|
||||||
|
float magMax[3]; // max values
|
||||||
|
} CALLIB_DATA;
|
||||||
|
|
||||||
|
// calLibErase() erases any current data in the EEPROM
|
||||||
|
|
||||||
|
void calLibErase(byte device);
|
||||||
|
|
||||||
|
// calLibWrite() writes new data to the EEPROM
|
||||||
|
|
||||||
|
void calLibWrite(byte device, CALLIB_DATA * calData);
|
||||||
|
|
||||||
|
// calLibRead() reads existing data and returns true if valid else false in not.
|
||||||
|
|
||||||
|
boolean calLibRead(byte device, CALLIB_DATA * calData);
|
||||||
|
|
||||||
|
#endif // _CALLIB_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,269 @@
|
||||||
|
// I2Cdev library collection - Main I2C device class header file
|
||||||
|
// Abstracts bit and byte I2C R/W functions into a convenient class
|
||||||
|
// 6/9/2012 by Jeff Rowberg <jeff@rowberg.net>
|
||||||
|
//
|
||||||
|
// Changelog:
|
||||||
|
// 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications
|
||||||
|
// 2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan)
|
||||||
|
// 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire
|
||||||
|
// - add compiler warnings when using outdated or IDE or limited I2Cdev implementation
|
||||||
|
// 2011-11-01 - fix write*Bits mask calculation (thanks sasquatch @ Arduino forums)
|
||||||
|
// 2011-10-03 - added automatic Arduino version detection for ease of use
|
||||||
|
// 2011-10-02 - added Gene Knight's NBWire TwoWire class implementation with small modifications
|
||||||
|
// 2011-08-31 - added support for Arduino 1.0 Wire library (methods are different from 0.x)
|
||||||
|
// 2011-08-03 - added optional timeout parameter to read* methods to easily change from default
|
||||||
|
// 2011-08-02 - added support for 16-bit registers
|
||||||
|
// - fixed incorrect Doxygen comments on some methods
|
||||||
|
// - added timeout value for read operations (thanks mem @ Arduino forums)
|
||||||
|
// 2011-07-30 - changed read/write function structures to return success or byte counts
|
||||||
|
// - made all methods static for multi-device memory savings
|
||||||
|
// 2011-07-28 - initial release
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
I2Cdev device library code is placed under the MIT license
|
||||||
|
Copyright (c) 2013 Jeff Rowberg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
===============================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _I2CDEV_H_
|
||||||
|
#define _I2CDEV_H_
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// I2C interface implementation setting
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE
|
||||||
|
//#define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_FASTWIRE
|
||||||
|
|
||||||
|
// comment this out if you are using a non-optimal IDE/implementation setting
|
||||||
|
// but want the compiler to shut up about it
|
||||||
|
#define I2CDEV_IMPLEMENTATION_WARNINGS
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// I2C interface implementation options
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#define I2CDEV_ARDUINO_WIRE 1 // Wire object from Arduino
|
||||||
|
#define I2CDEV_BUILTIN_NBWIRE 2 // Tweaked Wire object from Gene Knight's NBWire project
|
||||||
|
// ^^^ NBWire implementation is still buggy w/some interrupts!
|
||||||
|
#define I2CDEV_BUILTIN_FASTWIRE 3 // FastWire object from Francesco Ferrara's project
|
||||||
|
#define I2CDEV_I2CMASTER_LIBRARY 4 // I2C object from DSSCircuits I2C-Master Library at https://github.com/DSSCircuits/I2C-Master-Library
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Arduino-style "Serial.print" debug constant (uncomment to enable)
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//#define I2CDEV_SERIAL_DEBUG
|
||||||
|
|
||||||
|
#ifdef ARDUINO
|
||||||
|
#if ARDUINO < 100
|
||||||
|
#include "WProgram.h"
|
||||||
|
#else
|
||||||
|
#include "Arduino.h"
|
||||||
|
#endif
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
|
||||||
|
#include <Wire.h>
|
||||||
|
#endif
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_I2CMASTER_LIBRARY
|
||||||
|
#include <I2C.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];")
|
||||||
|
#define I2CDEV_DEFAULT_READ_TIMEOUT 1000
|
||||||
|
|
||||||
|
class I2Cdev {
|
||||||
|
public:
|
||||||
|
I2Cdev();
|
||||||
|
|
||||||
|
static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
|
||||||
|
static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data);
|
||||||
|
static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data);
|
||||||
|
static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data);
|
||||||
|
static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data);
|
||||||
|
static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data);
|
||||||
|
static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data);
|
||||||
|
static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data);
|
||||||
|
static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data);
|
||||||
|
|
||||||
|
static uint16_t readTimeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
|
||||||
|
//////////////////////
|
||||||
|
// FastWire 0.24
|
||||||
|
// This is a library to help faster programs to read I2C devices.
|
||||||
|
// Copyright(C) 2012
|
||||||
|
// Francesco Ferrara
|
||||||
|
//////////////////////
|
||||||
|
|
||||||
|
/* Master */
|
||||||
|
#define TW_START 0x08
|
||||||
|
#define TW_REP_START 0x10
|
||||||
|
|
||||||
|
/* Master Transmitter */
|
||||||
|
#define TW_MT_SLA_ACK 0x18
|
||||||
|
#define TW_MT_SLA_NACK 0x20
|
||||||
|
#define TW_MT_DATA_ACK 0x28
|
||||||
|
#define TW_MT_DATA_NACK 0x30
|
||||||
|
#define TW_MT_ARB_LOST 0x38
|
||||||
|
|
||||||
|
/* Master Receiver */
|
||||||
|
#define TW_MR_ARB_LOST 0x38
|
||||||
|
#define TW_MR_SLA_ACK 0x40
|
||||||
|
#define TW_MR_SLA_NACK 0x48
|
||||||
|
#define TW_MR_DATA_ACK 0x50
|
||||||
|
#define TW_MR_DATA_NACK 0x58
|
||||||
|
|
||||||
|
#define TW_OK 0
|
||||||
|
#define TW_ERROR 1
|
||||||
|
|
||||||
|
class Fastwire {
|
||||||
|
private:
|
||||||
|
static boolean waitInt();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void setup(int khz, boolean pullup);
|
||||||
|
static byte beginTransmission(byte device);
|
||||||
|
static byte write(byte value);
|
||||||
|
static byte writeBuf(byte device, byte address, byte *data, byte num);
|
||||||
|
static byte readBuf(byte device, byte address, byte *data, byte num);
|
||||||
|
static void reset();
|
||||||
|
static byte stop();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
|
||||||
|
// NBWire implementation based heavily on code by Gene Knight <Gene@Telobot.com>
|
||||||
|
// Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html
|
||||||
|
// Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html
|
||||||
|
|
||||||
|
#define NBWIRE_BUFFER_LENGTH 32
|
||||||
|
|
||||||
|
class TwoWire {
|
||||||
|
private:
|
||||||
|
static uint8_t rxBuffer[];
|
||||||
|
static uint8_t rxBufferIndex;
|
||||||
|
static uint8_t rxBufferLength;
|
||||||
|
|
||||||
|
static uint8_t txAddress;
|
||||||
|
static uint8_t txBuffer[];
|
||||||
|
static uint8_t txBufferIndex;
|
||||||
|
static uint8_t txBufferLength;
|
||||||
|
|
||||||
|
// static uint8_t transmitting;
|
||||||
|
static void (*user_onRequest)(void);
|
||||||
|
static void (*user_onReceive)(int);
|
||||||
|
static void onRequestService(void);
|
||||||
|
static void onReceiveService(uint8_t*, int);
|
||||||
|
|
||||||
|
public:
|
||||||
|
TwoWire();
|
||||||
|
void begin();
|
||||||
|
void begin(uint8_t);
|
||||||
|
void begin(int);
|
||||||
|
void beginTransmission(uint8_t);
|
||||||
|
//void beginTransmission(int);
|
||||||
|
uint8_t endTransmission(uint16_t timeout=0);
|
||||||
|
void nbendTransmission(void (*function)(int)) ;
|
||||||
|
uint8_t requestFrom(uint8_t, int, uint16_t timeout=0);
|
||||||
|
//uint8_t requestFrom(int, int);
|
||||||
|
void nbrequestFrom(uint8_t, int, void (*function)(int));
|
||||||
|
void send(uint8_t);
|
||||||
|
void send(uint8_t*, uint8_t);
|
||||||
|
//void send(int);
|
||||||
|
void send(char*);
|
||||||
|
uint8_t available(void);
|
||||||
|
uint8_t receive(void);
|
||||||
|
void onReceive(void (*)(int));
|
||||||
|
void onRequest(void (*)(void));
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TWI_READY 0
|
||||||
|
#define TWI_MRX 1
|
||||||
|
#define TWI_MTX 2
|
||||||
|
#define TWI_SRX 3
|
||||||
|
#define TWI_STX 4
|
||||||
|
|
||||||
|
#define TW_WRITE 0
|
||||||
|
#define TW_READ 1
|
||||||
|
|
||||||
|
#define TW_MT_SLA_NACK 0x20
|
||||||
|
#define TW_MT_DATA_NACK 0x30
|
||||||
|
|
||||||
|
#define CPU_FREQ 16000000L
|
||||||
|
#define TWI_FREQ 100000L
|
||||||
|
#define TWI_BUFFER_LENGTH 32
|
||||||
|
|
||||||
|
/* TWI Status is in TWSR, in the top 5 bits: TWS7 - TWS3 */
|
||||||
|
|
||||||
|
#define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3))
|
||||||
|
#define TW_STATUS (TWSR & TW_STATUS_MASK)
|
||||||
|
#define TW_START 0x08
|
||||||
|
#define TW_REP_START 0x10
|
||||||
|
#define TW_MT_SLA_ACK 0x18
|
||||||
|
#define TW_MT_SLA_NACK 0x20
|
||||||
|
#define TW_MT_DATA_ACK 0x28
|
||||||
|
#define TW_MT_DATA_NACK 0x30
|
||||||
|
#define TW_MT_ARB_LOST 0x38
|
||||||
|
#define TW_MR_ARB_LOST 0x38
|
||||||
|
#define TW_MR_SLA_ACK 0x40
|
||||||
|
#define TW_MR_SLA_NACK 0x48
|
||||||
|
#define TW_MR_DATA_ACK 0x50
|
||||||
|
#define TW_MR_DATA_NACK 0x58
|
||||||
|
#define TW_ST_SLA_ACK 0xA8
|
||||||
|
#define TW_ST_ARB_LOST_SLA_ACK 0xB0
|
||||||
|
#define TW_ST_DATA_ACK 0xB8
|
||||||
|
#define TW_ST_DATA_NACK 0xC0
|
||||||
|
#define TW_ST_LAST_DATA 0xC8
|
||||||
|
#define TW_SR_SLA_ACK 0x60
|
||||||
|
#define TW_SR_ARB_LOST_SLA_ACK 0x68
|
||||||
|
#define TW_SR_GCALL_ACK 0x70
|
||||||
|
#define TW_SR_ARB_LOST_GCALL_ACK 0x78
|
||||||
|
#define TW_SR_DATA_ACK 0x80
|
||||||
|
#define TW_SR_DATA_NACK 0x88
|
||||||
|
#define TW_SR_GCALL_DATA_ACK 0x90
|
||||||
|
#define TW_SR_GCALL_DATA_NACK 0x98
|
||||||
|
#define TW_SR_STOP 0xA0
|
||||||
|
#define TW_NO_INFO 0xF8
|
||||||
|
#define TW_BUS_ERROR 0x00
|
||||||
|
|
||||||
|
//#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
|
||||||
|
//#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))
|
||||||
|
|
||||||
|
#ifndef sbi // set bit
|
||||||
|
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
||||||
|
#endif // sbi
|
||||||
|
|
||||||
|
#ifndef cbi // clear bit
|
||||||
|
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
||||||
|
#endif // cbi
|
||||||
|
|
||||||
|
extern TwoWire Wire;
|
||||||
|
|
||||||
|
#endif // I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
|
||||||
|
|
||||||
|
#endif /* _I2CDEV_H_ */
|
|
@ -0,0 +1,231 @@
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTArduLink
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015 richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge,
|
||||||
|
// to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files
|
||||||
|
// (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute,
|
||||||
|
// sublicense, and/or sell copies of the Software, and
|
||||||
|
// to permit persons to whom the Software is furnished
|
||||||
|
// to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTArduLink.h"
|
||||||
|
#include "RTArduLinkHAL.h"
|
||||||
|
#include "RTArduLinkUtils.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
RTArduLink::RTArduLink()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RTArduLink::~RTArduLink()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLink::begin(const char *identitySuffix)
|
||||||
|
{
|
||||||
|
RTARDULINK_PORT *portInfo;
|
||||||
|
|
||||||
|
if (!RTArduLinkHALEEPROMValid())
|
||||||
|
RTArduLinkHALEEPROMDefault();
|
||||||
|
|
||||||
|
m_identitySuffix = identitySuffix;
|
||||||
|
|
||||||
|
// now set up host and subsystem ports based on EEPROM configuration
|
||||||
|
|
||||||
|
for (int i = 0; i < RTARDULINKHAL_MAX_PORTS; i++) {
|
||||||
|
portInfo = m_ports + i;
|
||||||
|
portInfo->index = i;
|
||||||
|
portInfo->inUse = RTArduLinkHALConfigurePort(&(portInfo->portHAL), i);
|
||||||
|
RTArduLinkRXFrameInit(&(portInfo->RXFrame), &(portInfo->RXFrameBuffer));
|
||||||
|
}
|
||||||
|
m_hostPort = m_ports;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTArduLink::background()
|
||||||
|
{
|
||||||
|
unsigned char index;
|
||||||
|
RTARDULINK_PORT *portInfo;
|
||||||
|
|
||||||
|
for (index = 0; index < RTARDULINKHAL_MAX_PORTS; index++) {
|
||||||
|
portInfo = m_ports + index;
|
||||||
|
if (!portInfo->inUse)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (RTArduLinkHALPortAvailable(&(portInfo->portHAL))) {
|
||||||
|
if (!RTArduLinkReassemble(&(portInfo->RXFrame), RTArduLinkHALPortRead(&(portInfo->portHAL)))) {
|
||||||
|
sendDebugMessage("Reassembly error");
|
||||||
|
} else {
|
||||||
|
if (portInfo->RXFrame.complete) {
|
||||||
|
processReceivedMessage(portInfo);
|
||||||
|
RTArduLinkRXFrameInit(&(portInfo->RXFrame), &(portInfo->RXFrameBuffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLink::processReceivedMessage(RTARDULINK_PORT *portInfo)
|
||||||
|
{
|
||||||
|
RTARDULINK_MESSAGE *message; // a pointer to the message part of the frame
|
||||||
|
unsigned int address;
|
||||||
|
|
||||||
|
message = &(portInfo->RXFrameBuffer.message); // get the message pointer
|
||||||
|
address = RTArduLinkConvertUC2ToUInt(message->messageAddress);
|
||||||
|
|
||||||
|
switch (portInfo->index) {
|
||||||
|
case RTARDULINK_HOST_PORT:
|
||||||
|
processHostMessage(); // came from this upstream link
|
||||||
|
return;
|
||||||
|
|
||||||
|
case RTARDULINK_DAISY_PORT: // came from dasiy chain port
|
||||||
|
if (address != RTARDULINK_HOST_PORT) // true if it came from a daisy chained subsystem, not a directly connected subsystem
|
||||||
|
RTArduLinkConvertIntToUC2(address + RTARDULINKHAL_MAX_PORTS, message->messageAddress);
|
||||||
|
else
|
||||||
|
RTArduLinkConvertIntToUC2(RTARDULINK_DAISY_PORT, message->messageAddress);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
RTArduLinkConvertIntToUC2(address + portInfo->index, message->messageAddress);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if get here, need to forward to host port
|
||||||
|
|
||||||
|
sendFrame(m_hostPort, &(portInfo->RXFrameBuffer), portInfo->RXFrameBuffer.messageLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTArduLink::processHostMessage()
|
||||||
|
{
|
||||||
|
RTARDULINK_MESSAGE *message; // a pointer to the message part of the frame
|
||||||
|
int identityLength;
|
||||||
|
int suffixLength;
|
||||||
|
unsigned int address;
|
||||||
|
|
||||||
|
message = &(m_hostPort->RXFrameBuffer.message); // get the message pointer
|
||||||
|
address = RTArduLinkConvertUC2ToUInt(message->messageAddress);
|
||||||
|
|
||||||
|
if (address == RTARDULINK_BROADCAST_ADDRESS) { // need to forward to downstream ports also
|
||||||
|
for (int i = RTARDULINK_HOST_PORT + 1; i < RTARDULINKHAL_MAX_PORTS; i++) {
|
||||||
|
if (m_ports[i].inUse)
|
||||||
|
sendFrame(m_ports + i, &(m_hostPort->RXFrameBuffer), m_hostPort->RXFrameBuffer.messageLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((address == RTARDULINK_MY_ADDRESS) || (address == RTARDULINK_BROADCAST_ADDRESS)) { // it's for me
|
||||||
|
switch (message->messageType)
|
||||||
|
{
|
||||||
|
case RTARDULINK_MESSAGE_POLL:
|
||||||
|
case RTARDULINK_MESSAGE_ECHO:
|
||||||
|
RTArduLinkConvertIntToUC2(RTARDULINK_MY_ADDRESS, message->messageAddress);
|
||||||
|
sendFrame(m_hostPort, &(m_hostPort->RXFrameBuffer), m_hostPort->RXFrameBuffer.messageLength); // just send the frame back as received
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RTARDULINK_MESSAGE_IDENTITY:
|
||||||
|
identityLength = strlen(RTArduLinkHALConfig.identity);
|
||||||
|
suffixLength = strlen(m_identitySuffix);
|
||||||
|
|
||||||
|
memcpy(message->data, RTArduLinkHALConfig.identity, identityLength + 1); // copy in identity
|
||||||
|
|
||||||
|
if ((identityLength + suffixLength) < RTARDULINK_DATA_MAX_LEN - 1) {
|
||||||
|
memcpy(message->data + identityLength, m_identitySuffix, suffixLength + 1); // copy in suffix
|
||||||
|
} else {
|
||||||
|
suffixLength = 0;
|
||||||
|
}
|
||||||
|
RTArduLinkConvertIntToUC2(RTARDULINK_MY_ADDRESS, message->messageAddress);
|
||||||
|
message->data[RTARDULINK_DATA_MAX_LEN - 1] = 0; // make sure zero terminated if it was truncated
|
||||||
|
sendFrame(m_hostPort, &(m_hostPort->RXFrameBuffer), RTARDULINK_MESSAGE_HEADER_LEN + identityLength + suffixLength + 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (message->messageType < RTARDULINK_MESSAGE_CUSTOM) { // illegal code
|
||||||
|
message->data[0] = RTARDULINK_RESPONSE_ILLEGAL_COMMAND;
|
||||||
|
message->data[1] = message->messageType; // this is the offending code
|
||||||
|
message->messageType = RTARDULINK_MESSAGE_ERROR;
|
||||||
|
RTArduLinkConvertIntToUC2(RTARDULINK_MY_ADDRESS, message->messageAddress);
|
||||||
|
sendFrame(m_hostPort, &(m_hostPort->RXFrameBuffer), RTARDULINK_MESSAGE_HEADER_LEN + 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
processCustomMessage(message->messageType, message->messageParam, message->data,
|
||||||
|
m_hostPort->RXFrameBuffer.messageLength - RTARDULINK_MESSAGE_HEADER_LEN); // see if anyone wants to process it
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address >= RTARDULINKHAL_MAX_PORTS) { // need to pass it to the first subsystem
|
||||||
|
if (!m_ports[RTARDULINK_DAISY_PORT].inUse)
|
||||||
|
return; // there is no daisy chain port
|
||||||
|
RTArduLinkConvertIntToUC2(address - RTARDULINKHAL_MAX_PORTS, message->messageAddress); // adjust the address
|
||||||
|
sendFrame(m_ports +RTARDULINK_DAISY_PORT, &(m_hostPort->RXFrameBuffer), m_hostPort->RXFrameBuffer.messageLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if get here, needs to go to a local subsystem port
|
||||||
|
|
||||||
|
if (m_ports[address].inUse) {
|
||||||
|
RTArduLinkConvertIntToUC2(0, message->messageAddress); // indicates that the target should process it
|
||||||
|
sendFrame(m_ports + address, &(m_hostPort->RXFrameBuffer), m_hostPort->RXFrameBuffer.messageLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLink::sendDebugMessage(const char *debugMessage)
|
||||||
|
{
|
||||||
|
RTARDULINK_FRAME frame;
|
||||||
|
int stringLength;
|
||||||
|
|
||||||
|
stringLength = strlen(debugMessage);
|
||||||
|
if (stringLength >= RTARDULINK_DATA_MAX_LEN)
|
||||||
|
stringLength = RTARDULINK_DATA_MAX_LEN-1;
|
||||||
|
memcpy(frame.message.data, debugMessage, stringLength);
|
||||||
|
frame.message.data[stringLength] = 0;
|
||||||
|
frame.message.messageType = RTARDULINK_MESSAGE_DEBUG;
|
||||||
|
RTArduLinkConvertIntToUC2(RTARDULINK_MY_ADDRESS, frame.message.messageAddress);
|
||||||
|
sendFrame(m_hostPort, &frame, RTARDULINK_MESSAGE_HEADER_LEN + stringLength + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLink::sendMessage(unsigned char messageType, unsigned char messageParam, unsigned char *data, int length)
|
||||||
|
{
|
||||||
|
RTARDULINK_FRAME frame;
|
||||||
|
|
||||||
|
RTArduLinkConvertIntToUC2(RTARDULINK_MY_ADDRESS, frame.message.messageAddress);
|
||||||
|
frame.message.messageType = messageType;
|
||||||
|
frame.message.messageParam = messageParam;
|
||||||
|
|
||||||
|
if (length > RTARDULINK_DATA_MAX_LEN)
|
||||||
|
length = RTARDULINK_DATA_MAX_LEN;
|
||||||
|
memcpy(frame.message.data, data, length);
|
||||||
|
|
||||||
|
sendFrame(m_hostPort, &frame, length + RTARDULINK_MESSAGE_HEADER_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLink::sendFrame(RTARDULINK_PORT *portInfo, RTARDULINK_FRAME *frame, int length)
|
||||||
|
{
|
||||||
|
frame->sync0 = RTARDULINK_MESSAGE_SYNC0;
|
||||||
|
frame->sync1 = RTARDULINK_MESSAGE_SYNC1;
|
||||||
|
frame->messageLength = length; // set length
|
||||||
|
RTArduLinkSetChecksum(frame); // compute checksum
|
||||||
|
RTArduLinkHALPortWrite(&(portInfo->portHAL), (unsigned char *)frame, frame->messageLength + RTARDULINK_FRAME_HEADER_LEN);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTArduLink
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015 richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge,
|
||||||
|
// to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files
|
||||||
|
// (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute,
|
||||||
|
// sublicense, and/or sell copies of the Software, and
|
||||||
|
// to permit persons to whom the Software is furnished
|
||||||
|
// to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTARDULINK_H
|
||||||
|
#define _RTARDULINK_H
|
||||||
|
|
||||||
|
#include "RTArduLinkDefs.h"
|
||||||
|
#include "RTArduLinkHAL.h"
|
||||||
|
|
||||||
|
#define RTARDULINK_HOST_PORT 0 // host port is always 0
|
||||||
|
#define RTARDULINK_DAISY_PORT 1 // daisy chain port is always 1
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int index; // port index
|
||||||
|
bool inUse; // true if in use
|
||||||
|
RTARDULINK_RXFRAME RXFrame; // structure to maintain receive frame state
|
||||||
|
RTARDULINK_FRAME RXFrameBuffer; // used to assemble received frames
|
||||||
|
RTARDULINKHAL_PORT portHAL; // the actual hardware port interface
|
||||||
|
} RTARDULINK_PORT;
|
||||||
|
|
||||||
|
class RTArduLink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTArduLink();
|
||||||
|
virtual ~RTArduLink();
|
||||||
|
|
||||||
|
void begin(const char *identitySuffix); // should be called in setup() code
|
||||||
|
void background(); // should be called once per loop()
|
||||||
|
void sendDebugMessage(const char *debugMesssage); // sends a debug message to the host port
|
||||||
|
void sendMessage(unsigned char messageType, unsigned char messageParam,
|
||||||
|
unsigned char *data, int length); // sends a message to the host port
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// These are functions that can be overridden
|
||||||
|
|
||||||
|
virtual void processCustomMessage(unsigned char messageType,
|
||||||
|
unsigned char messageParam, unsigned char *data, int dataLength) {}
|
||||||
|
|
||||||
|
RTARDULINK_PORT m_ports[RTARDULINKHAL_MAX_PORTS]; // port array
|
||||||
|
RTARDULINK_PORT *m_hostPort; // a link to the entry for the host port
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void processReceivedMessage(RTARDULINK_PORT *port); // process a completed message
|
||||||
|
void processHostMessage(); // special case for stuff received from the host port
|
||||||
|
void sendFrame(RTARDULINK_PORT *portInfo, RTARDULINK_FRAME *frame, int length); // send a frame to the host. length is length of data field
|
||||||
|
|
||||||
|
const char *m_identitySuffix; // what to add to the EEPROM identity string
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTARDULINK_H
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTArduLink
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015 richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge,
|
||||||
|
// to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files
|
||||||
|
// (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute,
|
||||||
|
// sublicense, and/or sell copies of the Software, and
|
||||||
|
// to permit persons to whom the Software is furnished
|
||||||
|
// to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
// The RTArduLink communications protocol works by exchanging frames across the host <-> subsystem interface.
|
||||||
|
// The structure RTARDULINK_FRAME defines the frame structure. There is a 4 byte header for frame level control
|
||||||
|
// while the remainder is available for higher level message. Note that the structure implies fixed length
|
||||||
|
// buffers which works well with the subsystem however only the number of bytes actually used are transferred
|
||||||
|
// across the interface.
|
||||||
|
//
|
||||||
|
// Note that there is no flow control at the frame level - it is assumed that the higher level interactions host -> subsystem
|
||||||
|
// are window 1 acknowledged transfers so that the maximum possible unprocessed frames is equal to the number
|
||||||
|
// of higher level services. Subsystem -> host transfers are always either responses to host commands or else regular
|
||||||
|
// status updates so the rate from subsystem to host is controlled by configuration.
|
||||||
|
//
|
||||||
|
// The frame integrity is protected by a single byte checksum. To keep things very simple, it is the 2s complement
|
||||||
|
// of the 8 bit sum of all the bytes in the message array. This is used in conjunction with 0xAA and 0x55 bytes
|
||||||
|
// to determine correct sync (in case of lost bytes which should not really happen!).
|
||||||
|
//
|
||||||
|
// Frame sync is obtained by reading bytes until the 0xAA pattern is seen. If the next byte is not 0x55, keep
|
||||||
|
// scanning. If it is, assume this byte is messageLength and the next one is the frameCksm value. Read in the
|
||||||
|
// message array based on messageLength and then calculate the checksum. If the checksum is correct, sync has been
|
||||||
|
// obtained and the message is valid. Otherwise, start looking for an 0xAA value again.
|
||||||
|
|
||||||
|
#ifndef _RTARDULINKDEFS_H
|
||||||
|
#define _RTARDULINKDEFS_H
|
||||||
|
|
||||||
|
// Some defines to cope with compiler differences
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#ifndef false
|
||||||
|
#define false 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef true
|
||||||
|
#define true 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef unsigned char bool;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NULL
|
||||||
|
#define NULL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Some general purpose typedefs - used especially for transferring values greater than
|
||||||
|
// 8 bits across the link and avoids endian issues. Assumes processor has 32 bit ints!
|
||||||
|
|
||||||
|
typedef unsigned char RTARDULINK_UC2[2]; // an array of two unsigned chars
|
||||||
|
typedef unsigned char RTARDULINK_UC4[4]; // an array of four unsigned chars
|
||||||
|
|
||||||
|
// Port speed codes
|
||||||
|
|
||||||
|
#define RTARDULINK_PORT_SPEED_OFF 0 // port is unused
|
||||||
|
#define RTARDULINK_PORT_SPEED_9600 1 // 9600 baud
|
||||||
|
#define RTARDULINK_PORT_SPEED_19200 2 // 19200 baud
|
||||||
|
#define RTARDULINK_PORT_SPEED_38400 3 // 38400 baud
|
||||||
|
#define RTARDULINK_PORT_SPEED_57600 4 // 57600 baud
|
||||||
|
#define RTARDULINK_PORT_SPEED_115200 5 // 115200 baud
|
||||||
|
|
||||||
|
#define RTARDULINK_PORT_SPEED_COUNT 6 // six codes total
|
||||||
|
|
||||||
|
extern unsigned long RTArduLinkSpeedMap[];
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Frame level defs and structure
|
||||||
|
|
||||||
|
#define RTARDULINK_FRAME_MAX_LEN 64 // maximum possible length of a frame
|
||||||
|
#define RTARDULINK_FRAME_HEADER_LEN 4 // 4 bytes in frame header (must correspond with the structure below!)
|
||||||
|
#define RTARDULINK_MESSAGE_HEADER_LEN 4 // 4 bytes in message header (must correspond with the structure below!)
|
||||||
|
#define RTARDULINK_MESSAGE_MAX_LEN (RTARDULINK_FRAME_MAX_LEN - RTARDULINK_FRAME_HEADER_LEN) // max length of message
|
||||||
|
#define RTARDULINK_DATA_MAX_LEN (RTARDULINK_MESSAGE_MAX_LEN - RTARDULINK_MESSAGE_HEADER_LEN)// max length of data field
|
||||||
|
|
||||||
|
#define RTARDULINK_MESSAGE_SYNC0 0xAA
|
||||||
|
#define RTARDULINK_MESSAGE_SYNC1 0x55
|
||||||
|
|
||||||
|
#define RTARDULINK_MY_ADDRESS 0 // the subsystem address for local processing
|
||||||
|
#define RTARDULINK_BROADCAST_ADDRESS 0xffff // the subsystem address for all subsystems
|
||||||
|
#define RTARDULINK_ADDRESSES 0x1000 // number of addresses
|
||||||
|
|
||||||
|
// RTARDULINK_MESSAGE is carried in the RTARDULINK_FRAME
|
||||||
|
//
|
||||||
|
// The messageAddress field allows subsystems to be daisy-chained. Valid addresses are 0 to 65534.
|
||||||
|
// Address 65535 is a broadcast and goes to all subsystems.
|
||||||
|
// Every message has the messageType and messageParam bytes but there can be from 0 to 56 bytes of data
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
RTARDULINK_UC2 messageAddress; // subsystem message address
|
||||||
|
unsigned char messageType; // message type code
|
||||||
|
unsigned char messageParam; // an optional parameter to the message type
|
||||||
|
unsigned char data[RTARDULINK_DATA_MAX_LEN]; // the actual data! Length is computed from messageLength.
|
||||||
|
} RTARDULINK_MESSAGE;
|
||||||
|
|
||||||
|
// RTARDULINK_FRAME is the lowest level structure used across the RTArduLink
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char sync0; // sync0 code
|
||||||
|
unsigned char sync1; // sync1 code
|
||||||
|
unsigned char messageLength; // the length of the message in the message field - between 4 and 60 bytes
|
||||||
|
unsigned char frameChecksum; // checksum for frame
|
||||||
|
RTARDULINK_MESSAGE message; // the actual message
|
||||||
|
} RTARDULINK_FRAME;
|
||||||
|
|
||||||
|
// RTARDULINK_RXFRAME is a type that is used to reassemble a frame from a stream of bytes in conjunction with RTArduLinkReassemble()
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
RTARDULINK_FRAME *frameBuffer; // the frame buffer pointer
|
||||||
|
int length; // current length of frame
|
||||||
|
int bytesLeft; // number of bytes needed to complete
|
||||||
|
bool complete; // true if frame is complete and correct (as far as checksum goes)
|
||||||
|
} RTARDULINK_RXFRAME;
|
||||||
|
|
||||||
|
// Message types
|
||||||
|
|
||||||
|
// RTARDULINK_MESSAGE_POLL
|
||||||
|
//
|
||||||
|
// The host should poll the RTArduLink at every RTARDULINK_POLL_INTERVAL.
|
||||||
|
// The subsystem will respond by echoing the poll message as received.
|
||||||
|
|
||||||
|
#define RTARDULINK_MESSAGE_POLL 0 // poll message
|
||||||
|
|
||||||
|
// RTARDULINK_MESSAGE_IDENTIFY
|
||||||
|
//
|
||||||
|
// The host can send this message to request an identity string from the subsystem.
|
||||||
|
// Only the messageType field is used in the request host -> subsystem. The subsystem
|
||||||
|
// responds with an identity string in the data field.
|
||||||
|
|
||||||
|
#define RTARDULINK_MESSAGE_IDENTITY 1 // identity message
|
||||||
|
|
||||||
|
// RTARDULINK_MESSAGE_DEBUG
|
||||||
|
//
|
||||||
|
// This can be used to send a debug message up to the host. The data field contains a debug message
|
||||||
|
|
||||||
|
#define RTARDULINK_MESSAGE_DEBUG 2 // debug message
|
||||||
|
|
||||||
|
// RTARDULINK_MESSAGE_INFO
|
||||||
|
//
|
||||||
|
// This can be used to send an info message up to the host. The data field contains the message
|
||||||
|
|
||||||
|
#define RTARDULINK_MESSAGE_INFO 3 // info message
|
||||||
|
|
||||||
|
// RTARDULINK_MESSAGE_ERROR
|
||||||
|
//
|
||||||
|
// This code is returned by the subsystem if it received a message with an illegal message type
|
||||||
|
// The first byte of the data is the error code. The rest of the data field depends on the error.
|
||||||
|
|
||||||
|
#define RTARDULINK_MESSAGE_ERROR 4 // illegal message type response
|
||||||
|
|
||||||
|
// RTARDULINK_MESSAGE_ECHO
|
||||||
|
//
|
||||||
|
// This message can be used to test link performance. The addressed subsystem just returns
|
||||||
|
// the entire message to the host.
|
||||||
|
|
||||||
|
#define RTARDULINK_MESSAGE_ECHO 5 // echo message
|
||||||
|
|
||||||
|
// RTARDULINK_MESSAGE_CUSTOM
|
||||||
|
//
|
||||||
|
// This is the first message code that should be used for custom messages 16-255 are available.
|
||||||
|
|
||||||
|
#define RTARDULINK_MESSAGE_CUSTOM 16 // start of custom messages
|
||||||
|
|
||||||
|
// RTArduLink response codes
|
||||||
|
|
||||||
|
#define RTARDULINK_RESPONSE_OK 0 // means things worked
|
||||||
|
#define RTARDULINK_RESPONSE_ILLEGAL_COMMAND 1 // not a supported message type, data[1] has offending type
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _RTARDULINKDEFS_H
|
|
@ -0,0 +1,66 @@
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTArduLink
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015 richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge,
|
||||||
|
// to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files
|
||||||
|
// (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute,
|
||||||
|
// sublicense, and/or sell copies of the Software, and
|
||||||
|
// to permit persons to whom the Software is furnished
|
||||||
|
// to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef RTARDULINKDEMODEFS_H_
|
||||||
|
#define RTARDULINKDEMODEFS_H_
|
||||||
|
|
||||||
|
#include "RTArduLinkUtils.h"
|
||||||
|
|
||||||
|
#define SERVO_COUNT 2 // 2 servo channels
|
||||||
|
#define PWM_COUNT 3 // 3 pwm channels
|
||||||
|
#define INPUT_COUNT 2 // 2 inputs
|
||||||
|
#define OUTPUT_COUNT 2 // 2 outputs
|
||||||
|
|
||||||
|
#define SERVO_MIN_VALUE 1000 // min servo value
|
||||||
|
#define SERVO_CTR_VALUE 1500 // center servo value
|
||||||
|
#define SERVO_MAX_VALUE 2000 // max servo value
|
||||||
|
|
||||||
|
#define PWM_MIN_VALUE 0 // min pwm value
|
||||||
|
#define PWM_CTR_VALUE 128 // center pwm value
|
||||||
|
#define PWM_MAX_VALUE 255 // max pwm value
|
||||||
|
|
||||||
|
// The command structure is sent from the host to the subsystem
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
RTARDULINK_UC2 servoPos[SERVO_COUNT]; // the servo positions
|
||||||
|
unsigned char pwmValue[PWM_COUNT]; // PWM values
|
||||||
|
unsigned char outputValue[OUTPUT_COUNT]; // the output pin values (true=high, false=low)
|
||||||
|
} RTARDULINKDEMO_COMMAND;
|
||||||
|
|
||||||
|
// the response structure is sent from the subsystem to the host
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char inputValue[INPUT_COUNT]; // the input pin values (true=high, false=low)
|
||||||
|
} RTARDULINKDEMO_RESPONSE;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* RTARDULINKDEMODEFS_H_ */
|
|
@ -0,0 +1,194 @@
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTArduLink
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015 richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge,
|
||||||
|
// to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files
|
||||||
|
// (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute,
|
||||||
|
// sublicense, and/or sell copies of the Software, and
|
||||||
|
// to permit persons to whom the Software is furnished
|
||||||
|
// to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "RTArduLinkHAL.h"
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Arduino HAL
|
||||||
|
|
||||||
|
|
||||||
|
// The global config structure
|
||||||
|
|
||||||
|
RTARDULINKHAL_EEPROM RTArduLinkHALConfig;
|
||||||
|
|
||||||
|
bool RTArduLinkHALAddHardwarePort(RTARDULINKHAL_PORT *port, long portSpeed, unsigned char hardwarePort);
|
||||||
|
|
||||||
|
// Port speed map array
|
||||||
|
|
||||||
|
unsigned long RTArduLinkHALSpeedMap[] = {0, 9600, 19200, 38400, 57600, 115200};
|
||||||
|
|
||||||
|
|
||||||
|
bool RTArduLinkHALConfigurePort(RTARDULINKHAL_PORT *port, int portIndex)
|
||||||
|
{
|
||||||
|
if (RTArduLinkHALConfig.portSpeed[portIndex] == RTARDULINK_PORT_SPEED_OFF)
|
||||||
|
return false; // port is not enabled
|
||||||
|
|
||||||
|
return RTArduLinkHALAddHardwarePort(port, RTArduLinkHALSpeedMap[RTArduLinkHALConfig.portSpeed[portIndex]],
|
||||||
|
RTArduLinkHALConfig.hardwarePort[portIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTArduLinkHALPortAvailable(RTARDULINKHAL_PORT *port)
|
||||||
|
{
|
||||||
|
return port->serialPort->available();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char RTArduLinkHALPortRead(RTARDULINKHAL_PORT *port)
|
||||||
|
{
|
||||||
|
return port->serialPort->read();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLinkHALPortWrite(RTARDULINKHAL_PORT *port, unsigned char *data, unsigned char length)
|
||||||
|
{
|
||||||
|
port->serialPort->write(data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTArduLinkHALAddHardwarePort(RTARDULINKHAL_PORT *port, long portSpeed, unsigned char hardwarePort)
|
||||||
|
{
|
||||||
|
HardwareSerial *hardPort;
|
||||||
|
|
||||||
|
switch (hardwarePort) {
|
||||||
|
case 0:
|
||||||
|
#if defined(USBCON)
|
||||||
|
/* Leonardo support */
|
||||||
|
hardPort = &Serial1;
|
||||||
|
#else
|
||||||
|
hardPort = &Serial;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
#if defined(UBRR1H)
|
||||||
|
hardPort = &Serial1;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
#if defined(UBRR2H)
|
||||||
|
hardPort = &Serial2;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
#if defined(UBRR3H)
|
||||||
|
hardPort = &Serial3;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
port->serialPort = hardPort;
|
||||||
|
hardPort->begin(portSpeed); // start the port
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTArduLinkHALEEPROMValid()
|
||||||
|
{
|
||||||
|
RTArduLinkHALEEPROMRead(); // see what it really is
|
||||||
|
return (RTArduLinkHALConfig.sig0 == RTARDULINKHAL_SIG0) &&
|
||||||
|
(RTArduLinkHALConfig.sig1 == RTARDULINKHAL_SIG1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMDisplay()
|
||||||
|
{
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
if ((RTArduLinkHALConfig.sig0 != RTARDULINKHAL_SIG0) ||
|
||||||
|
(RTArduLinkHALConfig.sig1 != RTARDULINKHAL_SIG1)) {
|
||||||
|
Serial.println("Invalid config");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Serial.print("Identity: ");
|
||||||
|
Serial.println(RTArduLinkHALConfig.identity);
|
||||||
|
|
||||||
|
for (int i = 0; i < RTARDULINKHAL_MAX_PORTS; i++)
|
||||||
|
RTArduLinkHALEEPROMDisplayPort(i, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMDisplayPort(int index, bool suppress)
|
||||||
|
{
|
||||||
|
if (suppress && (RTArduLinkHALConfig.portSpeed[index] == RTARDULINK_PORT_SPEED_OFF))
|
||||||
|
return;
|
||||||
|
Serial.print("Port index ");
|
||||||
|
Serial.print(index);
|
||||||
|
Serial.print(" speed=");
|
||||||
|
Serial.print(RTArduLinkHALConfig.portSpeed[index]);
|
||||||
|
Serial.print(", ");
|
||||||
|
Serial.print("hardware port number=");
|
||||||
|
Serial.println(RTArduLinkHALConfig.hardwarePort[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMDefault()
|
||||||
|
{
|
||||||
|
RTArduLinkHALConfig.sig0 = RTARDULINKHAL_SIG0; // set to valid signature
|
||||||
|
RTArduLinkHALConfig.sig1 = RTARDULINKHAL_SIG1;
|
||||||
|
strcpy(RTArduLinkHALConfig.identity, "RTArduLink_Arduino");
|
||||||
|
|
||||||
|
RTArduLinkHALConfig.portSpeed[0] = RTARDULINK_PORT_SPEED_115200;
|
||||||
|
for (int i = 1; i < RTARDULINKHAL_MAX_PORTS; i++)
|
||||||
|
RTArduLinkHALConfig.portSpeed[i] = RTARDULINK_PORT_SPEED_OFF;
|
||||||
|
|
||||||
|
for (int i = 0; i < RTARDULINKHAL_MAX_PORTS; i++)
|
||||||
|
RTArduLinkHALConfig.hardwarePort[i] = i;
|
||||||
|
|
||||||
|
RTArduLinkHALEEPROMWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMRead()
|
||||||
|
{
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
data = (unsigned char *)&RTArduLinkHALConfig;
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)sizeof(RTARDULINKHAL_EEPROM); i++)
|
||||||
|
*data++ = EEPROM.read(i + RTARDULINKHAL_EEPROM_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMWrite()
|
||||||
|
{
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
data = (unsigned char *)&RTArduLinkHALConfig;
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)sizeof(RTARDULINKHAL_EEPROM); i++)
|
||||||
|
EEPROM.write(i + RTARDULINKHAL_EEPROM_OFFSET, *data++);
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTArduLink
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015 richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge,
|
||||||
|
// to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files
|
||||||
|
// (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute,
|
||||||
|
// sublicense, and/or sell copies of the Software, and
|
||||||
|
// to permit persons to whom the Software is furnished
|
||||||
|
// to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTARDULINKHAL_H
|
||||||
|
#define _RTARDULINKHAL_H
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
// Target-specific includes
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Arduino HAL
|
||||||
|
|
||||||
|
#include <RTArduLinkDefs.h>
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
#define RTARDULINKHAL_MAX_SUBSYSTEM_PORTS 3 // maximum number of subsystem ports
|
||||||
|
#define RTARDULINKHAL_MAX_PORTS (RTARDULINKHAL_MAX_SUBSYSTEM_PORTS + 1) // max total ports (including host)
|
||||||
|
#define RTARDULINKHAL_EEPROM_OFFSET 256 // where the config starts in EEPROM
|
||||||
|
|
||||||
|
// RTARDULINKHAL_PORT should be modified as appropriate for the target.
|
||||||
|
// There is one copy of this per port. It contains all state needed about
|
||||||
|
// a serial port.
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
HardwareSerial *serialPort; // the serial port structure
|
||||||
|
} RTARDULINKHAL_PORT;
|
||||||
|
|
||||||
|
// RTARDULINKHAL_EEPROM is the target-specific structure used to
|
||||||
|
// store configs in EEPROM
|
||||||
|
|
||||||
|
// Signature bytes indicating valid config
|
||||||
|
|
||||||
|
#define RTARDULINKHAL_SIG0 0x38
|
||||||
|
#define RTARDULINKHAL_SIG1 0xc1
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char sig0; // signature byte 0
|
||||||
|
unsigned char sig1; // signature byte 1
|
||||||
|
char identity[RTARDULINK_DATA_MAX_LEN]; // identity string
|
||||||
|
unsigned char portSpeed[RTARDULINKHAL_MAX_PORTS]; // port speed codes
|
||||||
|
unsigned char hardwarePort[RTARDULINKHAL_MAX_PORTS]; // port number for hardware serial
|
||||||
|
} RTARDULINKHAL_EEPROM;
|
||||||
|
|
||||||
|
// The global config structure
|
||||||
|
|
||||||
|
extern RTARDULINKHAL_EEPROM RTArduLinkHALConfig;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
//
|
||||||
|
// These functions must be provided the RTArduLinkHAL for all implementations
|
||||||
|
|
||||||
|
// RTArduLinkHALConfigurePort() activates the specified port configuration specified by portIndex in port structure port.
|
||||||
|
|
||||||
|
bool RTArduLinkHALConfigurePort(RTARDULINKHAL_PORT *port, int portIndex);
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALPortAvailable() returns the number of bytes availabel on the specified port.
|
||||||
|
|
||||||
|
int RTArduLinkHALPortAvailable(RTARDULINKHAL_PORT *port);
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALPortRead() returns the next available byte from a port. Always check available bytes first
|
||||||
|
|
||||||
|
unsigned char RTArduLinkHALPortRead(RTARDULINKHAL_PORT *port);
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALPortWrite() writes length bytes of the block pointed to by data to the specified port.
|
||||||
|
|
||||||
|
void RTArduLinkHALPortWrite(RTARDULINKHAL_PORT *port, unsigned char *data, unsigned char length);
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALEEPROMValid() returns true if the EEPROM contains a valid configuration,
|
||||||
|
// false otherwise.
|
||||||
|
|
||||||
|
bool RTArduLinkHALEEPROMValid(); // returns true if a valid config
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALEEPROMDisplay() displays the current configuration
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMDisplay(); // display the config
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALEEPROMDisplayPort() displays the configuration for a single port
|
||||||
|
// If suppress is true, nothing is displayed if the port is not enabled. If false
|
||||||
|
// the port's data will be displayed regardless.
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMDisplayPort(int port, bool suppress); // display the port config
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALEEPROMDefault() writes a default config to EEPROM
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMDefault(); // write and load default settings
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALEEPROMRead() loads the EEPROM config into the RTArduLinkHALConfig
|
||||||
|
// global structure.
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMRead(); // to load the config
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkHALEEPROMWrite() writes the config in the RTArduLinkHALConfig
|
||||||
|
// global structure back to EEPROM.
|
||||||
|
|
||||||
|
void RTArduLinkHALEEPROMWrite(); // to write the config
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _RTARDULINKHAL_H
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTArduLink
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015 richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge,
|
||||||
|
// to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files
|
||||||
|
// (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute,
|
||||||
|
// sublicense, and/or sell copies of the Software, and
|
||||||
|
// to permit persons to whom the Software is furnished
|
||||||
|
// to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTArduLinkUtils.h"
|
||||||
|
|
||||||
|
// RTArduLinkRXFrameInit initializes the structure for a new frame using frameBuffer for storage
|
||||||
|
|
||||||
|
void RTArduLinkRXFrameInit(RTARDULINK_RXFRAME *RXFrame, RTARDULINK_FRAME *frameBuffer)
|
||||||
|
{
|
||||||
|
RXFrame->complete = false;
|
||||||
|
RXFrame->length = 0;
|
||||||
|
RXFrame->bytesLeft = 0;
|
||||||
|
RXFrame->frameBuffer = frameBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTArduLinkReassemble takes a sequence of received bytes and tries to complete a frame. It returns true if ok
|
||||||
|
// false if error. The caller can determine if the frame is complete by checking the complete flag.
|
||||||
|
|
||||||
|
bool RTArduLinkReassemble(RTARDULINK_RXFRAME *RXFrame, unsigned char data)
|
||||||
|
{
|
||||||
|
bool flag = true;
|
||||||
|
|
||||||
|
((unsigned char *)(RXFrame->frameBuffer))[RXFrame->length] = data; // save byte in correct place
|
||||||
|
switch (RXFrame->length) {
|
||||||
|
case 0: // waiting for sync0
|
||||||
|
if (RXFrame->frameBuffer->sync0 == RTARDULINK_MESSAGE_SYNC0) {
|
||||||
|
RXFrame->length = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // waiting for sync1
|
||||||
|
if (RXFrame->frameBuffer->sync1 == RTARDULINK_MESSAGE_SYNC1) {
|
||||||
|
RXFrame->length = 2;
|
||||||
|
} else {
|
||||||
|
RXFrame->length = 0; // try again if not correct two byte sequence
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // should be message length
|
||||||
|
if (RXFrame->frameBuffer->messageLength <= RTARDULINK_MESSAGE_MAX_LEN) {
|
||||||
|
RXFrame->length = 3;
|
||||||
|
RXFrame->bytesLeft = RXFrame->frameBuffer->messageLength + 1; // +1 to allow for the checksum
|
||||||
|
} else {
|
||||||
|
RXFrame->length = 0; // discard this and resync frame
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
RXFrame->length++;
|
||||||
|
RXFrame->bytesLeft--;
|
||||||
|
if (RXFrame->bytesLeft == 0) { // a complete frame!
|
||||||
|
if (!RTArduLinkCheckChecksum(RXFrame->frameBuffer)) {
|
||||||
|
RTArduLinkRXFrameInit(RXFrame, RXFrame->frameBuffer);
|
||||||
|
flag = false; // flag the error
|
||||||
|
} else {
|
||||||
|
// this is a valid frame (so far)
|
||||||
|
RXFrame->complete = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTArduLinkSetChecksum correctly sets the checksum field on an RCP frame prior to transmission
|
||||||
|
//
|
||||||
|
|
||||||
|
void RTArduLinkSetChecksum(RTARDULINK_FRAME *frame)
|
||||||
|
{
|
||||||
|
int cksm;
|
||||||
|
int i;
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
for (i = 0, cksm = 0, data = (unsigned char *)&(frame->message); i < frame->messageLength; i++)
|
||||||
|
cksm += *data++; // add up checksum
|
||||||
|
frame->frameChecksum = (255 - cksm) + 1; // 2s complement
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// RTArduLinkCheckChecksum checks a received frame's checksum.
|
||||||
|
//
|
||||||
|
// It adds up all the bytes from the nFrameCksm byte to the end of the frame. The result should be 0.
|
||||||
|
|
||||||
|
bool RTArduLinkCheckChecksum(RTARDULINK_FRAME *frame)
|
||||||
|
{
|
||||||
|
int length;
|
||||||
|
int i;
|
||||||
|
unsigned char *data;
|
||||||
|
unsigned char cksm;
|
||||||
|
|
||||||
|
length = frame->messageLength + 1;
|
||||||
|
cksm = 0;
|
||||||
|
data = (unsigned char *)&(frame->frameChecksum);
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
cksm += *data++;
|
||||||
|
|
||||||
|
return cksm == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UC2 and UC4 Conversion routines
|
||||||
|
//
|
||||||
|
|
||||||
|
long RTArduLinkConvertUC4ToLong(RTARDULINK_UC4 UC4)
|
||||||
|
{
|
||||||
|
long val;
|
||||||
|
|
||||||
|
val = UC4[3];
|
||||||
|
val += (long)UC4[2] << 8;
|
||||||
|
val += (long)UC4[1] << 16;
|
||||||
|
val += (long)UC4[0] << 24;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTArduLinkConvertLongToUC4(long val, RTARDULINK_UC4 UC4)
|
||||||
|
{
|
||||||
|
UC4[3] = val & 0xff;
|
||||||
|
UC4[2] = (val >> 8) & 0xff;
|
||||||
|
UC4[1] = (val >> 16) & 0xff;
|
||||||
|
UC4[0] = (val >> 24) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTArduLinkConvertUC2ToInt(RTARDULINK_UC2 UC2)
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
|
||||||
|
val = UC2[1];
|
||||||
|
val += (int)UC2[0] << 8;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int RTArduLinkConvertUC2ToUInt(RTARDULINK_UC2 UC2)
|
||||||
|
{
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
val = UC2[1];
|
||||||
|
val += (unsigned int)UC2[0] << 8;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTArduLinkConvertIntToUC2(int val, RTARDULINK_UC2 UC2)
|
||||||
|
{
|
||||||
|
UC2[1] = val & 0xff;
|
||||||
|
UC2[0] = (val >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTArduLinkCopyUC2(RTARDULINK_UC2 destUC2, RTARDULINK_UC2 sourceUC2)
|
||||||
|
{
|
||||||
|
destUC2[0] = sourceUC2[0];
|
||||||
|
destUC2[1] = sourceUC2[1];
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTArduLink
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015 richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge,
|
||||||
|
// to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files
|
||||||
|
// (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute,
|
||||||
|
// sublicense, and/or sell copies of the Software, and
|
||||||
|
// to permit persons to whom the Software is furnished
|
||||||
|
// to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice
|
||||||
|
// shall be included in all copies or substantial portions
|
||||||
|
// of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTARDULINKUTILS_H
|
||||||
|
#define _RTARDULINKUTILS_H
|
||||||
|
|
||||||
|
#include "RTArduLinkDefs.h"
|
||||||
|
|
||||||
|
// Function defs
|
||||||
|
|
||||||
|
void RTArduLinkRXFrameInit(RTARDULINK_RXFRAME *RXFrame, RTARDULINK_FRAME *frameBuffer); // initializes RTARDULINK_RXFRAME for a new frame
|
||||||
|
bool RTArduLinkReassemble(RTARDULINK_RXFRAME *RXFrame, unsigned char data); // adds a byte to the reassembly, returns false if error
|
||||||
|
|
||||||
|
// Checksum utilities
|
||||||
|
|
||||||
|
void RTArduLinkSetChecksum(RTARDULINK_FRAME *frame); // sets the checksum field prior to transmission
|
||||||
|
bool RTArduLinkCheckChecksum(RTARDULINK_FRAME *frame); // checks the checksum field after reception - returns true if ok, false if error
|
||||||
|
|
||||||
|
// Type conversion utilities
|
||||||
|
|
||||||
|
long RTArduLinkConvertUC4ToLong(RTARDULINK_UC4 uc4); // converts a 4 byte array to a signed long
|
||||||
|
void RTArduLinkConvertLongToUC4(long val, RTARDULINK_UC4 uc4); // converts a long to a four byte array
|
||||||
|
int RTArduLinkConvertUC2ToInt(RTARDULINK_UC2 uc2); // converts a 2 byte array to a signed integer
|
||||||
|
unsigned int RTArduLinkConvertUC2ToUInt(RTARDULINK_UC2 uc2);// converts a 2 byte array to an unsigned integer
|
||||||
|
void RTArduLinkConvertIntToUC2(int val, RTARDULINK_UC2 uc2);// converts an integer to a two byte array
|
||||||
|
void RTArduLinkCopyUC2(RTARDULINK_UC2 destUC2, RTARDULINK_UC2 sourceUC2); // copies a UC2
|
||||||
|
|
||||||
|
#endif // _RTARDULINKUTILS_H
|
|
@ -0,0 +1,235 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
|
||||||
|
#include "RTFusionRTQF.h"
|
||||||
|
|
||||||
|
#ifdef USE_SLERP
|
||||||
|
// The slerp power valule controls the influence of the measured state to correct the predicted state
|
||||||
|
// 0 = measured state ignored (just gyros), 1 = measured state overrides predicted state.
|
||||||
|
// In between 0 and 1 mixes the two conditions
|
||||||
|
|
||||||
|
#define RTQF_SLERP_POWER (RTFLOAT)0.02;
|
||||||
|
|
||||||
|
#else
|
||||||
|
// The QVALUE affects the gyro response.
|
||||||
|
|
||||||
|
#define RTQF_QVALUE (RTFLOAT)0.001
|
||||||
|
|
||||||
|
// The RVALUE controls the influence of the accels and compass.
|
||||||
|
// The bigger the value, the more sluggish the response.
|
||||||
|
|
||||||
|
#define RTQF_RVALUE (RTFLOAT)0.0005
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RTFusionRTQF::RTFusionRTQF()
|
||||||
|
{
|
||||||
|
#ifdef USE_SLERP
|
||||||
|
m_slerpPower = RTQF_SLERP_POWER;
|
||||||
|
#else
|
||||||
|
m_Q = RTQF_QVALUE;
|
||||||
|
m_R = RTQF_RVALUE;
|
||||||
|
#endif
|
||||||
|
m_enableGyro = true;
|
||||||
|
m_enableAccel = true;
|
||||||
|
m_enableCompass = true;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
RTFusionRTQF::~RTFusionRTQF()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTFusionRTQF::reset()
|
||||||
|
{
|
||||||
|
m_firstTime = true;
|
||||||
|
m_fusionPose = RTVector3();
|
||||||
|
m_fusionQPose.fromEuler(m_fusionPose);
|
||||||
|
m_measuredPose = RTVector3();
|
||||||
|
m_measuredQPose.fromEuler(m_measuredPose);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTFusionRTQF::newIMUData(const RTVector3& gyro, const RTVector3& accel, const RTVector3& compass, unsigned long timestamp)
|
||||||
|
{
|
||||||
|
RTVector3 fusionGyro;
|
||||||
|
|
||||||
|
if (m_firstTime) {
|
||||||
|
m_lastFusionTime = timestamp;
|
||||||
|
calculatePose(accel, compass);
|
||||||
|
|
||||||
|
// initialize the poses
|
||||||
|
|
||||||
|
m_fusionQPose.fromEuler(m_measuredPose);
|
||||||
|
m_fusionPose = m_measuredPose;
|
||||||
|
m_firstTime = false;
|
||||||
|
} else {
|
||||||
|
m_timeDelta = (RTFLOAT)(timestamp - m_lastFusionTime) / (RTFLOAT)1000;
|
||||||
|
m_lastFusionTime = timestamp;
|
||||||
|
if (m_timeDelta <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
calculatePose(accel, compass);
|
||||||
|
|
||||||
|
// predict();
|
||||||
|
|
||||||
|
RTFLOAT x2, y2, z2;
|
||||||
|
RTFLOAT qs, qx, qy,qz;
|
||||||
|
|
||||||
|
qs = m_fusionQPose.scalar();
|
||||||
|
qx = m_fusionQPose.x();
|
||||||
|
qy = m_fusionQPose.y();
|
||||||
|
qz = m_fusionQPose.z();
|
||||||
|
|
||||||
|
if (m_enableGyro)
|
||||||
|
fusionGyro = gyro;
|
||||||
|
else
|
||||||
|
fusionGyro = RTVector3();
|
||||||
|
|
||||||
|
x2 = fusionGyro.x() / (RTFLOAT)2.0;
|
||||||
|
y2 = fusionGyro.y() / (RTFLOAT)2.0;
|
||||||
|
z2 = fusionGyro.z() / (RTFLOAT)2.0;
|
||||||
|
|
||||||
|
// Predict new state
|
||||||
|
|
||||||
|
m_fusionQPose.setScalar(qs + (-x2 * qx - y2 * qy - z2 * qz) * m_timeDelta);
|
||||||
|
m_fusionQPose.setX(qx + (x2 * qs + z2 * qy - y2 * qz) * m_timeDelta);
|
||||||
|
m_fusionQPose.setY(qy + (y2 * qs - z2 * qx + x2 * qz) * m_timeDelta);
|
||||||
|
m_fusionQPose.setZ(qz + (z2 * qs + y2 * qx - x2 * qy) * m_timeDelta);
|
||||||
|
|
||||||
|
// update();
|
||||||
|
|
||||||
|
#ifdef USE_SLERP
|
||||||
|
if (m_enableCompass || m_enableAccel) {
|
||||||
|
|
||||||
|
// calculate rotation delta
|
||||||
|
|
||||||
|
m_rotationDelta = m_fusionQPose.conjugate() * m_measuredQPose;
|
||||||
|
m_rotationDelta.normalize();
|
||||||
|
|
||||||
|
// take it to the power (0 to 1) to give the desired amount of correction
|
||||||
|
|
||||||
|
RTFLOAT theta = acos(m_rotationDelta.scalar());
|
||||||
|
|
||||||
|
RTFLOAT sinPowerTheta = sin(theta * m_slerpPower);
|
||||||
|
RTFLOAT cosPowerTheta = cos(theta * m_slerpPower);
|
||||||
|
|
||||||
|
m_rotationUnitVector.setX(m_rotationDelta.x());
|
||||||
|
m_rotationUnitVector.setY(m_rotationDelta.y());
|
||||||
|
m_rotationUnitVector.setZ(m_rotationDelta.z());
|
||||||
|
m_rotationUnitVector.normalize();
|
||||||
|
|
||||||
|
m_rotationPower.setScalar(cosPowerTheta);
|
||||||
|
m_rotationPower.setX(sinPowerTheta * m_rotationUnitVector.x());
|
||||||
|
m_rotationPower.setY(sinPowerTheta * m_rotationUnitVector.y());
|
||||||
|
m_rotationPower.setZ(sinPowerTheta * m_rotationUnitVector.z());
|
||||||
|
m_rotationPower.normalize();
|
||||||
|
|
||||||
|
// multiple this by predicted value to get result
|
||||||
|
|
||||||
|
m_fusionQPose *= m_rotationPower;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (m_enableCompass || m_enableAccel) {
|
||||||
|
m_stateQError = m_measuredQPose - m_fusionQPose;
|
||||||
|
} else {
|
||||||
|
m_stateQError = RTQuaternion();
|
||||||
|
}
|
||||||
|
// make new state estimate
|
||||||
|
|
||||||
|
RTFLOAT qt = m_Q * m_timeDelta;
|
||||||
|
|
||||||
|
m_fusionQPose += m_stateQError * (qt / (qt + m_R));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_fusionQPose.normalize();
|
||||||
|
|
||||||
|
m_fusionQPose.toEuler(m_fusionPose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTFusionRTQF::calculatePose(const RTVector3& accel, const RTVector3& mag)
|
||||||
|
{
|
||||||
|
RTQuaternion m;
|
||||||
|
RTQuaternion q;
|
||||||
|
|
||||||
|
bool compassValid = (mag.x() != 0) || (mag.y() != 0) || (mag.z() != 0);
|
||||||
|
|
||||||
|
if (m_enableAccel) {
|
||||||
|
accel.accelToEuler(m_measuredPose);
|
||||||
|
} else {
|
||||||
|
m_measuredPose = m_fusionPose;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_enableCompass && compassValid) {
|
||||||
|
RTFLOAT cosX2 = cos(m_measuredPose.x() / 2.0f);
|
||||||
|
RTFLOAT sinX2 = sin(m_measuredPose.x() / 2.0f);
|
||||||
|
RTFLOAT cosY2 = cos(m_measuredPose.y() / 2.0f);
|
||||||
|
RTFLOAT sinY2 = sin(m_measuredPose.y() / 2.0f);
|
||||||
|
|
||||||
|
q.setScalar(cosX2 * cosY2);
|
||||||
|
q.setX(sinX2 * cosY2);
|
||||||
|
q.setY(cosX2 * sinY2);
|
||||||
|
q.setZ( - sinX2 * sinY2);
|
||||||
|
|
||||||
|
// normalize();
|
||||||
|
|
||||||
|
m.setScalar(0);
|
||||||
|
m.setX(mag.x());
|
||||||
|
m.setY(mag.y());
|
||||||
|
m.setZ(mag.z());
|
||||||
|
|
||||||
|
m = q * m * q.conjugate();
|
||||||
|
m_measuredPose.setZ(-atan2(m.y(), m.x()));
|
||||||
|
} else {
|
||||||
|
m_measuredPose.setZ(m_fusionPose.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_measuredQPose.fromEuler(m_measuredPose);
|
||||||
|
|
||||||
|
// check for quaternion aliasing. If the quaternion has the wrong sign
|
||||||
|
// the kalman filter will be very unhappy.
|
||||||
|
|
||||||
|
int maxIndex = -1;
|
||||||
|
RTFLOAT maxVal = -1000;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (fabs(m_measuredQPose.data(i)) > maxVal) {
|
||||||
|
maxVal = fabs(m_measuredQPose.data(i));
|
||||||
|
maxIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the biggest component has a different sign in the measured and kalman poses,
|
||||||
|
// change the sign of the measured pose to match.
|
||||||
|
|
||||||
|
if (((m_measuredQPose.data(maxIndex) < 0) && (m_fusionQPose.data(maxIndex) > 0)) ||
|
||||||
|
((m_measuredQPose.data(maxIndex) > 0) && (m_fusionQPose.data(maxIndex) < 0))) {
|
||||||
|
m_measuredQPose.setScalar(-m_measuredQPose.scalar());
|
||||||
|
m_measuredQPose.setX(-m_measuredQPose.x());
|
||||||
|
m_measuredQPose.setY(-m_measuredQPose.y());
|
||||||
|
m_measuredQPose.setZ(-m_measuredQPose.z());
|
||||||
|
m_measuredQPose.toEuler(m_measuredPose);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // #ifndef RTARDULINK_MODE
|
|
@ -0,0 +1,101 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTFUSIONRTQF_H
|
||||||
|
#define _RTFUSIONRTQF_H
|
||||||
|
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
|
||||||
|
#include "RTMath.h"
|
||||||
|
|
||||||
|
// Define this symbol to use more scientific prediction correction
|
||||||
|
|
||||||
|
#define USE_SLERP
|
||||||
|
|
||||||
|
class RTFusionRTQF
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTFusionRTQF();
|
||||||
|
~RTFusionRTQF();
|
||||||
|
|
||||||
|
// reset() resets the state but keeps any setting changes (such as enables)
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// newIMUData() should be called for subsequent updates
|
||||||
|
// deltaTime is in units of seconds
|
||||||
|
|
||||||
|
void newIMUData(const RTVector3& gyro, const RTVector3& accel, const RTVector3& compass, unsigned long timestamp);
|
||||||
|
|
||||||
|
// the following three functions control the influence of the gyro, accel and compass sensors
|
||||||
|
|
||||||
|
void setGyroEnable(bool enable) { m_enableGyro = enable;}
|
||||||
|
void setAccelEnable(bool enable) { m_enableAccel = enable; }
|
||||||
|
void setCompassEnable(bool enable) { m_enableCompass = enable;}
|
||||||
|
|
||||||
|
#ifdef USE_SLERP
|
||||||
|
// the following function can be called to set the SLERP power
|
||||||
|
void setSlerpPower(RTFLOAT power) { m_slerpPower = power; }
|
||||||
|
#else
|
||||||
|
// the following two functions can be called to customize the noise covariance
|
||||||
|
|
||||||
|
void setQ(RTFLOAT Q) { m_Q = Q; reset();}
|
||||||
|
void setR(RTFLOAT R) { if (R > 0) m_R = R; reset();}
|
||||||
|
#endif
|
||||||
|
inline const RTVector3& getMeasuredPose() {return m_measuredPose;}
|
||||||
|
inline const RTQuaternion& getMeasuredQPose() {return m_measuredQPose;}
|
||||||
|
inline const RTVector3& getFusionPose() {return m_fusionPose;}
|
||||||
|
inline const RTQuaternion& getFusionQPose() {return m_fusionQPose;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void calculatePose(const RTVector3& accel, const RTVector3& mag); // generates pose from accels and heading
|
||||||
|
|
||||||
|
RTFLOAT m_timeDelta; // time between predictions
|
||||||
|
|
||||||
|
RTQuaternion m_stateQError; // difference between stateQ and measuredQ
|
||||||
|
|
||||||
|
#ifdef USE_SLERP
|
||||||
|
RTFLOAT m_slerpPower; // a value 0 to 1 that controls measured state influence
|
||||||
|
RTQuaternion m_rotationDelta; // amount by which measured state differs from predicted
|
||||||
|
RTQuaternion m_rotationPower; // delta raised to the appopriate power
|
||||||
|
RTVector3 m_rotationUnitVector; // the vector part of the rotation delta
|
||||||
|
#else
|
||||||
|
RTFLOAT m_Q; // process noise covariance
|
||||||
|
RTFLOAT m_R; // the measurement noise covariance
|
||||||
|
#endif
|
||||||
|
RTQuaternion m_measuredQPose; // quaternion form of pose from measurement
|
||||||
|
RTVector3 m_measuredPose; // vector form of pose from measurement
|
||||||
|
RTQuaternion m_fusionQPose; // quaternion form of pose from fusion
|
||||||
|
RTVector3 m_fusionPose; // vector form of pose from fusion
|
||||||
|
|
||||||
|
bool m_enableGyro; // enables gyro as input
|
||||||
|
bool m_enableAccel; // enables accel as input
|
||||||
|
bool m_enableCompass; // enables compass a input
|
||||||
|
|
||||||
|
bool m_firstTime; // if first time after reset
|
||||||
|
unsigned long m_lastFusionTime; // for delta time calculation
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // #ifndef RTARDULINK_MODE
|
||||||
|
|
||||||
|
#endif // _RTFUSIONRTQF_H
|
|
@ -0,0 +1,357 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTIMU.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
#include "CalLib.h"
|
||||||
|
|
||||||
|
// this sets the learning rate for compass running average calculation
|
||||||
|
|
||||||
|
#define COMPASS_ALPHA (RTFLOAT)0.2
|
||||||
|
|
||||||
|
// this defines the accelerometer noise level
|
||||||
|
|
||||||
|
#define RTIMU_FUZZY_GYRO_ZERO (RTFLOAT)0.20
|
||||||
|
|
||||||
|
#define RTIMU_FUZZY_GYRO_ZERO_SQUARED (RTIMU_FUZZY_GYRO_ZERO * RTIMU_FUZZY_GYRO_ZERO)
|
||||||
|
|
||||||
|
// this defines the accelerometer noise level
|
||||||
|
|
||||||
|
#define RTIMU_FUZZY_ACCEL_ZERO (RTFLOAT)0.05
|
||||||
|
|
||||||
|
#define RTIMU_FUZZY_ACCEL_ZERO_SQUARED (RTIMU_FUZZY_ACCEL_ZERO * RTIMU_FUZZY_ACCEL_ZERO)
|
||||||
|
|
||||||
|
// Axis rotation array
|
||||||
|
|
||||||
|
#ifdef RTIMU_XNORTH_YEAST
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XEAST_YSOUTH
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, -1, 0, 1, 0, 0, 0, 0, 1};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XSOUTH_YWEST
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {-1, 0, 0, 0, -1, 0, 0, 0, 1};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XWEST_YNORTH
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 1, 0, -1, 0, 0, 0, 0, 1};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XNORTH_YWEST
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {1, 0, 0, 0, -1, 0, 0, 0, -1};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XEAST_YNORTH
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 1, 0, 1, 0, 0, 0, 0, -1};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XSOUTH_YEAST
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {-1, 0, 0, 0, 1, 0, 0, 0, -1};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XWEST_YSOUTH
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, -1, 0, -1, 0, 0, 0, 0, -1};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XUP_YNORTH
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 1, 0, 0, 0, -1, -1, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XUP_YEAST
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 0, 1, 0, 1, 0, -1, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XUP_YSOUTH
|
||||||
|
{0, -1, 0, 0, 0, 1, -1, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XUP_YWEST
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 0, -1, 0, -1, 0, -1, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XDOWN_YNORTH
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 1, 0, 0, 0, 1, 1, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XDOWN_YEAST
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 0, -1, 0, 1, 0, 1, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XDOWN_YSOUTH
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, -1, 0, 0, 0, -1, 1, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XDOWN_YWEST
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 0, 1, 0, -1, 0, 1, 0, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XNORTH_YUP
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {1, 0, 0, 0, 0, 1, 0, -1, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XEAST_YUP
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 0, -1, 1, 0, 0, 0, -1, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XSOUTH_YUP
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {-1, 0, 0, 0, 0, -1, 0, -1, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XWEST_YUP
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 0, 1, -1, 0, 0, 0, -1, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XNORTH_YDOWN
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {1, 0, 0, 0, 0, -1, 0, 1, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XEAST_YDOWN
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 0, 1, 1, 0, 0, 0, 1, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XSOUTH_YDOWN
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {-1, 0, 0, 0, 0, 1, 0, 1, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RTIMU_XWEST_YDOWN
|
||||||
|
RTFLOAT RTIMU::m_axisRotation[9] = {0, 0, -1, -1, 0, 0, 0, 1, 0};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(MPU9150_68) || defined(MPU9150_69)
|
||||||
|
#include "RTIMUMPU9150.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MPU9250_68) || defined(MPU9250_69)
|
||||||
|
#include "RTIMUMPU9250.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(LSM9DS0_6a) || defined(LSM9DS0_6b)
|
||||||
|
#include "RTIMULSM9DS0.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20HM303D_6a) || defined(GD20HM303D_6b)
|
||||||
|
#include "RTIMUGD20HM303D.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20M303DLHC_6a) || defined(GD20M303DLHC_6b)
|
||||||
|
#include "RTIMUGD20M303DLHC.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20HM303DLHC_6a) || defined(GD20HM303DLHC_6b)
|
||||||
|
#include "RTIMUGD20HM303DLHC.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BNO055_28) || defined(BNO055_29)
|
||||||
|
#include "RTIMUBNO055.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RTIMU *RTIMU::createIMU(RTIMUSettings *settings)
|
||||||
|
{
|
||||||
|
#if defined(MPU9150_68) || defined(MPU9150_69)
|
||||||
|
return new RTIMUMPU9150(settings);
|
||||||
|
#endif
|
||||||
|
#if defined(MPU9250_68) || defined(MPU9250_69)
|
||||||
|
return new RTIMUMPU9250(settings);
|
||||||
|
#endif
|
||||||
|
#if defined(LSM9DS0_6a) || defined(LSM9DS0_6b)
|
||||||
|
return new RTIMULSM9DS0(settings);
|
||||||
|
#endif
|
||||||
|
#if defined(GD20HM303D_6a) || defined(GD20HM303D_6b)
|
||||||
|
return new RTIMUGD20HM303D(settings);
|
||||||
|
#endif
|
||||||
|
#if defined(GD20M303DLHC_6a) || defined(GD20M303DLHC_6b)
|
||||||
|
return new RTIMUGD20M303DLHC(settings);
|
||||||
|
#endif
|
||||||
|
#if defined(GD20HM303DLHC_6a) || defined(GD20HM303DLHC_6b)
|
||||||
|
return new RTIMUGD20HM303DLHC(settings);
|
||||||
|
#endif
|
||||||
|
#if defined(BNO055_28) || defined(BNO055_29)
|
||||||
|
return new RTIMUBNO055(settings);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RTIMU::RTIMU(RTIMUSettings *settings)
|
||||||
|
{
|
||||||
|
m_settings = settings;
|
||||||
|
|
||||||
|
m_calibrationMode = false;
|
||||||
|
m_calibrationValid = false;
|
||||||
|
m_gyroBiasValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTIMU::~RTIMU()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTIMU::setCalibrationData()
|
||||||
|
{
|
||||||
|
float maxDelta = -1;
|
||||||
|
float delta;
|
||||||
|
CALLIB_DATA calData;
|
||||||
|
|
||||||
|
m_calibrationValid = false;
|
||||||
|
|
||||||
|
if (calLibRead(0, &calData)) {
|
||||||
|
if (calData.magValid != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find biggest range
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if ((calData.magMax[i] - calData.magMin[i]) > maxDelta)
|
||||||
|
maxDelta = calData.magMax[i] - calData.magMin[i];
|
||||||
|
}
|
||||||
|
if (maxDelta < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
maxDelta /= 2.0f; // this is the max +/- range
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
delta = (calData.magMax[i] - calData.magMin[i]) / 2.0f;
|
||||||
|
m_compassCalScale[i] = maxDelta / delta; // makes everything the same range
|
||||||
|
m_compassCalOffset[i] = (calData.magMax[i] + calData.magMin[i]) / 2.0f;
|
||||||
|
}
|
||||||
|
m_calibrationValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTIMU::gyroBiasInit()
|
||||||
|
{
|
||||||
|
m_gyroAlpha = 2.0f / m_sampleRate;
|
||||||
|
m_gyroSampleCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note - code assumes that this is the first thing called after axis swapping
|
||||||
|
// for each specific IMU chip has occurred.
|
||||||
|
|
||||||
|
void RTIMU::handleGyroBias()
|
||||||
|
{
|
||||||
|
// do axis rotation if necessary
|
||||||
|
#ifndef RTIMU_XNORTH_YEAST
|
||||||
|
// need to do an axis rotation
|
||||||
|
float *matrix = m_axisRotation;
|
||||||
|
RTVector3 tempGyro = m_gyro;
|
||||||
|
RTVector3 tempAccel = m_accel;
|
||||||
|
RTVector3 tempCompass = m_compass;
|
||||||
|
|
||||||
|
// do new x value
|
||||||
|
if (matrix[0] != 0) {
|
||||||
|
m_gyro.setX(tempGyro.x() * matrix[0]);
|
||||||
|
m_accel.setX(tempAccel.x() * matrix[0]);
|
||||||
|
m_compass.setX(tempCompass.x() * matrix[0]);
|
||||||
|
} else if (matrix[1] != 0) {
|
||||||
|
m_gyro.setX(tempGyro.y() * matrix[1]);
|
||||||
|
m_accel.setX(tempAccel.y() * matrix[1]);
|
||||||
|
m_compass.setX(tempCompass.y() * matrix[1]);
|
||||||
|
} else if (matrix[2] != 0) {
|
||||||
|
m_gyro.setX(tempGyro.z() * matrix[2]);
|
||||||
|
m_accel.setX(tempAccel.z() * matrix[2]);
|
||||||
|
m_compass.setX(tempCompass.z() * matrix[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do new y value
|
||||||
|
if (matrix[3] != 0) {
|
||||||
|
m_gyro.setY(tempGyro.x() * matrix[3]);
|
||||||
|
m_accel.setY(tempAccel.x() * matrix[3]);
|
||||||
|
m_compass.setY(tempCompass.x() * matrix[3]);
|
||||||
|
} else if (matrix[4] != 0) {
|
||||||
|
m_gyro.setY(tempGyro.y() * matrix[4]);
|
||||||
|
m_accel.setY(tempAccel.y() * matrix[4]);
|
||||||
|
m_compass.setY(tempCompass.y() * matrix[4]);
|
||||||
|
} else if (matrix[5] != 0) {
|
||||||
|
m_gyro.setY(tempGyro.z() * matrix[5]);
|
||||||
|
m_accel.setY(tempAccel.z() * matrix[5]);
|
||||||
|
m_compass.setY(tempCompass.z() * matrix[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do new z value
|
||||||
|
if (matrix[6] != 0) {
|
||||||
|
m_gyro.setZ(tempGyro.x() * matrix[6]);
|
||||||
|
m_accel.setZ(tempAccel.x() * matrix[6]);
|
||||||
|
m_compass.setZ(tempCompass.x() * matrix[6]);
|
||||||
|
} else if (matrix[7] != 0) {
|
||||||
|
m_gyro.setZ(tempGyro.y() * matrix[7]);
|
||||||
|
m_accel.setZ(tempAccel.y() * matrix[7]);
|
||||||
|
m_compass.setZ(tempCompass.y() * matrix[7]);
|
||||||
|
} else if (matrix[8] != 0) {
|
||||||
|
m_gyro.setZ(tempGyro.z() * matrix[8]);
|
||||||
|
m_accel.setZ(tempAccel.z() * matrix[8]);
|
||||||
|
m_compass.setZ(tempCompass.z() * matrix[8]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!m_gyroBiasValid) {
|
||||||
|
RTVector3 deltaAccel = m_previousAccel;
|
||||||
|
deltaAccel -= m_accel; // compute difference
|
||||||
|
m_previousAccel = m_accel;
|
||||||
|
|
||||||
|
if ((deltaAccel.squareLength() < RTIMU_FUZZY_ACCEL_ZERO_SQUARED) &&
|
||||||
|
(m_gyro.squareLength() < RTIMU_FUZZY_GYRO_ZERO_SQUARED)) {
|
||||||
|
// what we are seeing on the gyros should be bias only so learn from this
|
||||||
|
m_gyroBias.setX((1.0 - m_gyroAlpha) * m_gyroBias.x() + m_gyroAlpha * m_gyro.x());
|
||||||
|
m_gyroBias.setY((1.0 - m_gyroAlpha) * m_gyroBias.y() + m_gyroAlpha * m_gyro.y());
|
||||||
|
m_gyroBias.setZ((1.0 - m_gyroAlpha) * m_gyroBias.z() + m_gyroAlpha * m_gyro.z());
|
||||||
|
|
||||||
|
if (m_gyroSampleCount < (5 * m_sampleRate)) {
|
||||||
|
m_gyroSampleCount++;
|
||||||
|
|
||||||
|
if (m_gyroSampleCount == (5 * m_sampleRate)) {
|
||||||
|
m_gyroBiasValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_gyro -= m_gyroBias;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTIMU::calibrateAverageCompass()
|
||||||
|
{
|
||||||
|
// calibrate if required
|
||||||
|
|
||||||
|
if (!m_calibrationMode && m_calibrationValid) {
|
||||||
|
m_compass.setX((m_compass.x() - m_compassCalOffset[0]) * m_compassCalScale[0]);
|
||||||
|
m_compass.setY((m_compass.y() - m_compassCalOffset[1]) * m_compassCalScale[1]);
|
||||||
|
m_compass.setZ((m_compass.z() - m_compassCalOffset[2]) * m_compassCalScale[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update running average
|
||||||
|
|
||||||
|
m_compassAverage.setX(m_compass.x() * COMPASS_ALPHA + m_compassAverage.x() * (1.0 - COMPASS_ALPHA));
|
||||||
|
m_compassAverage.setY(m_compass.y() * COMPASS_ALPHA + m_compassAverage.y() * (1.0 - COMPASS_ALPHA));
|
||||||
|
m_compassAverage.setZ(m_compass.z() * COMPASS_ALPHA + m_compassAverage.z() * (1.0 - COMPASS_ALPHA));
|
||||||
|
|
||||||
|
m_compass = m_compassAverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMU::IMUGyroBiasValid()
|
||||||
|
{
|
||||||
|
return m_gyroBiasValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMU_H
|
||||||
|
#define _RTIMU_H
|
||||||
|
|
||||||
|
#include "RTMath.h"
|
||||||
|
#include "RTIMULibDefs.h"
|
||||||
|
#include "I2Cdev.h"
|
||||||
|
|
||||||
|
#define I2CWrite(x, y, z) I2Cdev::writeByte(x, y, z)
|
||||||
|
#define I2CRead(w, x, y, z) I2Cdev::readBytes(w, x, y, z)
|
||||||
|
|
||||||
|
class RTIMUSettings;
|
||||||
|
|
||||||
|
class RTIMU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// IMUs should always be created with the following call
|
||||||
|
|
||||||
|
static RTIMU *createIMU(RTIMUSettings *settings);
|
||||||
|
|
||||||
|
// Constructor/destructor
|
||||||
|
|
||||||
|
RTIMU(RTIMUSettings *settings);
|
||||||
|
virtual ~RTIMU();
|
||||||
|
|
||||||
|
// These functions must be provided by sub classes
|
||||||
|
|
||||||
|
virtual const char *IMUName() = 0; // the name of the IMU
|
||||||
|
virtual int IMUType() = 0; // the type code of the IMU
|
||||||
|
virtual int IMUInit() = 0; // set up the IMU
|
||||||
|
virtual int IMUGetPollInterval() = 0; // returns the recommended poll interval in mS
|
||||||
|
virtual bool IMURead() = 0; // get a sample
|
||||||
|
|
||||||
|
// This one wanted a similar name but isn't pure virtual
|
||||||
|
|
||||||
|
virtual bool IMUCompassCalValid() { return m_calibrationValid; }
|
||||||
|
|
||||||
|
// setCalibrationMode() turns off use of cal data so that raw data can be accumulated
|
||||||
|
// to derive calibration data
|
||||||
|
|
||||||
|
void setCalibrationMode(bool enable) { m_calibrationMode = enable; }
|
||||||
|
|
||||||
|
// setCalibrationData configured the cal data and also enables use if valid
|
||||||
|
|
||||||
|
void setCalibrationData();
|
||||||
|
|
||||||
|
// getCalibrationValid() returns true if the calibration data is being used
|
||||||
|
|
||||||
|
bool getCalibrationValid() { return !m_calibrationMode && m_calibrationValid; }
|
||||||
|
|
||||||
|
// returns true if enough samples for valid data
|
||||||
|
|
||||||
|
virtual bool IMUGyroBiasValid();
|
||||||
|
|
||||||
|
inline const RTVector3& getGyro() { return m_gyro; } // gets gyro rates in radians/sec
|
||||||
|
inline const RTVector3& getAccel() { return m_accel; } // get accel data in gs
|
||||||
|
inline const RTVector3& getCompass() { return m_compass; } // gets compass data in uT
|
||||||
|
inline unsigned long getTimestamp() { return m_timestamp; } // and the timestamp for it
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void gyroBiasInit(); // sets up gyro bias calculation
|
||||||
|
void handleGyroBias(); // adjust gyro for bias
|
||||||
|
void calibrateAverageCompass(); // calibrate and smooth compass
|
||||||
|
bool m_calibrationMode; // true if cal mode so don't use cal data!
|
||||||
|
bool m_calibrationValid; // tru if call data is valid and can be used
|
||||||
|
|
||||||
|
RTVector3 m_gyro; // the gyro readings
|
||||||
|
RTVector3 m_accel; // the accel readings
|
||||||
|
RTVector3 m_compass; // the compass readings
|
||||||
|
unsigned long m_timestamp; // the timestamp
|
||||||
|
|
||||||
|
RTIMUSettings *m_settings; // the settings object pointer
|
||||||
|
|
||||||
|
int m_sampleRate; // samples per second
|
||||||
|
uint64_t m_sampleInterval; // interval between samples in microseonds
|
||||||
|
|
||||||
|
RTFLOAT m_gyroAlpha; // gyro bias learning rate
|
||||||
|
int m_gyroSampleCount; // number of gyro samples used
|
||||||
|
bool m_gyroBiasValid; // true if the recorded gyro bias is valid
|
||||||
|
RTVector3 m_gyroBias; // the recorded gyro bias
|
||||||
|
|
||||||
|
RTVector3 m_previousAccel; // previous step accel for gyro learning
|
||||||
|
|
||||||
|
RTFLOAT m_compassCalOffset[3];
|
||||||
|
RTFLOAT m_compassCalScale[3];
|
||||||
|
RTVector3 m_compassAverage; // a running average to smooth the mag outputs
|
||||||
|
|
||||||
|
static RTFLOAT m_axisRotation[9]; // axis rotation matrix
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMU_H
|
|
@ -0,0 +1,180 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
// Based on the Adafruit BNO055 driver:
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
This is a library for the BNO055 orientation sensor
|
||||||
|
Designed specifically to work with the Adafruit BNO055 Breakout.
|
||||||
|
Pick one up today in the adafruit shop!
|
||||||
|
------> http://www.adafruit.com/products
|
||||||
|
These sensors use I2C to communicate, 2 pins are required to interface.
|
||||||
|
Adafruit invests time and resources providing this open source code,
|
||||||
|
please support Adafruit andopen-source hardware by purchasing products
|
||||||
|
from Adafruit!
|
||||||
|
Written by KTOWN for Adafruit Industries.
|
||||||
|
MIT license, all text above must be included in any redistribution
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "RTIMUBNO055.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
#if defined(BNO055_28) || defined(BNO055_29)
|
||||||
|
|
||||||
|
RTIMUBNO055::RTIMUBNO055(RTIMUSettings *settings) : RTIMU(settings)
|
||||||
|
{
|
||||||
|
m_sampleRate = 100;
|
||||||
|
m_sampleInterval = (unsigned long)1000 / m_sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTIMUBNO055::~RTIMUBNO055()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMUBNO055::IMUInit()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
|
||||||
|
m_slaveAddr = m_settings->m_I2CSlaveAddress;
|
||||||
|
m_lastReadTime = millis();
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_slaveAddr, BNO055_WHO_AM_I, &result))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (result != BNO055_ID) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, BNO055_OPER_MODE, BNO055_OPER_MODE_CONFIG))
|
||||||
|
return -3;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, BNO055_SYS_TRIGGER, 0x20))
|
||||||
|
return -4;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (!I2Cdev::readByte(m_slaveAddr, BNO055_WHO_AM_I, &result))
|
||||||
|
continue;
|
||||||
|
if (result == BNO055_ID)
|
||||||
|
break;
|
||||||
|
delay(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, BNO055_PWR_MODE, BNO055_PWR_MODE_NORMAL))
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, BNO055_PAGE_ID, 0))
|
||||||
|
return -6;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, BNO055_SYS_TRIGGER, 0x00))
|
||||||
|
return -7;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, BNO055_UNIT_SEL, 0x87))
|
||||||
|
return -8;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, BNO055_OPER_MODE, BNO055_OPER_MODE_NDOF))
|
||||||
|
return -9;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMUBNO055::IMUGetPollInterval()
|
||||||
|
{
|
||||||
|
return (7);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUBNO055::IMURead()
|
||||||
|
{
|
||||||
|
unsigned char buffer[24];
|
||||||
|
|
||||||
|
if ((millis() - m_lastReadTime) < m_sampleInterval)
|
||||||
|
return false; // too soon
|
||||||
|
|
||||||
|
m_lastReadTime = millis();
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, BNO055_ACCEL_DATA, 24, buffer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int16_t x, y, z;
|
||||||
|
|
||||||
|
// process accel data
|
||||||
|
|
||||||
|
x = (((uint16_t)buffer[1]) << 8) | ((uint16_t)buffer[0]);
|
||||||
|
y = (((uint16_t)buffer[3]) << 8) | ((uint16_t)buffer[2]);
|
||||||
|
z = (((uint16_t)buffer[5]) << 8) | ((uint16_t)buffer[4]);
|
||||||
|
|
||||||
|
m_accel.setX((RTFLOAT)y / 1000.0);
|
||||||
|
m_accel.setY((RTFLOAT)x / 1000.0);
|
||||||
|
m_accel.setZ((RTFLOAT)z / 1000.0);
|
||||||
|
|
||||||
|
// process mag data
|
||||||
|
|
||||||
|
x = (((uint16_t)buffer[7]) << 8) | ((uint16_t)buffer[6]);
|
||||||
|
y = (((uint16_t)buffer[9]) << 8) | ((uint16_t)buffer[8]);
|
||||||
|
z = (((uint16_t)buffer[11]) << 8) | ((uint16_t)buffer[10]);
|
||||||
|
|
||||||
|
m_compass.setX(-(RTFLOAT)y / 16.0);
|
||||||
|
m_compass.setY(-(RTFLOAT)x / 16.0);
|
||||||
|
m_compass.setZ(-(RTFLOAT)z / 16.0);
|
||||||
|
|
||||||
|
// process gyro data
|
||||||
|
|
||||||
|
x = (((uint16_t)buffer[13]) << 8) | ((uint16_t)buffer[12]);
|
||||||
|
y = (((uint16_t)buffer[15]) << 8) | ((uint16_t)buffer[14]);
|
||||||
|
z = (((uint16_t)buffer[17]) << 8) | ((uint16_t)buffer[16]);
|
||||||
|
|
||||||
|
m_gyro.setX(-(RTFLOAT)y / 900.0);
|
||||||
|
m_gyro.setY(-(RTFLOAT)x / 900.0);
|
||||||
|
m_gyro.setZ(-(RTFLOAT)z / 900.0);
|
||||||
|
|
||||||
|
// process euler angles
|
||||||
|
|
||||||
|
x = (((uint16_t)buffer[19]) << 8) | ((uint16_t)buffer[18]);
|
||||||
|
y = (((uint16_t)buffer[21]) << 8) | ((uint16_t)buffer[20]);
|
||||||
|
z = (((uint16_t)buffer[23]) << 8) | ((uint16_t)buffer[22]);
|
||||||
|
|
||||||
|
// put in structure and do axis remap
|
||||||
|
|
||||||
|
m_fusionPose.setX((RTFLOAT)y / 900.0);
|
||||||
|
m_fusionPose.setY((RTFLOAT)z / 900.0);
|
||||||
|
m_fusionPose.setZ((RTFLOAT)x / 900.0);
|
||||||
|
|
||||||
|
m_fusionQPose.fromEuler(m_fusionPose);
|
||||||
|
|
||||||
|
m_timestamp = millis();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,85 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _RTIMUBNO055_H
|
||||||
|
#define _RTIMUBNO055_H
|
||||||
|
|
||||||
|
#include "RTIMU.h"
|
||||||
|
|
||||||
|
// I2C Slave Addresses
|
||||||
|
|
||||||
|
#define BNO055_ADDRESS0 0x28
|
||||||
|
#define BNO055_ADDRESS1 0x29
|
||||||
|
#define BNO055_ID 0xa0
|
||||||
|
|
||||||
|
// Register map
|
||||||
|
|
||||||
|
#define BNO055_WHO_AM_I 0x00
|
||||||
|
#define BNO055_PAGE_ID 0x07
|
||||||
|
#define BNO055_ACCEL_DATA 0x08
|
||||||
|
#define BNO055_MAG_DATA 0x0e
|
||||||
|
#define BNO055_GYRO_DATA 0x14
|
||||||
|
#define BNO055_FUSED_EULER 0x1a
|
||||||
|
#define BNO055_FUSED_QUAT 0x20
|
||||||
|
#define BNO055_UNIT_SEL 0x3b
|
||||||
|
#define BNO055_OPER_MODE 0x3d
|
||||||
|
#define BNO055_PWR_MODE 0x3e
|
||||||
|
#define BNO055_SYS_TRIGGER 0x3f
|
||||||
|
#define BNO055_AXIS_MAP_CONFIG 0x41
|
||||||
|
#define BNO055_AXIS_MAP_SIGN 0x42
|
||||||
|
|
||||||
|
// Operation modes
|
||||||
|
|
||||||
|
#define BNO055_OPER_MODE_CONFIG 0x00
|
||||||
|
#define BNO055_OPER_MODE_NDOF 0x0c
|
||||||
|
|
||||||
|
// Power modes
|
||||||
|
|
||||||
|
#define BNO055_PWR_MODE_NORMAL 0x00
|
||||||
|
|
||||||
|
class RTIMUBNO055 : public RTIMU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTIMUBNO055(RTIMUSettings *settings);
|
||||||
|
~RTIMUBNO055();
|
||||||
|
|
||||||
|
inline const RTVector3& getFusionPose() { return m_fusionPose; }
|
||||||
|
inline const RTQuaternion& getFusionQPose() { return m_fusionQPose; }
|
||||||
|
|
||||||
|
virtual const char *IMUName() { return "BNO055"; }
|
||||||
|
virtual int IMUType() { return RTIMU_TYPE_BNO055; }
|
||||||
|
virtual int IMUInit();
|
||||||
|
virtual int IMUGetPollInterval();
|
||||||
|
virtual bool IMURead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char m_slaveAddr; // I2C address of BNO055
|
||||||
|
|
||||||
|
uint64_t m_lastReadTime;
|
||||||
|
|
||||||
|
RTQuaternion m_fusionQPose;
|
||||||
|
RTVector3 m_fusionPose;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMUBNO055_H
|
|
@ -0,0 +1,402 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTIMUGD20HM303D.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
|
||||||
|
#if defined(GD20HM303D_6a) || defined(GD20HM303D_6b)
|
||||||
|
|
||||||
|
RTIMUGD20HM303D::RTIMUGD20HM303D(RTIMUSettings *settings) : RTIMU(settings)
|
||||||
|
{
|
||||||
|
m_sampleRate = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTIMUGD20HM303D::~RTIMUGD20HM303D()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMUGD20HM303D::IMUInit()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
|
||||||
|
// configure IMU
|
||||||
|
|
||||||
|
m_gyroSlaveAddr = m_settings->m_I2CSlaveAddress;
|
||||||
|
if (m_gyroSlaveAddr == L3GD20H_ADDRESS0)
|
||||||
|
m_accelCompassSlaveAddr = LSM303D_ADDRESS0;
|
||||||
|
else
|
||||||
|
m_accelCompassSlaveAddr = LSM303D_ADDRESS1;
|
||||||
|
|
||||||
|
setCalibrationData();
|
||||||
|
|
||||||
|
// Set up the gyro
|
||||||
|
|
||||||
|
if (!I2CWrite(m_gyroSlaveAddr, L3GD20H_LOW_ODR, 0x04))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL5, 0x80))
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, L3GD20H_WHO_AM_I, 1, &result))
|
||||||
|
return -3;
|
||||||
|
|
||||||
|
if (result != L3GD20H_ID) {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setGyroSampleRate())
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
if (!setGyroCTRL2())
|
||||||
|
return -6;
|
||||||
|
|
||||||
|
if (!setGyroCTRL4())
|
||||||
|
return -7;
|
||||||
|
|
||||||
|
// Set up the accel/compass
|
||||||
|
|
||||||
|
if (!I2CRead(m_accelCompassSlaveAddr, LSM303D_WHO_AM_I, 1, &result))
|
||||||
|
return -8;
|
||||||
|
|
||||||
|
if (result != LSM303D_ID) {
|
||||||
|
return -9;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setAccelCTRL1())
|
||||||
|
return -10;
|
||||||
|
|
||||||
|
if (!setAccelCTRL2())
|
||||||
|
return -11;
|
||||||
|
|
||||||
|
if (!setCompassCTRL5())
|
||||||
|
return -12;
|
||||||
|
|
||||||
|
if (!setCompassCTRL6())
|
||||||
|
return -13;
|
||||||
|
|
||||||
|
if (!setCompassCTRL7())
|
||||||
|
return -14;
|
||||||
|
|
||||||
|
if (!setGyroCTRL5())
|
||||||
|
return -16;
|
||||||
|
|
||||||
|
gyroBiasInit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setGyroSampleRate()
|
||||||
|
{
|
||||||
|
unsigned char ctrl1;
|
||||||
|
unsigned char lowOdr = 0;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DGyroSampleRate) {
|
||||||
|
case L3GD20H_SAMPLERATE_12_5:
|
||||||
|
ctrl1 = 0x0f;
|
||||||
|
lowOdr = 1;
|
||||||
|
m_sampleRate = 13;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_25:
|
||||||
|
ctrl1 = 0x4f;
|
||||||
|
lowOdr = 1;
|
||||||
|
m_sampleRate = 25;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_50:
|
||||||
|
ctrl1 = 0x8f;
|
||||||
|
lowOdr = 1;
|
||||||
|
m_sampleRate = 50;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_100:
|
||||||
|
ctrl1 = 0x0f;
|
||||||
|
m_sampleRate = 100;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_200:
|
||||||
|
ctrl1 = 0x4f;
|
||||||
|
m_sampleRate = 200;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_400:
|
||||||
|
ctrl1 = 0x8f;
|
||||||
|
m_sampleRate = 400;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_800:
|
||||||
|
ctrl1 = 0xcf;
|
||||||
|
m_sampleRate = 800;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleInterval = (uint64_t)1000000 / m_sampleRate;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DGyroBW) {
|
||||||
|
case L3GD20H_BANDWIDTH_0:
|
||||||
|
ctrl1 |= 0x00;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_BANDWIDTH_1:
|
||||||
|
ctrl1 |= 0x10;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_BANDWIDTH_2:
|
||||||
|
ctrl1 |= 0x20;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_BANDWIDTH_3:
|
||||||
|
ctrl1 |= 0x30;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2CWrite(m_gyroSlaveAddr, L3GD20H_LOW_ODR, lowOdr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL1, ctrl1));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setGyroCTRL2()
|
||||||
|
{
|
||||||
|
if ((m_settings->m_GD20HM303DGyroHpf < L3GD20H_HPF_0) || (m_settings->m_GD20HM303DGyroHpf > L3GD20H_HPF_9)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL2, m_settings->m_GD20HM303DGyroHpf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setGyroCTRL4()
|
||||||
|
{
|
||||||
|
unsigned char ctrl4;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DGyroFsr) {
|
||||||
|
case L3GD20H_FSR_245:
|
||||||
|
ctrl4 = 0x00;
|
||||||
|
m_gyroScale = (RTFLOAT)0.00875 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_FSR_500:
|
||||||
|
ctrl4 = 0x10;
|
||||||
|
m_gyroScale = (RTFLOAT)0.0175 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_FSR_2000:
|
||||||
|
ctrl4 = 0x20;
|
||||||
|
m_gyroScale = (RTFLOAT)0.07 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL4, ctrl4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setGyroCTRL5()
|
||||||
|
{
|
||||||
|
unsigned char ctrl5;
|
||||||
|
|
||||||
|
// Turn on hpf
|
||||||
|
|
||||||
|
ctrl5 = 0x10;
|
||||||
|
|
||||||
|
#ifdef GD20HM303D_CACHE_MODE
|
||||||
|
// turn on fifo
|
||||||
|
|
||||||
|
ctrl5 |= 0x40;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL5, ctrl5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setAccelCTRL1()
|
||||||
|
{
|
||||||
|
unsigned char ctrl1;
|
||||||
|
|
||||||
|
if ((m_settings->m_GD20HM303DAccelSampleRate < 0) || (m_settings->m_GD20HM303DAccelSampleRate > 10)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl1 = (m_settings->m_GD20HM303DAccelSampleRate << 4) | 0x07;
|
||||||
|
|
||||||
|
return I2CWrite(m_accelCompassSlaveAddr, LSM303D_CTRL1, ctrl1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setAccelCTRL2()
|
||||||
|
{
|
||||||
|
unsigned char ctrl2;
|
||||||
|
|
||||||
|
if ((m_settings->m_GD20HM303DAccelLpf < 0) || (m_settings->m_GD20HM303DAccelLpf > 3)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DAccelFsr) {
|
||||||
|
case LSM303D_ACCEL_FSR_2:
|
||||||
|
m_accelScale = (RTFLOAT)0.000061;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303D_ACCEL_FSR_4:
|
||||||
|
m_accelScale = (RTFLOAT)0.000122;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303D_ACCEL_FSR_6:
|
||||||
|
m_accelScale = (RTFLOAT)0.000183;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303D_ACCEL_FSR_8:
|
||||||
|
m_accelScale = (RTFLOAT)0.000244;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303D_ACCEL_FSR_16:
|
||||||
|
m_accelScale = (RTFLOAT)0.000732;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl2 = (m_settings->m_GD20HM303DAccelLpf << 6) | (m_settings->m_GD20HM303DAccelFsr << 3);
|
||||||
|
|
||||||
|
return I2CWrite(m_accelCompassSlaveAddr, LSM303D_CTRL2, ctrl2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setCompassCTRL5()
|
||||||
|
{
|
||||||
|
unsigned char ctrl5;
|
||||||
|
|
||||||
|
if ((m_settings->m_GD20HM303DCompassSampleRate < 0) || (m_settings->m_GD20HM303DCompassSampleRate > 5)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl5 = (m_settings->m_GD20HM303DCompassSampleRate << 2);
|
||||||
|
|
||||||
|
#ifdef GD20HM303D_CACHE_MODE
|
||||||
|
// enable fifo
|
||||||
|
|
||||||
|
ctrl5 |= 0x40;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return I2CWrite(m_accelCompassSlaveAddr, LSM303D_CTRL5, ctrl5);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setCompassCTRL6()
|
||||||
|
{
|
||||||
|
unsigned char ctrl6;
|
||||||
|
|
||||||
|
// convert FSR to uT
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DCompassFsr) {
|
||||||
|
case LSM303D_COMPASS_FSR_2:
|
||||||
|
ctrl6 = 0;
|
||||||
|
m_compassScale = (RTFLOAT)0.008;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303D_COMPASS_FSR_4:
|
||||||
|
ctrl6 = 0x20;
|
||||||
|
m_compassScale = (RTFLOAT)0.016;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303D_COMPASS_FSR_8:
|
||||||
|
ctrl6 = 0x40;
|
||||||
|
m_compassScale = (RTFLOAT)0.032;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303D_COMPASS_FSR_12:
|
||||||
|
ctrl6 = 0x60;
|
||||||
|
m_compassScale = (RTFLOAT)0.0479;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I2CWrite(m_accelCompassSlaveAddr, LSM303D_CTRL6, ctrl6);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::setCompassCTRL7()
|
||||||
|
{
|
||||||
|
return I2CWrite(m_accelCompassSlaveAddr, LSM303D_CTRL7, 0x60);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int RTIMUGD20HM303D::IMUGetPollInterval()
|
||||||
|
{
|
||||||
|
return (400 / m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303D::IMURead()
|
||||||
|
{
|
||||||
|
unsigned char status;
|
||||||
|
unsigned char gyroData[6];
|
||||||
|
unsigned char accelData[6];
|
||||||
|
unsigned char compassData[6];
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, L3GD20H_STATUS, 1, &status))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((status & 0x8) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, 0x80 | L3GD20H_OUT_X_L, 6, gyroData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_timestamp = millis();
|
||||||
|
|
||||||
|
if (!I2CRead(m_accelCompassSlaveAddr, 0x80 | LSM303D_OUT_X_L_A, 6, accelData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2CRead(m_accelCompassSlaveAddr, 0x80 | LSM303D_OUT_X_L_M, 6, compassData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RTMath::convertToVector(gyroData, m_gyro, m_gyroScale, false);
|
||||||
|
RTMath::convertToVector(accelData, m_accel, m_accelScale, false);
|
||||||
|
RTMath::convertToVector(compassData, m_compass, m_compassScale, false);
|
||||||
|
|
||||||
|
// sort out gyro axes
|
||||||
|
|
||||||
|
m_gyro.setY(-m_gyro.y());
|
||||||
|
m_gyro.setZ(-m_gyro.z());
|
||||||
|
|
||||||
|
// sort out accel data;
|
||||||
|
|
||||||
|
m_accel.setX(-m_accel.x());
|
||||||
|
|
||||||
|
// sort out compass axes
|
||||||
|
|
||||||
|
m_compass.setY(-m_compass.y());
|
||||||
|
m_compass.setZ(-m_compass.z());
|
||||||
|
|
||||||
|
// now do standard processing
|
||||||
|
|
||||||
|
handleGyroBias();
|
||||||
|
calibrateAverageCompass();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,238 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMUGD20HM303D_H
|
||||||
|
#define _RTIMUGD20HM303D_H
|
||||||
|
|
||||||
|
#include "RTIMU.h"
|
||||||
|
|
||||||
|
// I2C Slave Addresses
|
||||||
|
|
||||||
|
#define L3GD20H_ADDRESS0 0x69
|
||||||
|
#define L3GD20H_ADDRESS1 0x6e
|
||||||
|
#define L3GD20H_ID 0xd7
|
||||||
|
|
||||||
|
#define LSM303D_ADDRESS0 0x19
|
||||||
|
#define LSM303D_ADDRESS1 0x1e
|
||||||
|
#define LSM303D_ID 0x49
|
||||||
|
|
||||||
|
// L3GD20H Register map
|
||||||
|
|
||||||
|
#define L3GD20H_WHO_AM_I 0x0f
|
||||||
|
#define L3GD20H_CTRL1 0x20
|
||||||
|
#define L3GD20H_CTRL2 0x21
|
||||||
|
#define L3GD20H_CTRL3 0x22
|
||||||
|
#define L3GD20H_CTRL4 0x23
|
||||||
|
#define L3GD20H_CTRL5 0x24
|
||||||
|
#define L3GD20H_OUT_TEMP 0x26
|
||||||
|
#define L3GD20H_STATUS 0x27
|
||||||
|
#define L3GD20H_OUT_X_L 0x28
|
||||||
|
#define L3GD20H_OUT_X_H 0x29
|
||||||
|
#define L3GD20H_OUT_Y_L 0x2a
|
||||||
|
#define L3GD20H_OUT_Y_H 0x2b
|
||||||
|
#define L3GD20H_OUT_Z_L 0x2c
|
||||||
|
#define L3GD20H_OUT_Z_H 0x2d
|
||||||
|
#define L3GD20H_FIFO_CTRL 0x2e
|
||||||
|
#define L3GD20H_FIFO_SRC 0x2f
|
||||||
|
#define L3GD20H_IG_CFG 0x30
|
||||||
|
#define L3GD20H_IG_SRC 0x31
|
||||||
|
#define L3GD20H_IG_THS_XH 0x32
|
||||||
|
#define L3GD20H_IG_THS_XL 0x33
|
||||||
|
#define L3GD20H_IG_THS_YH 0x34
|
||||||
|
#define L3GD20H_IG_THS_YL 0x35
|
||||||
|
#define L3GD20H_IG_THS_ZH 0x36
|
||||||
|
#define L3GD20H_IG_THS_ZL 0x37
|
||||||
|
#define L3GD20H_IG_DURATION 0x38
|
||||||
|
#define L3GD20H_LOW_ODR 0x39
|
||||||
|
|
||||||
|
// Gyro sample rate defines
|
||||||
|
|
||||||
|
#define L3GD20H_SAMPLERATE_12_5 0
|
||||||
|
#define L3GD20H_SAMPLERATE_25 1
|
||||||
|
#define L3GD20H_SAMPLERATE_50 2
|
||||||
|
#define L3GD20H_SAMPLERATE_100 3
|
||||||
|
#define L3GD20H_SAMPLERATE_200 4
|
||||||
|
#define L3GD20H_SAMPLERATE_400 5
|
||||||
|
#define L3GD20H_SAMPLERATE_800 6
|
||||||
|
|
||||||
|
// Gyro banwidth defines
|
||||||
|
|
||||||
|
#define L3GD20H_BANDWIDTH_0 0
|
||||||
|
#define L3GD20H_BANDWIDTH_1 1
|
||||||
|
#define L3GD20H_BANDWIDTH_2 2
|
||||||
|
#define L3GD20H_BANDWIDTH_3 3
|
||||||
|
|
||||||
|
// Gyro FSR defines
|
||||||
|
|
||||||
|
#define L3GD20H_FSR_245 0
|
||||||
|
#define L3GD20H_FSR_500 1
|
||||||
|
#define L3GD20H_FSR_2000 2
|
||||||
|
|
||||||
|
// Gyro high pass filter defines
|
||||||
|
|
||||||
|
#define L3GD20H_HPF_0 0
|
||||||
|
#define L3GD20H_HPF_1 1
|
||||||
|
#define L3GD20H_HPF_2 2
|
||||||
|
#define L3GD20H_HPF_3 3
|
||||||
|
#define L3GD20H_HPF_4 4
|
||||||
|
#define L3GD20H_HPF_5 5
|
||||||
|
#define L3GD20H_HPF_6 6
|
||||||
|
#define L3GD20H_HPF_7 7
|
||||||
|
#define L3GD20H_HPF_8 8
|
||||||
|
#define L3GD20H_HPF_9 9
|
||||||
|
|
||||||
|
// LSM303D Register Map
|
||||||
|
|
||||||
|
#define LSM303D_TEMP_OUT_L 0x05
|
||||||
|
#define LSM303D_TEMP_OUT_H 0x06
|
||||||
|
#define LSM303D_STATUS_M 0x07
|
||||||
|
#define LSM303D_OUT_X_L_M 0x08
|
||||||
|
#define LSM303D_OUT_X_H_M 0x09
|
||||||
|
#define LSM303D_OUT_Y_L_M 0x0a
|
||||||
|
#define LSM303D_OUT_Y_H_M 0x0b
|
||||||
|
#define LSM303D_OUT_Z_L_M 0x0c
|
||||||
|
#define LSM303D_OUT_Z_H_M 0x0d
|
||||||
|
#define LSM303D_WHO_AM_I 0x0f
|
||||||
|
#define LSM303D_INT_CTRL_M 0x12
|
||||||
|
#define LSM303D_INT_SRC_M 0x13
|
||||||
|
#define LSM303D_INT_THS_L_M 0x14
|
||||||
|
#define LSM303D_INT_THS_H_M 0x15
|
||||||
|
#define LSM303D_OFFSET_X_L_M 0x16
|
||||||
|
#define LSM303D_OFFSET_X_H_M 0x17
|
||||||
|
#define LSM303D_OFFSET_Y_L_M 0x18
|
||||||
|
#define LSM303D_OFFSET_Y_H_M 0x19
|
||||||
|
#define LSM303D_OFFSET_Z_L_M 0x1a
|
||||||
|
#define LSM303D_OFFSET_Z_H_M 0x1b
|
||||||
|
#define LSM303D_REFERENCE_X 0x1c
|
||||||
|
#define LSM303D_REFERENCE_Y 0x1d
|
||||||
|
#define LSM303D_REFERENCE_Z 0x1e
|
||||||
|
#define LSM303D_CTRL0 0x1f
|
||||||
|
#define LSM303D_CTRL1 0x20
|
||||||
|
#define LSM303D_CTRL2 0x21
|
||||||
|
#define LSM303D_CTRL3 0x22
|
||||||
|
#define LSM303D_CTRL4 0x23
|
||||||
|
#define LSM303D_CTRL5 0x24
|
||||||
|
#define LSM303D_CTRL6 0x25
|
||||||
|
#define LSM303D_CTRL7 0x26
|
||||||
|
#define LSM303D_STATUS_A 0x27
|
||||||
|
#define LSM303D_OUT_X_L_A 0x28
|
||||||
|
#define LSM303D_OUT_X_H_A 0x29
|
||||||
|
#define LSM303D_OUT_Y_L_A 0x2a
|
||||||
|
#define LSM303D_OUT_Y_H_A 0x2b
|
||||||
|
#define LSM303D_OUT_Z_L_A 0x2c
|
||||||
|
#define LSM303D_OUT_Z_H_A 0x2d
|
||||||
|
#define LSM303D_FIFO_CTRL 0x2e
|
||||||
|
#define LSM303D_FIFO_SRC 0x2f
|
||||||
|
#define LSM303D_IG_CFG1 0x30
|
||||||
|
#define LSM303D_IG_SRC1 0x31
|
||||||
|
#define LSM303D_IG_THS1 0x32
|
||||||
|
#define LSM303D_IG_DUR1 0x33
|
||||||
|
#define LSM303D_IG_CFG2 0x34
|
||||||
|
#define LSM303D_IG_SRC2 0x35
|
||||||
|
#define LSM303D_IG_THS2 0x36
|
||||||
|
#define LSM303D_IG_DUR2 0x37
|
||||||
|
#define LSM303D_CLICK_CFG 0x38
|
||||||
|
#define LSM303D_CLICK_SRC 0x39
|
||||||
|
#define LSM303D_CLICK_THS 0x3a
|
||||||
|
#define LSM303D_TIME_LIMIT 0x3b
|
||||||
|
#define LSM303D_TIME_LATENCY 0x3c
|
||||||
|
#define LSM303D_TIME_WINDOW 0x3d
|
||||||
|
#define LSM303D_ACT_THIS 0x3e
|
||||||
|
#define LSM303D_ACT_DUR 0x3f
|
||||||
|
|
||||||
|
// Accel sample rate defines
|
||||||
|
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_3_125 1
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_6_25 2
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_12_5 3
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_25 4
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_50 5
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_100 6
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_200 7
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_400 8
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_800 9
|
||||||
|
#define LSM303D_ACCEL_SAMPLERATE_1600 10
|
||||||
|
|
||||||
|
// Accel FSR
|
||||||
|
|
||||||
|
#define LSM303D_ACCEL_FSR_2 0
|
||||||
|
#define LSM303D_ACCEL_FSR_4 1
|
||||||
|
#define LSM303D_ACCEL_FSR_6 2
|
||||||
|
#define LSM303D_ACCEL_FSR_8 3
|
||||||
|
#define LSM303D_ACCEL_FSR_16 4
|
||||||
|
|
||||||
|
// Accel filter bandwidth
|
||||||
|
|
||||||
|
#define LSM303D_ACCEL_LPF_773 0
|
||||||
|
#define LSM303D_ACCEL_LPF_194 1
|
||||||
|
#define LSM303D_ACCEL_LPF_362 2
|
||||||
|
#define LSM303D_ACCEL_LPF_50 3
|
||||||
|
|
||||||
|
// Compass sample rate defines
|
||||||
|
|
||||||
|
#define LSM303D_COMPASS_SAMPLERATE_3_125 0
|
||||||
|
#define LSM303D_COMPASS_SAMPLERATE_6_25 1
|
||||||
|
#define LSM303D_COMPASS_SAMPLERATE_12_5 2
|
||||||
|
#define LSM303D_COMPASS_SAMPLERATE_25 3
|
||||||
|
#define LSM303D_COMPASS_SAMPLERATE_50 4
|
||||||
|
#define LSM303D_COMPASS_SAMPLERATE_100 5
|
||||||
|
|
||||||
|
// Compass FSR
|
||||||
|
|
||||||
|
#define LSM303D_COMPASS_FSR_2 0
|
||||||
|
#define LSM303D_COMPASS_FSR_4 1
|
||||||
|
#define LSM303D_COMPASS_FSR_8 2
|
||||||
|
#define LSM303D_COMPASS_FSR_12 3
|
||||||
|
|
||||||
|
class RTIMUGD20HM303D : public RTIMU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTIMUGD20HM303D(RTIMUSettings *settings);
|
||||||
|
~RTIMUGD20HM303D();
|
||||||
|
|
||||||
|
virtual const char *IMUName() { return "L3GD20H + LSM303D"; }
|
||||||
|
virtual int IMUType() { return RTIMU_TYPE_GD20HM303D; }
|
||||||
|
virtual int IMUInit();
|
||||||
|
virtual int IMUGetPollInterval();
|
||||||
|
virtual bool IMURead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool setGyroSampleRate();
|
||||||
|
bool setGyroCTRL2();
|
||||||
|
bool setGyroCTRL4();
|
||||||
|
bool setGyroCTRL5();
|
||||||
|
bool setAccelCTRL1();
|
||||||
|
bool setAccelCTRL2();
|
||||||
|
bool setCompassCTRL5();
|
||||||
|
bool setCompassCTRL6();
|
||||||
|
bool setCompassCTRL7();
|
||||||
|
|
||||||
|
unsigned char m_gyroSlaveAddr; // I2C address of L3GD20H
|
||||||
|
unsigned char m_accelCompassSlaveAddr; // I2C address of LSM303D
|
||||||
|
|
||||||
|
RTFLOAT m_gyroScale;
|
||||||
|
RTFLOAT m_accelScale;
|
||||||
|
RTFLOAT m_compassScale;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMUGD20HM303D_H
|
|
@ -0,0 +1,409 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTIMUGD20HM303DLHC.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
|
||||||
|
#if defined(GD20HM303DLHC_6a) || defined(GD20HM303DLHC_6b)
|
||||||
|
|
||||||
|
RTIMUGD20HM303DLHC::RTIMUGD20HM303DLHC(RTIMUSettings *settings) : RTIMU(settings)
|
||||||
|
{
|
||||||
|
m_sampleRate = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTIMUGD20HM303DLHC::~RTIMUGD20HM303DLHC()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMUGD20HM303DLHC::IMUInit()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
|
||||||
|
// configure IMU
|
||||||
|
|
||||||
|
m_gyroSlaveAddr = m_settings->m_I2CSlaveAddress;
|
||||||
|
m_accelSlaveAddr = LSM303DLHC_ACCEL_ADDRESS;
|
||||||
|
m_compassSlaveAddr = LSM303DLHC_COMPASS_ADDRESS;
|
||||||
|
|
||||||
|
setCalibrationData();
|
||||||
|
|
||||||
|
// Set up the gyro
|
||||||
|
|
||||||
|
if (!I2CWrite(m_gyroSlaveAddr, L3GD20H_LOW_ODR, 0x04))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL5, 0x80))
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, L3GD20H_WHO_AM_I, 1, &result))
|
||||||
|
return -3;
|
||||||
|
|
||||||
|
if (result != L3GD20H_ID) {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setGyroSampleRate())
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
if (!setGyroCTRL2())
|
||||||
|
return -6;
|
||||||
|
|
||||||
|
if (!setGyroCTRL4())
|
||||||
|
return -7;
|
||||||
|
|
||||||
|
// Set up the accel
|
||||||
|
|
||||||
|
if (!setAccelCTRL1())
|
||||||
|
return -8;
|
||||||
|
|
||||||
|
if (!setAccelCTRL4())
|
||||||
|
return -9;
|
||||||
|
|
||||||
|
// Set up the compass
|
||||||
|
|
||||||
|
if (!setCompassCRA())
|
||||||
|
return -10;
|
||||||
|
|
||||||
|
if (!setCompassCRB())
|
||||||
|
return -11;
|
||||||
|
|
||||||
|
if (!setCompassCRM())
|
||||||
|
return -12;
|
||||||
|
|
||||||
|
if (!setGyroCTRL5())
|
||||||
|
return -13;
|
||||||
|
|
||||||
|
gyroBiasInit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setGyroSampleRate()
|
||||||
|
{
|
||||||
|
unsigned char ctrl1;
|
||||||
|
unsigned char lowOdr = 0;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DLHCGyroSampleRate) {
|
||||||
|
case L3GD20H_SAMPLERATE_12_5:
|
||||||
|
ctrl1 = 0x0f;
|
||||||
|
lowOdr = 1;
|
||||||
|
m_sampleRate = 13;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_25:
|
||||||
|
ctrl1 = 0x4f;
|
||||||
|
lowOdr = 1;
|
||||||
|
m_sampleRate = 25;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_50:
|
||||||
|
ctrl1 = 0x8f;
|
||||||
|
lowOdr = 1;
|
||||||
|
m_sampleRate = 50;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_100:
|
||||||
|
ctrl1 = 0x0f;
|
||||||
|
m_sampleRate = 100;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_200:
|
||||||
|
ctrl1 = 0x4f;
|
||||||
|
m_sampleRate = 200;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_400:
|
||||||
|
ctrl1 = 0x8f;
|
||||||
|
m_sampleRate = 400;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_SAMPLERATE_800:
|
||||||
|
ctrl1 = 0xcf;
|
||||||
|
m_sampleRate = 800;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleInterval = (uint64_t)1000000 / m_sampleRate;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DLHCGyroBW) {
|
||||||
|
case L3GD20H_BANDWIDTH_0:
|
||||||
|
ctrl1 |= 0x00;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_BANDWIDTH_1:
|
||||||
|
ctrl1 |= 0x10;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_BANDWIDTH_2:
|
||||||
|
ctrl1 |= 0x20;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_BANDWIDTH_3:
|
||||||
|
ctrl1 |= 0x30;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2CWrite(m_gyroSlaveAddr, L3GD20H_LOW_ODR, lowOdr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL1, ctrl1));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setGyroCTRL2()
|
||||||
|
{
|
||||||
|
if ((m_settings->m_GD20HM303DLHCGyroHpf < L3GD20H_HPF_0) || (m_settings->m_GD20HM303DLHCGyroHpf > L3GD20H_HPF_9)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL2, m_settings->m_GD20HM303DLHCGyroHpf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setGyroCTRL4()
|
||||||
|
{
|
||||||
|
unsigned char ctrl4;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DLHCGyroFsr) {
|
||||||
|
case L3GD20H_FSR_245:
|
||||||
|
ctrl4 = 0x00;
|
||||||
|
m_gyroScale = (RTFLOAT)0.00875 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_FSR_500:
|
||||||
|
ctrl4 = 0x10;
|
||||||
|
m_gyroScale = (RTFLOAT)0.0175 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20H_FSR_2000:
|
||||||
|
ctrl4 = 0x20;
|
||||||
|
m_gyroScale = (RTFLOAT)0.07 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL4, ctrl4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setGyroCTRL5()
|
||||||
|
{
|
||||||
|
unsigned char ctrl5;
|
||||||
|
|
||||||
|
// Turn on hpf
|
||||||
|
|
||||||
|
ctrl5 = 0x10;
|
||||||
|
|
||||||
|
#ifdef GD20HM303DLHC_CACHE_MODE
|
||||||
|
// turn on fifo
|
||||||
|
|
||||||
|
ctrl5 |= 0x40;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20H_CTRL5, ctrl5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setAccelCTRL1()
|
||||||
|
{
|
||||||
|
unsigned char ctrl1;
|
||||||
|
|
||||||
|
if ((m_settings->m_GD20HM303DLHCAccelSampleRate < 1) || (m_settings->m_GD20HM303DLHCAccelSampleRate > 7)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl1 = (m_settings->m_GD20HM303DLHCAccelSampleRate << 4) | 0x07;
|
||||||
|
|
||||||
|
return I2CWrite(m_accelSlaveAddr, LSM303DLHC_CTRL1_A, ctrl1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setAccelCTRL4()
|
||||||
|
{
|
||||||
|
unsigned char ctrl4;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DLHCAccelFsr) {
|
||||||
|
case LSM303DLHC_ACCEL_FSR_2:
|
||||||
|
m_accelScale = (RTFLOAT)0.001 / (RTFLOAT)64;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_ACCEL_FSR_4:
|
||||||
|
m_accelScale = (RTFLOAT)0.002 / (RTFLOAT)64;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_ACCEL_FSR_8:
|
||||||
|
m_accelScale = (RTFLOAT)0.004 / (RTFLOAT)64;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_ACCEL_FSR_16:
|
||||||
|
m_accelScale = (RTFLOAT)0.012 / (RTFLOAT)64;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl4 = (m_settings->m_GD20HM303DLHCAccelFsr << 4);
|
||||||
|
|
||||||
|
return I2CWrite(m_accelSlaveAddr, LSM303DLHC_CTRL2_A, ctrl4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setCompassCRA()
|
||||||
|
{
|
||||||
|
unsigned char cra;
|
||||||
|
|
||||||
|
if ((m_settings->m_GD20HM303DLHCCompassSampleRate < 0) || (m_settings->m_GD20HM303DLHCCompassSampleRate > 7)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cra = (m_settings->m_GD20HM303DLHCCompassSampleRate << 2);
|
||||||
|
|
||||||
|
return I2CWrite(m_compassSlaveAddr, LSM303DLHC_CRA_M, cra);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setCompassCRB()
|
||||||
|
{
|
||||||
|
unsigned char crb;
|
||||||
|
|
||||||
|
// convert FSR to uT
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20HM303DLHCCompassFsr) {
|
||||||
|
case LSM303DLHC_COMPASS_FSR_1_3:
|
||||||
|
crb = 0x20;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)1100;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)980;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_1_9:
|
||||||
|
crb = 0x40;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)855;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)760;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_2_5:
|
||||||
|
crb = 0x60;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)670;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)600;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_4:
|
||||||
|
crb = 0x80;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)450;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)400;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_4_7:
|
||||||
|
crb = 0xa0;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)400;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)355;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_5_6:
|
||||||
|
crb = 0xc0;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)330;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)295;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_8_1:
|
||||||
|
crb = 0xe0;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)230;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)205;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I2CWrite(m_compassSlaveAddr, LSM303DLHC_CRB_M, crb);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::setCompassCRM()
|
||||||
|
{
|
||||||
|
return I2CWrite(m_compassSlaveAddr, LSM303DLHC_CRM_M, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int RTIMUGD20HM303DLHC::IMUGetPollInterval()
|
||||||
|
{
|
||||||
|
return (400 / m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20HM303DLHC::IMURead()
|
||||||
|
{
|
||||||
|
unsigned char status;
|
||||||
|
unsigned char gyroData[6];
|
||||||
|
unsigned char accelData[6];
|
||||||
|
unsigned char compassData[6];
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, L3GD20H_STATUS, 1, &status))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((status & 0x8) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, 0x80 | L3GD20H_OUT_X_L, 6, gyroData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_timestamp = millis();
|
||||||
|
|
||||||
|
if (!I2CRead(m_accelSlaveAddr, 0x80 | LSM303DLHC_OUT_X_L_A, 6, accelData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2CRead(m_compassSlaveAddr, 0x80 | LSM303DLHC_OUT_X_H_M, 6, compassData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RTMath::convertToVector(gyroData, m_gyro, m_gyroScale, false);
|
||||||
|
RTMath::convertToVector(accelData, m_accel, m_accelScale, false);
|
||||||
|
|
||||||
|
m_compass.setX((RTFLOAT)((int16_t)(((uint16_t)compassData[0] << 8) | (uint16_t)compassData[1])) * m_compassScaleXY);
|
||||||
|
m_compass.setY((RTFLOAT)((int16_t)(((uint16_t)compassData[2] << 8) | (uint16_t)compassData[3])) * m_compassScaleXY);
|
||||||
|
m_compass.setZ((RTFLOAT)((int16_t)(((uint16_t)compassData[4] << 8) | (uint16_t)compassData[5])) * m_compassScaleZ);
|
||||||
|
|
||||||
|
// sort out gyro axes
|
||||||
|
|
||||||
|
m_gyro.setY(-m_gyro.y());
|
||||||
|
m_gyro.setZ(-m_gyro.z());
|
||||||
|
|
||||||
|
// sort out accel data;
|
||||||
|
|
||||||
|
m_accel.setX(-m_accel.x());
|
||||||
|
|
||||||
|
// sort out compass axes
|
||||||
|
|
||||||
|
RTFLOAT temp;
|
||||||
|
|
||||||
|
temp = m_compass.z();
|
||||||
|
m_compass.setZ(-m_compass.y());
|
||||||
|
m_compass.setY(-temp);
|
||||||
|
|
||||||
|
// now do standard processing
|
||||||
|
|
||||||
|
handleGyroBias();
|
||||||
|
calibrateAverageCompass();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,208 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMUGD20HM303DLHC_H
|
||||||
|
#define _RTIMUGD20HM303DLHC_H
|
||||||
|
|
||||||
|
#include "RTIMU.h"
|
||||||
|
|
||||||
|
// I2C Slave Addresses
|
||||||
|
|
||||||
|
#define L3GD20H_ADDRESS0 0x6a
|
||||||
|
#define L3GD20H_ADDRESS1 0x6b
|
||||||
|
#define L3GD20H_ID 0xd7
|
||||||
|
|
||||||
|
#define LSM303DLHC_ACCEL_ADDRESS 0x19
|
||||||
|
#define LSM303DLHC_COMPASS_ADDRESS 0x1e
|
||||||
|
|
||||||
|
// L3GD20H Register map
|
||||||
|
|
||||||
|
#define L3GD20H_WHO_AM_I 0x0f
|
||||||
|
#define L3GD20H_CTRL1 0x20
|
||||||
|
#define L3GD20H_CTRL2 0x21
|
||||||
|
#define L3GD20H_CTRL3 0x22
|
||||||
|
#define L3GD20H_CTRL4 0x23
|
||||||
|
#define L3GD20H_CTRL5 0x24
|
||||||
|
#define L3GD20H_OUT_TEMP 0x26
|
||||||
|
#define L3GD20H_STATUS 0x27
|
||||||
|
#define L3GD20H_OUT_X_L 0x28
|
||||||
|
#define L3GD20H_OUT_X_H 0x29
|
||||||
|
#define L3GD20H_OUT_Y_L 0x2a
|
||||||
|
#define L3GD20H_OUT_Y_H 0x2b
|
||||||
|
#define L3GD20H_OUT_Z_L 0x2c
|
||||||
|
#define L3GD20H_OUT_Z_H 0x2d
|
||||||
|
#define L3GD20H_FIFO_CTRL 0x2e
|
||||||
|
#define L3GD20H_FIFO_SRC 0x2f
|
||||||
|
#define L3GD20H_IG_CFG 0x30
|
||||||
|
#define L3GD20H_IG_SRC 0x31
|
||||||
|
#define L3GD20H_IG_THS_XH 0x32
|
||||||
|
#define L3GD20H_IG_THS_XL 0x33
|
||||||
|
#define L3GD20H_IG_THS_YH 0x34
|
||||||
|
#define L3GD20H_IG_THS_YL 0x35
|
||||||
|
#define L3GD20H_IG_THS_ZH 0x36
|
||||||
|
#define L3GD20H_IG_THS_ZL 0x37
|
||||||
|
#define L3GD20H_IG_DURATION 0x38
|
||||||
|
#define L3GD20H_LOW_ODR 0x39
|
||||||
|
|
||||||
|
// Gyro sample rate defines
|
||||||
|
|
||||||
|
#define L3GD20H_SAMPLERATE_12_5 0
|
||||||
|
#define L3GD20H_SAMPLERATE_25 1
|
||||||
|
#define L3GD20H_SAMPLERATE_50 2
|
||||||
|
#define L3GD20H_SAMPLERATE_100 3
|
||||||
|
#define L3GD20H_SAMPLERATE_200 4
|
||||||
|
#define L3GD20H_SAMPLERATE_400 5
|
||||||
|
#define L3GD20H_SAMPLERATE_800 6
|
||||||
|
|
||||||
|
// Gyro banwidth defines
|
||||||
|
|
||||||
|
#define L3GD20H_BANDWIDTH_0 0
|
||||||
|
#define L3GD20H_BANDWIDTH_1 1
|
||||||
|
#define L3GD20H_BANDWIDTH_2 2
|
||||||
|
#define L3GD20H_BANDWIDTH_3 3
|
||||||
|
|
||||||
|
// Gyro FSR defines
|
||||||
|
|
||||||
|
#define L3GD20H_FSR_245 0
|
||||||
|
#define L3GD20H_FSR_500 1
|
||||||
|
#define L3GD20H_FSR_2000 2
|
||||||
|
|
||||||
|
// Gyro high pass filter defines
|
||||||
|
|
||||||
|
#define L3GD20H_HPF_0 0
|
||||||
|
#define L3GD20H_HPF_1 1
|
||||||
|
#define L3GD20H_HPF_2 2
|
||||||
|
#define L3GD20H_HPF_3 3
|
||||||
|
#define L3GD20H_HPF_4 4
|
||||||
|
#define L3GD20H_HPF_5 5
|
||||||
|
#define L3GD20H_HPF_6 6
|
||||||
|
#define L3GD20H_HPF_7 7
|
||||||
|
#define L3GD20H_HPF_8 8
|
||||||
|
#define L3GD20H_HPF_9 9
|
||||||
|
|
||||||
|
// LSM303DLHC Accel Register Map
|
||||||
|
|
||||||
|
#define LSM303DLHC_CTRL1_A 0x20
|
||||||
|
#define LSM303DLHC_CTRL2_A 0x21
|
||||||
|
#define LSM303DLHC_CTRL3_A 0x22
|
||||||
|
#define LSM303DLHC_CTRL4_A 0x23
|
||||||
|
#define LSM303DLHC_CTRL5_A 0x24
|
||||||
|
#define LSM303DLHC_CTRL6_A 0x25
|
||||||
|
#define LSM303DLHC_REF_A 0x26
|
||||||
|
#define LSM303DLHC_STATUS_A 0x27
|
||||||
|
#define LSM303DLHC_OUT_X_L_A 0x28
|
||||||
|
#define LSM303DLHC_OUT_X_H_A 0x29
|
||||||
|
#define LSM303DLHC_OUT_Y_L_A 0x2a
|
||||||
|
#define LSM303DLHC_OUT_Y_H_A 0x2b
|
||||||
|
#define LSM303DLHC_OUT_Z_L_A 0x2c
|
||||||
|
#define LSM303DLHC_OUT_Z_H_A 0x2d
|
||||||
|
#define LSM303DLHC_FIFO_CTRL_A 0x2e
|
||||||
|
#define LSM303DLHC_FIFO_SRC_A 0x2f
|
||||||
|
|
||||||
|
// LSM303DLHC Compass Register Map
|
||||||
|
|
||||||
|
#define LSM303DLHC_CRA_M 0x00
|
||||||
|
#define LSM303DLHC_CRB_M 0x01
|
||||||
|
#define LSM303DLHC_CRM_M 0x02
|
||||||
|
#define LSM303DLHC_OUT_X_H_M 0x03
|
||||||
|
#define LSM303DLHC_OUT_X_L_M 0x04
|
||||||
|
#define LSM303DLHC_OUT_Y_H_M 0x05
|
||||||
|
#define LSM303DLHC_OUT_Y_L_M 0x06
|
||||||
|
#define LSM303DLHC_OUT_Z_H_M 0x07
|
||||||
|
#define LSM303DLHC_OUT_Z_L_M 0x08
|
||||||
|
#define LSM303DLHC_STATUS_M 0x09
|
||||||
|
#define LSM303DLHC_TEMP_OUT_L_M 0x31
|
||||||
|
#define LSM303DLHC_TEMP_OUT_H_M 0x32
|
||||||
|
|
||||||
|
// Accel sample rate defines
|
||||||
|
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_1 1
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_10 2
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_25 3
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_50 4
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_100 5
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_200 6
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_400 7
|
||||||
|
|
||||||
|
// Accel FSR
|
||||||
|
|
||||||
|
#define LSM303DLHC_ACCEL_FSR_2 0
|
||||||
|
#define LSM303DLHC_ACCEL_FSR_4 1
|
||||||
|
#define LSM303DLHC_ACCEL_FSR_8 2
|
||||||
|
#define LSM303DLHC_ACCEL_FSR_16 3
|
||||||
|
|
||||||
|
// Compass sample rate defines
|
||||||
|
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_0_75 0
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_1_5 1
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_3 2
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_7_5 3
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_15 4
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_30 5
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_75 6
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_220 7
|
||||||
|
|
||||||
|
// Compass FSR
|
||||||
|
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_1_3 1
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_1_9 2
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_2_5 3
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_4 4
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_4_7 5
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_5_6 6
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_8_1 7
|
||||||
|
|
||||||
|
class RTIMUGD20HM303DLHC : public RTIMU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTIMUGD20HM303DLHC(RTIMUSettings *settings);
|
||||||
|
~RTIMUGD20HM303DLHC();
|
||||||
|
|
||||||
|
virtual const char *IMUName() { return "L3GD20H + LSM303DLHC"; }
|
||||||
|
virtual int IMUType() { return RTIMU_TYPE_GD20HM303DLHC; }
|
||||||
|
virtual int IMUInit();
|
||||||
|
virtual int IMUGetPollInterval();
|
||||||
|
virtual bool IMURead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool setGyroSampleRate();
|
||||||
|
bool setGyroCTRL2();
|
||||||
|
bool setGyroCTRL4();
|
||||||
|
bool setGyroCTRL5();
|
||||||
|
bool setAccelCTRL1();
|
||||||
|
bool setAccelCTRL4();
|
||||||
|
bool setCompassCRA();
|
||||||
|
bool setCompassCRB();
|
||||||
|
bool setCompassCRM();
|
||||||
|
|
||||||
|
unsigned char m_gyroSlaveAddr; // I2C address of L3GD20
|
||||||
|
unsigned char m_accelSlaveAddr; // I2C address of LSM303DLHC accel
|
||||||
|
unsigned char m_compassSlaveAddr; // I2C address of LSM303DLHC compass
|
||||||
|
|
||||||
|
RTFLOAT m_gyroScale;
|
||||||
|
RTFLOAT m_accelScale;
|
||||||
|
RTFLOAT m_compassScaleXY;
|
||||||
|
RTFLOAT m_compassScaleZ;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMUGD20HM303DLHC_H
|
|
@ -0,0 +1,382 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTIMUGD20M303DLHC.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
|
||||||
|
#if defined(GD20M303DLHC_6a) || defined(GD20M303DLHC_6b)
|
||||||
|
|
||||||
|
RTIMUGD20M303DLHC::RTIMUGD20M303DLHC(RTIMUSettings *settings) : RTIMU(settings)
|
||||||
|
{
|
||||||
|
m_sampleRate = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTIMUGD20M303DLHC::~RTIMUGD20M303DLHC()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMUGD20M303DLHC::IMUInit()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
|
||||||
|
// configure IMU
|
||||||
|
|
||||||
|
m_gyroSlaveAddr = m_settings->m_I2CSlaveAddress;
|
||||||
|
m_accelSlaveAddr = LSM303DLHC_ACCEL_ADDRESS;
|
||||||
|
m_compassSlaveAddr = LSM303DLHC_COMPASS_ADDRESS;
|
||||||
|
|
||||||
|
setCalibrationData();
|
||||||
|
|
||||||
|
// Set up the gyro
|
||||||
|
|
||||||
|
if (!I2CWrite(m_gyroSlaveAddr, L3GD20_CTRL5, 0x80))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, L3GD20_WHO_AM_I, 1, &result))
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!setGyroSampleRate())
|
||||||
|
return -4;
|
||||||
|
|
||||||
|
if (!setGyroCTRL2())
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
if (!setGyroCTRL4())
|
||||||
|
return -6;
|
||||||
|
|
||||||
|
// Set up the accel
|
||||||
|
|
||||||
|
if (!setAccelCTRL1())
|
||||||
|
return -7;
|
||||||
|
|
||||||
|
if (!setAccelCTRL4())
|
||||||
|
return -8;
|
||||||
|
|
||||||
|
// Set up the compass
|
||||||
|
|
||||||
|
if (!setCompassCRA())
|
||||||
|
return -9;
|
||||||
|
|
||||||
|
if (!setCompassCRB())
|
||||||
|
return -10;
|
||||||
|
|
||||||
|
if (!setCompassCRM())
|
||||||
|
return -11;
|
||||||
|
|
||||||
|
if (!setGyroCTRL5())
|
||||||
|
return -12;
|
||||||
|
|
||||||
|
gyroBiasInit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setGyroSampleRate()
|
||||||
|
{
|
||||||
|
unsigned char ctrl1;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20M303DLHCGyroSampleRate) {
|
||||||
|
case L3GD20_SAMPLERATE_95:
|
||||||
|
ctrl1 = 0x0f;
|
||||||
|
m_sampleRate = 95;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20_SAMPLERATE_190:
|
||||||
|
ctrl1 = 0x4f;
|
||||||
|
m_sampleRate = 190;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20_SAMPLERATE_380:
|
||||||
|
ctrl1 = 0x8f;
|
||||||
|
m_sampleRate = 380;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20_SAMPLERATE_760:
|
||||||
|
ctrl1 = 0xcf;
|
||||||
|
m_sampleRate = 760;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleInterval = (uint64_t)1000000 / m_sampleRate;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20M303DLHCGyroBW) {
|
||||||
|
case L3GD20_BANDWIDTH_0:
|
||||||
|
ctrl1 |= 0x00;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20_BANDWIDTH_1:
|
||||||
|
ctrl1 |= 0x10;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20_BANDWIDTH_2:
|
||||||
|
ctrl1 |= 0x20;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20_BANDWIDTH_3:
|
||||||
|
ctrl1 |= 0x30;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return (I2CWrite(m_gyroSlaveAddr, L3GD20_CTRL1, ctrl1));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setGyroCTRL2()
|
||||||
|
{
|
||||||
|
if ((m_settings->m_GD20M303DLHCGyroHpf < L3GD20_HPF_0) || (m_settings->m_GD20M303DLHCGyroHpf > L3GD20_HPF_9)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20_CTRL2, m_settings->m_GD20M303DLHCGyroHpf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setGyroCTRL4()
|
||||||
|
{
|
||||||
|
unsigned char ctrl4;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20M303DLHCGyroFsr) {
|
||||||
|
case L3GD20_FSR_250:
|
||||||
|
ctrl4 = 0x00;
|
||||||
|
m_gyroScale = (RTFLOAT)0.00875 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20_FSR_500:
|
||||||
|
ctrl4 = 0x10;
|
||||||
|
m_gyroScale = (RTFLOAT)0.0175 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case L3GD20_FSR_2000:
|
||||||
|
ctrl4 = 0x20;
|
||||||
|
m_gyroScale = (RTFLOAT)0.07 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20_CTRL4, ctrl4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setGyroCTRL5()
|
||||||
|
{
|
||||||
|
unsigned char ctrl5;
|
||||||
|
|
||||||
|
// Turn on hpf
|
||||||
|
|
||||||
|
ctrl5 = 0x10;
|
||||||
|
|
||||||
|
#ifdef GD20M303DLHC_CACHE_MODE
|
||||||
|
// turn on fifo
|
||||||
|
|
||||||
|
ctrl5 |= 0x40;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return I2CWrite(m_gyroSlaveAddr, L3GD20_CTRL5, ctrl5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setAccelCTRL1()
|
||||||
|
{
|
||||||
|
unsigned char ctrl1;
|
||||||
|
|
||||||
|
if ((m_settings->m_GD20M303DLHCAccelSampleRate < 1) || (m_settings->m_GD20M303DLHCAccelSampleRate > 7)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl1 = (m_settings->m_GD20M303DLHCAccelSampleRate << 4) | 0x07;
|
||||||
|
|
||||||
|
return I2CWrite(m_accelSlaveAddr, LSM303DLHC_CTRL1_A, ctrl1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setAccelCTRL4()
|
||||||
|
{
|
||||||
|
unsigned char ctrl4;
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20M303DLHCAccelFsr) {
|
||||||
|
case LSM303DLHC_ACCEL_FSR_2:
|
||||||
|
m_accelScale = (RTFLOAT)0.001 / (RTFLOAT)64;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_ACCEL_FSR_4:
|
||||||
|
m_accelScale = (RTFLOAT)0.002 / (RTFLOAT)64;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_ACCEL_FSR_8:
|
||||||
|
m_accelScale = (RTFLOAT)0.004 / (RTFLOAT)64;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_ACCEL_FSR_16:
|
||||||
|
m_accelScale = (RTFLOAT)0.012 / (RTFLOAT)64;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl4 = (m_settings->m_GD20M303DLHCAccelFsr << 4);
|
||||||
|
|
||||||
|
return I2CWrite(m_accelSlaveAddr, LSM303DLHC_CTRL2_A, ctrl4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setCompassCRA()
|
||||||
|
{
|
||||||
|
unsigned char cra;
|
||||||
|
|
||||||
|
if ((m_settings->m_GD20M303DLHCCompassSampleRate < 0) || (m_settings->m_GD20M303DLHCCompassSampleRate > 7)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cra = (m_settings->m_GD20M303DLHCCompassSampleRate << 2);
|
||||||
|
|
||||||
|
return I2CWrite(m_compassSlaveAddr, LSM303DLHC_CRA_M, cra);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setCompassCRB()
|
||||||
|
{
|
||||||
|
unsigned char crb;
|
||||||
|
|
||||||
|
// convert FSR to uT
|
||||||
|
|
||||||
|
switch (m_settings->m_GD20M303DLHCCompassFsr) {
|
||||||
|
case LSM303DLHC_COMPASS_FSR_1_3:
|
||||||
|
crb = 0x20;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)1100;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)980;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_1_9:
|
||||||
|
crb = 0x40;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)855;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)760;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_2_5:
|
||||||
|
crb = 0x60;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)670;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)600;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_4:
|
||||||
|
crb = 0x80;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)450;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)400;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_4_7:
|
||||||
|
crb = 0xa0;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)400;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)355;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_5_6:
|
||||||
|
crb = 0xc0;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)330;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)295;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM303DLHC_COMPASS_FSR_8_1:
|
||||||
|
crb = 0xe0;
|
||||||
|
m_compassScaleXY = (RTFLOAT)100 / (RTFLOAT)230;
|
||||||
|
m_compassScaleZ = (RTFLOAT)100 / (RTFLOAT)205;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I2CWrite(m_compassSlaveAddr, LSM303DLHC_CRB_M, crb);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::setCompassCRM()
|
||||||
|
{
|
||||||
|
return I2CWrite(m_compassSlaveAddr, LSM303DLHC_CRM_M, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int RTIMUGD20M303DLHC::IMUGetPollInterval()
|
||||||
|
{
|
||||||
|
return (400 / m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUGD20M303DLHC::IMURead()
|
||||||
|
{
|
||||||
|
unsigned char status;
|
||||||
|
unsigned char gyroData[6];
|
||||||
|
unsigned char accelData[6];
|
||||||
|
unsigned char compassData[6];
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, L3GD20_STATUS, 1, &status))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((status & 0x8) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2CRead(m_gyroSlaveAddr, 0x80 | L3GD20_OUT_X_L, 6, gyroData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_timestamp = millis();
|
||||||
|
|
||||||
|
if (!I2CRead(m_accelSlaveAddr, 0x80 | LSM303DLHC_OUT_X_L_A, 6, accelData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2CRead(m_compassSlaveAddr, 0x80 | LSM303DLHC_OUT_X_H_M, 6, compassData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RTMath::convertToVector(gyroData, m_gyro, m_gyroScale, false);
|
||||||
|
RTMath::convertToVector(accelData, m_accel, m_accelScale, false);
|
||||||
|
|
||||||
|
m_compass.setX((RTFLOAT)((int16_t)(((uint16_t)compassData[0] << 8) | (uint16_t)compassData[1])) * m_compassScaleXY);
|
||||||
|
m_compass.setY((RTFLOAT)((int16_t)(((uint16_t)compassData[2] << 8) | (uint16_t)compassData[3])) * m_compassScaleXY);
|
||||||
|
m_compass.setZ((RTFLOAT)((int16_t)(((uint16_t)compassData[4] << 8) | (uint16_t)compassData[5])) * m_compassScaleZ);
|
||||||
|
|
||||||
|
// sort out gyro axes
|
||||||
|
|
||||||
|
m_gyro.setY(-m_gyro.y());
|
||||||
|
m_gyro.setZ(-m_gyro.z());
|
||||||
|
|
||||||
|
// sort out accel data;
|
||||||
|
|
||||||
|
m_accel.setX(-m_accel.x());
|
||||||
|
|
||||||
|
// sort out compass axes
|
||||||
|
|
||||||
|
RTFLOAT temp;
|
||||||
|
|
||||||
|
temp = m_compass.z();
|
||||||
|
m_compass.setZ(-m_compass.y());
|
||||||
|
m_compass.setY(-temp);
|
||||||
|
|
||||||
|
// now do standard processing
|
||||||
|
|
||||||
|
handleGyroBias();
|
||||||
|
calibrateAverageCompass();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,204 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMUGD20M303DLHC_H
|
||||||
|
#define _RTIMUGD20M303DLHC_H
|
||||||
|
|
||||||
|
#include "RTIMU.h"
|
||||||
|
|
||||||
|
// I2C Slave Addresses
|
||||||
|
// cambiado a 0x69
|
||||||
|
#define L3GD20_ADDRESS0 0x69
|
||||||
|
#define L3GD20_ADDRESS1 0x69
|
||||||
|
#define L3GD20_ID 0xd4
|
||||||
|
|
||||||
|
#define LSM303DLHC_ACCEL_ADDRESS 0x19
|
||||||
|
#define LSM303DLHC_COMPASS_ADDRESS 0x1e
|
||||||
|
|
||||||
|
// L3GD20 Register map
|
||||||
|
|
||||||
|
#define L3GD20_WHO_AM_I 0x0f
|
||||||
|
#define L3GD20_CTRL1 0x20
|
||||||
|
#define L3GD20_CTRL2 0x21
|
||||||
|
#define L3GD20_CTRL3 0x22
|
||||||
|
#define L3GD20_CTRL4 0x23
|
||||||
|
#define L3GD20_CTRL5 0x24
|
||||||
|
#define L3GD20_OUT_TEMP 0x26
|
||||||
|
#define L3GD20_STATUS 0x27
|
||||||
|
#define L3GD20_OUT_X_L 0x28
|
||||||
|
#define L3GD20_OUT_X_H 0x29
|
||||||
|
#define L3GD20_OUT_Y_L 0x2a
|
||||||
|
#define L3GD20_OUT_Y_H 0x2b
|
||||||
|
#define L3GD20_OUT_Z_L 0x2c
|
||||||
|
#define L3GD20_OUT_Z_H 0x2d
|
||||||
|
#define L3GD20_FIFO_CTRL 0x2e
|
||||||
|
#define L3GD20_FIFO_SRC 0x2f
|
||||||
|
#define L3GD20_IG_CFG 0x30
|
||||||
|
#define L3GD20_IG_SRC 0x31
|
||||||
|
#define L3GD20_IG_THS_XH 0x32
|
||||||
|
#define L3GD20_IG_THS_XL 0x33
|
||||||
|
#define L3GD20_IG_THS_YH 0x34
|
||||||
|
#define L3GD20_IG_THS_YL 0x35
|
||||||
|
#define L3GD20_IG_THS_ZH 0x36
|
||||||
|
#define L3GD20_IG_THS_ZL 0x37
|
||||||
|
#define L3GD20_IG_DURATION 0x38
|
||||||
|
|
||||||
|
// Gyro sample rate defines
|
||||||
|
|
||||||
|
#define L3GD20_SAMPLERATE_95 0
|
||||||
|
#define L3GD20_SAMPLERATE_190 1
|
||||||
|
#define L3GD20_SAMPLERATE_380 2
|
||||||
|
#define L3GD20_SAMPLERATE_760 3
|
||||||
|
|
||||||
|
// Gyro banwidth defines
|
||||||
|
|
||||||
|
#define L3GD20_BANDWIDTH_0 0
|
||||||
|
#define L3GD20_BANDWIDTH_1 1
|
||||||
|
#define L3GD20_BANDWIDTH_2 2
|
||||||
|
#define L3GD20_BANDWIDTH_3 3
|
||||||
|
|
||||||
|
// Gyro FSR defines
|
||||||
|
|
||||||
|
#define L3GD20_FSR_250 0
|
||||||
|
#define L3GD20_FSR_500 1
|
||||||
|
#define L3GD20_FSR_2000 2
|
||||||
|
|
||||||
|
// Gyro high pass filter defines
|
||||||
|
|
||||||
|
#define L3GD20_HPF_0 0
|
||||||
|
#define L3GD20_HPF_1 1
|
||||||
|
#define L3GD20_HPF_2 2
|
||||||
|
#define L3GD20_HPF_3 3
|
||||||
|
#define L3GD20_HPF_4 4
|
||||||
|
#define L3GD20_HPF_5 5
|
||||||
|
#define L3GD20_HPF_6 6
|
||||||
|
#define L3GD20_HPF_7 7
|
||||||
|
#define L3GD20_HPF_8 8
|
||||||
|
#define L3GD20_HPF_9 9
|
||||||
|
|
||||||
|
// LSM303DLHC Accel Register Map
|
||||||
|
|
||||||
|
#define LSM303DLHC_CTRL1_A 0x20
|
||||||
|
#define LSM303DLHC_CTRL2_A 0x21
|
||||||
|
#define LSM303DLHC_CTRL3_A 0x22
|
||||||
|
#define LSM303DLHC_CTRL4_A 0x23
|
||||||
|
#define LSM303DLHC_CTRL5_A 0x24
|
||||||
|
#define LSM303DLHC_CTRL6_A 0x25
|
||||||
|
#define LSM303DLHC_REF_A 0x26
|
||||||
|
#define LSM303DLHC_STATUS_A 0x27
|
||||||
|
#define LSM303DLHC_OUT_X_L_A 0x28
|
||||||
|
#define LSM303DLHC_OUT_X_H_A 0x29
|
||||||
|
#define LSM303DLHC_OUT_Y_L_A 0x2a
|
||||||
|
#define LSM303DLHC_OUT_Y_H_A 0x2b
|
||||||
|
#define LSM303DLHC_OUT_Z_L_A 0x2c
|
||||||
|
#define LSM303DLHC_OUT_Z_H_A 0x2d
|
||||||
|
#define LSM303DLHC_FIFO_CTRL_A 0x2e
|
||||||
|
#define LSM303DLHC_FIFO_SRC_A 0x2f
|
||||||
|
|
||||||
|
// LSM303DLHC Compass Register Map
|
||||||
|
|
||||||
|
#define LSM303DLHC_CRA_M 0x00
|
||||||
|
#define LSM303DLHC_CRB_M 0x01
|
||||||
|
#define LSM303DLHC_CRM_M 0x02
|
||||||
|
#define LSM303DLHC_OUT_X_H_M 0x03
|
||||||
|
#define LSM303DLHC_OUT_X_L_M 0x04
|
||||||
|
#define LSM303DLHC_OUT_Y_H_M 0x05
|
||||||
|
#define LSM303DLHC_OUT_Y_L_M 0x06
|
||||||
|
#define LSM303DLHC_OUT_Z_H_M 0x07
|
||||||
|
#define LSM303DLHC_OUT_Z_L_M 0x08
|
||||||
|
#define LSM303DLHC_STATUS_M 0x09
|
||||||
|
#define LSM303DLHC_TEMP_OUT_L_M 0x31
|
||||||
|
#define LSM303DLHC_TEMP_OUT_H_M 0x32
|
||||||
|
|
||||||
|
// Accel sample rate defines
|
||||||
|
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_1 1
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_10 2
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_25 3
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_50 4
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_100 5
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_200 6
|
||||||
|
#define LSM303DLHC_ACCEL_SAMPLERATE_400 7
|
||||||
|
|
||||||
|
// Accel FSR
|
||||||
|
|
||||||
|
#define LSM303DLHC_ACCEL_FSR_2 0
|
||||||
|
#define LSM303DLHC_ACCEL_FSR_4 1
|
||||||
|
#define LSM303DLHC_ACCEL_FSR_8 2
|
||||||
|
#define LSM303DLHC_ACCEL_FSR_16 3
|
||||||
|
|
||||||
|
// Compass sample rate defines
|
||||||
|
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_0_75 0
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_1_5 1
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_3 2
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_7_5 3
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_15 4
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_30 5
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_75 6
|
||||||
|
#define LSM303DLHC_COMPASS_SAMPLERATE_220 7
|
||||||
|
|
||||||
|
// Compass FSR
|
||||||
|
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_1_3 1
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_1_9 2
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_2_5 3
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_4 4
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_4_7 5
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_5_6 6
|
||||||
|
#define LSM303DLHC_COMPASS_FSR_8_1 7
|
||||||
|
|
||||||
|
class RTIMUGD20M303DLHC : public RTIMU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTIMUGD20M303DLHC(RTIMUSettings *settings);
|
||||||
|
~RTIMUGD20M303DLHC();
|
||||||
|
|
||||||
|
virtual const char *IMUName() { return "L3GD20 + LSM303DLHC"; }
|
||||||
|
virtual int IMUType() { return RTIMU_TYPE_GD20M303DLHC; }
|
||||||
|
virtual int IMUInit();
|
||||||
|
virtual int IMUGetPollInterval();
|
||||||
|
virtual bool IMURead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool setGyroSampleRate();
|
||||||
|
bool setGyroCTRL2();
|
||||||
|
bool setGyroCTRL4();
|
||||||
|
bool setGyroCTRL5();
|
||||||
|
bool setAccelCTRL1();
|
||||||
|
bool setAccelCTRL4();
|
||||||
|
bool setCompassCRA();
|
||||||
|
bool setCompassCRB();
|
||||||
|
bool setCompassCRM();
|
||||||
|
|
||||||
|
unsigned char m_gyroSlaveAddr; // I2C address of L3GD20
|
||||||
|
unsigned char m_accelSlaveAddr; // I2C address of LSM303DLHC accel
|
||||||
|
unsigned char m_compassSlaveAddr; // I2C address of LSM303DLHC compass
|
||||||
|
|
||||||
|
RTFLOAT m_gyroScale;
|
||||||
|
RTFLOAT m_accelScale;
|
||||||
|
RTFLOAT m_compassScaleXY;
|
||||||
|
RTFLOAT m_compassScaleZ;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMUGD20M303DLHC_H
|
|
@ -0,0 +1,363 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTIMULSM9DS0.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
|
||||||
|
#if defined(LSM9DS0_6a) || defined(LSM9DS0_6b)
|
||||||
|
|
||||||
|
|
||||||
|
RTIMULSM9DS0::RTIMULSM9DS0(RTIMUSettings *settings) : RTIMU(settings)
|
||||||
|
{
|
||||||
|
m_sampleRate = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTIMULSM9DS0::~RTIMULSM9DS0()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMULSM9DS0::IMUInit()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
|
||||||
|
// configure IMU
|
||||||
|
|
||||||
|
m_gyroSlaveAddr = m_settings->m_I2CSlaveAddress;
|
||||||
|
if (m_gyroSlaveAddr == LSM9DS0_GYRO_ADDRESS0)
|
||||||
|
m_accelCompassSlaveAddr = LSM9DS0_ACCELMAG_ADDRESS0;
|
||||||
|
else
|
||||||
|
m_accelCompassSlaveAddr = LSM9DS0_ACCELMAG_ADDRESS1;
|
||||||
|
|
||||||
|
setCalibrationData();
|
||||||
|
|
||||||
|
// Set up the gyro
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_gyroSlaveAddr, LSM9DS0_GYRO_CTRL5, 0x80))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_gyroSlaveAddr, LSM9DS0_GYRO_WHO_AM_I, &result))
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
if (result != LSM9DS0_GYRO_ID) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setGyroSampleRate())
|
||||||
|
return -4;
|
||||||
|
|
||||||
|
if (!setGyroCTRL2())
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
if (!setGyroCTRL4())
|
||||||
|
return -6;
|
||||||
|
|
||||||
|
// Set up the accel
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_accelCompassSlaveAddr, LSM9DS0_WHO_AM_I, &result))
|
||||||
|
return -7;
|
||||||
|
|
||||||
|
if (result != LSM9DS0_ACCELMAG_ID) {
|
||||||
|
return -8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setAccelCTRL1())
|
||||||
|
return 9;
|
||||||
|
|
||||||
|
if (!setAccelCTRL2())
|
||||||
|
return -10;
|
||||||
|
|
||||||
|
if (!setCompassCTRL5())
|
||||||
|
return 11;
|
||||||
|
|
||||||
|
if (!setCompassCTRL6())
|
||||||
|
return -12;
|
||||||
|
|
||||||
|
if (!setCompassCTRL7())
|
||||||
|
return -13;
|
||||||
|
|
||||||
|
if (!setGyroCTRL5())
|
||||||
|
return -14;
|
||||||
|
|
||||||
|
gyroBiasInit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setGyroSampleRate()
|
||||||
|
{
|
||||||
|
unsigned char ctrl1;
|
||||||
|
|
||||||
|
switch (m_settings->m_LSM9DS0GyroSampleRate) {
|
||||||
|
case LSM9DS0_GYRO_SAMPLERATE_95:
|
||||||
|
ctrl1 = 0x0f;
|
||||||
|
m_sampleRate = 95;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_GYRO_SAMPLERATE_190:
|
||||||
|
ctrl1 = 0x4f;
|
||||||
|
m_sampleRate = 190;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_GYRO_SAMPLERATE_380:
|
||||||
|
ctrl1 = 0x8f;
|
||||||
|
m_sampleRate = 380;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_GYRO_SAMPLERATE_760:
|
||||||
|
ctrl1 = 0xcf;
|
||||||
|
m_sampleRate = 760;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleInterval = (uint64_t)1000000 / m_sampleRate;
|
||||||
|
|
||||||
|
switch (m_settings->m_LSM9DS0GyroBW) {
|
||||||
|
case LSM9DS0_GYRO_BANDWIDTH_0:
|
||||||
|
ctrl1 |= 0x00;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_GYRO_BANDWIDTH_1:
|
||||||
|
ctrl1 |= 0x10;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_GYRO_BANDWIDTH_2:
|
||||||
|
ctrl1 |= 0x20;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_GYRO_BANDWIDTH_3:
|
||||||
|
ctrl1 |= 0x30;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return (I2Cdev::writeByte(m_gyroSlaveAddr, LSM9DS0_GYRO_CTRL1, ctrl1));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setGyroCTRL2()
|
||||||
|
{
|
||||||
|
if ((m_settings->m_LSM9DS0GyroHpf < LSM9DS0_GYRO_HPF_0) || (m_settings->m_LSM9DS0GyroHpf > LSM9DS0_GYRO_HPF_9)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return I2Cdev::writeByte(m_gyroSlaveAddr, LSM9DS0_GYRO_CTRL2, m_settings->m_LSM9DS0GyroHpf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setGyroCTRL4()
|
||||||
|
{
|
||||||
|
unsigned char ctrl4;
|
||||||
|
|
||||||
|
switch (m_settings->m_LSM9DS0GyroFsr) {
|
||||||
|
case LSM9DS0_GYRO_FSR_250:
|
||||||
|
ctrl4 = 0x00;
|
||||||
|
m_gyroScale = (RTFLOAT)0.00875 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_GYRO_FSR_500:
|
||||||
|
ctrl4 = 0x10;
|
||||||
|
m_gyroScale = (RTFLOAT)0.0175 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_GYRO_FSR_2000:
|
||||||
|
ctrl4 = 0x20;
|
||||||
|
m_gyroScale = (RTFLOAT)0.07 * RTMATH_DEGREE_TO_RAD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I2Cdev::writeByte(m_gyroSlaveAddr, LSM9DS0_GYRO_CTRL4, ctrl4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setGyroCTRL5()
|
||||||
|
{
|
||||||
|
unsigned char ctrl5;
|
||||||
|
|
||||||
|
// Turn on hpf
|
||||||
|
|
||||||
|
ctrl5 = 0x10;
|
||||||
|
|
||||||
|
return I2Cdev::writeByte(m_gyroSlaveAddr, LSM9DS0_GYRO_CTRL5, ctrl5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setAccelCTRL1()
|
||||||
|
{
|
||||||
|
unsigned char ctrl1;
|
||||||
|
|
||||||
|
if ((m_settings->m_LSM9DS0AccelSampleRate < 0) || (m_settings->m_LSM9DS0AccelSampleRate > 10)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl1 = (m_settings->m_LSM9DS0AccelSampleRate << 4) | 0x07;
|
||||||
|
|
||||||
|
return I2Cdev::writeByte(m_accelCompassSlaveAddr, LSM9DS0_CTRL1, ctrl1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setAccelCTRL2()
|
||||||
|
{
|
||||||
|
unsigned char ctrl2;
|
||||||
|
|
||||||
|
if ((m_settings->m_LSM9DS0AccelLpf < 0) || (m_settings->m_LSM9DS0AccelLpf > 3)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_settings->m_LSM9DS0AccelFsr) {
|
||||||
|
case LSM9DS0_ACCEL_FSR_2:
|
||||||
|
m_accelScale = (RTFLOAT)0.000061;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_ACCEL_FSR_4:
|
||||||
|
m_accelScale = (RTFLOAT)0.000122;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_ACCEL_FSR_6:
|
||||||
|
m_accelScale = (RTFLOAT)0.000183;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_ACCEL_FSR_8:
|
||||||
|
m_accelScale = (RTFLOAT)0.000244;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_ACCEL_FSR_16:
|
||||||
|
m_accelScale = (RTFLOAT)0.000732;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl2 = (m_settings->m_LSM9DS0AccelLpf << 6) | (m_settings->m_LSM9DS0AccelFsr << 3);
|
||||||
|
|
||||||
|
return I2Cdev::writeByte(m_accelCompassSlaveAddr, LSM9DS0_CTRL2, ctrl2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setCompassCTRL5()
|
||||||
|
{
|
||||||
|
unsigned char ctrl5;
|
||||||
|
|
||||||
|
if ((m_settings->m_LSM9DS0CompassSampleRate < 0) || (m_settings->m_LSM9DS0CompassSampleRate > 5)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl5 = (m_settings->m_LSM9DS0CompassSampleRate << 2);
|
||||||
|
|
||||||
|
return I2Cdev::writeByte(m_accelCompassSlaveAddr, LSM9DS0_CTRL5, ctrl5);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setCompassCTRL6()
|
||||||
|
{
|
||||||
|
unsigned char ctrl6;
|
||||||
|
|
||||||
|
// convert FSR to uT
|
||||||
|
|
||||||
|
switch (m_settings->m_LSM9DS0CompassFsr) {
|
||||||
|
case LSM9DS0_COMPASS_FSR_2:
|
||||||
|
ctrl6 = 0;
|
||||||
|
m_compassScale = (RTFLOAT)0.008;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_COMPASS_FSR_4:
|
||||||
|
ctrl6 = 0x20;
|
||||||
|
m_compassScale = (RTFLOAT)0.016;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_COMPASS_FSR_8:
|
||||||
|
ctrl6 = 0x40;
|
||||||
|
m_compassScale = (RTFLOAT)0.032;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSM9DS0_COMPASS_FSR_12:
|
||||||
|
ctrl6 = 0x60;
|
||||||
|
m_compassScale = (RTFLOAT)0.0479;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I2Cdev::writeByte(m_accelCompassSlaveAddr, LSM9DS0_CTRL6, ctrl6);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::setCompassCTRL7()
|
||||||
|
{
|
||||||
|
return I2Cdev::writeByte(m_accelCompassSlaveAddr, LSM9DS0_CTRL7, 0x60);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMULSM9DS0::IMUGetPollInterval()
|
||||||
|
{
|
||||||
|
return (400 / m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMULSM9DS0::IMURead()
|
||||||
|
{
|
||||||
|
unsigned char status;
|
||||||
|
unsigned char gyroData[6];
|
||||||
|
unsigned char accelData[6];
|
||||||
|
unsigned char compassData[6];
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_gyroSlaveAddr, LSM9DS0_GYRO_STATUS, &status))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ((status & 0x8) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_gyroSlaveAddr, 0x80 | LSM9DS0_GYRO_OUT_X_L, 6, gyroData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_timestamp = millis();
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_accelCompassSlaveAddr, 0x80 | LSM9DS0_OUT_X_L_A, 6, accelData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_accelCompassSlaveAddr, 0x80 | LSM9DS0_OUT_X_L_M, 6, compassData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RTMath::convertToVector(gyroData, m_gyro, m_gyroScale, false);
|
||||||
|
RTMath::convertToVector(accelData, m_accel, m_accelScale, false);
|
||||||
|
RTMath::convertToVector(compassData, m_compass, m_compassScale, false);
|
||||||
|
|
||||||
|
// sort out gyro axes
|
||||||
|
|
||||||
|
m_gyro.setY(-m_gyro.y());
|
||||||
|
m_gyro.setZ(-m_gyro.z());
|
||||||
|
|
||||||
|
// sort out accel data;
|
||||||
|
|
||||||
|
m_accel.setX(-m_accel.x());
|
||||||
|
|
||||||
|
// sort out compass axes
|
||||||
|
|
||||||
|
m_compass.setY(-m_compass.y());
|
||||||
|
|
||||||
|
// now do standard processing
|
||||||
|
|
||||||
|
handleGyroBias();
|
||||||
|
calibrateAverageCompass();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,246 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMULSM9DS0_H
|
||||||
|
#define _RTIMULSM9DS0_H
|
||||||
|
|
||||||
|
#include "RTIMU.h"
|
||||||
|
|
||||||
|
// I2C Slave Addresses
|
||||||
|
|
||||||
|
#define LSM9DS0_GYRO_ADDRESS0 0x6a
|
||||||
|
#define LSM9DS0_GYRO_ADDRESS1 0x6b
|
||||||
|
#define LSM9DS0_GYRO_ID 0xd4
|
||||||
|
|
||||||
|
#define LSM9DS0_ACCELMAG_ADDRESS0 0x1e
|
||||||
|
#define LSM9DS0_ACCELMAG_ADDRESS1 0x1d
|
||||||
|
#define LSM9DS0_ACCELMAG_ID 0x49
|
||||||
|
|
||||||
|
// L3GD20 Register map
|
||||||
|
|
||||||
|
#define LSM9DS0_GYRO_WHO_AM_I 0x0f
|
||||||
|
#define LSM9DS0_GYRO_CTRL1 0x20
|
||||||
|
#define LSM9DS0_GYRO_CTRL2 0x21
|
||||||
|
#define LSM9DS0_GYRO_CTRL3 0x22
|
||||||
|
#define LSM9DS0_GYRO_CTRL4 0x23
|
||||||
|
#define LSM9DS0_GYRO_CTRL5 0x24
|
||||||
|
#define LSM9DS0_GYRO_OUT_TEMP 0x26
|
||||||
|
#define LSM9DS0_GYRO_STATUS 0x27
|
||||||
|
#define LSM9DS0_GYRO_OUT_X_L 0x28
|
||||||
|
#define LSM9DS0_GYRO_OUT_X_H 0x29
|
||||||
|
#define LSM9DS0_GYRO_OUT_Y_L 0x2a
|
||||||
|
#define LSM9DS0_GYRO_OUT_Y_H 0x2b
|
||||||
|
#define LSM9DS0_GYRO_OUT_Z_L 0x2c
|
||||||
|
#define LSM9DS0_GYRO_OUT_Z_H 0x2d
|
||||||
|
#define LSM9DS0_GYRO_FIFO_CTRL 0x2e
|
||||||
|
#define LSM9DS0_GYRO_FIFO_SRC 0x2f
|
||||||
|
#define LSM9DS0_GYRO_IG_CFG 0x30
|
||||||
|
#define LSM9DS0_GYRO_IG_SRC 0x31
|
||||||
|
#define LSM9DS0_GYRO_IG_THS_XH 0x32
|
||||||
|
#define LSM9DS0_GYRO_IG_THS_XL 0x33
|
||||||
|
#define LSM9DS0_GYRO_IG_THS_YH 0x34
|
||||||
|
#define LSM9DS0_GYRO_IG_THS_YL 0x35
|
||||||
|
#define LSM9DS0_GYRO_IG_THS_ZH 0x36
|
||||||
|
#define LSM9DS0_GYRO_IG_THS_ZL 0x37
|
||||||
|
#define LSM9DS0_GYRO_IG_DURATION 0x38
|
||||||
|
|
||||||
|
// Gyro sample rate defines
|
||||||
|
|
||||||
|
#define LSM9DS0_GYRO_SAMPLERATE_95 0
|
||||||
|
#define LSM9DS0_GYRO_SAMPLERATE_190 1
|
||||||
|
#define LSM9DS0_GYRO_SAMPLERATE_380 2
|
||||||
|
#define LSM9DS0_GYRO_SAMPLERATE_760 3
|
||||||
|
|
||||||
|
// Gyro banwidth defines
|
||||||
|
|
||||||
|
#define LSM9DS0_GYRO_BANDWIDTH_0 0
|
||||||
|
#define LSM9DS0_GYRO_BANDWIDTH_1 1
|
||||||
|
#define LSM9DS0_GYRO_BANDWIDTH_2 2
|
||||||
|
#define LSM9DS0_GYRO_BANDWIDTH_3 3
|
||||||
|
|
||||||
|
// Gyro FSR defines
|
||||||
|
|
||||||
|
#define LSM9DS0_GYRO_FSR_250 0
|
||||||
|
#define LSM9DS0_GYRO_FSR_500 1
|
||||||
|
#define LSM9DS0_GYRO_FSR_2000 2
|
||||||
|
|
||||||
|
// Gyro high pass filter defines
|
||||||
|
|
||||||
|
#define LSM9DS0_GYRO_HPF_0 0
|
||||||
|
#define LSM9DS0_GYRO_HPF_1 1
|
||||||
|
#define LSM9DS0_GYRO_HPF_2 2
|
||||||
|
#define LSM9DS0_GYRO_HPF_3 3
|
||||||
|
#define LSM9DS0_GYRO_HPF_4 4
|
||||||
|
#define LSM9DS0_GYRO_HPF_5 5
|
||||||
|
#define LSM9DS0_GYRO_HPF_6 6
|
||||||
|
#define LSM9DS0_GYRO_HPF_7 7
|
||||||
|
#define LSM9DS0_GYRO_HPF_8 8
|
||||||
|
#define LSM9DS0_GYRO_HPF_9 9
|
||||||
|
|
||||||
|
// Accel/Mag Register Map
|
||||||
|
|
||||||
|
#define LSM9DS0_TEMP_OUT_L 0x05
|
||||||
|
#define LSM9DS0_TEMP_OUT_H 0x06
|
||||||
|
#define LSM9DS0_STATUS_M 0x07
|
||||||
|
#define LSM9DS0_OUT_X_L_M 0x08
|
||||||
|
#define LSM9DS0_OUT_X_H_M 0x09
|
||||||
|
#define LSM9DS0_OUT_Y_L_M 0x0a
|
||||||
|
#define LSM9DS0_OUT_Y_H_M 0x0b
|
||||||
|
#define LSM9DS0_OUT_Z_L_M 0x0c
|
||||||
|
#define LSM9DS0_OUT_Z_H_M 0x0d
|
||||||
|
#define LSM9DS0_WHO_AM_I 0x0f
|
||||||
|
#define LSM9DS0_INT_CTRL_M 0x12
|
||||||
|
#define LSM9DS0_INT_SRC_M 0x13
|
||||||
|
#define LSM9DS0_INT_THS_L_M 0x14
|
||||||
|
#define LSM9DS0_INT_THS_H_M 0x15
|
||||||
|
#define LSM9DS0_OFFSET_X_L_M 0x16
|
||||||
|
#define LSM9DS0_OFFSET_X_H_M 0x17
|
||||||
|
#define LSM9DS0_OFFSET_Y_L_M 0x18
|
||||||
|
#define LSM9DS0_OFFSET_Y_H_M 0x19
|
||||||
|
#define LSM9DS0_OFFSET_Z_L_M 0x1a
|
||||||
|
#define LSM9DS0_OFFSET_Z_H_M 0x1b
|
||||||
|
#define LSM9DS0_REFERENCE_X 0x1c
|
||||||
|
#define LSM9DS0_REFERENCE_Y 0x1d
|
||||||
|
#define LSM9DS0_REFERENCE_Z 0x1e
|
||||||
|
#define LSM9DS0_CTRL0 0x1f
|
||||||
|
#define LSM9DS0_CTRL1 0x20
|
||||||
|
#define LSM9DS0_CTRL2 0x21
|
||||||
|
#define LSM9DS0_CTRL3 0x22
|
||||||
|
#define LSM9DS0_CTRL4 0x23
|
||||||
|
#define LSM9DS0_CTRL5 0x24
|
||||||
|
#define LSM9DS0_CTRL6 0x25
|
||||||
|
#define LSM9DS0_CTRL7 0x26
|
||||||
|
#define LSM9DS0_STATUS_A 0x27
|
||||||
|
#define LSM9DS0_OUT_X_L_A 0x28
|
||||||
|
#define LSM9DS0_OUT_X_H_A 0x29
|
||||||
|
#define LSM9DS0_OUT_Y_L_A 0x2a
|
||||||
|
#define LSM9DS0_OUT_Y_H_A 0x2b
|
||||||
|
#define LSM9DS0_OUT_Z_L_A 0x2c
|
||||||
|
#define LSM9DS0_OUT_Z_H_A 0x2d
|
||||||
|
#define LSM9DS0_FIFO_CTRL 0x2e
|
||||||
|
#define LSM9DS0_FIFO_SRC 0x2f
|
||||||
|
#define LSM9DS0_IG_CFG1 0x30
|
||||||
|
#define LSM9DS0_IG_SRC1 0x31
|
||||||
|
#define LSM9DS0_IG_THS1 0x32
|
||||||
|
#define LSM9DS0_IG_DUR1 0x33
|
||||||
|
#define LSM9DS0_IG_CFG2 0x34
|
||||||
|
#define LSM9DS0_IG_SRC2 0x35
|
||||||
|
#define LSM9DS0_IG_THS2 0x36
|
||||||
|
#define LSM9DS0_IG_DUR2 0x37
|
||||||
|
#define LSM9DS0_CLICK_CFG 0x38
|
||||||
|
#define LSM9DS0_CLICK_SRC 0x39
|
||||||
|
#define LSM9DS0_CLICK_THS 0x3a
|
||||||
|
#define LSM9DS0_TIME_LIMIT 0x3b
|
||||||
|
#define LSM9DS0_TIME_LATENCY 0x3c
|
||||||
|
#define LSM9DS0_TIME_WINDOW 0x3d
|
||||||
|
#define LSM9DS0_ACT_THIS 0x3e
|
||||||
|
#define LSM9DS0_ACT_DUR 0x3f
|
||||||
|
|
||||||
|
// Accel sample rate defines
|
||||||
|
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_3_125 1
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_6_25 2
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_12_5 3
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_25 4
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_50 5
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_100 6
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_200 7
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_400 8
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_800 9
|
||||||
|
#define LSM9DS0_ACCEL_SAMPLERATE_1600 10
|
||||||
|
|
||||||
|
// Accel FSR
|
||||||
|
|
||||||
|
#define LSM9DS0_ACCEL_FSR_2 0
|
||||||
|
#define LSM9DS0_ACCEL_FSR_4 1
|
||||||
|
#define LSM9DS0_ACCEL_FSR_6 2
|
||||||
|
#define LSM9DS0_ACCEL_FSR_8 3
|
||||||
|
#define LSM9DS0_ACCEL_FSR_16 4
|
||||||
|
|
||||||
|
// Accel filter bandwidth
|
||||||
|
|
||||||
|
#define LSM9DS0_ACCEL_LPF_773 0
|
||||||
|
#define LSM9DS0_ACCEL_LPF_194 1
|
||||||
|
#define LSM9DS0_ACCEL_LPF_362 2
|
||||||
|
#define LSM9DS0_ACCEL_LPF_50 3
|
||||||
|
|
||||||
|
// Compass sample rate defines
|
||||||
|
|
||||||
|
#define LSM9DS0_COMPASS_SAMPLERATE_3_125 0
|
||||||
|
#define LSM9DS0_COMPASS_SAMPLERATE_6_25 1
|
||||||
|
#define LSM9DS0_COMPASS_SAMPLERATE_12_5 2
|
||||||
|
#define LSM9DS0_COMPASS_SAMPLERATE_25 3
|
||||||
|
#define LSM9DS0_COMPASS_SAMPLERATE_50 4
|
||||||
|
#define LSM9DS0_COMPASS_SAMPLERATE_100 5
|
||||||
|
|
||||||
|
// Compass FSR
|
||||||
|
|
||||||
|
#define LSM9DS0_COMPASS_FSR_2 0
|
||||||
|
#define LSM9DS0_COMPASS_FSR_4 1
|
||||||
|
#define LSM9DS0_COMPASS_FSR_8 2
|
||||||
|
#define LSM9DS0_COMPASS_FSR_12 3
|
||||||
|
|
||||||
|
|
||||||
|
class RTIMULSM9DS0 : public RTIMU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTIMULSM9DS0(RTIMUSettings *settings);
|
||||||
|
~RTIMULSM9DS0();
|
||||||
|
|
||||||
|
virtual const char *IMUName() { return "LSM9DS0"; }
|
||||||
|
virtual int IMUType() { return RTIMU_TYPE_LSM9DS0; }
|
||||||
|
virtual int IMUInit();
|
||||||
|
virtual int IMUGetPollInterval();
|
||||||
|
virtual bool IMURead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool setGyroSampleRate();
|
||||||
|
bool setGyroCTRL2();
|
||||||
|
bool setGyroCTRL4();
|
||||||
|
bool setGyroCTRL5();
|
||||||
|
bool setAccelCTRL1();
|
||||||
|
bool setAccelCTRL2();
|
||||||
|
bool setCompassCTRL5();
|
||||||
|
bool setCompassCTRL6();
|
||||||
|
bool setCompassCTRL7();
|
||||||
|
|
||||||
|
unsigned char m_gyroSlaveAddr; // I2C address of gyro
|
||||||
|
unsigned char m_accelCompassSlaveAddr; // I2C address of accel and mag
|
||||||
|
unsigned char m_bus; // I2C bus (usually 1 for Raspberry Pi for example)
|
||||||
|
|
||||||
|
RTFLOAT m_gyroScale;
|
||||||
|
RTFLOAT m_accelScale;
|
||||||
|
RTFLOAT m_compassScale;
|
||||||
|
|
||||||
|
#ifdef LSM9DS0_CACHE_MODE
|
||||||
|
bool m_firstTime; // if first sample
|
||||||
|
|
||||||
|
LSM9DS0_CACHE_BLOCK m_cache[LSM9DS0_CACHE_BLOCK_COUNT]; // the cache itself
|
||||||
|
int m_cacheIn; // the in index
|
||||||
|
int m_cacheOut; // the out index
|
||||||
|
int m_cacheCount; // number of used cache blocks
|
||||||
|
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMULSM9DS0_H
|
|
@ -0,0 +1,103 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMULIBDEFS_H
|
||||||
|
#define _RTIMULIBDEFS_H
|
||||||
|
|
||||||
|
#include "RTMath.h"
|
||||||
|
#include "RTPressureDefs.h"
|
||||||
|
|
||||||
|
// IMU enable defs - only one should be enabled, the rest commented out
|
||||||
|
|
||||||
|
//#define MPU9150_68 // MPU9150 at address 0x68
|
||||||
|
//#define MPU9250_68 // MPU9250 at address 0x68
|
||||||
|
#define GD20M303DLHC_6b // GD20 + M303DLHC at address 0x6b
|
||||||
|
|
||||||
|
//#define MPU9150_68 // MPU9150 at address 0x68
|
||||||
|
//#define MPU9150_69 // MPU9150 at address 0x69
|
||||||
|
//#define MPU9250_68 // MPU9250 at address 0x68
|
||||||
|
//#define MPU9250_69 // MPU9250 at address 0x69
|
||||||
|
//#define LSM9DS0_6a // LSM9DS0 at address 0x6a
|
||||||
|
//#define LSM9DS0_6b // LSM9DS0 at address 0x6b
|
||||||
|
//#define GD20HM303D_6a // GD20H + M303D at address 0x6a
|
||||||
|
//#define GD20HM303D_6b // GD20H + M303D at address 0x6b
|
||||||
|
//#define GD20HM303DLHC_6a // GD20H + M303DLHC at address 0x6a
|
||||||
|
//#define GD20HM303DLHC_6b // GD20H + M303DLHC at address 0x6b
|
||||||
|
//#define GD20M303DLHC_6a // GD20 + M303DLHC at address 0x6a
|
||||||
|
//#define GD20M303DLHC_6b // GD20 + M303DLHC at address 0x6b
|
||||||
|
//#define BNO055_28 // BNO055 at address 0x28
|
||||||
|
//#define BNO055_29 // BNO055 at address 0x29
|
||||||
|
|
||||||
|
// IMU type codes
|
||||||
|
|
||||||
|
#define RTIMU_TYPE_MPU9150 1 // InvenSense MPU9150
|
||||||
|
#define RTIMU_TYPE_LSM9DS0 2 // STM LSM9DS0 (eg Sparkfun IMU)
|
||||||
|
#define RTIMU_TYPE_GD20HM303D 3 // STM L3GD20H/LSM303D (Pololu Altimu)
|
||||||
|
#define RTIMU_TYPE_GD20M303DLHC 4 // STM L3GD20/LSM303DHLC (old Adafruit IMU)
|
||||||
|
#define RTIMU_TYPE_MPU9250 5 // InvenSense MPU9250
|
||||||
|
#define RTIMU_TYPE_GD20HM303DLHC 6 // STM L3GD20H/LSM303DHLC (new Adafruit IMU)
|
||||||
|
#define RTIMU_TYPE_BNO055 7 // BNO055
|
||||||
|
|
||||||
|
// Pressure enable defs - only one should be enabled, the rest commented out
|
||||||
|
|
||||||
|
//#define BMP180 // BMP180
|
||||||
|
//#define LPS25H_5c // LPS25H at standard address
|
||||||
|
//#define LPS25H_5d // LPS25H at option address
|
||||||
|
//#define MS5611_76 // MS5611 at standard address
|
||||||
|
//#define MS5611_77 // MS5611 at option address
|
||||||
|
|
||||||
|
// IMU Axis rotation defs
|
||||||
|
//
|
||||||
|
// These allow the IMU to be virtually repositioned if it is in a non-standard configuration
|
||||||
|
// Standard configuration is X pointing at north, Y pointing east and Z pointing down
|
||||||
|
// with the IMU horizontal. There are 24 different possible orientations as defined
|
||||||
|
// below. Setting the axis rotation code to non-zero values performs the repositioning.
|
||||||
|
//
|
||||||
|
// Uncomment the one required
|
||||||
|
|
||||||
|
#define RTIMU_XNORTH_YEAST 0 // this is the default identity matrix
|
||||||
|
//#define RTIMU_XEAST_YSOUTH 1
|
||||||
|
//#define RTIMU_XSOUTH_YWEST 2
|
||||||
|
//#define RTIMU_XWEST_YNORTH 3
|
||||||
|
//#define RTIMU_XNORTH_YWEST 4
|
||||||
|
//#define RTIMU_XEAST_YNORTH 5
|
||||||
|
//#define RTIMU_XSOUTH_YEAST 6
|
||||||
|
//#define RTIMU_XWEST_YSOUTH 7
|
||||||
|
//#define RTIMU_XUP_YNORTH 8
|
||||||
|
//#define RTIMU_XUP_YEAST 9
|
||||||
|
//#define RTIMU_XUP_YSOUTH 10
|
||||||
|
//#define RTIMU_XUP_YWEST 11
|
||||||
|
//#define RTIMU_XDOWN_YNORTH 12
|
||||||
|
//#define RTIMU_XDOWN_YEAST 13
|
||||||
|
//#define RTIMU_XDOWN_YSOUTH 14
|
||||||
|
//#define RTIMU_XDOWN_YWEST 15
|
||||||
|
//#define RTIMU_XNORTH_YUP 16
|
||||||
|
//#define RTIMU_XEAST_YUP 17
|
||||||
|
//#define RTIMU_XSOUTH_YUP 18
|
||||||
|
//#define RTIMU_XWEST_YUP 19
|
||||||
|
//#define RTIMU_XNORTH_YDOWN 20
|
||||||
|
//#define RTIMU_XEAST_YDOWN 21
|
||||||
|
//#define RTIMU_XSOUTH_YDOWN 22
|
||||||
|
//#define RTIMU_XWEST_YDOWN 23
|
||||||
|
|
||||||
|
#endif // _RTIMULIBDEFS_H
|
|
@ -0,0 +1,545 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTIMUMPU9150.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
|
||||||
|
#if defined(MPU9150_68) || defined(MPU9150_69)
|
||||||
|
|
||||||
|
RTIMUMPU9150::RTIMUMPU9150(RTIMUSettings *settings) : RTIMU(settings)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RTIMUMPU9150::~RTIMUMPU9150()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::setLpf(unsigned char lpf)
|
||||||
|
{
|
||||||
|
switch (lpf) {
|
||||||
|
case MPU9150_LPF_256:
|
||||||
|
case MPU9150_LPF_188:
|
||||||
|
case MPU9150_LPF_98:
|
||||||
|
case MPU9150_LPF_42:
|
||||||
|
case MPU9150_LPF_20:
|
||||||
|
case MPU9150_LPF_10:
|
||||||
|
case MPU9150_LPF_5:
|
||||||
|
m_lpf = lpf;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::setSampleRate(int rate)
|
||||||
|
{
|
||||||
|
if ((rate < MPU9150_SAMPLERATE_MIN) || (rate > MPU9150_SAMPLERATE_MAX)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_sampleRate = rate;
|
||||||
|
m_sampleInterval = (unsigned long)1000 / m_sampleRate;
|
||||||
|
if (m_sampleInterval == 0)
|
||||||
|
m_sampleInterval = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::setCompassRate(int rate)
|
||||||
|
{
|
||||||
|
if ((rate < MPU9150_COMPASSRATE_MIN) || (rate > MPU9150_COMPASSRATE_MAX)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_compassRate = rate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::setGyroFsr(unsigned char fsr)
|
||||||
|
{
|
||||||
|
switch (fsr) {
|
||||||
|
case MPU9150_GYROFSR_250:
|
||||||
|
m_gyroFsr = fsr;
|
||||||
|
m_gyroScale = RTMATH_PI / (131.0 * 180.0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9150_GYROFSR_500:
|
||||||
|
m_gyroFsr = fsr;
|
||||||
|
m_gyroScale = RTMATH_PI / (62.5 * 180.0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9150_GYROFSR_1000:
|
||||||
|
m_gyroFsr = fsr;
|
||||||
|
m_gyroScale = RTMATH_PI / (32.8 * 180.0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9150_GYROFSR_2000:
|
||||||
|
m_gyroFsr = fsr;
|
||||||
|
m_gyroScale = RTMATH_PI / (16.4 * 180.0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::setAccelFsr(unsigned char fsr)
|
||||||
|
{
|
||||||
|
switch (fsr) {
|
||||||
|
case MPU9150_ACCELFSR_2:
|
||||||
|
m_accelFsr = fsr;
|
||||||
|
m_accelScale = 1.0/16384.0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9150_ACCELFSR_4:
|
||||||
|
m_accelFsr = fsr;
|
||||||
|
m_accelScale = 1.0/8192.0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9150_ACCELFSR_8:
|
||||||
|
m_accelFsr = fsr;
|
||||||
|
m_accelScale = 1.0/4096.0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9150_ACCELFSR_16:
|
||||||
|
m_accelFsr = fsr;
|
||||||
|
m_accelScale = 1.0/2048.0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int RTIMUMPU9150::IMUInit()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
|
||||||
|
m_firstTime = true;
|
||||||
|
m_compassPresent = true;
|
||||||
|
|
||||||
|
#ifdef MPU9150_CACHE_MODE
|
||||||
|
m_cacheIn = m_cacheOut = m_cacheCount = 0;
|
||||||
|
#endif
|
||||||
|
// configure IMU
|
||||||
|
|
||||||
|
m_slaveAddr = m_settings->m_I2CSlaveAddress;
|
||||||
|
setSampleRate(m_settings->m_MPU9150GyroAccelSampleRate);
|
||||||
|
setCompassRate(m_settings->m_MPU9150CompassSampleRate);
|
||||||
|
setLpf(m_settings->m_MPU9150GyroAccelLpf);
|
||||||
|
setGyroFsr(m_settings->m_MPU9150GyroFsr);
|
||||||
|
setAccelFsr(m_settings->m_MPU9150AccelFsr);
|
||||||
|
|
||||||
|
setCalibrationData();
|
||||||
|
|
||||||
|
// reset the MPU9150
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_PWR_MGMT_1, 0x80))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_PWR_MGMT_1, 0x00))
|
||||||
|
return -4;
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_slaveAddr, MPU9150_WHO_AM_I, &result))
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
if (result != 0x68) {
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now configure the various components
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_LPF_CONFIG, m_lpf))
|
||||||
|
return -7;
|
||||||
|
|
||||||
|
if (!setSampleRate())
|
||||||
|
return -8;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_GYRO_CONFIG, m_gyroFsr))
|
||||||
|
return -9;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_ACCEL_CONFIG, m_accelFsr))
|
||||||
|
return -10;
|
||||||
|
|
||||||
|
// now configure compass
|
||||||
|
|
||||||
|
result = configureCompass();
|
||||||
|
if (result < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// enable the sensors
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_PWR_MGMT_1, 1))
|
||||||
|
return -28;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_PWR_MGMT_2, 0))
|
||||||
|
return -29;
|
||||||
|
|
||||||
|
// select the data to go into the FIFO and enable
|
||||||
|
|
||||||
|
if (!resetFifo())
|
||||||
|
return -30;
|
||||||
|
|
||||||
|
gyroBiasInit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::configureCompass()
|
||||||
|
{
|
||||||
|
unsigned char asa[3];
|
||||||
|
unsigned char id;
|
||||||
|
|
||||||
|
m_compassIs5883 = false;
|
||||||
|
m_compassDataLength = 8;
|
||||||
|
|
||||||
|
if (!bypassOn())
|
||||||
|
return -11;
|
||||||
|
|
||||||
|
// get fuse ROM data
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(AK8975_ADDRESS, AK8975_CNTL, 0)) {
|
||||||
|
// check to see if an HMC5883L is fitted
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(HMC5883_ADDRESS, HMC5883_ID, 1, &id)) {
|
||||||
|
bypassOff();
|
||||||
|
|
||||||
|
// this is returning true so that MPU-6050 by itself will work
|
||||||
|
|
||||||
|
m_compassPresent = false;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (id != 0x48) { // incorrect id for HMC5883L
|
||||||
|
|
||||||
|
bypassOff();
|
||||||
|
|
||||||
|
// this is returning true so that MPU-6050 by itself will work
|
||||||
|
|
||||||
|
m_compassPresent = false;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HMC5883 is present - use that
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(HMC5883_ADDRESS, HMC5883_CONFIG_A, 0x38)) {
|
||||||
|
bypassOff();
|
||||||
|
return -12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(HMC5883_ADDRESS, HMC5883_CONFIG_B, 0x20)) {
|
||||||
|
bypassOff();
|
||||||
|
return -12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(HMC5883_ADDRESS, HMC5883_MODE, 0x00)) {
|
||||||
|
bypassOff();
|
||||||
|
return -12;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Detected MPU-6050 with HMC5883");
|
||||||
|
|
||||||
|
m_compassDataLength = 6;
|
||||||
|
m_compassIs5883 = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(AK8975_ADDRESS, AK8975_CNTL, 0x0f)) {
|
||||||
|
bypassOff();
|
||||||
|
return -13;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(AK8975_ADDRESS, AK8975_ASAX, 3, asa)) {
|
||||||
|
bypassOff();
|
||||||
|
return -14;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert asa to usable scale factor
|
||||||
|
|
||||||
|
m_compassAdjust[0] = ((float)asa[0] - 128.0) / 256.0 + 1.0f;
|
||||||
|
m_compassAdjust[1] = ((float)asa[1] - 128.0) / 256.0 + 1.0f;
|
||||||
|
m_compassAdjust[2] = ((float)asa[2] - 128.0) / 256.0 + 1.0f;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(AK8975_ADDRESS, AK8975_CNTL, 0)) {
|
||||||
|
bypassOff();
|
||||||
|
return -15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bypassOff())
|
||||||
|
return -16;
|
||||||
|
|
||||||
|
// now set up MPU9150 to talk to the compass chip
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_MST_CTRL, 0x40))
|
||||||
|
return -17;
|
||||||
|
|
||||||
|
if (m_compassIs5883) {
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV0_ADDR, 0x80 | HMC5883_ADDRESS))
|
||||||
|
return -18;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV0_REG, HMC5883_DATA_X_HI))
|
||||||
|
return -19;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV0_CTRL, 0x86))
|
||||||
|
return -20;
|
||||||
|
} else {
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV0_ADDR, 0x80 | AK8975_ADDRESS))
|
||||||
|
return -18;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV0_REG, AK8975_ST1))
|
||||||
|
return -19;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV0_CTRL, 0x88))
|
||||||
|
return -20;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV1_ADDR, AK8975_ADDRESS))
|
||||||
|
return -21;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV1_REG, AK8975_CNTL))
|
||||||
|
return -22;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV1_CTRL, 0x81))
|
||||||
|
return -23;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV1_DO, 0x1))
|
||||||
|
return -24;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_MST_DELAY_CTRL, 0x3))
|
||||||
|
return -25;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_YG_OFFS_TC, 0x80))
|
||||||
|
return -26;
|
||||||
|
|
||||||
|
if (!setCompassRate())
|
||||||
|
return -27;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::resetFifo()
|
||||||
|
{
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_INT_ENABLE, 0))
|
||||||
|
return false;
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_FIFO_EN, 0))
|
||||||
|
return false;
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_USER_CTRL, 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_USER_CTRL, 0x04))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_USER_CTRL, 0x60))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_INT_ENABLE, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_FIFO_EN, 0x78))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::bypassOn()
|
||||||
|
{
|
||||||
|
unsigned char userControl;
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_slaveAddr, MPU9150_USER_CTRL, &userControl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
userControl &= ~0x20;
|
||||||
|
userControl |= 2;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_USER_CTRL, userControl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_INT_PIN_CFG, 0x82))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::bypassOff()
|
||||||
|
{
|
||||||
|
unsigned char userControl;
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_slaveAddr, MPU9150_USER_CTRL, &userControl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
userControl |= 0x20;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_USER_CTRL, userControl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_INT_PIN_CFG, 0x80))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::setSampleRate()
|
||||||
|
{
|
||||||
|
int clockRate = 1000;
|
||||||
|
|
||||||
|
if (m_lpf == MPU9150_LPF_256)
|
||||||
|
clockRate = 8000;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_SMPRT_DIV, (unsigned char)(clockRate / m_sampleRate - 1)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::setCompassRate()
|
||||||
|
{
|
||||||
|
int rate;
|
||||||
|
|
||||||
|
rate = m_sampleRate / m_compassRate - 1;
|
||||||
|
|
||||||
|
if (rate > 31)
|
||||||
|
rate = 31;
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9150_I2C_SLV4_CTRL, rate))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMUMPU9150::IMUGetPollInterval()
|
||||||
|
{
|
||||||
|
return (400 / m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9150::IMURead()
|
||||||
|
{
|
||||||
|
unsigned char fifoCount[2];
|
||||||
|
unsigned int count;
|
||||||
|
unsigned char fifoData[12];
|
||||||
|
unsigned char compassData[8];
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, MPU9150_FIFO_COUNT_H, 2, fifoCount))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
count = ((unsigned int)fifoCount[0] << 8) + fifoCount[1];
|
||||||
|
|
||||||
|
if (count == 1024) {
|
||||||
|
resetFifo();
|
||||||
|
m_timestamp += m_sampleInterval * (1024 / MPU9150_FIFO_CHUNK_SIZE + 1); // try to fix timestamp
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > MPU9150_FIFO_CHUNK_SIZE * 40) {
|
||||||
|
// more than 40 samples behind - going too slowly so discard some samples but maintain timestamp correctly
|
||||||
|
while (count >= MPU9150_FIFO_CHUNK_SIZE * 10) {
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, MPU9150_FIFO_R_W, MPU9150_FIFO_CHUNK_SIZE, fifoData))
|
||||||
|
return false;
|
||||||
|
count -= MPU9150_FIFO_CHUNK_SIZE;
|
||||||
|
m_timestamp += m_sampleInterval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count < MPU9150_FIFO_CHUNK_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, MPU9150_FIFO_R_W, MPU9150_FIFO_CHUNK_SIZE, fifoData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, MPU9150_EXT_SENS_DATA_00, m_compassDataLength, compassData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RTMath::convertToVector(fifoData, m_accel, m_accelScale, true);
|
||||||
|
RTMath::convertToVector(fifoData + 6, m_gyro, m_gyroScale, true);
|
||||||
|
|
||||||
|
if (m_compassIs5883)
|
||||||
|
RTMath::convertToVector(compassData, m_compass, 0.092f, true);
|
||||||
|
else
|
||||||
|
RTMath::convertToVector(compassData + 1, m_compass, 0.3f, false);
|
||||||
|
|
||||||
|
|
||||||
|
// sort out gyro axes
|
||||||
|
|
||||||
|
m_gyro.setY(-m_gyro.y());
|
||||||
|
m_gyro.setZ(-m_gyro.z());
|
||||||
|
|
||||||
|
// sort out accel data;
|
||||||
|
|
||||||
|
m_accel.setX(-m_accel.x());
|
||||||
|
|
||||||
|
if (m_compassPresent) {
|
||||||
|
if (m_compassIs5883) {
|
||||||
|
// sort out compass axes
|
||||||
|
|
||||||
|
float temp;
|
||||||
|
|
||||||
|
temp = m_compass.y();
|
||||||
|
m_compass.setY(-m_compass.z());
|
||||||
|
m_compass.setZ(-temp);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// use the compass fuse data adjustments
|
||||||
|
|
||||||
|
m_compass.setX(m_compass.x() * m_compassAdjust[0]);
|
||||||
|
m_compass.setY(m_compass.y() * m_compassAdjust[1]);
|
||||||
|
m_compass.setZ(m_compass.z() * m_compassAdjust[2]);
|
||||||
|
|
||||||
|
// sort out compass axes
|
||||||
|
|
||||||
|
float temp;
|
||||||
|
|
||||||
|
temp = m_compass.x();
|
||||||
|
m_compass.setX(m_compass.y());
|
||||||
|
m_compass.setY(-temp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_compass.setX(0);
|
||||||
|
m_compass.setY(0);
|
||||||
|
m_compass.setZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now do standard processing
|
||||||
|
|
||||||
|
handleGyroBias();
|
||||||
|
if (m_compassPresent)
|
||||||
|
calibrateAverageCompass();
|
||||||
|
|
||||||
|
if (m_firstTime)
|
||||||
|
m_timestamp = millis();
|
||||||
|
else
|
||||||
|
m_timestamp += m_sampleInterval;
|
||||||
|
|
||||||
|
m_firstTime = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,171 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMUMPU9150_H
|
||||||
|
#define _RTIMUMPU9150_H
|
||||||
|
|
||||||
|
#include "RTIMU.h"
|
||||||
|
|
||||||
|
// MPU9150 I2C Slave Addresses
|
||||||
|
|
||||||
|
#define MPU9150_ADDRESS0 0x68
|
||||||
|
#define MPU9150_ADDRESS1 0x69
|
||||||
|
#define MPU9150_ID 0x68
|
||||||
|
|
||||||
|
// these magnetometers are on aux bus
|
||||||
|
|
||||||
|
#define AK8975_ADDRESS 0x0c
|
||||||
|
#define HMC5883_ADDRESS 0x1e
|
||||||
|
|
||||||
|
// Register map
|
||||||
|
|
||||||
|
#define MPU9150_YG_OFFS_TC 0x01
|
||||||
|
#define MPU9150_SMPRT_DIV 0x19
|
||||||
|
#define MPU9150_LPF_CONFIG 0x1a
|
||||||
|
#define MPU9150_GYRO_CONFIG 0x1b
|
||||||
|
#define MPU9150_ACCEL_CONFIG 0x1c
|
||||||
|
#define MPU9150_FIFO_EN 0x23
|
||||||
|
#define MPU9150_I2C_MST_CTRL 0x24
|
||||||
|
#define MPU9150_I2C_SLV0_ADDR 0x25
|
||||||
|
#define MPU9150_I2C_SLV0_REG 0x26
|
||||||
|
#define MPU9150_I2C_SLV0_CTRL 0x27
|
||||||
|
#define MPU9150_I2C_SLV1_ADDR 0x28
|
||||||
|
#define MPU9150_I2C_SLV1_REG 0x29
|
||||||
|
#define MPU9150_I2C_SLV1_CTRL 0x2a
|
||||||
|
#define MPU9150_I2C_SLV4_CTRL 0x34
|
||||||
|
#define MPU9150_INT_PIN_CFG 0x37
|
||||||
|
#define MPU9150_INT_ENABLE 0x38
|
||||||
|
#define MPU9150_INT_STATUS 0x3a
|
||||||
|
#define MPU9150_ACCEL_XOUT_H 0x3b
|
||||||
|
#define MPU9150_GYRO_XOUT_H 0x43
|
||||||
|
#define MPU9150_EXT_SENS_DATA_00 0x49
|
||||||
|
#define MPU9150_I2C_SLV1_DO 0x64
|
||||||
|
#define MPU9150_I2C_MST_DELAY_CTRL 0x67
|
||||||
|
#define MPU9150_USER_CTRL 0x6a
|
||||||
|
#define MPU9150_PWR_MGMT_1 0x6b
|
||||||
|
#define MPU9150_PWR_MGMT_2 0x6c
|
||||||
|
#define MPU9150_FIFO_COUNT_H 0x72
|
||||||
|
#define MPU9150_FIFO_R_W 0x74
|
||||||
|
#define MPU9150_WHO_AM_I 0x75
|
||||||
|
|
||||||
|
// sample rate defines (applies to gyros and accels, not mags)
|
||||||
|
|
||||||
|
#define MPU9150_SAMPLERATE_MIN 5 // 5 samples per second is the lowest
|
||||||
|
#define MPU9150_SAMPLERATE_MAX 1000 // 1000 samples per second is the absolute maximum
|
||||||
|
|
||||||
|
// compass rate defines
|
||||||
|
|
||||||
|
#define MPU9150_COMPASSRATE_MIN 1 // 1 samples per second is the lowest
|
||||||
|
#define MPU9150_COMPASSRATE_MAX 100 // 100 samples per second is maximum
|
||||||
|
|
||||||
|
// LPF options (gyros and accels)
|
||||||
|
|
||||||
|
#define MPU9150_LPF_256 0 // gyro: 256Hz, accel: 260Hz
|
||||||
|
#define MPU9150_LPF_188 1 // gyro: 188Hz, accel: 184Hz
|
||||||
|
#define MPU9150_LPF_98 2 // gyro: 98Hz, accel: 98Hz
|
||||||
|
#define MPU9150_LPF_42 3 // gyro: 42Hz, accel: 44Hz
|
||||||
|
#define MPU9150_LPF_20 4 // gyro: 20Hz, accel: 21Hz
|
||||||
|
#define MPU9150_LPF_10 5 // gyro: 10Hz, accel: 10Hz
|
||||||
|
#define MPU9150_LPF_5 6 // gyro: 5Hz, accel: 5Hz
|
||||||
|
|
||||||
|
// Gyro FSR options
|
||||||
|
|
||||||
|
#define MPU9150_GYROFSR_250 0 // +/- 250 degrees per second
|
||||||
|
#define MPU9150_GYROFSR_500 8 // +/- 500 degrees per second
|
||||||
|
#define MPU9150_GYROFSR_1000 0x10 // +/- 1000 degrees per second
|
||||||
|
#define MPU9150_GYROFSR_2000 0x18 // +/- 2000 degrees per second
|
||||||
|
|
||||||
|
// Accel FSR options
|
||||||
|
|
||||||
|
#define MPU9150_ACCELFSR_2 0 // +/- 2g
|
||||||
|
#define MPU9150_ACCELFSR_4 8 // +/- 4g
|
||||||
|
#define MPU9150_ACCELFSR_8 0x10 // +/- 8g
|
||||||
|
#define MPU9150_ACCELFSR_16 0x18 // +/- 16g
|
||||||
|
|
||||||
|
|
||||||
|
// AK8975 compass registers
|
||||||
|
|
||||||
|
#define AK8975_DEVICEID 0x0 // the device ID
|
||||||
|
#define AK8975_ST1 0x02 // status 1
|
||||||
|
#define AK8975_CNTL 0x0a // control reg
|
||||||
|
#define AK8975_ASAX 0x10 // start of the fuse ROM data
|
||||||
|
|
||||||
|
// HMC5883 compass registers
|
||||||
|
|
||||||
|
#define HMC5883_CONFIG_A 0x00 // configuration A
|
||||||
|
#define HMC5883_CONFIG_B 0x01 // configuration B
|
||||||
|
#define HMC5883_MODE 0x02 // mode
|
||||||
|
#define HMC5883_DATA_X_HI 0x03 // data x msb
|
||||||
|
#define HMC5883_STATUS 0x09 // status
|
||||||
|
#define HMC5883_ID 0x0a // id
|
||||||
|
|
||||||
|
// FIFO transfer size
|
||||||
|
|
||||||
|
#define MPU9150_FIFO_CHUNK_SIZE 12 // gyro and accels take 12 bytes
|
||||||
|
|
||||||
|
class RTIMUMPU9150 : public RTIMU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTIMUMPU9150(RTIMUSettings *settings);
|
||||||
|
~RTIMUMPU9150();
|
||||||
|
|
||||||
|
bool setLpf(unsigned char lpf);
|
||||||
|
bool setSampleRate(int rate);
|
||||||
|
bool setCompassRate(int rate);
|
||||||
|
bool setGyroFsr(unsigned char fsr);
|
||||||
|
bool setAccelFsr(unsigned char fsr);
|
||||||
|
|
||||||
|
virtual const char *IMUName() { return "MPU-9150"; }
|
||||||
|
virtual int IMUType() { return RTIMU_TYPE_MPU9150; }
|
||||||
|
virtual int IMUInit();
|
||||||
|
virtual bool IMURead();
|
||||||
|
virtual int IMUGetPollInterval();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool configureCompass(); // configure the compass
|
||||||
|
bool bypassOn(); // talk to compass
|
||||||
|
bool bypassOff(); // talk to MPU9150
|
||||||
|
bool setSampleRate();
|
||||||
|
bool setCompassRate();
|
||||||
|
bool resetFifo();
|
||||||
|
|
||||||
|
bool m_firstTime; // if first sample
|
||||||
|
|
||||||
|
unsigned char m_slaveAddr; // I2C address of MPU9150
|
||||||
|
unsigned char m_bus; // I2C bus (usually 1 for Raspberry Pi for example)
|
||||||
|
|
||||||
|
unsigned char m_lpf; // low pass filter setting
|
||||||
|
int m_compassRate; // compass sample rate in Hz
|
||||||
|
unsigned char m_gyroFsr;
|
||||||
|
unsigned char m_accelFsr;
|
||||||
|
|
||||||
|
RTFLOAT m_gyroScale;
|
||||||
|
RTFLOAT m_accelScale;
|
||||||
|
|
||||||
|
RTFLOAT m_compassAdjust[3]; // the compass fuse ROM values converted for use
|
||||||
|
bool m_compassPresent; // false for MPU-6050
|
||||||
|
bool m_compassIs5883; // if it is an MPU-6050/HMC5883 combo
|
||||||
|
int m_compassDataLength; // 8 for MPU-9150, 6 for HMC5883
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMUMPU9150_H
|
|
@ -0,0 +1,493 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTIMUMPU9250.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
|
||||||
|
#if defined(MPU9250_68) || defined(MPU9250_69)
|
||||||
|
|
||||||
|
RTIMUMPU9250::RTIMUMPU9250(RTIMUSettings *settings) : RTIMU(settings)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RTIMUMPU9250::~RTIMUMPU9250()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setSampleRate(int rate)
|
||||||
|
{
|
||||||
|
if ((rate < MPU9250_SAMPLERATE_MIN) || (rate > MPU9250_SAMPLERATE_MAX)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_sampleRate = rate;
|
||||||
|
m_sampleInterval = (unsigned long)1000 / m_sampleRate;
|
||||||
|
if (m_sampleInterval == 0)
|
||||||
|
m_sampleInterval = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setGyroLpf(unsigned char lpf)
|
||||||
|
{
|
||||||
|
switch (lpf) {
|
||||||
|
case MPU9250_GYRO_LPF_8800:
|
||||||
|
case MPU9250_GYRO_LPF_3600:
|
||||||
|
case MPU9250_GYRO_LPF_250:
|
||||||
|
case MPU9250_GYRO_LPF_184:
|
||||||
|
case MPU9250_GYRO_LPF_92:
|
||||||
|
case MPU9250_GYRO_LPF_41:
|
||||||
|
case MPU9250_GYRO_LPF_20:
|
||||||
|
case MPU9250_GYRO_LPF_10:
|
||||||
|
case MPU9250_GYRO_LPF_5:
|
||||||
|
m_gyroLpf = lpf;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setAccelLpf(unsigned char lpf)
|
||||||
|
{
|
||||||
|
switch (lpf) {
|
||||||
|
case MPU9250_ACCEL_LPF_1130:
|
||||||
|
case MPU9250_ACCEL_LPF_460:
|
||||||
|
case MPU9250_ACCEL_LPF_184:
|
||||||
|
case MPU9250_ACCEL_LPF_92:
|
||||||
|
case MPU9250_ACCEL_LPF_41:
|
||||||
|
case MPU9250_ACCEL_LPF_20:
|
||||||
|
case MPU9250_ACCEL_LPF_10:
|
||||||
|
case MPU9250_ACCEL_LPF_5:
|
||||||
|
m_accelLpf = lpf;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setCompassRate(int rate)
|
||||||
|
{
|
||||||
|
if ((rate < MPU9250_COMPASSRATE_MIN) || (rate > MPU9250_COMPASSRATE_MAX)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_compassRate = rate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setGyroFsr(unsigned char fsr)
|
||||||
|
{
|
||||||
|
switch (fsr) {
|
||||||
|
case MPU9250_GYROFSR_250:
|
||||||
|
m_gyroFsr = fsr;
|
||||||
|
m_gyroScale = RTMATH_PI / (131.0 * 180.0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9250_GYROFSR_500:
|
||||||
|
m_gyroFsr = fsr;
|
||||||
|
m_gyroScale = RTMATH_PI / (62.5 * 180.0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9250_GYROFSR_1000:
|
||||||
|
m_gyroFsr = fsr;
|
||||||
|
m_gyroScale = RTMATH_PI / (32.8 * 180.0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9250_GYROFSR_2000:
|
||||||
|
m_gyroFsr = fsr;
|
||||||
|
m_gyroScale = RTMATH_PI / (16.4 * 180.0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setAccelFsr(unsigned char fsr)
|
||||||
|
{
|
||||||
|
switch (fsr) {
|
||||||
|
case MPU9250_ACCELFSR_2:
|
||||||
|
m_accelFsr = fsr;
|
||||||
|
m_accelScale = 1.0/16384.0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9250_ACCELFSR_4:
|
||||||
|
m_accelFsr = fsr;
|
||||||
|
m_accelScale = 1.0/8192.0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9250_ACCELFSR_8:
|
||||||
|
m_accelFsr = fsr;
|
||||||
|
m_accelScale = 1.0/4096.0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MPU9250_ACCELFSR_16:
|
||||||
|
m_accelFsr = fsr;
|
||||||
|
m_accelScale = 1.0/2048.0;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int RTIMUMPU9250::IMUInit()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
unsigned char asa[3];
|
||||||
|
|
||||||
|
m_firstTime = true;
|
||||||
|
|
||||||
|
#ifdef MPU9250_CACHE_MODE
|
||||||
|
m_cacheIn = m_cacheOut = m_cacheCount = 0;
|
||||||
|
#endif
|
||||||
|
// configure IMU
|
||||||
|
|
||||||
|
m_slaveAddr = m_settings->m_I2CSlaveAddress;
|
||||||
|
|
||||||
|
setSampleRate(m_settings->m_MPU9250GyroAccelSampleRate);
|
||||||
|
setCompassRate(m_settings->m_MPU9250CompassSampleRate);
|
||||||
|
setGyroLpf(m_settings->m_MPU9250GyroLpf);
|
||||||
|
setAccelLpf(m_settings->m_MPU9250AccelLpf);
|
||||||
|
setGyroFsr(m_settings->m_MPU9250GyroFsr);
|
||||||
|
setAccelFsr(m_settings->m_MPU9250AccelFsr);
|
||||||
|
|
||||||
|
setCalibrationData();
|
||||||
|
|
||||||
|
// reset the MPU9250
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_PWR_MGMT_1, 0x80))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_PWR_MGMT_1, 0x00))
|
||||||
|
return -4;
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_slaveAddr, MPU9250_WHO_AM_I, &result))
|
||||||
|
return -5;
|
||||||
|
|
||||||
|
if (result != MPU9250_ID) {
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now configure the various components
|
||||||
|
|
||||||
|
if (!setGyroConfig())
|
||||||
|
return -7;
|
||||||
|
|
||||||
|
if (!setAccelConfig())
|
||||||
|
return -8;
|
||||||
|
|
||||||
|
if (!setSampleRate())
|
||||||
|
return -9;
|
||||||
|
|
||||||
|
// now configure compass
|
||||||
|
|
||||||
|
if (!bypassOn())
|
||||||
|
return -11;
|
||||||
|
|
||||||
|
// get fuse ROM data
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(AK8963_ADDRESS, AK8963_CNTL, 0)) {
|
||||||
|
bypassOff();
|
||||||
|
return -12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(AK8963_ADDRESS, AK8963_CNTL, 0x0f)) {
|
||||||
|
bypassOff();
|
||||||
|
return -13;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(AK8963_ADDRESS, AK8963_ASAX, 3, asa)) {
|
||||||
|
bypassOff();
|
||||||
|
return -14;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert asa to usable scale factor
|
||||||
|
|
||||||
|
m_compassAdjust[0] = ((float)asa[0] - 128.0) / 256.0 + 1.0f;
|
||||||
|
m_compassAdjust[1] = ((float)asa[1] - 128.0) / 256.0 + 1.0f;
|
||||||
|
m_compassAdjust[2] = ((float)asa[2] - 128.0) / 256.0 + 1.0f;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(AK8963_ADDRESS, AK8963_CNTL, 0)) {
|
||||||
|
bypassOff();
|
||||||
|
return -15;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bypassOff())
|
||||||
|
return -16;
|
||||||
|
|
||||||
|
// now set up MPU9250 to talk to the compass chip
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_MST_CTRL, 0x40))
|
||||||
|
return -17;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_SLV0_ADDR, 0x80 | AK8963_ADDRESS))
|
||||||
|
return -18;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_SLV0_REG, AK8963_ST1))
|
||||||
|
return -19;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_SLV0_CTRL, 0x88))
|
||||||
|
return -20;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_SLV1_ADDR, AK8963_ADDRESS))
|
||||||
|
return -21;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_SLV1_REG, AK8963_CNTL))
|
||||||
|
return -22;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_SLV1_CTRL, 0x81))
|
||||||
|
return -23;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_SLV1_DO, 0x1))
|
||||||
|
return -24;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_MST_DELAY_CTRL, 0x3))
|
||||||
|
return -25;
|
||||||
|
|
||||||
|
if (!setCompassRate())
|
||||||
|
return -27;
|
||||||
|
|
||||||
|
// enable the sensors
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_PWR_MGMT_1, 1))
|
||||||
|
return -28;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_PWR_MGMT_2, 0))
|
||||||
|
return -29;
|
||||||
|
|
||||||
|
// select the data to go into the FIFO and enable
|
||||||
|
|
||||||
|
if (!resetFifo())
|
||||||
|
return -30;
|
||||||
|
|
||||||
|
gyroBiasInit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::resetFifo()
|
||||||
|
{
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_INT_ENABLE, 0))
|
||||||
|
return false;
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_FIFO_EN, 0))
|
||||||
|
return false;
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_USER_CTRL, 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_USER_CTRL, 0x04))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_USER_CTRL, 0x60))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_INT_ENABLE, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_FIFO_EN, 0x78))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::bypassOn()
|
||||||
|
{
|
||||||
|
unsigned char userControl;
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_slaveAddr, MPU9250_USER_CTRL, &userControl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
userControl &= ~0x20;
|
||||||
|
userControl |= 2;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_USER_CTRL, userControl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_INT_PIN_CFG, 0x82))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::bypassOff()
|
||||||
|
{
|
||||||
|
unsigned char userControl;
|
||||||
|
|
||||||
|
if (!I2Cdev::readByte(m_slaveAddr, MPU9250_USER_CTRL, &userControl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
userControl |= 0x20;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_USER_CTRL, userControl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_INT_PIN_CFG, 0x80))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setGyroConfig()
|
||||||
|
{
|
||||||
|
unsigned char gyroConfig = m_gyroFsr + ((m_gyroLpf >> 3) & 3);
|
||||||
|
unsigned char gyroLpf = m_gyroLpf & 7;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_GYRO_CONFIG, gyroConfig))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_GYRO_LPF, gyroLpf))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setAccelConfig()
|
||||||
|
{
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_ACCEL_CONFIG, m_accelFsr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_ACCEL_LPF, m_accelLpf))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setSampleRate()
|
||||||
|
{
|
||||||
|
if (m_sampleRate > 1000)
|
||||||
|
return true; // SMPRT not used above 1000Hz
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_SMPRT_DIV, (unsigned char) (1000 / m_sampleRate - 1)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::setCompassRate()
|
||||||
|
{
|
||||||
|
int rate;
|
||||||
|
|
||||||
|
rate = m_sampleRate / m_compassRate - 1;
|
||||||
|
|
||||||
|
if (rate > 31)
|
||||||
|
rate = 31;
|
||||||
|
if (!I2Cdev::writeByte(m_slaveAddr, MPU9250_I2C_SLV4_CTRL, rate))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RTIMUMPU9250::IMUGetPollInterval()
|
||||||
|
{
|
||||||
|
return (400 / m_sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTIMUMPU9250::IMURead()
|
||||||
|
{
|
||||||
|
unsigned char fifoCount[2];
|
||||||
|
unsigned int count;
|
||||||
|
unsigned char fifoData[12];
|
||||||
|
unsigned char compassData[8];
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, MPU9250_FIFO_COUNT_H, 2, fifoCount))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
count = ((unsigned int)fifoCount[0] << 8) + fifoCount[1];
|
||||||
|
|
||||||
|
if (count == 1024) {
|
||||||
|
resetFifo();
|
||||||
|
m_timestamp += m_sampleInterval * (1024 / MPU9250_FIFO_CHUNK_SIZE + 1); // try to fix timestamp
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > MPU9250_FIFO_CHUNK_SIZE * 40) {
|
||||||
|
// more than 40 samples behind - going too slowly so discard some samples but maintain timestamp correctly
|
||||||
|
while (count >= MPU9250_FIFO_CHUNK_SIZE * 10) {
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, MPU9250_FIFO_R_W, MPU9250_FIFO_CHUNK_SIZE, fifoData))
|
||||||
|
return false;
|
||||||
|
count -= MPU9250_FIFO_CHUNK_SIZE;
|
||||||
|
m_timestamp += m_sampleInterval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count < MPU9250_FIFO_CHUNK_SIZE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, MPU9250_FIFO_R_W, MPU9250_FIFO_CHUNK_SIZE, fifoData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_slaveAddr, MPU9250_EXT_SENS_DATA_00, 8, compassData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RTMath::convertToVector(fifoData, m_accel, m_accelScale, true);
|
||||||
|
RTMath::convertToVector(fifoData + 6, m_gyro, m_gyroScale, true);
|
||||||
|
RTMath::convertToVector(compassData + 1, m_compass, 0.6f, false);
|
||||||
|
|
||||||
|
// sort out gyro axes
|
||||||
|
|
||||||
|
m_gyro.setY(-m_gyro.y());
|
||||||
|
m_gyro.setZ(-m_gyro.z());
|
||||||
|
|
||||||
|
// sort out accel data;
|
||||||
|
|
||||||
|
m_accel.setX(-m_accel.x());
|
||||||
|
|
||||||
|
// use the fuse data adjustments for compass
|
||||||
|
|
||||||
|
m_compass.setX(m_compass.x() * m_compassAdjust[0]);
|
||||||
|
m_compass.setY(m_compass.y() * m_compassAdjust[1]);
|
||||||
|
m_compass.setZ(m_compass.z() * m_compassAdjust[2]);
|
||||||
|
|
||||||
|
// sort out compass axes
|
||||||
|
|
||||||
|
float temp;
|
||||||
|
|
||||||
|
temp = m_compass.x();
|
||||||
|
m_compass.setX(m_compass.y());
|
||||||
|
m_compass.setY(-temp);
|
||||||
|
|
||||||
|
// now do standard processing
|
||||||
|
|
||||||
|
handleGyroBias();
|
||||||
|
calibrateAverageCompass();
|
||||||
|
|
||||||
|
if (m_firstTime)
|
||||||
|
m_timestamp = millis();
|
||||||
|
else
|
||||||
|
m_timestamp += m_sampleInterval;
|
||||||
|
|
||||||
|
m_firstTime = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,175 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMUMPU9250_H
|
||||||
|
#define _RTIMUMPU9250_H
|
||||||
|
|
||||||
|
#include "RTIMU.h"
|
||||||
|
|
||||||
|
// MPU9250 I2C Slave Addresses
|
||||||
|
|
||||||
|
#define MPU9250_ADDRESS0 0x68
|
||||||
|
#define MPU9250_ADDRESS1 0x69
|
||||||
|
#define MPU9250_ID 0x71
|
||||||
|
|
||||||
|
#define AK8963_ADDRESS 0x0c
|
||||||
|
|
||||||
|
// Register map
|
||||||
|
|
||||||
|
#define MPU9250_SMPRT_DIV 0x19
|
||||||
|
#define MPU9250_GYRO_LPF 0x1a
|
||||||
|
#define MPU9250_GYRO_CONFIG 0x1b
|
||||||
|
#define MPU9250_ACCEL_CONFIG 0x1c
|
||||||
|
#define MPU9250_ACCEL_LPF 0x1d
|
||||||
|
#define MPU9250_FIFO_EN 0x23
|
||||||
|
#define MPU9250_I2C_MST_CTRL 0x24
|
||||||
|
#define MPU9250_I2C_SLV0_ADDR 0x25
|
||||||
|
#define MPU9250_I2C_SLV0_REG 0x26
|
||||||
|
#define MPU9250_I2C_SLV0_CTRL 0x27
|
||||||
|
#define MPU9250_I2C_SLV1_ADDR 0x28
|
||||||
|
#define MPU9250_I2C_SLV1_REG 0x29
|
||||||
|
#define MPU9250_I2C_SLV1_CTRL 0x2a
|
||||||
|
#define MPU9250_I2C_SLV2_ADDR 0x2b
|
||||||
|
#define MPU9250_I2C_SLV2_REG 0x2c
|
||||||
|
#define MPU9250_I2C_SLV2_CTRL 0x2d
|
||||||
|
#define MPU9250_I2C_SLV4_CTRL 0x34
|
||||||
|
#define MPU9250_INT_PIN_CFG 0x37
|
||||||
|
#define MPU9250_INT_ENABLE 0x38
|
||||||
|
#define MPU9250_INT_STATUS 0x3a
|
||||||
|
#define MPU9250_ACCEL_XOUT_H 0x3b
|
||||||
|
#define MPU9250_GYRO_XOUT_H 0x43
|
||||||
|
#define MPU9250_EXT_SENS_DATA_00 0x49
|
||||||
|
#define MPU9250_I2C_SLV1_DO 0x64
|
||||||
|
#define MPU9250_I2C_MST_DELAY_CTRL 0x67
|
||||||
|
#define MPU9250_USER_CTRL 0x6a
|
||||||
|
#define MPU9250_PWR_MGMT_1 0x6b
|
||||||
|
#define MPU9250_PWR_MGMT_2 0x6c
|
||||||
|
#define MPU9250_FIFO_COUNT_H 0x72
|
||||||
|
#define MPU9250_FIFO_R_W 0x74
|
||||||
|
#define MPU9250_WHO_AM_I 0x75
|
||||||
|
|
||||||
|
// sample rate defines (applies to gyros and accels, not mags)
|
||||||
|
|
||||||
|
#define MPU9250_SAMPLERATE_MIN 5 // 5 samples per second is the lowest
|
||||||
|
#define MPU9250_SAMPLERATE_MAX 1000 // 1000 samples per second is the absolute maximum
|
||||||
|
|
||||||
|
// compass rate defines
|
||||||
|
|
||||||
|
#define MPU9250_COMPASSRATE_MIN 1 // 1 samples per second is the lowest
|
||||||
|
#define MPU9250_COMPASSRATE_MAX 100 // 100 samples per second is maximum
|
||||||
|
|
||||||
|
// Gyro LPF options
|
||||||
|
|
||||||
|
#define MPU9250_GYRO_LPF_8800 0x11 // 8800Hz, 0.64mS delay
|
||||||
|
#define MPU9250_GYRO_LPF_3600 0x10 // 3600Hz, 0.11mS delay
|
||||||
|
#define MPU9250_GYRO_LPF_250 0x00 // 250Hz, 0.97mS delay
|
||||||
|
#define MPU9250_GYRO_LPF_184 0x01 // 184Hz, 2.9mS delay
|
||||||
|
#define MPU9250_GYRO_LPF_92 0x02 // 92Hz, 3.9mS delay
|
||||||
|
#define MPU9250_GYRO_LPF_41 0x03 // 41Hz, 5.9mS delay
|
||||||
|
#define MPU9250_GYRO_LPF_20 0x04 // 20Hz, 9.9mS delay
|
||||||
|
#define MPU9250_GYRO_LPF_10 0x05 // 10Hz, 17.85mS delay
|
||||||
|
#define MPU9250_GYRO_LPF_5 0x06 // 5Hz, 33.48mS delay
|
||||||
|
|
||||||
|
// Gyro FSR options
|
||||||
|
|
||||||
|
#define MPU9250_GYROFSR_250 0 // +/- 250 degrees per second
|
||||||
|
#define MPU9250_GYROFSR_500 8 // +/- 500 degrees per second
|
||||||
|
#define MPU9250_GYROFSR_1000 0x10 // +/- 1000 degrees per second
|
||||||
|
#define MPU9250_GYROFSR_2000 0x18 // +/- 2000 degrees per second
|
||||||
|
|
||||||
|
// Accel FSR options
|
||||||
|
|
||||||
|
#define MPU9250_ACCELFSR_2 0 // +/- 2g
|
||||||
|
#define MPU9250_ACCELFSR_4 8 // +/- 4g
|
||||||
|
#define MPU9250_ACCELFSR_8 0x10 // +/- 8g
|
||||||
|
#define MPU9250_ACCELFSR_16 0x18 // +/- 16g
|
||||||
|
|
||||||
|
// Accel LPF options
|
||||||
|
|
||||||
|
#define MPU9250_ACCEL_LPF_1130 0x08 // 1130Hz, 0.75mS delay
|
||||||
|
#define MPU9250_ACCEL_LPF_460 0x00 // 460Hz, 1.94mS delay
|
||||||
|
#define MPU9250_ACCEL_LPF_184 0x01 // 184Hz, 5.80mS delay
|
||||||
|
#define MPU9250_ACCEL_LPF_92 0x02 // 92Hz, 7.80mS delay
|
||||||
|
#define MPU9250_ACCEL_LPF_41 0x03 // 41Hz, 11.80mS delay
|
||||||
|
#define MPU9250_ACCEL_LPF_20 0x04 // 20Hz, 19.80mS delay
|
||||||
|
#define MPU9250_ACCEL_LPF_10 0x05 // 10Hz, 35.70mS delay
|
||||||
|
#define MPU9250_ACCEL_LPF_5 0x06 // 5Hz, 66.96mS delay
|
||||||
|
|
||||||
|
// AK8963 compass registers
|
||||||
|
|
||||||
|
#define AK8963_DEVICEID 0x48 // the device ID
|
||||||
|
#define AK8963_ST1 0x02 // status 1
|
||||||
|
#define AK8963_CNTL 0x0a // control reg
|
||||||
|
#define AK8963_ASAX 0x10 // start of the fuse ROM data
|
||||||
|
|
||||||
|
// FIFO transfer size
|
||||||
|
|
||||||
|
#define MPU9250_FIFO_CHUNK_SIZE 12 // gyro and accels take 12 bytes
|
||||||
|
|
||||||
|
class RTIMUMPU9250 : public RTIMU
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTIMUMPU9250(RTIMUSettings *settings);
|
||||||
|
~RTIMUMPU9250();
|
||||||
|
|
||||||
|
bool setGyroLpf(unsigned char lpf);
|
||||||
|
bool setAccelLpf(unsigned char lpf);
|
||||||
|
bool setSampleRate(int rate);
|
||||||
|
bool setCompassRate(int rate);
|
||||||
|
bool setGyroFsr(unsigned char fsr);
|
||||||
|
bool setAccelFsr(unsigned char fsr);
|
||||||
|
|
||||||
|
virtual const char *IMUName() { return "MPU-9250"; }
|
||||||
|
virtual int IMUType() { return RTIMU_TYPE_MPU9250; }
|
||||||
|
virtual int IMUInit();
|
||||||
|
virtual bool IMURead();
|
||||||
|
virtual int IMUGetPollInterval();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool setGyroConfig();
|
||||||
|
bool setAccelConfig();
|
||||||
|
bool setSampleRate();
|
||||||
|
bool compassSetup();
|
||||||
|
bool setCompassRate();
|
||||||
|
bool resetFifo();
|
||||||
|
bool bypassOn();
|
||||||
|
bool bypassOff();
|
||||||
|
|
||||||
|
bool m_firstTime; // if first sample
|
||||||
|
|
||||||
|
unsigned char m_slaveAddr; // I2C address of MPU9250
|
||||||
|
unsigned char m_bus; // I2C bus (usually 1 for Raspberry Pi for example)
|
||||||
|
|
||||||
|
unsigned char m_gyroLpf; // gyro low pass filter setting
|
||||||
|
unsigned char m_accelLpf; // accel low pass filter setting
|
||||||
|
int m_compassRate; // compass sample rate in Hz
|
||||||
|
unsigned char m_gyroFsr;
|
||||||
|
unsigned char m_accelFsr;
|
||||||
|
|
||||||
|
RTFLOAT m_gyroScale;
|
||||||
|
RTFLOAT m_accelScale;
|
||||||
|
|
||||||
|
RTFLOAT m_compassAdjust[3]; // the compass fuse ROM values converted for use
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMUMPU9250_H
|
|
@ -0,0 +1,308 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
|
||||||
|
#if defined(MPU9150_68) || defined(MPU9150_69)
|
||||||
|
#include "RTIMUMPU9150.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MPU9250_68) || defined(MPU9250_69)
|
||||||
|
#include "RTIMUMPU9250.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(LSM9DS0_6a) || defined(LSM9DS0_6b)
|
||||||
|
#include "RTIMULSM9DS0.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20HM303D_6a) || defined(GD20HM303D_6b)
|
||||||
|
#include "RTIMUGD20HM303D.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20M303DLHC_6a) || defined(GD20M303DLHC_6b)
|
||||||
|
#include "RTIMUGD20M303DLHC.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20HM303DLHC_6a) || defined(GD20HM303DLHC_6b)
|
||||||
|
#include "RTIMUGD20HM303DLHC.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BNO055_28) || defined(BNO055_29)
|
||||||
|
#include "RTIMUBNO055.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BMP180)
|
||||||
|
#include "RTPressureBMP180.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(LPS25H_5c) || defined (LPS25H_5d)
|
||||||
|
#include "RTPressureLPS25H.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MS5611_76) || defined (MS5611_77)
|
||||||
|
#include "RTPressureMS5611.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RATE_TIMER_INTERVAL 2
|
||||||
|
|
||||||
|
RTIMUSettings::RTIMUSettings()
|
||||||
|
{
|
||||||
|
// preset general defaults
|
||||||
|
|
||||||
|
m_imuType = -1;
|
||||||
|
m_I2CSlaveAddress = 0;
|
||||||
|
|
||||||
|
#ifdef MPU9150_68
|
||||||
|
// MPU9150 defaults
|
||||||
|
|
||||||
|
m_MPU9150GyroAccelSampleRate = 50;
|
||||||
|
m_MPU9150CompassSampleRate = 25;
|
||||||
|
m_MPU9150GyroAccelLpf = MPU9150_LPF_20;
|
||||||
|
m_MPU9150GyroFsr = MPU9150_GYROFSR_1000;
|
||||||
|
m_MPU9150AccelFsr = MPU9150_ACCELFSR_8;
|
||||||
|
m_imuType = RTIMU_TYPE_MPU9150;
|
||||||
|
m_I2CSlaveAddress = MPU9150_ADDRESS0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPU9150_69
|
||||||
|
// MPU9150 defaults
|
||||||
|
|
||||||
|
m_MPU9150GyroAccelSampleRate = 50;
|
||||||
|
m_MPU9150CompassSampleRate = 25;
|
||||||
|
m_MPU9150GyroAccelLpf = MPU9150_LPF_20;
|
||||||
|
m_MPU9150GyroFsr = MPU9150_GYROFSR_1000;
|
||||||
|
m_MPU9150AccelFsr = MPU9150_ACCELFSR_8;
|
||||||
|
m_imuType = RTIMU_TYPE_MPU9150;
|
||||||
|
m_I2CSlaveAddress = MPU9150_ADDRESS1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPU9250_68
|
||||||
|
// MPU9250 defaults
|
||||||
|
|
||||||
|
m_MPU9250GyroAccelSampleRate = 80;
|
||||||
|
m_MPU9250CompassSampleRate = 40;
|
||||||
|
m_MPU9250GyroLpf = MPU9250_GYRO_LPF_41;
|
||||||
|
m_MPU9250AccelLpf = MPU9250_ACCEL_LPF_41;
|
||||||
|
m_MPU9250GyroFsr = MPU9250_GYROFSR_1000;
|
||||||
|
m_MPU9250AccelFsr = MPU9250_ACCELFSR_8;
|
||||||
|
m_I2CSlaveAddress = MPU9250_ADDRESS0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MPU9250_69
|
||||||
|
// MPU9250 defaults
|
||||||
|
|
||||||
|
m_MPU9250GyroAccelSampleRate = 80;
|
||||||
|
m_MPU9250CompassSampleRate = 40;
|
||||||
|
m_MPU9250GyroLpf = MPU9250_GYRO_LPF_41;
|
||||||
|
m_MPU9250AccelLpf = MPU9250_ACCEL_LPF_41;
|
||||||
|
m_MPU9250GyroFsr = MPU9250_GYROFSR_1000;
|
||||||
|
m_MPU9250AccelFsr = MPU9250_ACCELFSR_8;
|
||||||
|
m_I2CSlaveAddress = MPU9250_ADDRESS1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LSM9DS0_6a
|
||||||
|
// LSM9DS0 defaults
|
||||||
|
|
||||||
|
m_LSM9DS0GyroSampleRate = LSM9DS0_GYRO_SAMPLERATE_95;
|
||||||
|
m_LSM9DS0GyroBW = LSM9DS0_GYRO_BANDWIDTH_1;
|
||||||
|
m_LSM9DS0GyroHpf = LSM9DS0_GYRO_HPF_4;
|
||||||
|
m_LSM9DS0GyroFsr = LSM9DS0_GYRO_FSR_500;
|
||||||
|
|
||||||
|
m_LSM9DS0AccelSampleRate = LSM9DS0_ACCEL_SAMPLERATE_50;
|
||||||
|
m_LSM9DS0AccelFsr = LSM9DS0_ACCEL_FSR_8;
|
||||||
|
m_LSM9DS0AccelLpf = LSM9DS0_ACCEL_LPF_50;
|
||||||
|
|
||||||
|
m_LSM9DS0CompassSampleRate = LSM9DS0_COMPASS_SAMPLERATE_50;
|
||||||
|
m_LSM9DS0CompassFsr = LSM9DS0_COMPASS_FSR_2;
|
||||||
|
|
||||||
|
m_imuType = RTIMU_TYPE_LSM9DS0;
|
||||||
|
m_I2CSlaveAddress = LSM9DS0_GYRO_ADDRESS0;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LSM9DS0_6b
|
||||||
|
// LSM9DS0 defaults
|
||||||
|
|
||||||
|
m_LSM9DS0GyroSampleRate = LSM9DS0_GYRO_SAMPLERATE_95;
|
||||||
|
m_LSM9DS0GyroBW = LSM9DS0_GYRO_BANDWIDTH_1;
|
||||||
|
m_LSM9DS0GyroHpf = LSM9DS0_GYRO_HPF_4;
|
||||||
|
m_LSM9DS0GyroFsr = LSM9DS0_GYRO_FSR_500;
|
||||||
|
|
||||||
|
m_LSM9DS0AccelSampleRate = LSM9DS0_ACCEL_SAMPLERATE_50;
|
||||||
|
m_LSM9DS0AccelFsr = LSM9DS0_ACCEL_FSR_8;
|
||||||
|
m_LSM9DS0AccelLpf = LSM9DS0_ACCEL_LPF_50;
|
||||||
|
|
||||||
|
m_LSM9DS0CompassSampleRate = LSM9DS0_COMPASS_SAMPLERATE_50;
|
||||||
|
m_LSM9DS0CompassFsr = LSM9DS0_COMPASS_FSR_2;
|
||||||
|
|
||||||
|
m_imuType = RTIMU_TYPE_LSM9DS0;
|
||||||
|
m_I2CSlaveAddress = LSM9DS0_GYRO_ADDRESS1;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GD20HM303D_6a
|
||||||
|
// GD20HM303D defaults
|
||||||
|
|
||||||
|
m_GD20HM303DGyroSampleRate = L3GD20H_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DGyroBW = L3GD20H_BANDWIDTH_1;
|
||||||
|
m_GD20HM303DGyroHpf = L3GD20H_HPF_4;
|
||||||
|
m_GD20HM303DGyroFsr = L3GD20H_FSR_500;
|
||||||
|
|
||||||
|
m_GD20HM303DAccelSampleRate = LSM303D_ACCEL_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DAccelFsr = LSM303D_ACCEL_FSR_8;
|
||||||
|
m_GD20HM303DAccelLpf = LSM303D_ACCEL_LPF_50;
|
||||||
|
|
||||||
|
m_GD20HM303DCompassSampleRate = LSM303D_COMPASS_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DCompassFsr = LSM303D_COMPASS_FSR_2;
|
||||||
|
|
||||||
|
m_imuType = RTIMU_TYPE_GD20HM303D;
|
||||||
|
m_I2CSlaveAddress = L3GD20H_ADDRESS0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GD20HM303D_6b
|
||||||
|
// GD20HM303D defaults
|
||||||
|
|
||||||
|
m_GD20HM303DGyroSampleRate = L3GD20H_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DGyroBW = L3GD20H_BANDWIDTH_1;
|
||||||
|
m_GD20HM303DGyroHpf = L3GD20H_HPF_4;
|
||||||
|
m_GD20HM303DGyroFsr = L3GD20H_FSR_500;
|
||||||
|
|
||||||
|
m_GD20HM303DAccelSampleRate = LSM303D_ACCEL_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DAccelFsr = LSM303D_ACCEL_FSR_8;
|
||||||
|
m_GD20HM303DAccelLpf = LSM303D_ACCEL_LPF_50;
|
||||||
|
|
||||||
|
m_GD20HM303DCompassSampleRate = LSM303D_COMPASS_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DCompassFsr = LSM303D_COMPASS_FSR_2;
|
||||||
|
|
||||||
|
m_imuType = RTIMU_TYPE_GD20HM303D;
|
||||||
|
m_I2CSlaveAddress = L3GD20H_ADDRESS1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GD20M303DLHC_6a
|
||||||
|
// GD20M303DLHC defaults
|
||||||
|
|
||||||
|
m_GD20M303DLHCGyroSampleRate = L3GD20_SAMPLERATE_95;
|
||||||
|
m_GD20M303DLHCGyroBW = L3GD20_BANDWIDTH_1;
|
||||||
|
m_GD20M303DLHCGyroHpf = L3GD20_HPF_4;
|
||||||
|
m_GD20M303DLHCGyroFsr = L3GD20_FSR_500;
|
||||||
|
|
||||||
|
m_GD20M303DLHCAccelSampleRate = LSM303DLHC_ACCEL_SAMPLERATE_50;
|
||||||
|
m_GD20M303DLHCAccelFsr = LSM303DLHC_ACCEL_FSR_8;
|
||||||
|
|
||||||
|
m_GD20M303DLHCCompassSampleRate = LSM303DLHC_COMPASS_SAMPLERATE_30;
|
||||||
|
m_GD20M303DLHCCompassFsr = LSM303DLHC_COMPASS_FSR_1_3;
|
||||||
|
|
||||||
|
m_imuType = RTIMU_TYPE_GD20M303DLHC;
|
||||||
|
m_I2CSlaveAddress = L3GD20_ADDRESS0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GD20M303DLHC_6b
|
||||||
|
// GD20M303DLHC defaults
|
||||||
|
|
||||||
|
m_GD20M303DLHCGyroSampleRate = L3GD20_SAMPLERATE_95;
|
||||||
|
m_GD20M303DLHCGyroBW = L3GD20_BANDWIDTH_1;
|
||||||
|
m_GD20M303DLHCGyroHpf = L3GD20_HPF_4;
|
||||||
|
m_GD20M303DLHCGyroFsr = L3GD20_FSR_500;
|
||||||
|
|
||||||
|
m_GD20M303DLHCAccelSampleRate = LSM303DLHC_ACCEL_SAMPLERATE_50;
|
||||||
|
m_GD20M303DLHCAccelFsr = LSM303DLHC_ACCEL_FSR_8;
|
||||||
|
|
||||||
|
m_GD20M303DLHCCompassSampleRate = LSM303DLHC_COMPASS_SAMPLERATE_30;
|
||||||
|
m_GD20M303DLHCCompassFsr = LSM303DLHC_COMPASS_FSR_1_3;
|
||||||
|
|
||||||
|
m_imuType = RTIMU_TYPE_GD20M303DLHC;
|
||||||
|
m_I2CSlaveAddress = L3GD20_ADDRESS1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GD20HM303DLHC_6a
|
||||||
|
// GD20HM303DLHC defaults
|
||||||
|
|
||||||
|
m_GD20HM303DLHCGyroSampleRate = L3GD20H_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DLHCGyroBW = L3GD20H_BANDWIDTH_1;
|
||||||
|
m_GD20HM303DLHCGyroHpf = L3GD20H_HPF_4;
|
||||||
|
m_GD20HM303DLHCGyroFsr = L3GD20H_FSR_500;
|
||||||
|
|
||||||
|
m_GD20HM303DLHCAccelSampleRate = LSM303DLHC_ACCEL_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DLHCAccelFsr = LSM303DLHC_ACCEL_FSR_8;
|
||||||
|
|
||||||
|
m_GD20HM303DLHCCompassSampleRate = LSM303DLHC_COMPASS_SAMPLERATE_30;
|
||||||
|
m_GD20HM303DLHCCompassFsr = LSM303DLHC_COMPASS_FSR_1_3;
|
||||||
|
|
||||||
|
m_imuType = RTIMU_TYPE_GD20HM303DLHC;
|
||||||
|
m_I2CSlaveAddress = L3GD20H_ADDRESS0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GD20HM303DLHC_6b
|
||||||
|
// GD20M303DLHC defaults
|
||||||
|
|
||||||
|
m_GD20HM303DLHCGyroSampleRate = L3GD20H_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DLHCGyroBW = L3GD20H_BANDWIDTH_1;
|
||||||
|
m_GD20HM303DLHCGyroHpf = L3GD20H_HPF_4;
|
||||||
|
m_GD20HM303DLHCGyroFsr = L3GD20H_FSR_500;
|
||||||
|
|
||||||
|
m_GD20HM303DLHCAccelSampleRate = LSM303DLHC_ACCEL_SAMPLERATE_50;
|
||||||
|
m_GD20HM303DLHCAccelFsr = LSM303DLHC_ACCEL_FSR_8;
|
||||||
|
|
||||||
|
m_GD20HM303DLHCCompassSampleRate = LSM303DLHC_COMPASS_SAMPLERATE_30;
|
||||||
|
m_GD20HM303DLHCCompassFsr = LSM303DLHC_COMPASS_FSR_1_3;
|
||||||
|
|
||||||
|
m_imuType = RTIMU_TYPE_GD20HM303DLHC;
|
||||||
|
m_I2CSlaveAddress = L3GD20H_ADDRESS1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BNO055_28
|
||||||
|
m_imuType = RTIMU_TYPE_BNO055;
|
||||||
|
m_I2CSlaveAddress = BNO055_ADDRESS0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BNO055_29
|
||||||
|
m_imuType = RTIMU_TYPE_BNO055;
|
||||||
|
m_I2CSlaveAddress = BNO055_ADDRESS1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BMP180
|
||||||
|
m_pressureType = RTPRESSURE_TYPE_BMP180;
|
||||||
|
m_I2CPressureAddress = BMP180_ADDRESS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LPS25H_5c
|
||||||
|
m_pressureType = RTPRESSURE_TYPE_LPS25H;
|
||||||
|
m_I2CPressureAddress = LPS25H_ADDRESS0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LPS25H_5d
|
||||||
|
m_pressureType = RTPRESSURE_TYPE_LPS25H;
|
||||||
|
m_I2CPressureAddress = LPS25H_ADDRESS1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MS5611_76
|
||||||
|
m_pressureType = RTPRESSURE_TYPE_MS5611;
|
||||||
|
m_I2CPressureAddress = MS5611_ADDRESS0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MS5611_77
|
||||||
|
m_pressureType = RTPRESSURE_TYPE_MS5611;
|
||||||
|
m_I2CPressureAddress = MS5611_ADDRESS1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTIMUSETTINGS_H
|
||||||
|
#define _RTIMUSETTINGS_H
|
||||||
|
|
||||||
|
#include "RTMath.h"
|
||||||
|
#include "RTIMULibDefs.h"
|
||||||
|
|
||||||
|
class RTIMUSettings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTIMUSettings();
|
||||||
|
|
||||||
|
// These are the local variables
|
||||||
|
|
||||||
|
int m_imuType; // type code of imu in use
|
||||||
|
unsigned char m_I2CSlaveAddress; // I2C slave address of the imu
|
||||||
|
int m_pressureType; // type code of pressure sensor in use
|
||||||
|
unsigned char m_I2CPressureAddress; // I2C slave address of the pressure sensor
|
||||||
|
|
||||||
|
// IMU-specific vars
|
||||||
|
|
||||||
|
#if defined(MPU9150_68) || defined(MPU9150_69)
|
||||||
|
// MPU9150
|
||||||
|
|
||||||
|
int m_MPU9150GyroAccelSampleRate; // the sample rate (samples per second) for gyro and accel
|
||||||
|
int m_MPU9150CompassSampleRate; // same for the compass
|
||||||
|
int m_MPU9150GyroAccelLpf; // low pass filter code for the gyro and accel
|
||||||
|
int m_MPU9150GyroFsr; // FSR code for the gyro
|
||||||
|
int m_MPU9150AccelFsr; // FSR code for the accel
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MPU9250_68) || defined(MPU9250_69)
|
||||||
|
// MPU9250
|
||||||
|
|
||||||
|
int m_MPU9250GyroAccelSampleRate; // the sample rate (samples per second) for gyro and accel
|
||||||
|
int m_MPU9250CompassSampleRate; // same for the compass
|
||||||
|
int m_MPU9250GyroLpf; // low pass filter code for the gyro
|
||||||
|
int m_MPU9250AccelLpf; // low pass filter code for the accel
|
||||||
|
int m_MPU9250GyroFsr; // FSR code for the gyro
|
||||||
|
int m_MPU9250AccelFsr; // FSR code for the accel
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(LSM9DS0_6a) || defined(LSM9DS0_6b)
|
||||||
|
// LSM9DS0
|
||||||
|
|
||||||
|
int m_LSM9DS0GyroSampleRate; // the gyro sample rate
|
||||||
|
int m_LSM9DS0GyroBW; // the gyro bandwidth code
|
||||||
|
int m_LSM9DS0GyroHpf; // the gyro high pass filter cutoff code
|
||||||
|
int m_LSM9DS0GyroFsr; // the gyro full scale range
|
||||||
|
|
||||||
|
int m_LSM9DS0AccelSampleRate; // the accel sample rate
|
||||||
|
int m_LSM9DS0AccelFsr; // the accel full scale range
|
||||||
|
int m_LSM9DS0AccelLpf; // the accel low pass filter
|
||||||
|
|
||||||
|
int m_LSM9DS0CompassSampleRate; // the compass sample rate
|
||||||
|
int m_LSM9DS0CompassFsr; // the compass full scale range
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20HM303D_6a) || defined(GD20HM303D_6b)
|
||||||
|
int m_GD20HM303DGyroSampleRate; // the gyro sample rate
|
||||||
|
int m_GD20HM303DGyroBW; // the gyro bandwidth code
|
||||||
|
int m_GD20HM303DGyroHpf; // the gyro high pass filter cutoff code
|
||||||
|
int m_GD20HM303DGyroFsr; // the gyro full scale range
|
||||||
|
|
||||||
|
int m_GD20HM303DAccelSampleRate; // the accel sample rate
|
||||||
|
int m_GD20HM303DAccelFsr; // the accel full scale range
|
||||||
|
int m_GD20HM303DAccelLpf; // the accel low pass filter
|
||||||
|
|
||||||
|
int m_GD20HM303DCompassSampleRate; // the compass sample rate
|
||||||
|
int m_GD20HM303DCompassFsr; // the compass full scale range
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20M303DLHC_6a) || defined(GD20M303DLHC_6b)
|
||||||
|
int m_GD20M303DLHCGyroSampleRate; // the gyro sample rate
|
||||||
|
int m_GD20M303DLHCGyroBW; // the gyro bandwidth code
|
||||||
|
int m_GD20M303DLHCGyroHpf; // the gyro high pass filter cutoff code
|
||||||
|
int m_GD20M303DLHCGyroFsr; // the gyro full scale range
|
||||||
|
|
||||||
|
int m_GD20M303DLHCAccelSampleRate; // the accel sample rate
|
||||||
|
int m_GD20M303DLHCAccelFsr; // the accel full scale range
|
||||||
|
|
||||||
|
int m_GD20M303DLHCCompassSampleRate; // the compass sample rate
|
||||||
|
int m_GD20M303DLHCCompassFsr; // the compass full scale range
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GD20HM303DLHC_6a) || defined(GD20HM303DLHC_6b)
|
||||||
|
int m_GD20HM303DLHCGyroSampleRate; // the gyro sample rate
|
||||||
|
int m_GD20HM303DLHCGyroBW; // the gyro bandwidth code
|
||||||
|
int m_GD20HM303DLHCGyroHpf; // the gyro high pass filter cutoff code
|
||||||
|
int m_GD20HM303DLHCGyroFsr; // the gyro full scale range
|
||||||
|
|
||||||
|
int m_GD20HM303DLHCAccelSampleRate; // the accel sample rate
|
||||||
|
int m_GD20HM303DLHCAccelFsr; // the accel full scale range
|
||||||
|
|
||||||
|
int m_GD20HM303DLHCCompassSampleRate; // the compass sample rate
|
||||||
|
int m_GD20HM303DLHCCompassFsr; // the compass full scale range
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTIMUSETTINGS_H
|
||||||
|
|
|
@ -0,0 +1,419 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTMath.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
void RTMath::display(const char *label, RTVector3& vec)
|
||||||
|
{
|
||||||
|
Serial.print(label);
|
||||||
|
Serial.print(" x:"); Serial.print(vec.x());
|
||||||
|
Serial.print(" y:"); Serial.print(vec.y());
|
||||||
|
Serial.print(" z:"); Serial.print(vec.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTMath::displayDegrees(const char *label, RTVector3& vec)
|
||||||
|
{
|
||||||
|
Serial.print(label);
|
||||||
|
Serial.print(" x:"); Serial.print(vec.x() * RTMATH_RAD_TO_DEGREE);
|
||||||
|
Serial.print(" y:"); Serial.print(vec.y() * RTMATH_RAD_TO_DEGREE);
|
||||||
|
Serial.print(" z:"); Serial.print(vec.z() * RTMATH_RAD_TO_DEGREE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTMath::displayRollPitchYaw(const char *label, RTVector3& vec)
|
||||||
|
{
|
||||||
|
Serial.print(label);
|
||||||
|
Serial.print(" roll:"); Serial.print(vec.x() * RTMATH_RAD_TO_DEGREE);
|
||||||
|
Serial.print(" pitch:"); Serial.print(vec.y() * RTMATH_RAD_TO_DEGREE);
|
||||||
|
Serial.print(" yaw:"); Serial.print(vec.z() * RTMATH_RAD_TO_DEGREE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTMath::display(const char *label, RTQuaternion& quat)
|
||||||
|
{
|
||||||
|
Serial.print(label);
|
||||||
|
Serial.print(" scalar:"); Serial.print(quat.scalar());
|
||||||
|
Serial.print(" x:"); Serial.print(quat.x());
|
||||||
|
Serial.print(" y:"); Serial.print(quat.y());
|
||||||
|
Serial.print(" z:"); Serial.print(quat.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
RTVector3 RTMath::poseFromAccelMag(const RTVector3& accel, const RTVector3& mag)
|
||||||
|
{
|
||||||
|
RTVector3 result;
|
||||||
|
RTQuaternion m;
|
||||||
|
RTQuaternion q;
|
||||||
|
|
||||||
|
accel.accelToEuler(result);
|
||||||
|
|
||||||
|
// q.fromEuler(result);
|
||||||
|
// since result.z() is always 0, this can be optimized a little
|
||||||
|
|
||||||
|
RTFLOAT cosX2 = cos(result.x() / 2.0f);
|
||||||
|
RTFLOAT sinX2 = sin(result.x() / 2.0f);
|
||||||
|
RTFLOAT cosY2 = cos(result.y() / 2.0f);
|
||||||
|
RTFLOAT sinY2 = sin(result.y() / 2.0f);
|
||||||
|
|
||||||
|
q.setScalar(cosX2 * cosY2);
|
||||||
|
q.setX(sinX2 * cosY2);
|
||||||
|
q.setY(cosX2 * sinY2);
|
||||||
|
q.setZ(-sinX2 * sinY2);
|
||||||
|
// q.normalize();
|
||||||
|
|
||||||
|
m.setScalar(0);
|
||||||
|
m.setX(mag.x());
|
||||||
|
m.setY(mag.y());
|
||||||
|
m.setZ(mag.z());
|
||||||
|
|
||||||
|
m = q * m * q.conjugate();
|
||||||
|
result.setZ(-atan2(m.y(), m.x()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTMath::convertToVector(unsigned char *rawData, RTVector3& vec, RTFLOAT scale, bool bigEndian)
|
||||||
|
{
|
||||||
|
if (bigEndian) {
|
||||||
|
vec.setX((RTFLOAT)((int16_t)(((uint16_t)rawData[0] << 8) | (uint16_t)rawData[1])) * scale);
|
||||||
|
vec.setY((RTFLOAT)((int16_t)(((uint16_t)rawData[2] << 8) | (uint16_t)rawData[3])) * scale);
|
||||||
|
vec.setZ((RTFLOAT)((int16_t)(((uint16_t)rawData[4] << 8) | (uint16_t)rawData[5])) * scale);
|
||||||
|
} else {
|
||||||
|
vec.setX((RTFLOAT)((int16_t)(((uint16_t)rawData[1] << 8) | (uint16_t)rawData[0])) * scale);
|
||||||
|
vec.setY((RTFLOAT)((int16_t)(((uint16_t)rawData[3] << 8) | (uint16_t)rawData[2])) * scale);
|
||||||
|
vec.setZ((RTFLOAT)((int16_t)(((uint16_t)rawData[5] << 8) | (uint16_t)rawData[4])) * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifndef RTARDULINK_MODE
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
//
|
||||||
|
// The RTVector3 class
|
||||||
|
|
||||||
|
RTVector3::RTVector3()
|
||||||
|
{
|
||||||
|
zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
RTVector3::RTVector3(RTFLOAT x, RTFLOAT y, RTFLOAT z)
|
||||||
|
{
|
||||||
|
m_data[0] = x;
|
||||||
|
m_data[1] = y;
|
||||||
|
m_data[2] = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTVector3& RTVector3::operator =(const RTVector3& vec)
|
||||||
|
{
|
||||||
|
if (this == &vec)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
m_data[0] = vec.m_data[0];
|
||||||
|
m_data[1] = vec.m_data[1];
|
||||||
|
m_data[2] = vec.m_data[2];
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const RTVector3& RTVector3::operator +=(RTVector3& vec)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
m_data[i] += vec.m_data[i];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RTVector3& RTVector3::operator -=(RTVector3& vec)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
m_data[i] -= vec.m_data[i];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTVector3::zero()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
m_data[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
RTFLOAT RTVector3::dotProduct(const RTVector3& a, const RTVector3& b)
|
||||||
|
{
|
||||||
|
return a.x() * b.x() + a.y() * b.y() + a.z() * b.z();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTVector3::crossProduct(const RTVector3& a, const RTVector3& b, RTVector3& d)
|
||||||
|
{
|
||||||
|
d.setX(a.y() * b.z() - a.z() * b.y());
|
||||||
|
d.setY(a.z() * b.x() - a.x() * b.z());
|
||||||
|
d.setZ(a.x() * b.y() - a.y() * b.x());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTVector3::accelToEuler(RTVector3& rollPitchYaw) const
|
||||||
|
{
|
||||||
|
RTVector3 normAccel = *this;
|
||||||
|
|
||||||
|
normAccel.normalize();
|
||||||
|
|
||||||
|
rollPitchYaw.setX(atan2(normAccel.y(), normAccel.z()));
|
||||||
|
rollPitchYaw.setY(-atan2(normAccel.x(), sqrt(normAccel.y() * normAccel.y() + normAccel.z() * normAccel.z())));
|
||||||
|
rollPitchYaw.setZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTVector3::accelToQuaternion(RTQuaternion& qPose) const
|
||||||
|
{
|
||||||
|
RTVector3 normAccel = *this;
|
||||||
|
RTVector3 vec;
|
||||||
|
RTVector3 z(0, 0, 1.0);
|
||||||
|
|
||||||
|
normAccel.normalize();
|
||||||
|
|
||||||
|
RTFLOAT angle = acos(RTVector3::dotProduct(z, normAccel));
|
||||||
|
RTVector3::crossProduct(normAccel, z, vec);
|
||||||
|
vec.normalize();
|
||||||
|
|
||||||
|
qPose.fromAngleVector(angle, vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTVector3::normalize()
|
||||||
|
{
|
||||||
|
RTFLOAT length = (RTFLOAT)sqrt(m_data[0] * m_data[0] + m_data[1] * m_data[1] +
|
||||||
|
m_data[2] * m_data[2]);
|
||||||
|
|
||||||
|
if ((length == 0) || (length == 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_data[0] /= length;
|
||||||
|
m_data[1] /= length;
|
||||||
|
m_data[2] /= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTFLOAT RTVector3::length()
|
||||||
|
{
|
||||||
|
return (RTFLOAT)sqrt(m_data[0] * m_data[0] + m_data[1] * m_data[1] +
|
||||||
|
m_data[2] * m_data[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifndef RTARDULINK_MODE
|
||||||
|
|
||||||
|
RTFLOAT RTVector3::squareLength()
|
||||||
|
{
|
||||||
|
return m_data[0] * m_data[0] + m_data[1] * m_data[1] +
|
||||||
|
m_data[2] * m_data[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
//----------------------------------------------------------
|
||||||
|
//
|
||||||
|
// The RTQuaternion class
|
||||||
|
|
||||||
|
RTQuaternion::RTQuaternion()
|
||||||
|
{
|
||||||
|
zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
RTQuaternion::RTQuaternion(RTFLOAT scalar, RTFLOAT x, RTFLOAT y, RTFLOAT z)
|
||||||
|
{
|
||||||
|
m_data[0] = scalar;
|
||||||
|
m_data[1] = x;
|
||||||
|
m_data[2] = y;
|
||||||
|
m_data[3] = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTQuaternion& RTQuaternion::operator =(const RTQuaternion& quat)
|
||||||
|
{
|
||||||
|
if (this == &quat)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
m_data[0] = quat.m_data[0];
|
||||||
|
m_data[1] = quat.m_data[1];
|
||||||
|
m_data[2] = quat.m_data[2];
|
||||||
|
m_data[3] = quat.m_data[3];
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTQuaternion& RTQuaternion::operator +=(const RTQuaternion& quat)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
m_data[i] += quat.m_data[i];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTQuaternion& RTQuaternion::operator -=(const RTQuaternion& quat)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
m_data[i] -= quat.m_data[i];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTQuaternion& RTQuaternion::operator -=(const RTFLOAT val)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
m_data[i] -= val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTQuaternion& RTQuaternion::operator *=(const RTQuaternion& qb)
|
||||||
|
{
|
||||||
|
RTQuaternion qa;
|
||||||
|
|
||||||
|
qa = *this;
|
||||||
|
|
||||||
|
m_data[0] = qa.scalar() * qb.scalar() - qa.x() * qb.x() - qa.y() * qb.y() - qa.z() * qb.z();
|
||||||
|
m_data[1] = qa.scalar() * qb.x() + qa.x() * qb.scalar() + qa.y() * qb.z() - qa.z() * qb.y();
|
||||||
|
m_data[2] = qa.scalar() * qb.y() - qa.x() * qb.z() + qa.y() * qb.scalar() + qa.z() * qb.x();
|
||||||
|
m_data[3] = qa.scalar() * qb.z() + qa.x() * qb.y() - qa.y() * qb.x() + qa.z() * qb.scalar();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RTQuaternion& RTQuaternion::operator *=(const RTFLOAT val)
|
||||||
|
{
|
||||||
|
m_data[0] *= val;
|
||||||
|
m_data[1] *= val;
|
||||||
|
m_data[2] *= val;
|
||||||
|
m_data[3] *= val;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const RTQuaternion RTQuaternion::operator *(const RTQuaternion& qb) const
|
||||||
|
{
|
||||||
|
RTQuaternion result = *this;
|
||||||
|
result *= qb;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RTQuaternion RTQuaternion::operator *(const RTFLOAT val) const
|
||||||
|
{
|
||||||
|
RTQuaternion result = *this;
|
||||||
|
result *= val;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const RTQuaternion RTQuaternion::operator -(const RTQuaternion& qb) const
|
||||||
|
{
|
||||||
|
RTQuaternion result = *this;
|
||||||
|
result -= qb;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RTQuaternion RTQuaternion::operator -(const RTFLOAT val) const
|
||||||
|
{
|
||||||
|
RTQuaternion result = *this;
|
||||||
|
result -= val;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTQuaternion::zero()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
m_data[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTQuaternion::normalize()
|
||||||
|
{
|
||||||
|
RTFLOAT length = sqrt(m_data[0] * m_data[0] + m_data[1] * m_data[1] +
|
||||||
|
m_data[2] * m_data[2] + m_data[3] * m_data[3]);
|
||||||
|
|
||||||
|
if ((length == 0) || (length == 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_data[0] /= length;
|
||||||
|
m_data[1] /= length;
|
||||||
|
m_data[2] /= length;
|
||||||
|
m_data[3] /= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTQuaternion::toEuler(RTVector3& vec)
|
||||||
|
{
|
||||||
|
vec.setX(atan2(2.0 * (m_data[2] * m_data[3] + m_data[0] * m_data[1]),
|
||||||
|
1 - 2.0 * (m_data[1] * m_data[1] + m_data[2] * m_data[2])));
|
||||||
|
|
||||||
|
vec.setY(asin(2.0 * (m_data[0] * m_data[2] - m_data[1] * m_data[3])));
|
||||||
|
|
||||||
|
vec.setZ(atan2(2.0 * (m_data[1] * m_data[2] + m_data[0] * m_data[3]),
|
||||||
|
1 - 2.0 * (m_data[2] * m_data[2] + m_data[3] * m_data[3])));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTQuaternion::fromEuler(RTVector3& vec)
|
||||||
|
{
|
||||||
|
RTFLOAT cosX2 = cos(vec.x() / 2.0f);
|
||||||
|
RTFLOAT sinX2 = sin(vec.x() / 2.0f);
|
||||||
|
RTFLOAT cosY2 = cos(vec.y() / 2.0f);
|
||||||
|
RTFLOAT sinY2 = sin(vec.y() / 2.0f);
|
||||||
|
RTFLOAT cosZ2 = cos(vec.z() / 2.0f);
|
||||||
|
RTFLOAT sinZ2 = sin(vec.z() / 2.0f);
|
||||||
|
|
||||||
|
m_data[0] = cosX2 * cosY2 * cosZ2 + sinX2 * sinY2 * sinZ2;
|
||||||
|
m_data[1] = sinX2 * cosY2 * cosZ2 - cosX2 * sinY2 * sinZ2;
|
||||||
|
m_data[2] = cosX2 * sinY2 * cosZ2 + sinX2 * cosY2 * sinZ2;
|
||||||
|
m_data[3] = cosX2 * cosY2 * sinZ2 - sinX2 * sinY2 * cosZ2;
|
||||||
|
normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
RTQuaternion RTQuaternion::conjugate() const
|
||||||
|
{
|
||||||
|
RTQuaternion q;
|
||||||
|
q.setScalar(m_data[0]);
|
||||||
|
q.setX(-m_data[1]);
|
||||||
|
q.setY(-m_data[2]);
|
||||||
|
q.setZ(-m_data[3]);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTQuaternion::toAngleVector(RTFLOAT& angle, RTVector3& vec)
|
||||||
|
{
|
||||||
|
RTFLOAT halfTheta;
|
||||||
|
RTFLOAT sinHalfTheta;
|
||||||
|
|
||||||
|
halfTheta = acos(m_data[0]);
|
||||||
|
sinHalfTheta = sin(halfTheta);
|
||||||
|
|
||||||
|
if (sinHalfTheta == 0) {
|
||||||
|
vec.setX(1.0);
|
||||||
|
vec.setY(0);
|
||||||
|
vec.setZ(0);
|
||||||
|
} else {
|
||||||
|
vec.setX(m_data[1] / sinHalfTheta);
|
||||||
|
vec.setY(m_data[1] / sinHalfTheta);
|
||||||
|
vec.setZ(m_data[1] / sinHalfTheta);
|
||||||
|
}
|
||||||
|
angle = 2.0 * halfTheta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTQuaternion::fromAngleVector(const RTFLOAT& angle, const RTVector3& vec)
|
||||||
|
{
|
||||||
|
RTFLOAT sinHalfTheta = sin(angle / 2.0);
|
||||||
|
m_data[0] = cos(angle / 2.0);
|
||||||
|
m_data[1] = vec.x() * sinHalfTheta;
|
||||||
|
m_data[2] = vec.y() * sinHalfTheta;
|
||||||
|
m_data[3] = vec.z() * sinHalfTheta;
|
||||||
|
}
|
||||||
|
#endif // #ifndef RTARDULINK_MODE
|
|
@ -0,0 +1,161 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTMATH_H_
|
||||||
|
#define _RTMATH_H_
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// The fundamental float type
|
||||||
|
|
||||||
|
#ifdef RTMATH_USE_DOUBLE
|
||||||
|
typedef double RTFLOAT;
|
||||||
|
#else
|
||||||
|
typedef float RTFLOAT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Useful constants
|
||||||
|
|
||||||
|
#define RTMATH_PI 3.1415926535
|
||||||
|
#define RTMATH_DEGREE_TO_RAD (M_PI / 180.0)
|
||||||
|
#define RTMATH_RAD_TO_DEGREE (180.0 / M_PI)
|
||||||
|
|
||||||
|
class RTVector3;
|
||||||
|
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
class RTQuaternion;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class RTMath
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
// convenient display routines
|
||||||
|
|
||||||
|
static void display(const char *label, RTVector3& vec);
|
||||||
|
static void displayDegrees(const char *label, RTVector3& vec);
|
||||||
|
static void displayRollPitchYaw(const char *label, RTVector3& vec);
|
||||||
|
static void display(const char *label, RTQuaternion& quat);
|
||||||
|
|
||||||
|
// poseFromAccelMag generates pose Euler angles from measured settings
|
||||||
|
|
||||||
|
static RTVector3 poseFromAccelMag(const RTVector3& accel, const RTVector3& mag);
|
||||||
|
|
||||||
|
// Takes signed 16 bit data from a char array and converts it to a vector of scaled RTFLOATs
|
||||||
|
|
||||||
|
static void convertToVector(unsigned char *rawData, RTVector3& vec, RTFLOAT scale, bool bigEndian);
|
||||||
|
|
||||||
|
#endif // #ifndef RTARDULINK_MODE
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RTVector3
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTVector3();
|
||||||
|
RTVector3(RTFLOAT x, RTFLOAT y, RTFLOAT z);
|
||||||
|
|
||||||
|
const RTVector3& operator +=(RTVector3& vec);
|
||||||
|
const RTVector3& operator -=(RTVector3& vec);
|
||||||
|
|
||||||
|
RTVector3& operator =(const RTVector3& vec);
|
||||||
|
|
||||||
|
RTFLOAT squareLength();
|
||||||
|
void zero();
|
||||||
|
|
||||||
|
inline RTFLOAT x() const { return m_data[0]; }
|
||||||
|
inline RTFLOAT y() const { return m_data[1]; }
|
||||||
|
inline RTFLOAT z() const { return m_data[2]; }
|
||||||
|
inline RTFLOAT data(const int i) const { return m_data[i]; }
|
||||||
|
|
||||||
|
inline void setX(const RTFLOAT val) { m_data[0] = val; }
|
||||||
|
inline void setY(const RTFLOAT val) { m_data[1] = val; }
|
||||||
|
inline void setZ(const RTFLOAT val) { m_data[2] = val; }
|
||||||
|
inline void setData(const int i, RTFLOAT val) { m_data[i] = val; }
|
||||||
|
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
RTFLOAT length();
|
||||||
|
void normalize();
|
||||||
|
|
||||||
|
const char *display();
|
||||||
|
const char *displayDegrees();
|
||||||
|
|
||||||
|
static RTFLOAT dotProduct(const RTVector3& a, const RTVector3& b);
|
||||||
|
static void crossProduct(const RTVector3& a, const RTVector3& b, RTVector3& d);
|
||||||
|
|
||||||
|
void accelToEuler(RTVector3& rollPitchYaw) const;
|
||||||
|
void accelToQuaternion(RTQuaternion& qPose) const;
|
||||||
|
#endif // #ifndef RTARDULINK_MODE
|
||||||
|
|
||||||
|
private:
|
||||||
|
RTFLOAT m_data[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef RTARDULINK_MODE
|
||||||
|
class RTQuaternion
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTQuaternion();
|
||||||
|
RTQuaternion(RTFLOAT scalar, RTFLOAT x, RTFLOAT y, RTFLOAT z);
|
||||||
|
|
||||||
|
RTQuaternion& operator +=(const RTQuaternion& quat);
|
||||||
|
RTQuaternion& operator -=(const RTQuaternion& quat);
|
||||||
|
RTQuaternion& operator *=(const RTQuaternion& qb);
|
||||||
|
RTQuaternion& operator *=(const RTFLOAT val);
|
||||||
|
RTQuaternion& operator -=(const RTFLOAT val);
|
||||||
|
|
||||||
|
RTQuaternion& operator =(const RTQuaternion& quat);
|
||||||
|
const RTQuaternion operator *(const RTQuaternion& qb) const;
|
||||||
|
const RTQuaternion operator *(const RTFLOAT val) const;
|
||||||
|
const RTQuaternion operator -(const RTQuaternion& qb) const;
|
||||||
|
const RTQuaternion operator -(const RTFLOAT val) const;
|
||||||
|
|
||||||
|
void normalize();
|
||||||
|
void toEuler(RTVector3& vec);
|
||||||
|
void fromEuler(RTVector3& vec);
|
||||||
|
RTQuaternion conjugate() const;
|
||||||
|
void toAngleVector(RTFLOAT& angle, RTVector3& vec);
|
||||||
|
void fromAngleVector(const RTFLOAT& angle, const RTVector3& vec);
|
||||||
|
|
||||||
|
void zero();
|
||||||
|
const char *display();
|
||||||
|
|
||||||
|
inline RTFLOAT scalar() const { return m_data[0]; }
|
||||||
|
inline RTFLOAT x() const { return m_data[1]; }
|
||||||
|
inline RTFLOAT y() const { return m_data[2]; }
|
||||||
|
inline RTFLOAT z() const { return m_data[3]; }
|
||||||
|
inline RTFLOAT data(const int i) const { return m_data[i]; }
|
||||||
|
|
||||||
|
inline void setScalar(const RTFLOAT val) { m_data[0] = val; }
|
||||||
|
inline void setX(const RTFLOAT val) { m_data[1] = val; }
|
||||||
|
inline void setY(const RTFLOAT val) { m_data[2] = val; }
|
||||||
|
inline void setZ(const RTFLOAT val) { m_data[3] = val; }
|
||||||
|
inline void setData(const int i, RTFLOAT val) { m_data[i] = val; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RTFLOAT m_data[4];
|
||||||
|
};
|
||||||
|
#endif // #ifndef RTARDULINK_MODE
|
||||||
|
|
||||||
|
#endif /* _RTMATH_H_ */
|
|
@ -0,0 +1,59 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
#include "RTPressure.h"
|
||||||
|
|
||||||
|
#if defined(BMP180)
|
||||||
|
#include "RTPressureBMP180.h"
|
||||||
|
#endif
|
||||||
|
#if defined(LPS25H_5c) || defined(LPS25H_5d)
|
||||||
|
#include "RTPressureLPS25H.h"
|
||||||
|
#endif
|
||||||
|
#if defined(MS5611_76) || defined(MS5611_77)
|
||||||
|
#include "RTPressureMS5611.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RTPressure *RTPressure::createPressure(RTIMUSettings *settings)
|
||||||
|
{
|
||||||
|
#if defined(BMP180)
|
||||||
|
return new RTPressureBMP180(settings);
|
||||||
|
#endif
|
||||||
|
#if defined(LPS25H_5c) || defined(LPS25H_5d)
|
||||||
|
return new RTPressureLPS25H(settings);
|
||||||
|
#endif
|
||||||
|
#if defined(MS5611_76) || defined(MS5611_77)
|
||||||
|
return new RTPressureMS5611(settings);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RTPressure::RTPressure(RTIMUSettings *settings)
|
||||||
|
{
|
||||||
|
m_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTPressure::~RTPressure()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTPRESSURE_H
|
||||||
|
#define _RTPRESSURE_H
|
||||||
|
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
#include "RTIMULibDefs.h"
|
||||||
|
#include "RTPressureDefs.h"
|
||||||
|
#include "I2Cdev.h"
|
||||||
|
|
||||||
|
class RTPressure
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Pressure sensor objects should always be created with the following call
|
||||||
|
|
||||||
|
static RTPressure *createPressure(RTIMUSettings *settings);
|
||||||
|
|
||||||
|
// Constructor/destructor
|
||||||
|
|
||||||
|
RTPressure(RTIMUSettings *settings);
|
||||||
|
virtual ~RTPressure();
|
||||||
|
|
||||||
|
// These functions must be provided by sub classes
|
||||||
|
|
||||||
|
virtual const char *pressureName() = 0; // the name of the pressure sensor
|
||||||
|
virtual int pressureType() = 0; // the type code of the pressure sensor
|
||||||
|
virtual bool pressureInit() = 0; // set up the pressure sensor
|
||||||
|
virtual bool pressureRead(float &latestPressure, float &latestTemperature) = 0; // get latest value
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RTIMUSettings *m_settings; // the settings object pointer
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTPRESSURE_H
|
|
@ -0,0 +1,227 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTPressureBMP180.h"
|
||||||
|
|
||||||
|
#if defined(BMP180)
|
||||||
|
|
||||||
|
RTPressureBMP180::RTPressureBMP180(RTIMUSettings *settings) : RTPressure(settings)
|
||||||
|
{
|
||||||
|
m_validReadings = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTPressureBMP180::~RTPressureBMP180()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTPressureBMP180::pressureInit()
|
||||||
|
{
|
||||||
|
unsigned char result;
|
||||||
|
unsigned char data[22];
|
||||||
|
|
||||||
|
m_pressureAddr = m_settings->m_I2CPressureAddress;
|
||||||
|
|
||||||
|
// check ID of chip
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, BMP180_REG_ID, 1, &result))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (result != BMP180_ID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get calibration data
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, BMP180_REG_AC1, 22, data))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_AC1 = (int16_t)(((uint16_t)data[0]) << 8) + (uint16_t)data[1];
|
||||||
|
m_AC2 = (int16_t)(((uint16_t)data[2]) << 8) + (uint16_t)data[3];
|
||||||
|
m_AC3 = (int16_t)(((uint16_t)data[4]) << 8) + (uint16_t)data[4];
|
||||||
|
m_AC4 = (((uint16_t)data[6]) << 8) + (uint16_t)data[7];
|
||||||
|
m_AC5 = (((uint16_t)data[8]) << 8) + (uint16_t)data[9];
|
||||||
|
m_AC6 = (((uint16_t)data[10]) << 8) + (uint16_t)data[11];
|
||||||
|
m_B1 = (int16_t)(((uint16_t)data[12]) << 8) + (uint16_t)data[13];
|
||||||
|
m_B2 = (int16_t)(((uint16_t)data[14]) << 8) + (uint16_t)data[15];
|
||||||
|
m_MB = (int16_t)(((uint16_t)data[16]) << 8) + (uint16_t)data[17];
|
||||||
|
m_MC = (int16_t)(((uint16_t)data[18]) << 8) + (uint16_t)data[19];
|
||||||
|
m_MD = (int16_t)(((uint16_t)data[20]) << 8) + (uint16_t)data[21];
|
||||||
|
|
||||||
|
m_state = BMP180_STATE_IDLE;
|
||||||
|
m_oss = BMP180_SCO_PRESSURECONV_UHR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTPressureBMP180::pressureRead(float &latestPressure, float &latestTemperature)
|
||||||
|
{
|
||||||
|
latestPressure = 0;
|
||||||
|
latestTemperature = 0;
|
||||||
|
|
||||||
|
if (m_state == BMP180_STATE_IDLE) {
|
||||||
|
// start a temperature conversion
|
||||||
|
if (!I2Cdev::writeByte(m_pressureAddr, BMP180_REG_SCO, BMP180_SCO_TEMPCONV)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
m_state = BMP180_STATE_TEMPERATURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pressureBackground();
|
||||||
|
|
||||||
|
if (m_validReadings) {
|
||||||
|
latestTemperature = m_temperature;
|
||||||
|
latestPressure = m_pressure;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTPressureBMP180::pressureBackground()
|
||||||
|
{
|
||||||
|
uint8_t data[2];
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
case BMP180_STATE_IDLE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BMP180_STATE_TEMPERATURE:
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, BMP180_REG_SCO, 1, data)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((data[0] & 0x20) == 0x20)
|
||||||
|
break; // conversion not finished
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, BMP180_REG_RESULT, 2, data)) {
|
||||||
|
m_state = BMP180_STATE_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_rawTemperature = (((uint16_t)data[0]) << 8) | (uint16_t)data[1];
|
||||||
|
|
||||||
|
data[0] = 0x34 + (m_oss << 6);
|
||||||
|
if (!I2Cdev::writeBytes(m_pressureAddr, BMP180_REG_SCO, 1, data)) {
|
||||||
|
m_state = BMP180_STATE_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_state = BMP180_STATE_PRESSURE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BMP180_STATE_PRESSURE:
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, BMP180_REG_SCO, 1, data)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((data[0] & 0x20) == 0x20)
|
||||||
|
break; // conversion not finished
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, BMP180_REG_RESULT, 2, data)) {
|
||||||
|
m_state = BMP180_STATE_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_rawPressure = (((uint16_t)data[0]) << 8) | (uint16_t)data[1];
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, BMP180_REG_XLSB, 1, data)) {
|
||||||
|
m_state = BMP180_STATE_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call this function for testing only
|
||||||
|
// should give T = 150 (15.0C) and pressure 6996 (699.6hPa)
|
||||||
|
|
||||||
|
// setTestData();
|
||||||
|
|
||||||
|
int32_t pressure = ((((uint32_t)(m_rawPressure)) << 8) + (uint32_t)(data[0])) >> (8 - m_oss);
|
||||||
|
|
||||||
|
m_state = BMP180_STATE_IDLE;
|
||||||
|
|
||||||
|
// calculate compensated temperature
|
||||||
|
|
||||||
|
int32_t X1 = ((m_rawTemperature - (int32_t)m_AC6) * (int32_t)m_AC5) / 32768;
|
||||||
|
|
||||||
|
if ((X1 + m_MD) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t X2 = ((int32_t)m_MC * 2048) / (X1 + (int32_t)m_MD);
|
||||||
|
int32_t B5 = X1 + X2;
|
||||||
|
m_temperature = (RTFLOAT)((B5 + 8) / 16) / (RTFLOAT)10;
|
||||||
|
|
||||||
|
// calculate compensated pressure
|
||||||
|
|
||||||
|
int32_t B6 = B5 - 4000;
|
||||||
|
// printf("B6 = %d\n", B6);
|
||||||
|
X1 = (m_B2 * ((B6 * B6) / 4096)) / 2048;
|
||||||
|
// printf("X1 = %d\n", X1);
|
||||||
|
X2 = (m_AC2 * B6) / 2048;
|
||||||
|
// printf("X2 = %d\n", X2);
|
||||||
|
int32_t X3 = X1 + X2;
|
||||||
|
// printf("X3 = %d\n", X3);
|
||||||
|
int32_t B3 = (((m_AC1 * 4 + X3) << m_oss) + 2) / 4;
|
||||||
|
// printf("B3 = %d\n", B3);
|
||||||
|
X1 = (m_AC3 * B6) / 8192;
|
||||||
|
// printf("X1 = %d\n", X1);
|
||||||
|
X2 = (m_B1 * ((B6 * B6) / 4096)) / 65536;
|
||||||
|
// printf("X2 = %d\n", X2);
|
||||||
|
X3 = ((X1 + X2) + 2) / 4;
|
||||||
|
// printf("X3 = %d\n", X3);
|
||||||
|
uint32_t B4 = (m_AC4 * (uint32_t)(X3 + 32768)) / 32768;
|
||||||
|
// printf("B4 = %d\n", B4);
|
||||||
|
uint32_t B7 = (uint32_t)(pressure - B3) * (50000 >> m_oss);
|
||||||
|
// printf("B7 = %d\n", B7);
|
||||||
|
|
||||||
|
int32_t p;
|
||||||
|
if (B7 < 0x80000000)
|
||||||
|
p = (B7 * 2) / B4;
|
||||||
|
else
|
||||||
|
p = (B7 / B4) * 2;
|
||||||
|
|
||||||
|
// printf("p = %d\n", p);
|
||||||
|
X1 = (p / 256) * (p / 256);
|
||||||
|
// printf("X1 = %d\n", X1);
|
||||||
|
X1 = (X1 * 3038) / 65536;
|
||||||
|
// printf("X1 = %d\n", X1);
|
||||||
|
X2 = (-7357 * p) / 65536;
|
||||||
|
// printf("X2 = %d\n", X2);
|
||||||
|
m_pressure = (RTFLOAT)(p + (X1 + X2 + 3791) / 16) / (RTFLOAT)100; // the extra 100 factor is to get 1hPa units
|
||||||
|
|
||||||
|
m_validReadings = true;
|
||||||
|
|
||||||
|
// printf("UP = %d, P = %f, UT = %d, T = %f\n", m_rawPressure, m_pressure, m_rawTemperature, m_temperature);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTPressureBMP180::setTestData()
|
||||||
|
{
|
||||||
|
m_AC1 = 408;
|
||||||
|
m_AC2 = -72;
|
||||||
|
m_AC3 = -14383;
|
||||||
|
m_AC4 = 32741;
|
||||||
|
m_AC5 = 32757;
|
||||||
|
m_AC6 = 23153;
|
||||||
|
m_B1 = 6190;
|
||||||
|
m_B2 = 4;
|
||||||
|
m_MB = -32767;
|
||||||
|
m_MC = -8711;
|
||||||
|
m_MD = 2868;
|
||||||
|
|
||||||
|
m_rawTemperature = 27898;
|
||||||
|
m_rawPressure = 23843;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,88 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTPRESSUREBMP180_H_
|
||||||
|
#define _RTPRESSUREBMP180_H_
|
||||||
|
|
||||||
|
#include "RTPressure.h"
|
||||||
|
|
||||||
|
// State definitions
|
||||||
|
|
||||||
|
#define BMP180_STATE_IDLE 0
|
||||||
|
#define BMP180_STATE_TEMPERATURE 1
|
||||||
|
#define BMP180_STATE_PRESSURE 2
|
||||||
|
|
||||||
|
// Conversion reg defs
|
||||||
|
|
||||||
|
#define BMP180_SCO_TEMPCONV 0x2e // temperature conversion
|
||||||
|
#define BMP180_SCO_PRESSURECONV_ULP 0 // ultra low power pressure conversion
|
||||||
|
#define BMP180_SCO_PRESSURECONV_STD 1 // standard pressure conversion
|
||||||
|
#define BMP180_SCO_PRESSURECONV_HR 2 // high res pressure conversion
|
||||||
|
#define BMP180_SCO_PRESSURECONV_UHR 3 // ultra high res pressure conversion
|
||||||
|
|
||||||
|
class RTIMUSettings;
|
||||||
|
|
||||||
|
class RTPressureBMP180 : public RTPressure
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTPressureBMP180(RTIMUSettings *settings);
|
||||||
|
~RTPressureBMP180();
|
||||||
|
|
||||||
|
virtual const char *pressureName() { return "BMP180"; }
|
||||||
|
virtual int pressureType() { return RTPRESSURE_TYPE_BMP180; }
|
||||||
|
virtual bool pressureInit();
|
||||||
|
virtual bool pressureRead(float &latestPressure, float &latestTemperature);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void pressureBackground();
|
||||||
|
void setTestData();
|
||||||
|
|
||||||
|
unsigned char m_pressureAddr; // I2C address
|
||||||
|
RTFLOAT m_pressure; // the current pressure
|
||||||
|
RTFLOAT m_temperature; // the current temperature
|
||||||
|
|
||||||
|
// This is the calibration data read from the sensor
|
||||||
|
|
||||||
|
int16_t m_AC1;
|
||||||
|
int16_t m_AC2;
|
||||||
|
int16_t m_AC3;
|
||||||
|
uint16_t m_AC4;
|
||||||
|
uint16_t m_AC5;
|
||||||
|
uint16_t m_AC6;
|
||||||
|
int16_t m_B1;
|
||||||
|
int16_t m_B2;
|
||||||
|
int16_t m_MB;
|
||||||
|
int16_t m_MC;
|
||||||
|
int16_t m_MD;
|
||||||
|
|
||||||
|
int m_state;
|
||||||
|
int m_oss;
|
||||||
|
|
||||||
|
int32_t m_rawPressure;
|
||||||
|
int32_t m_rawTemperature;
|
||||||
|
|
||||||
|
bool m_validReadings;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTPRESSUREBMP180_H_
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTPRESSUREDEFS_H
|
||||||
|
#define _RTPRESSUREDEFS_H
|
||||||
|
|
||||||
|
// Pressure sensor type codes
|
||||||
|
|
||||||
|
#define RTPRESSURE_TYPE_AUTODISCOVER 0 // audodiscover the pressure sensor
|
||||||
|
#define RTPRESSURE_TYPE_NULL 1 // if no physical hardware
|
||||||
|
#define RTPRESSURE_TYPE_BMP180 2 // BMP180
|
||||||
|
#define RTPRESSURE_TYPE_LPS25H 3 // LPS25H
|
||||||
|
#define RTPRESSURE_TYPE_MS5611 4 // MS5611
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
//
|
||||||
|
// BMP180
|
||||||
|
|
||||||
|
// BMP180 I2C Slave Addresse
|
||||||
|
|
||||||
|
#define BMP180_ADDRESS 0x77
|
||||||
|
#define BMP180_REG_ID 0xd0
|
||||||
|
#define BMP180_ID 0x55
|
||||||
|
|
||||||
|
// Register map
|
||||||
|
|
||||||
|
#define BMP180_REG_AC1 0xaa
|
||||||
|
#define BMP180_REG_SCO 0xf4
|
||||||
|
#define BMP180_REG_RESULT 0xf6
|
||||||
|
#define BMP180_REG_XLSB 0xf8
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
//
|
||||||
|
// LPS25H
|
||||||
|
|
||||||
|
// LPS25H I2C Slave Addresse
|
||||||
|
|
||||||
|
#define LPS25H_ADDRESS0 0x5c
|
||||||
|
#define LPS25H_ADDRESS1 0x5d
|
||||||
|
#define LPS25H_REG_ID 0x0f
|
||||||
|
#define LPS25H_ID 0xbd
|
||||||
|
|
||||||
|
// Register map
|
||||||
|
|
||||||
|
#define LPS25H_REF_P_XL 0x08
|
||||||
|
#define LPS25H_REF_P_XH 0x09
|
||||||
|
#define LPS25H_RES_CONF 0x10
|
||||||
|
#define LPS25H_CTRL_REG_1 0x20
|
||||||
|
#define LPS25H_CTRL_REG_2 0x21
|
||||||
|
#define LPS25H_CTRL_REG_3 0x22
|
||||||
|
#define LPS25H_CTRL_REG_4 0x23
|
||||||
|
#define LPS25H_INT_CFG 0x24
|
||||||
|
#define LPS25H_INT_SOURCE 0x25
|
||||||
|
#define LPS25H_STATUS_REG 0x27
|
||||||
|
#define LPS25H_PRESS_OUT_XL 0x28
|
||||||
|
#define LPS25H_PRESS_OUT_L 0x29
|
||||||
|
#define LPS25H_PRESS_OUT_H 0x2a
|
||||||
|
#define LPS25H_TEMP_OUT_L 0x2b
|
||||||
|
#define LPS25H_TEMP_OUT_H 0x2c
|
||||||
|
#define LPS25H_FIFO_CTRL 0x2e
|
||||||
|
#define LPS25H_FIFO_STATUS 0x2f
|
||||||
|
#define LPS25H_THS_P_L 0x30
|
||||||
|
#define LPS25H_THS_P_H 0x31
|
||||||
|
#define LPS25H_RPDS_L 0x39
|
||||||
|
#define LPS25H_RPDS_H 0x3a
|
||||||
|
|
||||||
|
//----------------------------------------------------------
|
||||||
|
//
|
||||||
|
// MS5611
|
||||||
|
|
||||||
|
// MS5611 I2C Slave Addresses
|
||||||
|
|
||||||
|
#define MS5611_ADDRESS0 0x76
|
||||||
|
#define MS5611_ADDRESS1 0x77
|
||||||
|
|
||||||
|
// commands
|
||||||
|
|
||||||
|
#define MS5611_CMD_RESET 0x1e
|
||||||
|
#define MS5611_CMD_CONV_D1 0x48
|
||||||
|
#define MS5611_CMD_CONV_D2 0x58
|
||||||
|
#define MS5611_CMD_PROM 0xa0
|
||||||
|
#define MS5611_CMD_ADC 0x00
|
||||||
|
|
||||||
|
#endif // _RTPRESSUREDEFS_H
|
|
@ -0,0 +1,90 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTPressureLPS25H.h"
|
||||||
|
#include "RTPressureDefs.h"
|
||||||
|
|
||||||
|
#if defined(LPS25H_5c) || defined(LPS25H_5d)
|
||||||
|
|
||||||
|
RTPressureLPS25H::RTPressureLPS25H(RTIMUSettings *settings) : RTPressure(settings)
|
||||||
|
{
|
||||||
|
m_pressureValid = false;
|
||||||
|
m_temperatureValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTPressureLPS25H::~RTPressureLPS25H()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTPressureLPS25H::pressureInit()
|
||||||
|
{
|
||||||
|
m_pressureAddr = m_settings->m_I2CPressureAddress;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_pressureAddr, LPS25H_CTRL_REG_1, 0xc4))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_pressureAddr, LPS25H_RES_CONF, 0x05))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_pressureAddr, LPS25H_FIFO_CTRL, 0xc0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!I2Cdev::writeByte(m_pressureAddr, LPS25H_CTRL_REG_2, 0x40))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RTPressureLPS25H::pressureRead(float &latestPressure, float &latestTemperature)
|
||||||
|
{
|
||||||
|
unsigned char rawData[3];
|
||||||
|
unsigned char status;
|
||||||
|
|
||||||
|
latestPressure = 0;
|
||||||
|
latestTemperature = 0;
|
||||||
|
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, LPS25H_STATUS_REG, 1, &status))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (status & 2) {
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, LPS25H_PRESS_OUT_XL + 0x80, 3, rawData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_pressure = (RTFLOAT)((((unsigned long)rawData[2]) << 16) | (((unsigned long)rawData[1]) << 8) | (unsigned long)rawData[0]) / (RTFLOAT)4096;
|
||||||
|
m_pressureValid = true;
|
||||||
|
}
|
||||||
|
if (status & 1) {
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, LPS25H_TEMP_OUT_L + 0x80, 2, rawData))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_temperature = (int16_t)((((unsigned int)rawData[1]) << 8) | (unsigned int)rawData[0]) / (RTFLOAT)480 + (RTFLOAT)42.5;
|
||||||
|
m_temperatureValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
latestPressure = m_pressure;
|
||||||
|
latestTemperature = m_temperature;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,53 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTPRESSURELPS25H_H_
|
||||||
|
#define _RTPRESSURELPS25H_H_
|
||||||
|
|
||||||
|
#include "RTPressure.h"
|
||||||
|
|
||||||
|
class RTIMUSettings;
|
||||||
|
|
||||||
|
class RTPressureLPS25H : public RTPressure
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTPressureLPS25H(RTIMUSettings *settings);
|
||||||
|
~RTPressureLPS25H();
|
||||||
|
|
||||||
|
virtual const char *pressureName() { return "LPS25H"; }
|
||||||
|
virtual int pressureType() { return RTPRESSURE_TYPE_LPS25H; }
|
||||||
|
virtual bool pressureInit();
|
||||||
|
virtual bool pressureRead(float &latestPressure, float &latestTemperature);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char m_pressureAddr; // I2C address
|
||||||
|
|
||||||
|
RTFLOAT m_pressure; // the current pressure
|
||||||
|
RTFLOAT m_temperature; // the current temperature
|
||||||
|
bool m_pressureValid;
|
||||||
|
bool m_temperatureValid;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTPRESSURELPS25H_H_
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#include "RTPressureMS5611.h"
|
||||||
|
|
||||||
|
RTPressureMS5611::RTPressureMS5611(RTIMUSettings *settings) : RTPressure(settings)
|
||||||
|
{
|
||||||
|
m_validReadings = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RTPressureMS5611::~RTPressureMS5611()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTPressureMS5611::pressureInit()
|
||||||
|
{
|
||||||
|
unsigned char cmd = MS5611_CMD_PROM + 2;
|
||||||
|
unsigned char data[2];
|
||||||
|
|
||||||
|
m_pressureAddr = m_settings->m_I2CPressureAddress;
|
||||||
|
|
||||||
|
// get calibration data
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, cmd, 2, data))
|
||||||
|
return false;
|
||||||
|
m_calData[i] = (((uint16_t)data[0]) << 8) + (uint16_t)data[1];
|
||||||
|
// printf("Cal index: %d, data: %d\n", i, m_calData[i]);
|
||||||
|
cmd += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_state = MS5611_STATE_IDLE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RTPressureMS5611::pressureRead(float &latestPressure, float &latestTemperature)
|
||||||
|
{
|
||||||
|
if (m_state == MS5611_STATE_IDLE) {
|
||||||
|
// start pressure conversion
|
||||||
|
if (!I2Cdev::writeBytes(m_pressureAddr, MS5611_CMD_CONV_D1, 0, 0)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
m_state = MS5611_STATE_PRESSURE;
|
||||||
|
m_timer = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pressureBackground();
|
||||||
|
|
||||||
|
if (m_validReadings) {
|
||||||
|
latestTemperature = m_temperature;
|
||||||
|
latestPressure = m_pressure;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RTPressureMS5611::pressureBackground()
|
||||||
|
{
|
||||||
|
uint8_t data[3];
|
||||||
|
|
||||||
|
switch (m_state) {
|
||||||
|
case MS5611_STATE_IDLE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MS5611_STATE_PRESSURE:
|
||||||
|
if ((millis() - m_timer) < 10)
|
||||||
|
break; // not time yet
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, MS5611_CMD_ADC, 3, data)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_D1 = (((uint32_t)data[0]) << 16) + (((uint32_t)data[1]) << 8) + (uint32_t)data[2];
|
||||||
|
|
||||||
|
// start temperature conversion
|
||||||
|
|
||||||
|
if (!I2Cdev::writeBytes(m_pressureAddr, MS5611_CMD_CONV_D2, 0, 0)) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
m_state = MS5611_STATE_TEMPERATURE;
|
||||||
|
m_timer = millis();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MS5611_STATE_TEMPERATURE:
|
||||||
|
if ((millis() - m_timer) < 10)
|
||||||
|
break; // not time yet
|
||||||
|
if (!I2Cdev::readBytes(m_pressureAddr, MS5611_CMD_ADC, 3, data)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_D2 = (((uint32_t)data[0]) << 16) + (((uint32_t)data[1]) << 8) + (uint32_t)data[2];
|
||||||
|
|
||||||
|
// call this function for testing only
|
||||||
|
// should give T = 2007 (20.07C) and pressure 100009 (1000.09hPa)
|
||||||
|
|
||||||
|
// setTestData();
|
||||||
|
|
||||||
|
// now calculate the real values
|
||||||
|
|
||||||
|
int64_t deltaT = (int32_t)m_D2 - (((int32_t)m_calData[4]) << 8);
|
||||||
|
|
||||||
|
int32_t temperature = 2000 + ((deltaT * (int64_t)m_calData[5]) >> 23); // note - this needs to be divided by 100
|
||||||
|
|
||||||
|
int64_t offset = ((int64_t)m_calData[1] << 16) + (((int64_t)m_calData[3] * deltaT) >> 7);
|
||||||
|
int64_t sens = ((int64_t)m_calData[0] << 15) + (((int64_t)m_calData[2] * deltaT) >> 8);
|
||||||
|
|
||||||
|
// do second order temperature compensation
|
||||||
|
|
||||||
|
if (temperature < 2000) {
|
||||||
|
int64_t T2 = (deltaT * deltaT) >> 31;
|
||||||
|
int64_t offset2 = 5 * ((temperature - 2000) * (temperature - 2000)) / 2;
|
||||||
|
int64_t sens2 = offset2 / 2;
|
||||||
|
if (temperature < -1500) {
|
||||||
|
offset2 += 7 * (temperature + 1500) * (temperature + 1500);
|
||||||
|
sens2 += 11 * ((temperature + 1500) * (temperature + 1500)) / 2;
|
||||||
|
}
|
||||||
|
temperature -= T2;
|
||||||
|
offset -= offset2;
|
||||||
|
sens -= sens2;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pressure = (RTFLOAT)(((((int64_t)m_D1 * sens) >> 21) - offset) >> 15) / (RTFLOAT)100.0;
|
||||||
|
m_temperature = (RTFLOAT)temperature / (RTFLOAT)100;
|
||||||
|
|
||||||
|
// printf("Temp: %f, pressure: %f\n", m_temperature, m_pressure);
|
||||||
|
|
||||||
|
m_validReadings = true;
|
||||||
|
m_state = MS5611_STATE_IDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RTPressureMS5611::setTestData()
|
||||||
|
{
|
||||||
|
m_calData[0] = 40127;
|
||||||
|
m_calData[1] = 36924;
|
||||||
|
m_calData[2] = 23317;
|
||||||
|
m_calData[3] = 23282;
|
||||||
|
m_calData[4] = 33464;
|
||||||
|
m_calData[5] = 28312;
|
||||||
|
|
||||||
|
m_D1 = 9085466;
|
||||||
|
m_D2 = 8569150;
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This file is part of RTIMULib-Arduino
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-2015, richards-tech
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
// Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef _RTPRESSUREMS5611_H_
|
||||||
|
#define _RTPRESSUREMS5611_H_
|
||||||
|
|
||||||
|
#include "RTPressure.h"
|
||||||
|
|
||||||
|
// State definitions
|
||||||
|
|
||||||
|
#define MS5611_STATE_IDLE 0
|
||||||
|
#define MS5611_STATE_TEMPERATURE 1
|
||||||
|
#define MS5611_STATE_PRESSURE 2
|
||||||
|
|
||||||
|
class RTIMUSettings;
|
||||||
|
|
||||||
|
class RTPressureMS5611 : public RTPressure
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RTPressureMS5611(RTIMUSettings *settings);
|
||||||
|
~RTPressureMS5611();
|
||||||
|
|
||||||
|
virtual const char *pressureName() { return "MS5611"; }
|
||||||
|
virtual int pressureType() { return RTPRESSURE_TYPE_MS5611; }
|
||||||
|
virtual bool pressureInit();
|
||||||
|
virtual bool pressureRead(float &latestPressure, float &latestTemperature);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void pressureBackground();
|
||||||
|
void setTestData();
|
||||||
|
|
||||||
|
unsigned char m_pressureAddr; // I2C address
|
||||||
|
RTFLOAT m_pressure; // the current pressure
|
||||||
|
RTFLOAT m_temperature; // the current temperature
|
||||||
|
|
||||||
|
int m_state;
|
||||||
|
|
||||||
|
uint16_t m_calData[6]; // calibration data
|
||||||
|
|
||||||
|
uint32_t m_D1;
|
||||||
|
uint32_t m_D2;
|
||||||
|
|
||||||
|
long m_timer; // used to time conversions
|
||||||
|
|
||||||
|
bool m_validReadings;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _RTPRESSUREMS5611_H_
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
#include "SLIPEncodedBluetoothSerial.h"
|
||||||
|
#include "BluetoothSerial.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
CONSTRUCTOR
|
||||||
|
*/
|
||||||
|
//instantiate with the tranmission layer
|
||||||
|
//use BluetoothSerial
|
||||||
|
SLIPEncodedBluetoothSerial::SLIPEncodedBluetoothSerial(BluetoothSerial &s){
|
||||||
|
serial = &s;
|
||||||
|
rstate = CHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t eot = 0300;
|
||||||
|
static const uint8_t slipesc = 0333;
|
||||||
|
static const uint8_t slipescend = 0334;
|
||||||
|
static const uint8_t slipescesc = 0335;
|
||||||
|
/*
|
||||||
|
SERIAL METHODS
|
||||||
|
*/
|
||||||
|
bool SLIPEncodedBluetoothSerial::endofPacket()
|
||||||
|
{
|
||||||
|
if(rstate == SECONDEOT)
|
||||||
|
{
|
||||||
|
rstate = CHAR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (rstate==FIRSTEOT)
|
||||||
|
{
|
||||||
|
if(serial->available())
|
||||||
|
{
|
||||||
|
uint8_t c =serial->peek();
|
||||||
|
if(c==eot)
|
||||||
|
{
|
||||||
|
serial->read(); // throw it on the floor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rstate = CHAR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int SLIPEncodedBluetoothSerial::available(){
|
||||||
|
back:
|
||||||
|
int cnt = serial->available();
|
||||||
|
|
||||||
|
if(cnt==0)
|
||||||
|
return 0;
|
||||||
|
if(rstate==CHAR)
|
||||||
|
{
|
||||||
|
uint8_t c =serial->peek();
|
||||||
|
if(c==slipesc)
|
||||||
|
{
|
||||||
|
rstate = SLIPESC;
|
||||||
|
serial->read(); // throw it on the floor
|
||||||
|
goto back;
|
||||||
|
}
|
||||||
|
else if( c==eot)
|
||||||
|
{
|
||||||
|
rstate = FIRSTEOT;
|
||||||
|
serial->read(); // throw it on the floor
|
||||||
|
goto back;
|
||||||
|
}
|
||||||
|
return 1; // we may have more but this is the only sure bet
|
||||||
|
}
|
||||||
|
else if(rstate==SLIPESC)
|
||||||
|
return 1;
|
||||||
|
else if(rstate==FIRSTEOT)
|
||||||
|
{
|
||||||
|
if(serial->peek()==eot)
|
||||||
|
{
|
||||||
|
rstate = SECONDEOT;
|
||||||
|
serial->read(); // throw it on the floor
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rstate = CHAR;
|
||||||
|
}else if (rstate==SECONDEOT) {
|
||||||
|
rstate = CHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//reads a byte from the buffer
|
||||||
|
int SLIPEncodedBluetoothSerial::read(){
|
||||||
|
back:
|
||||||
|
uint8_t c = serial->read();
|
||||||
|
if(rstate==CHAR)
|
||||||
|
{
|
||||||
|
if(c==slipesc)
|
||||||
|
{
|
||||||
|
rstate=SLIPESC;
|
||||||
|
goto back;
|
||||||
|
}
|
||||||
|
else if(c==eot){
|
||||||
|
|
||||||
|
return -1; // xxx this is an error
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if(rstate==SLIPESC)
|
||||||
|
{
|
||||||
|
rstate=CHAR;
|
||||||
|
if(c==slipescend)
|
||||||
|
return eot;
|
||||||
|
else if(c==slipescesc)
|
||||||
|
return slipesc;
|
||||||
|
else {
|
||||||
|
// insert some error code here
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// as close as we can get to correct behavior
|
||||||
|
int SLIPEncodedBluetoothSerial::peek(){
|
||||||
|
uint8_t c = serial->peek();
|
||||||
|
if(rstate==SLIPESC)
|
||||||
|
{
|
||||||
|
if(c==slipescend)
|
||||||
|
return eot;
|
||||||
|
else if(c==slipescesc)
|
||||||
|
return slipesc;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
//the arduino and wiring libraries have different return types for the write function
|
||||||
|
#if defined(WIRING) || defined(BOARD_DEFS_H)
|
||||||
|
|
||||||
|
//encode SLIP
|
||||||
|
void SLIPEncodedBluetoothSerial::write(uint8_t b){
|
||||||
|
if(b == eot){
|
||||||
|
serial->write(slipesc);
|
||||||
|
return serial->write(slipescend);
|
||||||
|
} else if(b==slipesc) {
|
||||||
|
serial->write(slipesc);
|
||||||
|
return serial->write(slipescesc);
|
||||||
|
} else {
|
||||||
|
return serial->write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SLIPEncodedBluetoothSerial::write(const uint8_t *buffer, size_t size) { while(size--) write(*buffer++); }
|
||||||
|
#else
|
||||||
|
//encode SLIP
|
||||||
|
size_t SLIPEncodedBluetoothSerial::write(uint8_t b){
|
||||||
|
if(b == eot){
|
||||||
|
serial->write(slipesc);
|
||||||
|
return serial->write(slipescend);
|
||||||
|
} else if(b==slipesc) {
|
||||||
|
serial->write(slipesc);
|
||||||
|
return serial->write(slipescesc);
|
||||||
|
} else {
|
||||||
|
return serial->write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t SLIPEncodedBluetoothSerial::write(const uint8_t *buffer, size_t size) { size_t result=0; while(size--) result = write(*buffer++); return result; }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SLIPEncodedBluetoothSerial::begin(String name){
|
||||||
|
serial->begin(name);
|
||||||
|
}
|
||||||
|
//SLIP specific method which begins a transmitted packet
|
||||||
|
void SLIPEncodedBluetoothSerial::beginPacket() { serial->write(eot); }
|
||||||
|
|
||||||
|
//signify the end of the packet with an EOT
|
||||||
|
void SLIPEncodedBluetoothSerial::endPacket(){
|
||||||
|
serial->write(eot);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SLIPEncodedBluetoothSerial::flush(){
|
||||||
|
serial->flush();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Extends the Serial class to encode SLIP over serial
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SLIPEncodedBluetoothSerial_h
|
||||||
|
#define SLIPEncodedBluetoothSerial_h
|
||||||
|
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include <Stream.h>
|
||||||
|
#include "BluetoothSerial.h"
|
||||||
|
|
||||||
|
|
||||||
|
class SLIPEncodedBluetoothSerial: public Stream{
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum erstate {CHAR, FIRSTEOT, SECONDEOT, SLIPESC } rstate;
|
||||||
|
|
||||||
|
//the serial port used
|
||||||
|
BluetoothSerial * serial;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//the serial port used
|
||||||
|
SLIPEncodedBluetoothSerial(BluetoothSerial & );
|
||||||
|
|
||||||
|
|
||||||
|
int available();
|
||||||
|
int read();
|
||||||
|
int peek();
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
//same as Serial.begin
|
||||||
|
void begin(String);
|
||||||
|
|
||||||
|
//SLIP specific method which begins a transmitted packet
|
||||||
|
void beginPacket();
|
||||||
|
|
||||||
|
//SLIP specific method which ends a transmittedpacket
|
||||||
|
void endPacket();
|
||||||
|
// SLIP specific method which indicates that an EOT was received
|
||||||
|
bool endofPacket();
|
||||||
|
|
||||||
|
|
||||||
|
//the arduino and wiring libraries have different return types for the write function
|
||||||
|
#if defined(WIRING) || defined(BOARD_DEFS_H)
|
||||||
|
void write(uint8_t b);
|
||||||
|
void write(const uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
|
#else
|
||||||
|
//overrides the Stream's write function to encode SLIP
|
||||||
|
size_t write(uint8_t b);
|
||||||
|
size_t write(const uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
|
//using Print::write;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,310 @@
|
||||||
|
// ESP32 Dev Module
|
||||||
|
|
||||||
|
#include "Wire.h"
|
||||||
|
#include "mpu9250.h"
|
||||||
|
|
||||||
|
#include <OSCBundle.h>
|
||||||
|
#include <OSCBoards.h>
|
||||||
|
|
||||||
|
// POSITION CALCULATION
|
||||||
|
#include <BasicLinearAlgebra.h>
|
||||||
|
#include "math.h"
|
||||||
|
|
||||||
|
using namespace BLA;
|
||||||
|
|
||||||
|
#define SERIAL_OSC
|
||||||
|
//#define WIFI_OSC
|
||||||
|
#define BT_OSC
|
||||||
|
|
||||||
|
#define OUTPUT_READABLE_WORLDACCEL
|
||||||
|
|
||||||
|
// SERIAL
|
||||||
|
#ifdef BOARD_HAS_USB_SERIAL
|
||||||
|
#include <SLIPEncodedUSBSerial.h>
|
||||||
|
SLIPEncodedUSBSerial SLIPSerial( thisBoardsSerialUSB );
|
||||||
|
#else
|
||||||
|
#include <SLIPEncodedSerial.h>
|
||||||
|
SLIPEncodedSerial SLIPSerial(Serial); // Change to Serial1 or Serial2 etc. for boards with multiple serial ports that don’t have Serial
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// WIFI
|
||||||
|
#ifdef WIFI_OSC
|
||||||
|
#include <WiFi.h>
|
||||||
|
const char* ssid = "Grajski"; // your network SSID (name of wifi network)
|
||||||
|
const char* password = "nedeladanes"; // your network password
|
||||||
|
|
||||||
|
// Multicast IP / port
|
||||||
|
const IPAddress castIp = IPAddress(224,0,1,9);
|
||||||
|
const int port = 6696;
|
||||||
|
bool connected = false;
|
||||||
|
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
WiFiUDP udp;
|
||||||
|
|
||||||
|
void connectToWiFi(const char * ssid, const char * pwd){
|
||||||
|
Serial.println("Connecting to WiFi network: " + String(ssid));
|
||||||
|
|
||||||
|
// delete old config
|
||||||
|
WiFi.disconnect(true);
|
||||||
|
//register event handler
|
||||||
|
WiFi.onEvent(WiFiEvent);
|
||||||
|
|
||||||
|
//Initiate connection
|
||||||
|
WiFi.begin(ssid, pwd);
|
||||||
|
|
||||||
|
Serial.println("Waiting for WIFI connection...");
|
||||||
|
}
|
||||||
|
|
||||||
|
//wifi event handler
|
||||||
|
void WiFiEvent(WiFiEvent_t event){
|
||||||
|
switch(event) {
|
||||||
|
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||||
|
//When connected set
|
||||||
|
Serial.print("WiFi connected! IP address: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
//initializes the UDP state
|
||||||
|
//This initializes the transfer buffer
|
||||||
|
udp.begin(WiFi.localIP(), port);
|
||||||
|
connected = true;
|
||||||
|
break;
|
||||||
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||||
|
connected = false;
|
||||||
|
Serial.println("\n\n\n================\nLOST WIFI CONNECTION!\n\n\nTrying again soon...\n\n\n");
|
||||||
|
delay(1000);
|
||||||
|
connectToWiFi(ssid, password);
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Bluetooth
|
||||||
|
#ifdef BT_OSC
|
||||||
|
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
|
||||||
|
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <SLIPEncodedSerial.h>
|
||||||
|
#include "BluetoothSerial.h"
|
||||||
|
#include "SLIPEncodedBluetoothSerial.h"
|
||||||
|
|
||||||
|
BluetoothSerial SerialBT;
|
||||||
|
SLIPEncodedBluetoothSerial SLIPBTSerial(SerialBT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Motion sensor object
|
||||||
|
bfs::Mpu9250 mpu(&Wire, bfs::Mpu9250::I2C_ADDR_PRIM);
|
||||||
|
|
||||||
|
// MPU control/status vars
|
||||||
|
bool dmpReady = false; // set true if DMP init was successful
|
||||||
|
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
|
||||||
|
bool devStatus; // return status after each device operation (0 = success, !0 = error)
|
||||||
|
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
|
||||||
|
uint16_t fifoCount; // count of all bytes currently in FIFO
|
||||||
|
uint8_t fifoBuffer[64]; // FIFO storage buffer
|
||||||
|
|
||||||
|
// orientation/motion vars
|
||||||
|
|
||||||
|
float euler[3]; // [psi, theta, phi] Euler angle container
|
||||||
|
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
|
||||||
|
uint32_t timeOn = 0; // Uptime counter for movement calculation
|
||||||
|
Matrix<3> position; // [x,y,z] tracks position of device
|
||||||
|
Matrix<3> speed; // [x,y,z] tracks speed of device
|
||||||
|
Matrix<3> eulerVector;
|
||||||
|
Matrix<3> eulerDiffVector;
|
||||||
|
bool reset; // For quaternion calibration
|
||||||
|
|
||||||
|
|
||||||
|
// Sem dobimo vrednosti pospeskomerja in ziroskopa
|
||||||
|
int16_t AcX,AcY,AcZ;
|
||||||
|
float GyX, GyY, GyZ;
|
||||||
|
|
||||||
|
// Keys
|
||||||
|
byte keys[] = {16, 17, 5, 18};
|
||||||
|
byte pressed[] = {0, 0, 0, 0};
|
||||||
|
byte KEYLEN = 4;
|
||||||
|
|
||||||
|
|
||||||
|
float clamp(float value,float min,float max) {
|
||||||
|
return fmaxf( min, fminf(max, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* OSC MSG channels */
|
||||||
|
OSCBundle bundle;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
// Basic(debug) serial init
|
||||||
|
// Serial.begin(115200); // set this as high as you can reliably run on your platform
|
||||||
|
Serial.println("Starting up...");
|
||||||
|
|
||||||
|
// I2C init
|
||||||
|
Wire.begin();
|
||||||
|
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
|
||||||
|
|
||||||
|
#ifdef SERIAL_OSC
|
||||||
|
SLIPSerial.begin(115200); // set this as high as you can reliably run on your platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Keys
|
||||||
|
for(int i = 0; i < KEYLEN; i++) {
|
||||||
|
pinMode(keys[i], INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position and speed tracking
|
||||||
|
|
||||||
|
timeOn = 0;
|
||||||
|
position.Fill(0);
|
||||||
|
speed.Fill(0);
|
||||||
|
|
||||||
|
// Start MPU
|
||||||
|
if (!mpu.Begin()) {
|
||||||
|
Serial.println("IMU initialization failed");
|
||||||
|
while(1) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the sample rate divider */
|
||||||
|
if (!mpu.ConfigSrd(19)) {
|
||||||
|
Serial.println("Error configured SRD");
|
||||||
|
while(1) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIFI_OSC
|
||||||
|
// WIFI init
|
||||||
|
Serial.print("Attempting to connect to SSID: ");
|
||||||
|
Serial.println(ssid);
|
||||||
|
connectToWiFi(ssid, password);
|
||||||
|
|
||||||
|
// attempt to connect to Wifi network:
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
Serial.print(".");
|
||||||
|
// wait 1 second for re-trying
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BT_OSC
|
||||||
|
//SerialBT.begin("wavey wind");
|
||||||
|
SerialBT.begin("wavey wind 2");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (mpu.Read()) {
|
||||||
|
Serial.print(mpu.new_imu_data());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.new_mag_data());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.accel_x_mps2());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.accel_y_mps2());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.accel_z_mps2());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.gyro_x_radps());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.gyro_y_radps());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.gyro_z_radps());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.mag_x_ut());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.mag_y_ut());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.mag_z_ut());
|
||||||
|
Serial.print("\t");
|
||||||
|
Serial.print(mpu.die_temp_c());
|
||||||
|
Serial.print("\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Euler - rotacija
|
||||||
|
//eulerVector = eulerFromQuaternion(q);
|
||||||
|
//bundle.add("/euler").add(eulerVector(0)).add(eulerVector(1)).add(eulerVector(2)); // X Y Z
|
||||||
|
|
||||||
|
// Quaterion difference - rotacijska razlika (prejsnji reading - trenutni reading)
|
||||||
|
//bundle.add("/quaternionDiff").add(diff.w).add(diff.y * -1).add(diff.z).add(diff.x * -1); // W X Y Z
|
||||||
|
|
||||||
|
// Rotation diff value in euler angle
|
||||||
|
//eulerDiffVector = eulerFromQuaternion(diff);
|
||||||
|
//bundle.add("/eulerDiff").add(eulerDiffVector(0)).add(eulerDiffVector(1)).add(eulerDiffVector(2)); // X Y Z
|
||||||
|
|
||||||
|
#ifdef OUTPUT_READABLE_REALACCEL
|
||||||
|
// display real acceleration, adjusted to remove gravity
|
||||||
|
|
||||||
|
//AcX = aaReal.x;
|
||||||
|
//AcY = aaReal.y;
|
||||||
|
//AcZ = aaReal.z;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OUTPUT_READABLE_WORLDACCEL
|
||||||
|
// display initial world-frame acceleration, adjusted to remove gravity
|
||||||
|
// and rotated based on known orientation from quaternion
|
||||||
|
|
||||||
|
//AcX = aaWorld.x;
|
||||||
|
//AcY = aaWorld.y;
|
||||||
|
//AcZ = aaWorld.z;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Calculate speed and position from accelerometer data
|
||||||
|
/*
|
||||||
|
int prevTime = timeOn;
|
||||||
|
timeOn = millis();
|
||||||
|
int elapsedTime = timeOn - prevTime;
|
||||||
|
Matrix<3> speedGain = {AcX * elapsedTime, AcY * elapsedTime, AcZ * elapsedTime};
|
||||||
|
|
||||||
|
//Assume linear acceleration over measured time window, multiply time by halfpoint between last-known and current speed
|
||||||
|
position = position + (((speed + speedGain) + speed) /2 * elapsedTime);
|
||||||
|
speed += speedGain;
|
||||||
|
|
||||||
|
bundle.add("/position/").add(position(0)).add(position(1)).add(position(2));
|
||||||
|
bundle.add("/speed/").add(speed(0)).add(speed(1)).add(speed(2));
|
||||||
|
*/
|
||||||
|
// Accelerometer
|
||||||
|
//bundle.add("/accel").add(AcX).add(AcY).add(AcZ); ; // X Y Z
|
||||||
|
|
||||||
|
// Keys held down
|
||||||
|
//bundle.add("/keys"); // A B C D E
|
||||||
|
|
||||||
|
// Send keys
|
||||||
|
for(int i = 0; i < KEYLEN; i++) {
|
||||||
|
pressed[i] = !digitalRead(keys[i]);
|
||||||
|
bundle.getOSCMessage("/keys")->add(pressed[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset calibration euler?
|
||||||
|
if (pressed[0] && pressed[1] && pressed[2] && pressed[3]) {
|
||||||
|
if (!reset) {
|
||||||
|
//cq = q.getConjugate();
|
||||||
|
reset = true;
|
||||||
|
Serial.println("Quaternion calibrate");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (reset) {
|
||||||
|
reset = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SERIAL_OSC
|
||||||
|
SLIPSerial.beginPacket();
|
||||||
|
bundle.send(SLIPSerial);
|
||||||
|
SLIPSerial.endPacket();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef WIFI_OSC
|
||||||
|
udp.beginPacket(castIp, port);
|
||||||
|
bundle.send(udp);
|
||||||
|
udp.endPacket();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Some bug below, it seems
|
||||||
|
#ifdef BT_OSC
|
||||||
|
SLIPBTSerial.beginPacket();
|
||||||
|
bundle.send(SLIPBTSerial);
|
||||||
|
SLIPBTSerial.endPacket();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bundle.empty();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,194 @@
|
||||||
|
|
||||||
|
// Implementation of Sebastian Madgwick's "...efficient orientation filter for... inertial/magnetic sensor arrays"
|
||||||
|
// (see http://www.x-io.co.uk/category/open-source/ for examples and more details)
|
||||||
|
// which fuses acceleration, rotation rate, and magnetic moments to produce a quaternion-based estimate of absolute
|
||||||
|
// device orientation -- which can be converted to yaw, pitch, and roll. Useful for stabilizing quadcopters, etc.
|
||||||
|
// The performance of the orientation filter is at least as good as conventional Kalman-based filtering algorithms
|
||||||
|
// but is much less computationally intensive---it can be performed on a 3.3 V Pro Mini operating at 8 MHz!
|
||||||
|
void MadgwickQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz)
|
||||||
|
{
|
||||||
|
float q1 = q[0], q2 = q[1], q3 = q[2], q4 = q[3]; // short name local variable for readability
|
||||||
|
float norm;
|
||||||
|
float hx, hy, _2bx, _2bz;
|
||||||
|
float s1, s2, s3, s4;
|
||||||
|
float qDot1, qDot2, qDot3, qDot4;
|
||||||
|
|
||||||
|
// Auxiliary variables to avoid repeated arithmetic
|
||||||
|
float _2q1mx;
|
||||||
|
float _2q1my;
|
||||||
|
float _2q1mz;
|
||||||
|
float _2q2mx;
|
||||||
|
float _4bx;
|
||||||
|
float _4bz;
|
||||||
|
float _2q1 = 2.0f * q1;
|
||||||
|
float _2q2 = 2.0f * q2;
|
||||||
|
float _2q3 = 2.0f * q3;
|
||||||
|
float _2q4 = 2.0f * q4;
|
||||||
|
float _2q1q3 = 2.0f * q1 * q3;
|
||||||
|
float _2q3q4 = 2.0f * q3 * q4;
|
||||||
|
float q1q1 = q1 * q1;
|
||||||
|
float q1q2 = q1 * q2;
|
||||||
|
float q1q3 = q1 * q3;
|
||||||
|
float q1q4 = q1 * q4;
|
||||||
|
float q2q2 = q2 * q2;
|
||||||
|
float q2q3 = q2 * q3;
|
||||||
|
float q2q4 = q2 * q4;
|
||||||
|
float q3q3 = q3 * q3;
|
||||||
|
float q3q4 = q3 * q4;
|
||||||
|
float q4q4 = q4 * q4;
|
||||||
|
|
||||||
|
// Normalise accelerometer measurement
|
||||||
|
norm = sqrtf(ax * ax + ay * ay + az * az);
|
||||||
|
if (norm == 0.0f) return; // handle NaN
|
||||||
|
norm = 1.0f/norm;
|
||||||
|
ax *= norm;
|
||||||
|
ay *= norm;
|
||||||
|
az *= norm;
|
||||||
|
|
||||||
|
// Normalise magnetometer measurement
|
||||||
|
norm = sqrtf(mx * mx + my * my + mz * mz);
|
||||||
|
if (norm == 0.0f) return; // handle NaN
|
||||||
|
norm = 1.0f/norm;
|
||||||
|
mx *= norm;
|
||||||
|
my *= norm;
|
||||||
|
mz *= norm;
|
||||||
|
|
||||||
|
// Reference direction of Earth's magnetic field
|
||||||
|
_2q1mx = 2.0f * q1 * mx;
|
||||||
|
_2q1my = 2.0f * q1 * my;
|
||||||
|
_2q1mz = 2.0f * q1 * mz;
|
||||||
|
_2q2mx = 2.0f * q2 * mx;
|
||||||
|
hx = mx * q1q1 - _2q1my * q4 + _2q1mz * q3 + mx * q2q2 + _2q2 * my * q3 + _2q2 * mz * q4 - mx * q3q3 - mx * q4q4;
|
||||||
|
hy = _2q1mx * q4 + my * q1q1 - _2q1mz * q2 + _2q2mx * q3 - my * q2q2 + my * q3q3 + _2q3 * mz * q4 - my * q4q4;
|
||||||
|
_2bx = sqrtf(hx * hx + hy * hy);
|
||||||
|
_2bz = -_2q1mx * q3 + _2q1my * q2 + mz * q1q1 + _2q2mx * q4 - mz * q2q2 + _2q3 * my * q4 - mz * q3q3 + mz * q4q4;
|
||||||
|
_4bx = 2.0f * _2bx;
|
||||||
|
_4bz = 2.0f * _2bz;
|
||||||
|
|
||||||
|
// Gradient decent algorithm corrective step
|
||||||
|
s1 = -_2q3 * (2.0f * q2q4 - _2q1q3 - ax) + _2q2 * (2.0f * q1q2 + _2q3q4 - ay) - _2bz * q3 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q4 + _2bz * q2) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q3 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
|
||||||
|
s2 = _2q4 * (2.0f * q2q4 - _2q1q3 - ax) + _2q1 * (2.0f * q1q2 + _2q3q4 - ay) - 4.0f * q2 * (1.0f - 2.0f * q2q2 - 2.0f * q3q3 - az) + _2bz * q4 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q3 + _2bz * q1) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q4 - _4bz * q2) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
|
||||||
|
s3 = -_2q1 * (2.0f * q2q4 - _2q1q3 - ax) + _2q4 * (2.0f * q1q2 + _2q3q4 - ay) - 4.0f * q3 * (1.0f - 2.0f * q2q2 - 2.0f * q3q3 - az) + (-_4bx * q3 - _2bz * q1) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q2 + _2bz * q4) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q1 - _4bz * q3) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
|
||||||
|
s4 = _2q2 * (2.0f * q2q4 - _2q1q3 - ax) + _2q3 * (2.0f * q1q2 + _2q3q4 - ay) + (-_4bx * q4 + _2bz * q2) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q1 + _2bz * q3) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q2 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz);
|
||||||
|
norm = sqrtf(s1 * s1 + s2 * s2 + s3 * s3 + s4 * s4); // normalise step magnitude
|
||||||
|
norm = 1.0f/norm;
|
||||||
|
s1 *= norm;
|
||||||
|
s2 *= norm;
|
||||||
|
s3 *= norm;
|
||||||
|
s4 *= norm;
|
||||||
|
|
||||||
|
// Compute rate of change of quaternion
|
||||||
|
qDot1 = 0.5f * (-q2 * gx - q3 * gy - q4 * gz) - beta * s1;
|
||||||
|
qDot2 = 0.5f * (q1 * gx + q3 * gz - q4 * gy) - beta * s2;
|
||||||
|
qDot3 = 0.5f * (q1 * gy - q2 * gz + q4 * gx) - beta * s3;
|
||||||
|
qDot4 = 0.5f * (q1 * gz + q2 * gy - q3 * gx) - beta * s4;
|
||||||
|
|
||||||
|
// Integrate to yield quaternion
|
||||||
|
q1 += qDot1 * deltat;
|
||||||
|
q2 += qDot2 * deltat;
|
||||||
|
q3 += qDot3 * deltat;
|
||||||
|
q4 += qDot4 * deltat;
|
||||||
|
norm = sqrtf(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); // normalise quaternion
|
||||||
|
norm = 1.0f/norm;
|
||||||
|
q[0] = q1 * norm;
|
||||||
|
q[1] = q2 * norm;
|
||||||
|
q[2] = q3 * norm;
|
||||||
|
q[3] = q4 * norm;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Similar to Madgwick scheme but uses proportional and integral filtering on the error between estimated reference vectors and
|
||||||
|
// measured ones.
|
||||||
|
void MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz)
|
||||||
|
{
|
||||||
|
float q1 = q[0], q2 = q[1], q3 = q[2], q4 = q[3]; // short name local variable for readability
|
||||||
|
float norm;
|
||||||
|
float hx, hy, bx, bz;
|
||||||
|
float vx, vy, vz, wx, wy, wz;
|
||||||
|
float ex, ey, ez;
|
||||||
|
float pa, pb, pc;
|
||||||
|
|
||||||
|
// Auxiliary variables to avoid repeated arithmetic
|
||||||
|
float q1q1 = q1 * q1;
|
||||||
|
float q1q2 = q1 * q2;
|
||||||
|
float q1q3 = q1 * q3;
|
||||||
|
float q1q4 = q1 * q4;
|
||||||
|
float q2q2 = q2 * q2;
|
||||||
|
float q2q3 = q2 * q3;
|
||||||
|
float q2q4 = q2 * q4;
|
||||||
|
float q3q3 = q3 * q3;
|
||||||
|
float q3q4 = q3 * q4;
|
||||||
|
float q4q4 = q4 * q4;
|
||||||
|
|
||||||
|
// Normalise accelerometer measurement
|
||||||
|
norm = sqrtf(ax * ax + ay * ay + az * az);
|
||||||
|
if (norm == 0.0f) return; // handle NaN
|
||||||
|
norm = 1.0f / norm; // use reciprocal for division
|
||||||
|
ax *= norm;
|
||||||
|
ay *= norm;
|
||||||
|
az *= norm;
|
||||||
|
|
||||||
|
// Normalise magnetometer measurement
|
||||||
|
norm = sqrtf(mx * mx + my * my + mz * mz);
|
||||||
|
if (norm == 0.0f) return; // handle NaN
|
||||||
|
norm = 1.0f / norm; // use reciprocal for division
|
||||||
|
mx *= norm;
|
||||||
|
my *= norm;
|
||||||
|
mz *= norm;
|
||||||
|
|
||||||
|
// Reference direction of Earth's magnetic field
|
||||||
|
hx = 2.0f * mx * (0.5f - q3q3 - q4q4) + 2.0f * my * (q2q3 - q1q4) + 2.0f * mz * (q2q4 + q1q3);
|
||||||
|
hy = 2.0f * mx * (q2q3 + q1q4) + 2.0f * my * (0.5f - q2q2 - q4q4) + 2.0f * mz * (q3q4 - q1q2);
|
||||||
|
bx = sqrtf((hx * hx) + (hy * hy));
|
||||||
|
bz = 2.0f * mx * (q2q4 - q1q3) + 2.0f * my * (q3q4 + q1q2) + 2.0f * mz * (0.5f - q2q2 - q3q3);
|
||||||
|
|
||||||
|
// Estimated direction of gravity and magnetic field
|
||||||
|
vx = 2.0f * (q2q4 - q1q3);
|
||||||
|
vy = 2.0f * (q1q2 + q3q4);
|
||||||
|
vz = q1q1 - q2q2 - q3q3 + q4q4;
|
||||||
|
wx = 2.0f * bx * (0.5f - q3q3 - q4q4) + 2.0f * bz * (q2q4 - q1q3);
|
||||||
|
wy = 2.0f * bx * (q2q3 - q1q4) + 2.0f * bz * (q1q2 + q3q4);
|
||||||
|
wz = 2.0f * bx * (q1q3 + q2q4) + 2.0f * bz * (0.5f - q2q2 - q3q3);
|
||||||
|
|
||||||
|
// Error is cross product between estimated direction and measured direction of gravity
|
||||||
|
ex = (ay * vz - az * vy) + (my * wz - mz * wy);
|
||||||
|
ey = (az * vx - ax * vz) + (mz * wx - mx * wz);
|
||||||
|
ez = (ax * vy - ay * vx) + (mx * wy - my * wx);
|
||||||
|
if (Ki > 0.0f)
|
||||||
|
{
|
||||||
|
eInt[0] += ex; // accumulate integral error
|
||||||
|
eInt[1] += ey;
|
||||||
|
eInt[2] += ez;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eInt[0] = 0.0f; // prevent integral wind up
|
||||||
|
eInt[1] = 0.0f;
|
||||||
|
eInt[2] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply feedback terms
|
||||||
|
gx = gx + Kp * ex + Ki * eInt[0];
|
||||||
|
gy = gy + Kp * ey + Ki * eInt[1];
|
||||||
|
gz = gz + Kp * ez + Ki * eInt[2];
|
||||||
|
|
||||||
|
// Integrate rate of change of quaternion
|
||||||
|
pa = q2;
|
||||||
|
pb = q3;
|
||||||
|
pc = q4;
|
||||||
|
q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltat);
|
||||||
|
q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * deltat);
|
||||||
|
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * deltat);
|
||||||
|
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * deltat);
|
||||||
|
|
||||||
|
// Normalise quaternion
|
||||||
|
norm = sqrtf(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
|
||||||
|
norm = 1.0f / norm;
|
||||||
|
q[0] = q1 * norm;
|
||||||
|
q[1] = q2 * norm;
|
||||||
|
q[2] = q3 * norm;
|
||||||
|
q[3] = q4 * norm;
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,269 @@
|
||||||
|
// I2Cdev library collection - Main I2C device class header file
|
||||||
|
// Abstracts bit and byte I2C R/W functions into a convenient class
|
||||||
|
// 6/9/2012 by Jeff Rowberg <jeff@rowberg.net>
|
||||||
|
//
|
||||||
|
// Changelog:
|
||||||
|
// 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications
|
||||||
|
// 2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan)
|
||||||
|
// 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire
|
||||||
|
// - add compiler warnings when using outdated or IDE or limited I2Cdev implementation
|
||||||
|
// 2011-11-01 - fix write*Bits mask calculation (thanks sasquatch @ Arduino forums)
|
||||||
|
// 2011-10-03 - added automatic Arduino version detection for ease of use
|
||||||
|
// 2011-10-02 - added Gene Knight's NBWire TwoWire class implementation with small modifications
|
||||||
|
// 2011-08-31 - added support for Arduino 1.0 Wire library (methods are different from 0.x)
|
||||||
|
// 2011-08-03 - added optional timeout parameter to read* methods to easily change from default
|
||||||
|
// 2011-08-02 - added support for 16-bit registers
|
||||||
|
// - fixed incorrect Doxygen comments on some methods
|
||||||
|
// - added timeout value for read operations (thanks mem @ Arduino forums)
|
||||||
|
// 2011-07-30 - changed read/write function structures to return success or byte counts
|
||||||
|
// - made all methods static for multi-device memory savings
|
||||||
|
// 2011-07-28 - initial release
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
I2Cdev device library code is placed under the MIT license
|
||||||
|
Copyright (c) 2013 Jeff Rowberg
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
===============================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _I2CDEV_H_
|
||||||
|
#define _I2CDEV_H_
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// I2C interface implementation setting
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE
|
||||||
|
//#define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_FASTWIRE
|
||||||
|
|
||||||
|
// comment this out if you are using a non-optimal IDE/implementation setting
|
||||||
|
// but want the compiler to shut up about it
|
||||||
|
#define I2CDEV_IMPLEMENTATION_WARNINGS
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// I2C interface implementation options
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
#define I2CDEV_ARDUINO_WIRE 1 // Wire object from Arduino
|
||||||
|
#define I2CDEV_BUILTIN_NBWIRE 2 // Tweaked Wire object from Gene Knight's NBWire project
|
||||||
|
// ^^^ NBWire implementation is still buggy w/some interrupts!
|
||||||
|
#define I2CDEV_BUILTIN_FASTWIRE 3 // FastWire object from Francesco Ferrara's project
|
||||||
|
#define I2CDEV_I2CMASTER_LIBRARY 4 // I2C object from DSSCircuits I2C-Master Library at https://github.com/DSSCircuits/I2C-Master-Library
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Arduino-style "Serial.print" debug constant (uncomment to enable)
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//#define I2CDEV_SERIAL_DEBUG
|
||||||
|
|
||||||
|
#ifdef ARDUINO
|
||||||
|
#if ARDUINO < 100
|
||||||
|
#include "WProgram.h"
|
||||||
|
#else
|
||||||
|
#include "Arduino.h"
|
||||||
|
#endif
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
|
||||||
|
#include <Wire.h>
|
||||||
|
#endif
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_I2CMASTER_LIBRARY
|
||||||
|
#include <I2C.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];")
|
||||||
|
#define I2CDEV_DEFAULT_READ_TIMEOUT 1000
|
||||||
|
|
||||||
|
class I2Cdev {
|
||||||
|
public:
|
||||||
|
I2Cdev();
|
||||||
|
|
||||||
|
static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout);
|
||||||
|
|
||||||
|
static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data);
|
||||||
|
static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data);
|
||||||
|
static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data);
|
||||||
|
static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data);
|
||||||
|
static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data);
|
||||||
|
static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data);
|
||||||
|
static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data);
|
||||||
|
static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data);
|
||||||
|
|
||||||
|
static uint16_t readTimeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
|
||||||
|
//////////////////////
|
||||||
|
// FastWire 0.24
|
||||||
|
// This is a library to help faster programs to read I2C devices.
|
||||||
|
// Copyright(C) 2012
|
||||||
|
// Francesco Ferrara
|
||||||
|
//////////////////////
|
||||||
|
|
||||||
|
/* Master */
|
||||||
|
#define TW_START 0x08
|
||||||
|
#define TW_REP_START 0x10
|
||||||
|
|
||||||
|
/* Master Transmitter */
|
||||||
|
#define TW_MT_SLA_ACK 0x18
|
||||||
|
#define TW_MT_SLA_NACK 0x20
|
||||||
|
#define TW_MT_DATA_ACK 0x28
|
||||||
|
#define TW_MT_DATA_NACK 0x30
|
||||||
|
#define TW_MT_ARB_LOST 0x38
|
||||||
|
|
||||||
|
/* Master Receiver */
|
||||||
|
#define TW_MR_ARB_LOST 0x38
|
||||||
|
#define TW_MR_SLA_ACK 0x40
|
||||||
|
#define TW_MR_SLA_NACK 0x48
|
||||||
|
#define TW_MR_DATA_ACK 0x50
|
||||||
|
#define TW_MR_DATA_NACK 0x58
|
||||||
|
|
||||||
|
#define TW_OK 0
|
||||||
|
#define TW_ERROR 1
|
||||||
|
|
||||||
|
class Fastwire {
|
||||||
|
private:
|
||||||
|
static boolean waitInt();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void setup(int khz, boolean pullup);
|
||||||
|
static byte beginTransmission(byte device);
|
||||||
|
static byte write(byte value);
|
||||||
|
static byte writeBuf(byte device, byte address, byte *data, byte num);
|
||||||
|
static byte readBuf(byte device, byte address, byte *data, byte num);
|
||||||
|
static void reset();
|
||||||
|
static byte stop();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
|
||||||
|
// NBWire implementation based heavily on code by Gene Knight <Gene@Telobot.com>
|
||||||
|
// Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html
|
||||||
|
// Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html
|
||||||
|
|
||||||
|
#define NBWIRE_BUFFER_LENGTH 32
|
||||||
|
|
||||||
|
class TwoWire {
|
||||||
|
private:
|
||||||
|
static uint8_t rxBuffer[];
|
||||||
|
static uint8_t rxBufferIndex;
|
||||||
|
static uint8_t rxBufferLength;
|
||||||
|
|
||||||
|
static uint8_t txAddress;
|
||||||
|
static uint8_t txBuffer[];
|
||||||
|
static uint8_t txBufferIndex;
|
||||||
|
static uint8_t txBufferLength;
|
||||||
|
|
||||||
|
// static uint8_t transmitting;
|
||||||
|
static void (*user_onRequest)(void);
|
||||||
|
static void (*user_onReceive)(int);
|
||||||
|
static void onRequestService(void);
|
||||||
|
static void onReceiveService(uint8_t*, int);
|
||||||
|
|
||||||
|
public:
|
||||||
|
TwoWire();
|
||||||
|
void begin();
|
||||||
|
void begin(uint8_t);
|
||||||
|
void begin(int);
|
||||||
|
void beginTransmission(uint8_t);
|
||||||
|
//void beginTransmission(int);
|
||||||
|
uint8_t endTransmission(uint16_t timeout=0);
|
||||||
|
void nbendTransmission(void (*function)(int)) ;
|
||||||
|
uint8_t requestFrom(uint8_t, int, uint16_t timeout=0);
|
||||||
|
//uint8_t requestFrom(int, int);
|
||||||
|
void nbrequestFrom(uint8_t, int, void (*function)(int));
|
||||||
|
void send(uint8_t);
|
||||||
|
void send(uint8_t*, uint8_t);
|
||||||
|
//void send(int);
|
||||||
|
void send(char*);
|
||||||
|
uint8_t available(void);
|
||||||
|
uint8_t receive(void);
|
||||||
|
void onReceive(void (*)(int));
|
||||||
|
void onRequest(void (*)(void));
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TWI_READY 0
|
||||||
|
#define TWI_MRX 1
|
||||||
|
#define TWI_MTX 2
|
||||||
|
#define TWI_SRX 3
|
||||||
|
#define TWI_STX 4
|
||||||
|
|
||||||
|
#define TW_WRITE 0
|
||||||
|
#define TW_READ 1
|
||||||
|
|
||||||
|
#define TW_MT_SLA_NACK 0x20
|
||||||
|
#define TW_MT_DATA_NACK 0x30
|
||||||
|
|
||||||
|
#define CPU_FREQ 16000000L
|
||||||
|
#define TWI_FREQ 100000L
|
||||||
|
#define TWI_BUFFER_LENGTH 32
|
||||||
|
|
||||||
|
/* TWI Status is in TWSR, in the top 5 bits: TWS7 - TWS3 */
|
||||||
|
|
||||||
|
#define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3))
|
||||||
|
#define TW_STATUS (TWSR & TW_STATUS_MASK)
|
||||||
|
#define TW_START 0x08
|
||||||
|
#define TW_REP_START 0x10
|
||||||
|
#define TW_MT_SLA_ACK 0x18
|
||||||
|
#define TW_MT_SLA_NACK 0x20
|
||||||
|
#define TW_MT_DATA_ACK 0x28
|
||||||
|
#define TW_MT_DATA_NACK 0x30
|
||||||
|
#define TW_MT_ARB_LOST 0x38
|
||||||
|
#define TW_MR_ARB_LOST 0x38
|
||||||
|
#define TW_MR_SLA_ACK 0x40
|
||||||
|
#define TW_MR_SLA_NACK 0x48
|
||||||
|
#define TW_MR_DATA_ACK 0x50
|
||||||
|
#define TW_MR_DATA_NACK 0x58
|
||||||
|
#define TW_ST_SLA_ACK 0xA8
|
||||||
|
#define TW_ST_ARB_LOST_SLA_ACK 0xB0
|
||||||
|
#define TW_ST_DATA_ACK 0xB8
|
||||||
|
#define TW_ST_DATA_NACK 0xC0
|
||||||
|
#define TW_ST_LAST_DATA 0xC8
|
||||||
|
#define TW_SR_SLA_ACK 0x60
|
||||||
|
#define TW_SR_ARB_LOST_SLA_ACK 0x68
|
||||||
|
#define TW_SR_GCALL_ACK 0x70
|
||||||
|
#define TW_SR_ARB_LOST_GCALL_ACK 0x78
|
||||||
|
#define TW_SR_DATA_ACK 0x80
|
||||||
|
#define TW_SR_DATA_NACK 0x88
|
||||||
|
#define TW_SR_GCALL_DATA_ACK 0x90
|
||||||
|
#define TW_SR_GCALL_DATA_NACK 0x98
|
||||||
|
#define TW_SR_STOP 0xA0
|
||||||
|
#define TW_NO_INFO 0xF8
|
||||||
|
#define TW_BUS_ERROR 0x00
|
||||||
|
|
||||||
|
//#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
|
||||||
|
//#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))
|
||||||
|
|
||||||
|
#ifndef sbi // set bit
|
||||||
|
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
||||||
|
#endif // sbi
|
||||||
|
|
||||||
|
#ifndef cbi // clear bit
|
||||||
|
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
||||||
|
#endif // cbi
|
||||||
|
|
||||||
|
extern TwoWire Wire;
|
||||||
|
|
||||||
|
#endif // I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE
|
||||||
|
|
||||||
|
#endif /* _I2CDEV_H_ */
|
|
@ -0,0 +1,181 @@
|
||||||
|
#include "SLIPEncodedBluetoothSerial.h"
|
||||||
|
#include "BluetoothSerial.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
CONSTRUCTOR
|
||||||
|
*/
|
||||||
|
//instantiate with the tranmission layer
|
||||||
|
//use BluetoothSerial
|
||||||
|
SLIPEncodedBluetoothSerial::SLIPEncodedBluetoothSerial(BluetoothSerial &s){
|
||||||
|
serial = &s;
|
||||||
|
rstate = CHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t eot = 0300;
|
||||||
|
static const uint8_t slipesc = 0333;
|
||||||
|
static const uint8_t slipescend = 0334;
|
||||||
|
static const uint8_t slipescesc = 0335;
|
||||||
|
/*
|
||||||
|
SERIAL METHODS
|
||||||
|
*/
|
||||||
|
bool SLIPEncodedBluetoothSerial::endofPacket()
|
||||||
|
{
|
||||||
|
if(rstate == SECONDEOT)
|
||||||
|
{
|
||||||
|
rstate = CHAR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (rstate==FIRSTEOT)
|
||||||
|
{
|
||||||
|
if(serial->available())
|
||||||
|
{
|
||||||
|
uint8_t c =serial->peek();
|
||||||
|
if(c==eot)
|
||||||
|
{
|
||||||
|
serial->read(); // throw it on the floor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rstate = CHAR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int SLIPEncodedBluetoothSerial::available(){
|
||||||
|
back:
|
||||||
|
int cnt = serial->available();
|
||||||
|
|
||||||
|
if(cnt==0)
|
||||||
|
return 0;
|
||||||
|
if(rstate==CHAR)
|
||||||
|
{
|
||||||
|
uint8_t c =serial->peek();
|
||||||
|
if(c==slipesc)
|
||||||
|
{
|
||||||
|
rstate = SLIPESC;
|
||||||
|
serial->read(); // throw it on the floor
|
||||||
|
goto back;
|
||||||
|
}
|
||||||
|
else if( c==eot)
|
||||||
|
{
|
||||||
|
rstate = FIRSTEOT;
|
||||||
|
serial->read(); // throw it on the floor
|
||||||
|
goto back;
|
||||||
|
}
|
||||||
|
return 1; // we may have more but this is the only sure bet
|
||||||
|
}
|
||||||
|
else if(rstate==SLIPESC)
|
||||||
|
return 1;
|
||||||
|
else if(rstate==FIRSTEOT)
|
||||||
|
{
|
||||||
|
if(serial->peek()==eot)
|
||||||
|
{
|
||||||
|
rstate = SECONDEOT;
|
||||||
|
serial->read(); // throw it on the floor
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rstate = CHAR;
|
||||||
|
}else if (rstate==SECONDEOT) {
|
||||||
|
rstate = CHAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//reads a byte from the buffer
|
||||||
|
int SLIPEncodedBluetoothSerial::read(){
|
||||||
|
back:
|
||||||
|
uint8_t c = serial->read();
|
||||||
|
if(rstate==CHAR)
|
||||||
|
{
|
||||||
|
if(c==slipesc)
|
||||||
|
{
|
||||||
|
rstate=SLIPESC;
|
||||||
|
goto back;
|
||||||
|
}
|
||||||
|
else if(c==eot){
|
||||||
|
|
||||||
|
return -1; // xxx this is an error
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if(rstate==SLIPESC)
|
||||||
|
{
|
||||||
|
rstate=CHAR;
|
||||||
|
if(c==slipescend)
|
||||||
|
return eot;
|
||||||
|
else if(c==slipescesc)
|
||||||
|
return slipesc;
|
||||||
|
else {
|
||||||
|
// insert some error code here
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// as close as we can get to correct behavior
|
||||||
|
int SLIPEncodedBluetoothSerial::peek(){
|
||||||
|
uint8_t c = serial->peek();
|
||||||
|
if(rstate==SLIPESC)
|
||||||
|
{
|
||||||
|
if(c==slipescend)
|
||||||
|
return eot;
|
||||||
|
else if(c==slipescesc)
|
||||||
|
return slipesc;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
//the arduino and wiring libraries have different return types for the write function
|
||||||
|
#if defined(WIRING) || defined(BOARD_DEFS_H)
|
||||||
|
|
||||||
|
//encode SLIP
|
||||||
|
void SLIPEncodedBluetoothSerial::write(uint8_t b){
|
||||||
|
if(b == eot){
|
||||||
|
serial->write(slipesc);
|
||||||
|
return serial->write(slipescend);
|
||||||
|
} else if(b==slipesc) {
|
||||||
|
serial->write(slipesc);
|
||||||
|
return serial->write(slipescesc);
|
||||||
|
} else {
|
||||||
|
return serial->write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SLIPEncodedBluetoothSerial::write(const uint8_t *buffer, size_t size) { while(size--) write(*buffer++); }
|
||||||
|
#else
|
||||||
|
//encode SLIP
|
||||||
|
size_t SLIPEncodedBluetoothSerial::write(uint8_t b){
|
||||||
|
if(b == eot){
|
||||||
|
serial->write(slipesc);
|
||||||
|
return serial->write(slipescend);
|
||||||
|
} else if(b==slipesc) {
|
||||||
|
serial->write(slipesc);
|
||||||
|
return serial->write(slipescesc);
|
||||||
|
} else {
|
||||||
|
return serial->write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t SLIPEncodedBluetoothSerial::write(const uint8_t *buffer, size_t size) { size_t result=0; while(size--) result = write(*buffer++); return result; }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SLIPEncodedBluetoothSerial::begin(String name){
|
||||||
|
serial->begin(name);
|
||||||
|
}
|
||||||
|
//SLIP specific method which begins a transmitted packet
|
||||||
|
void SLIPEncodedBluetoothSerial::beginPacket() { serial->write(eot); }
|
||||||
|
|
||||||
|
//signify the end of the packet with an EOT
|
||||||
|
void SLIPEncodedBluetoothSerial::endPacket(){
|
||||||
|
serial->write(eot);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SLIPEncodedBluetoothSerial::flush(){
|
||||||
|
serial->flush();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Extends the Serial class to encode SLIP over serial
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SLIPEncodedBluetoothSerial_h
|
||||||
|
#define SLIPEncodedBluetoothSerial_h
|
||||||
|
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include <Stream.h>
|
||||||
|
#include "BluetoothSerial.h"
|
||||||
|
|
||||||
|
|
||||||
|
class SLIPEncodedBluetoothSerial: public Stream{
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum erstate {CHAR, FIRSTEOT, SECONDEOT, SLIPESC } rstate;
|
||||||
|
|
||||||
|
//the serial port used
|
||||||
|
BluetoothSerial * serial;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
//the serial port used
|
||||||
|
SLIPEncodedBluetoothSerial(BluetoothSerial & );
|
||||||
|
|
||||||
|
|
||||||
|
int available();
|
||||||
|
int read();
|
||||||
|
int peek();
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
//same as Serial.begin
|
||||||
|
void begin(String);
|
||||||
|
|
||||||
|
//SLIP specific method which begins a transmitted packet
|
||||||
|
void beginPacket();
|
||||||
|
|
||||||
|
//SLIP specific method which ends a transmittedpacket
|
||||||
|
void endPacket();
|
||||||
|
// SLIP specific method which indicates that an EOT was received
|
||||||
|
bool endofPacket();
|
||||||
|
|
||||||
|
|
||||||
|
//the arduino and wiring libraries have different return types for the write function
|
||||||
|
#if defined(WIRING) || defined(BOARD_DEFS_H)
|
||||||
|
void write(uint8_t b);
|
||||||
|
void write(const uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
|
#else
|
||||||
|
//overrides the Stream's write function to encode SLIP
|
||||||
|
size_t write(uint8_t b);
|
||||||
|
size_t write(const uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
|
//using Print::write;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,175 @@
|
||||||
|
// ESP32 Dev Module
|
||||||
|
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
// Parameters for device
|
||||||
|
#define BTNAME "kegel 2"
|
||||||
|
|
||||||
|
// IMU libraries
|
||||||
|
#include "I2Cdev.h"
|
||||||
|
#include "RTIMUSettings.h"
|
||||||
|
#include "RTIMU.h"
|
||||||
|
#include "RTFusionRTQF.h"
|
||||||
|
#include "CalLib.h"
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
#include <OSCBundle.h>
|
||||||
|
#include <OSCBoards.h>
|
||||||
|
|
||||||
|
#define BT_OSC
|
||||||
|
|
||||||
|
#define DISPLAY_INTERVAL 5 // interval between pose displays
|
||||||
|
|
||||||
|
|
||||||
|
// Bluetooth
|
||||||
|
#ifdef BT_OSC
|
||||||
|
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
|
||||||
|
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <SLIPEncodedSerial.h>
|
||||||
|
#include "BluetoothSerial.h"
|
||||||
|
#include "SLIPEncodedBluetoothSerial.h"
|
||||||
|
|
||||||
|
BluetoothSerial SerialBT;
|
||||||
|
SLIPEncodedBluetoothSerial SLIPBTSerial(SerialBT);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Motion sensor objects
|
||||||
|
RTIMU *imu; // the IMU object
|
||||||
|
RTFusionRTQF fusion; // the fusion object
|
||||||
|
RTIMUSettings settings; // the settings object
|
||||||
|
|
||||||
|
unsigned long lastDisplay;
|
||||||
|
unsigned long lastRate;
|
||||||
|
int sampleCount;
|
||||||
|
RTQuaternion gravity;
|
||||||
|
|
||||||
|
bool reset; // For quaternion calibration
|
||||||
|
|
||||||
|
/* OSC MSG channels */
|
||||||
|
OSCBundle bundle;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
int errcode;
|
||||||
|
// Basic(debug) serial init
|
||||||
|
Serial.begin(115200); // set this as high as you can reliably run on your platform
|
||||||
|
Serial.println("Starting up...");
|
||||||
|
|
||||||
|
// Init EEPROM based on magnet calibration size requirement
|
||||||
|
EEPROM.begin(512);
|
||||||
|
|
||||||
|
// I2C init
|
||||||
|
Wire.begin();
|
||||||
|
Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
|
||||||
|
|
||||||
|
// create the imu object
|
||||||
|
imu = RTIMU::createIMU(&settings);
|
||||||
|
|
||||||
|
Serial.print("ArduinoIMU starting using device "); Serial.println(imu->IMUName());
|
||||||
|
if ((errcode = imu->IMUInit()) < 0) {
|
||||||
|
Serial.print("Failed to init IMU: "); Serial.println(errcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (imu->getCalibrationValid())
|
||||||
|
Serial.println("Using compass calibration");
|
||||||
|
else
|
||||||
|
Serial.println("No valid compass calibration data");
|
||||||
|
|
||||||
|
// Gravity obj
|
||||||
|
gravity.setScalar(0);
|
||||||
|
gravity.setX(0);
|
||||||
|
gravity.setY(0);
|
||||||
|
gravity.setZ(1);
|
||||||
|
/*
|
||||||
|
fusion.setSlerpPower(0.02);
|
||||||
|
fusion.setGyroEnable(true);
|
||||||
|
fusion.setAccelEnable(true);
|
||||||
|
fusion.setCompassEnable(true);
|
||||||
|
*/
|
||||||
|
|
||||||
|
lastDisplay = lastRate = millis();
|
||||||
|
sampleCount = 0;
|
||||||
|
|
||||||
|
#ifdef BT_OSC
|
||||||
|
SerialBT.begin(BTNAME);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
unsigned long now = millis();
|
||||||
|
unsigned long delta;
|
||||||
|
RTVector3 realAccel;
|
||||||
|
RTQuaternion rotatedGravity;
|
||||||
|
RTQuaternion fusedConjugate;
|
||||||
|
RTQuaternion qTemp;
|
||||||
|
int loopCount = 0;
|
||||||
|
|
||||||
|
// get the latest data if ready yet
|
||||||
|
while (imu->IMURead()) {
|
||||||
|
// this flushes remaining data in case we are falling behind
|
||||||
|
if (++loopCount >= 10)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fusion.newIMUData(imu->getGyro(), imu->getAccel(), imu->getCompass(), imu->getTimestamp());
|
||||||
|
|
||||||
|
// do gravity rotation and subtraction
|
||||||
|
|
||||||
|
// create the conjugate of the pose
|
||||||
|
fusedConjugate = fusion.getFusionQPose().conjugate();
|
||||||
|
|
||||||
|
// now do the rotation - takes two steps with qTemp as the intermediate variable
|
||||||
|
qTemp = gravity * fusion.getFusionQPose();
|
||||||
|
rotatedGravity = fusedConjugate * qTemp;
|
||||||
|
|
||||||
|
// now adjust the measured accel and change the signs to make sense
|
||||||
|
realAccel.setX(-(imu->getAccel().x() - rotatedGravity.x()));
|
||||||
|
realAccel.setY(-(imu->getAccel().y() - rotatedGravity.y()));
|
||||||
|
realAccel.setZ(-(imu->getAccel().z() - rotatedGravity.z()));
|
||||||
|
|
||||||
|
sampleCount++;
|
||||||
|
if ((delta = now - lastRate) >= 1000) {
|
||||||
|
Serial.print("Sample rate: "); Serial.print(sampleCount);
|
||||||
|
if (!imu->IMUGyroBiasValid())
|
||||||
|
Serial.println(", calculating gyro bias");
|
||||||
|
else
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
|
sampleCount = 0;
|
||||||
|
lastRate = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
//RTMath::display("Accel:", realAccel);
|
||||||
|
//Serial.println();
|
||||||
|
|
||||||
|
// Quaternion - rotacija
|
||||||
|
//bundle.add("/quaternion").add(fusion.getFusionQPose().scalar()).add(fusion.getFusionQPose().x()).add(fusion.getFusionQPose().y()).add(fusion.getFusionQPose().z()); // W X Y Z
|
||||||
|
|
||||||
|
// Euler - rotacija
|
||||||
|
//eulerVector = eulerFromQuaternion(q);
|
||||||
|
//bundle.add("/euler").add(eulerVector(0)).add(eulerVector(1)).add(eulerVector(2)); // X Y Z
|
||||||
|
//bundle.add("/euler").add(fusion.getFusionPose().x()).add(fusion.getFusionPose().y()).add(fusion.getFusionPose().z());
|
||||||
|
// Quaterion difference - rotacijska razlika (prejsnji reading - trenutni reading)
|
||||||
|
//bundle.add("/quaternionDiff").add(diff.w).add(diff.y * -1).add(diff.z).add(diff.x * -1); // W X Y Z
|
||||||
|
|
||||||
|
// Rotation diff value in euler angle
|
||||||
|
//eulerDiffVector = eulerFromQuaternion(diff);
|
||||||
|
//bundle.add("/eulerDiff").add(eulerDiffVector(0)).add(eulerDiffVector(1)).add(eulerDiffVector(2)); // X Y Z
|
||||||
|
|
||||||
|
if ((now - lastDisplay) >= DISPLAY_INTERVAL) {
|
||||||
|
lastDisplay = now;
|
||||||
|
// Accelerometer
|
||||||
|
bundle.add("/accel").add(realAccel.x()).add(realAccel.y()).add(realAccel.z());
|
||||||
|
|
||||||
|
// Some bug below, it seems
|
||||||
|
#ifdef BT_OSC
|
||||||
|
SLIPBTSerial.beginPacket();
|
||||||
|
bundle.send(SLIPBTSerial);
|
||||||
|
SLIPBTSerial.endPacket();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bundle.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue