/** * Tlv493d.cpp - Library for Arduino to control the TLV493D-A1B6 3D magnetic sensor. * * The 3D magnetic sensor TLV493D-A1B6 offers accurate three dimensional sensing with extremely low power consumption * in a small 6-pin package. With an opportunity to detect the magnetic field in x, y, and z-direction the sensor is * ideally suited for the measurement of 3D movements, linear movements and rotation movements. * * Have a look at the application note/reference manual for more information. * * Copyright (c) 2018 Infineon Technologies AG * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY,OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "Tlv493d.h" #include "./util/RegMask.h" #include "./util/BusInterface2.h" #include Tlv493d::Tlv493d(void) { mXdata = 0; mYdata = 0; mZdata = 0; mTempdata = 0; mExpectedFrameCount = 0x00; } Tlv493d::~Tlv493d(void) { end(); } void Tlv493d::begin(void) { begin(Wire, TLV493D_ADDRESS1, true); } void Tlv493d::begin(TwoWire &bus) { begin(bus, TLV493D_ADDRESS1, true); } void Tlv493d::begin(TwoWire &bus, Tlv493d_Address_t slaveAddress, bool reset) { /** * Workaround for kit2go vdd enable */ #ifdef TLV493D_A1B6_KIT2GO pinMode(LED2, OUTPUT); digitalWrite(LED2, HIGH); delay(50); #endif initInterface(&mInterface, &bus, slaveAddress); delay(TLV493D_STARTUPDELAY); mInterface.bus->begin(); if(reset == true) { resetSensor(mInterface.adress); } // get all register data from sensor tlv493d::readOut(&mInterface); // copy factory settings to write registers setRegBits(tlv493d::W_RES1, getRegBits(tlv493d::R_RES1)); setRegBits(tlv493d::W_RES1, getRegBits(tlv493d::R_RES1)); setRegBits(tlv493d::W_RES1, getRegBits(tlv493d::R_RES1)); // enable parity detection setRegBits(tlv493d::W_PARITY_EN, 1); // config sensor to lowpower mode // also contains parity calculation and writeout to sensor setAccessMode(TLV493D_DEFAULTMODE); } void Tlv493d::end(void) { disableInterrupt(); setAccessMode(POWERDOWNMODE); } bool Tlv493d::setAccessMode(AccessMode_e mode) { bool ret = BUS_ERROR; const tlv493d::AccessMode_t *modeConfig = &(tlv493d::accModes[mode]); setRegBits(tlv493d::W_FAST, modeConfig->fast); setRegBits(tlv493d::W_LOWPOWER, modeConfig->lp); setRegBits(tlv493d::W_LP_PERIOD, modeConfig->lpPeriod); calcParity(); ret = tlv493d::writeOut(&mInterface); if ( ret != BUS_ERROR ) { mMode = mode; } return ret; } void Tlv493d::enableInterrupt(void) { setRegBits(tlv493d::W_INT, 1); calcParity(); tlv493d::writeOut(&mInterface); } void Tlv493d::disableInterrupt(void) { setRegBits(tlv493d::W_INT, 0); calcParity(); tlv493d::writeOut(&mInterface); } void Tlv493d::enableTemp(void) { setRegBits(tlv493d::W_TEMP_NEN, 0); calcParity(); tlv493d::writeOut(&mInterface); } void Tlv493d::disableTemp(void) { setRegBits(tlv493d::W_TEMP_NEN, 1); calcParity(); tlv493d::writeOut(&mInterface); } uint16_t Tlv493d::getMeasurementDelay(void) { return tlv493d::accModes[mMode].measurementTime; } Tlv493d_Error_t Tlv493d::updateData(void) { Tlv493d_Error_t ret = TLV493D_NO_ERROR; // in POWERDOWNMODE, sensor has to be switched on for one measurement uint8_t powerdown = 0; if(mMode == POWERDOWNMODE) { if (setAccessMode(MASTERCONTROLLEDMODE) != BUS_OK) { ret = TLV493D_BUS_ERROR; } delay(getMeasurementDelay()); powerdown = 1; } if(ret == TLV493D_NO_ERROR) { #ifdef TLV493D_ACCELERATE_READOUT // just read the most important results in FASTMODE, if this behaviour is desired if(mMode == FASTMODE) { if (readOut(&mInterface, TLV493D_FAST_READOUT) != BUS_OK) { ret = TLV493D_BUS_ERROR; } } else { if (readOut(&mInterface, TLV493D_MEASUREMENT_READOUT) != BUS_OK) { ret = TLV493D_BUS_ERROR; } } #else if (readOut(&mInterface, TLV493D_MEASUREMENT_READOUT) != BUS_OK) { ret = TLV493D_BUS_ERROR; } #endif if (ret == TLV493D_NO_ERROR) { // construct results from registers mXdata = concatResults(getRegBits(tlv493d::R_BX1), getRegBits(tlv493d::R_BX2), true); mYdata = concatResults(getRegBits(tlv493d::R_BY1), getRegBits(tlv493d::R_BY2), true); mZdata = concatResults(getRegBits(tlv493d::R_BZ1), getRegBits(tlv493d::R_BZ2), true); mTempdata = concatResults(getRegBits(tlv493d::R_TEMP1), getRegBits(tlv493d::R_TEMP2), false); // switch sensor back to POWERDOWNMODE, if it was in POWERDOWNMODE before if(powerdown) { if (setAccessMode(POWERDOWNMODE) != BUS_OK) { ret = TLV493D_BUS_ERROR; } } if (ret == TLV493D_NO_ERROR) { // if the return value is 0, all results are from the same frame // otherwise some results may be outdated if(getRegBits(tlv493d::R_CHANNEL) != 0) { ret = TLV493D_FRAME_ERROR; } // Todo: removed due to a lot of frame errors // // if received frame count does not match expected one (frame count from 0 to 3) // else if( getRegBits(tlv493d::R_FRAMECOUNTER) != (mExpectedFrameCount % 4) ) // { // ret = TLV493D_FRAME_ERROR; // } } } } mExpectedFrameCount = getRegBits(tlv493d::R_FRAMECOUNTER) + 1; return ret; } // SBEZEK uint8_t Tlv493d::getExpectedFrameCount(void) { return mExpectedFrameCount; } float Tlv493d::getX(void) { return static_cast(mXdata) * TLV493D_B_MULT; } float Tlv493d::getY(void) { return static_cast(mYdata) * TLV493D_B_MULT; } float Tlv493d::getZ(void) { return static_cast(mZdata) * TLV493D_B_MULT; } float Tlv493d::getTemp(void) { return static_cast(mTempdata-TLV493D_TEMP_OFFSET) * TLV493D_TEMP_MULT; } float Tlv493d::getAmount(void) { // sqrt(x^2 + y^2 + z^2) return TLV493D_B_MULT * sqrt(pow(static_cast(mXdata), 2) + pow(static_cast(mYdata), 2) + pow(static_cast(mZdata), 2)); } float Tlv493d::getAzimuth(void) { // arctan(y/x) return atan2(static_cast(mYdata), static_cast(mXdata)); } float Tlv493d::getPolar(void) { // arctan(z/(sqrt(x^2+y^2))) return atan2(static_cast(mZdata), sqrt(pow(static_cast(mXdata), 2) + pow(static_cast(mYdata), 2))); } /* internal function called by begin() * The sensor has a special reset sequence which allows to change its i2c address by setting SDA to high or low during a reset. * As some i2c peripherals may not cope with this, the simplest way is to use for this very few bytes bitbanging on the SCL/SDA lines. * Furthermore, as the uC may be stopped during a i2c transmission, a special recovery sequence allows to bring the bus back to * an operating state. */ void Tlv493d::resetSensor(uint8_t adr) // Recovery & Reset - this can be handled by any uC as it uses bitbanging { mInterface.bus->beginTransmission(0x00); if (adr == TLV493D_ADDRESS1) { // if the sensor shall be initialized with i2c address 0x1F mInterface.bus->write(0xFF); } else { // if the sensor shall be initialized with address 0x5E mInterface.bus->write((uint8_t)0x00); } mInterface.bus->endTransmission(true); } void Tlv493d::setRegBits(uint8_t regMaskIndex, uint8_t data) { if(regMaskIndex < TLV493D_NUM_OF_REGMASKS) { tlv493d::setToRegs(&(tlv493d::regMasks[regMaskIndex]), mInterface.regWriteData, data); } } uint8_t Tlv493d::getRegBits(uint8_t regMaskIndex) { if(regMaskIndex < TLV493D_NUM_OF_REGMASKS) { const tlv493d::RegMask_t *mask = &(tlv493d::regMasks[regMaskIndex]); if(mask->rw == REGMASK_READ) { return tlv493d::getFromRegs(mask, mInterface.regReadData); } else { return tlv493d::getFromRegs(mask, mInterface.regWriteData); } } return 0; } void Tlv493d::calcParity(void) { uint8_t i; uint8_t y = 0x00; // set parity bit to 1 // algorithm will calculate an even parity and replace this bit, // so parity becomes odd setRegBits(tlv493d::W_PARITY, 1); // combine array to one byte first for(i = 0; i < TLV493D_BUSIF_WRITESIZE; i++) { y ^= mInterface.regWriteData[i]; } // combine all bits of this byte y = y ^ (y >> 1); y = y ^ (y >> 2); y = y ^ (y >> 4); // parity is in the LSB of y setRegBits(tlv493d::W_PARITY, y&0x01); } int16_t Tlv493d::concatResults(uint8_t upperByte, uint8_t lowerByte, bool upperFull) { int16_t value=0x0000; //16-bit signed integer for 12-bit values of sensor if(upperFull) { value=upperByte<<8; value|=(lowerByte&0x0F)<<4; } else { value=(upperByte&0x0F)<<12; value|=lowerByte<<4; } value>>=4; //shift left so that value is a signed 12 bit integer return value; }