Driver for the HSCDTD008A Geomagnetic Sensor.
HSCDTD008A.cpp
- Committer:
- hudakz
- Date:
- 2021-06-20
- Revision:
- 0:ccf912737de7
- Child:
- 1:b90695c17177
File content as of revision 0:ccf912737de7:
/* * 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, PinName drdy /*= NC*/, uint8_t addr /*= 0x0C*/ ) : _i2c(new I2C(sda, scl)), _drdy(drdy), _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 the 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 the 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 }