Driver for the HSCDTD008A Geomagnetic Sensor.

Dependents:   HSCDTD008A_Hello

HSCDTD008A.cpp

Committer:
hudakz
Date:
2021-06-20
Revision:
1:b90695c17177
Parent:
0:ccf912737de7

File content as of revision 1:b90695c17177:

/*
 * Copyright (c) 2020 Zoltan Hudak <hudakz@outlook.com>
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mbed.h"
#include "HSCDTD008A.h"

/*$off*/
const char STB      = 0x0C; // Self test response
const char INFO1    = 0x0D; // More info version
const char INFO2    = 0x0E; // More info ALPS
const char WIA      = 0x0F; // Who I am
const char OUTX_LSB = 0x10; // Output X LSB
const char OUTX_MSB = 0x11; // Output X MSB
const char OUTY_LSB = 0x12; // Output Y LSB
const char OUTY_MSB = 0x13; // Output Y MSB
const char OUTZ_LSB = 0x14; // Output Z LSB
const char OUTZ_MSB = 0x15; // Output Z MSB
const char STAT     = 0x18; // Status
const char FFPT     = 0x19; // FIFO Pointer Status
const char CTRL1    = 0x1B; // Control1
const char CTRL2    = 0x1C; // Control2
const char CTRL3    = 0x1D; // Control3
const char CTRL4    = 0x1E; // Control4
const char OFFX_LSB = 0x20; // Offset X LSB
const char OFFX_MSB = 0x21; // Offset X MSB
const char OFFY_LSB = 0x22; // Offset Y LSB
const char OFFY_MSB = 0x23; // Offset Y MSB
const char OFFZ_LSB = 0x24; // Offset Z LSB
const char OFFZ_MSB = 0x25; // Offset Z MSB
const char ITHR_LSB = 0x26; // Interrupt Threshold LSB. Comparison value.Note: Enabled if CTRL2.FCO
const char ITHR_MSB = 0x27; // Interrupt Threshold MSB. Not Used (Read Only)
const char TEMP     = 0x31; // Temperature Data, Signed Integer. LSB = 1°C, 1000 0000 = -128°C, 0000 0000 = 0°C, 0111 1111 = 127°C
//
/**
 * @brief
 * @note
 * @param
 * @retval
 */
void printBinary(uint8_t val)
{
    for (int i = 7; i >= 0; i--) {
        if (val & (1<< i))
            putc('1', stdout);
        else
            putc('0', stdout);
    }
}
/*$on*/

/**
 * @brief   Constructor
 * @note
 * @param
 * @retval
 */
HSCDTD008A::HSCDTD008A(PinName sda, PinName scl, uint8_t addr /*= 0x0C*/ ) :
    _i2c(new I2C(sda, scl)),
    _addr(addr << 1),           // convert to 8bit address
    _x(0),
    _y(0),
    _z(0)
{
    _i2c->frequency(400000);    // select 400kHz clock
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int16_t HSCDTD008A::toInt16(uint16_t word)
{
    if (word & (1 << 7))
        return(-(uint16_t(~word + 1)));
    else
        return(word);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::softReset()
{
    const char  soft_reset[] = { CTRL3, (1 << SRST) };
    char        ret;

    _i2c->write(_addr, soft_reset, 2);

    while (true) {
        ThisThread::sleep_for(1ms);

        // read CTRL3 register
        _i2c->write(_addr, &CTRL3, 1);
        _i2c->read(_addr, &ret, 1);

        if (ret & (1 << SRST) == 0) {
            break;  // done
        }
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
uint8_t HSCDTD008A::selfTest()
{
    const char  start_selftest[] = { CTRL3, (1 << STC) };
    char        ret;

    _i2c->write(_addr, start_selftest, 2);

    // read Status register
    _i2c->write(_addr, &STB, 1);
    _i2c->read(_addr, &ret, 1);

    if (ret == 0xAA) {
        ThisThread::sleep_for(1ms);

        // read Status register
        _i2c->write(_addr, &STB, 1);
        _i2c->read(_addr, &ret, 1);
        if (ret == 0x55) {
            return OK;
        }
    }

    return ERROR;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::calibrateOffsets()
{
    const char  start_calibration[] = { CTRL3, (1 << OCL) };
    char        ret;

    _i2c->write(_addr, start_calibration, 2);

    while (true) {
        ThisThread::sleep_for(1ms);

        // read Control3 register
        _i2c->write(_addr, &CTRL3, 1);
        _i2c->read(_addr, &ret, 1);

        if ((ret & (1 << OCL)) == 0) {
            break;  // done
        }
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::setDriftOffsetX(uint16_t val)
{
    const char  set_offx_lsb[] = { OFFX_LSB, (char)(val & 0xFF) };
    const char  set_offx_msb[] = { OFFX_MSB, (char)((val >> 8) & 0xFF) };

    _i2c->write(_addr, set_offx_lsb, 2);
    _i2c->write(_addr, set_offx_msb, 2);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::setDriftOffsetY(uint16_t val)
{
    const char  set_offy_lsb[] = { OFFY_LSB, (char)(val & 0xFF) };
    const char  set_offy_msb[] = { OFFY_MSB, (char)((val >> 8) & 0xFF) };

    _i2c->write(_addr, set_offy_lsb, 2);
    _i2c->write(_addr, set_offy_msb, 2);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::setDriftOffsetZ(uint16_t val)
{
    const char  set_offz_lsb[] = { OFFZ_LSB, (char)(val & 0xFF) };
    const char  set_offz_msb[] = { OFFZ_MSB, (char)((val >> 8) & 0xFF) };

    _i2c->write(_addr, set_offz_lsb, 2);
    _i2c->write(_addr, set_offz_msb, 2);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::compensateTemp()
{
    const char  measure_temperature[] = { CTRL3, (1 << TCS) };
    char        ret;

    forcedMode();
    _i2c->write(_addr, measure_temperature, 2);

    while (true) {
        ThisThread::sleep_for(1ms);

        // read Status register
        _i2c->write(_addr, &STAT, 1);
        _i2c->read(_addr, &ret, 1);

        if (ret & (1 << TRDY)) {
            break;  // done
        }
    }

    standbyMode();
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::enableFifo()
{
    const char  enable_fifo[] = { CTRL2, (1 << FF) };

    _i2c->write(_addr, enable_fifo, 2);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::disableFifo()
{
    const char  enable_fifo[] = { CTRL2, (0 << FF) };

    _i2c->write(_addr, enable_fifo, 2);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
uint8_t HSCDTD008A::getFifoPointer()
{
    char    ret;

    // read FIFO pointer register
    _i2c->write(_addr, &FFPT, 1);
    _i2c->read(_addr, &ret, 1);

    return(ret & FP);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
bool HSCDTD008A::isFifoFull()
{
    char    ret;

    // read Status register
    _i2c->write(_addr, &STAT, 1);
    _i2c->read(_addr, &ret, 1);

    if (ret & (1 << FFU))
        return true;
    else
        return false;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
bool HSCDTD008A::isFifoOverrun()
{
    char    ret;

    // read Status register
    _i2c->write(_addr, &STAT, 1);
    _i2c->read(_addr, &ret, 1);

    if (ret & ((1 << FFU) | (1 << DOR)))
        return true;
    else
        return false;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
bool HSCDTD008A::isDataReady()
{
    char    ret;

    // read Status register
    _i2c->write(_addr, &STAT, 1);
    _i2c->read(_addr, &ret, 1);

    if (ret & (1 << DRDY) == 0)
        return true;
    else
        return false;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::standbyMode()
{
    const char  select_standby_mode[] = { CTRL1, (0 << PC) };

    _i2c->write(_addr, select_standby_mode, 2);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::normalMode(uint8_t odr /*= 0b01*/, bool enableDataReady /*= false*/ )
{
    const char  enable_data_ready[] = { CTRL2, (1 << DEN) | (0 << DRP) };                   // enable Data Ready with  ACTIVE LOW control
    const char  select_normal_mode[] = { CTRL1, (1 << PC) | (0b11 << ODR) | (0 << FS) };    // set active mode to normal

    if (enableDataReady)
        _i2c->write(_addr, enable_data_ready, 2);

    _i2c->write(_addr, select_normal_mode, 2);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::forcedMode()
{
    const char  select_forced_mode[] = { CTRL1, (1 << PC) | (1 << FS) };

    _i2c->write(_addr, select_forced_mode, 2);
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
bool HSCDTD008A::getResolution()
{
    char    ret;

    // read CTRL4 register
    _i2c->write(_addr, &CTRL4, 1);
    _i2c->read(_addr, &ret, 1);

    // check RS bit
    if (ret & (1 << RS))
        return true;    // 15bit output resolution
    else
        return false;   // 14bit output resolution
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::setResolution(bool fifteen_bits)
{
    char    ret;
    char    cmd[2] = { CTRL4, 0 };

    // read CTRL4 register
    _i2c->write(_addr, &CTRL4, 1);
    _i2c->read(_addr, &ret, 1);

    if (fifteen_bits)
        ret |= (1 << RS);   // set RS bit
    else
        ret &= ~(0 << RS);  // clear RS bit
    cmd[1] = RS;            // set output resolution
    _i2c->write(_addr, cmd, 2);
}

/**
 * @brief
 * @note    Shall be called in forced mode
 * @param
 * @retval
 */
uint8_t HSCDTD008A::measure()
{
    const char  start_measurement[] = { CTRL3, (1 << FRC) };    // Start measurement in force mode (returns to 0 when finished)
    char        ret;
    char        data[6];

    //  Start measurement in forced mode
    _i2c->write(_addr, &STAT, 1);
    _i2c->read(_addr, &ret, 1);
    if (!(ret & (1 << DRDY))) {
        _i2c->write(_addr, start_measurement, 2);
    }

    // Read Status register
    _i2c->write(_addr, &STAT, 1);
    _i2c->read(_addr, &ret, 1);

    // Is data ready?
    if (ret & (1 << DRDY)) {
        readData();

        return OK;
    }

    return ERROR;
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void HSCDTD008A::readData()
{
    char    data[6];

    _i2c->write(_addr, &OUTX_LSB, 1);
    _i2c->read(_addr, data, 6);

    _x = *((uint16_t*) &data[0]);   // two bytes, LSB first
    _y = *((uint16_t*) &data[2]);   // two bytes, LSB first
    _z = *((uint16_t*) &data[4]);   // two bytes, LSB first

    // Debug print
    //printf("x = ");
    //printBinary(data[1]);
    //putc(' ', stdout);
    //printBinary(data[0]);
    //printf(" = %d \t= %f mT\r\n", _x, x());
    //printf("y = ");
    //printBinary(data[3]);
    //putc(' ', stdout);
    //printBinary(data[2]);
    //printf(" = %d \t= %f mT\r\n", _y, y());
    //printf("z = ");
    //printBinary(data[5]);
    //putc(' ', stdout);
    //printBinary(data[4]);
    //printf(" = %d \t= %f mT\r\n", _z, z());
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
float HSCDTD008A::x()
{
    return toInt16(_x) * RANGE / RESOL; // mT
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
float HSCDTD008A::y()
{
    return toInt16(_y) * RANGE / RESOL; // mT
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
float HSCDTD008A::z()
{
    return toInt16(_z) * RANGE / RESOL; // mT
}