Driver for the HSCDTD008A Geomagnetic Sensor.
Revision 0:ccf912737de7, committed 2021-06-20
- Comitter:
- hudakz
- Date:
- Sun Jun 20 13:55:42 2021 +0000
- Child:
- 1:b90695c17177
- Commit message:
- Driver for the HSCDTD008A Geomagnetic Sensor.
Changed in this revision
HSCDTD008A.cpp | Show annotated file Show diff for this revision Revisions of this file |
HSCDTD008A.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HSCDTD008A.cpp Sun Jun 20 13:55:42 2021 +0000 @@ -0,0 +1,543 @@ +/* + * 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 +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HSCDTD008A.h Sun Jun 20 13:55:42 2021 +0000 @@ -0,0 +1,139 @@ +/* + * 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/>. + */ + +#ifndef HSCDTD008A_H +#define HSCDTD008A_H + +#include "mbed.h" + +#define RANGE (2.4 + 2.4) // mT (+/- 2.4 mT) +#define RESOL 1024 // resolution = 1024 bit + +/* + * Status Register (STAT) + */ + +// (Read Only) +#define DRDY 6 // Data Ready Detection +#define DOR 5 // Data Overrun Detection +#define FFU 2 // FIFO full alarm +#define TRDY 1 // Temperature ready +#define ORDY 0 // Offset ready + +/* + * FIFO Pointer Status Register (FFPT). + */ + +// (Read Only) +#define FP (0b00001111) // Number of data in FIFO : 0 - 8 + +/* + * Control 1 Register (CTRL1) + */ + +// (Write/Read) +#define PC 7 // Power Mode Control 0 = Stand-by Mode (Default), 1 = Active Mode +#define ODR 3 // Output Data Rate Control in Normal State 00 = 0.5 Hz, 01 = 10Hz (Default), 10 = 20Hz, 11 = 100Hz +#define FS 1 // State Control in Active Mode 0 = Normal State, 1 = Force State (Default) + +/* + * Control 2 Register (CTRL2) + */ + +// - When a CTRL2 register value was changed during the measurement, +// The contents of the change are reflected after measurement. +// (Write/Read) +#define AVG 7 // Must be used default setting. 0 = (Default) +#define FCO 6 // Data storage method at FIFO. 0 = Direct (Default) , 1 = Comparison +#define AOR 5 // Choice of method of data Comparison at FIFO. 0 = OR(Default) , 1 = AND +#define FF 4 // FIFO Enable. 0 = Disable (Default) , 1 = Enable +#define DEN 3 // Data Ready Function Control Enable. 0 = Disabled (Default), 1 = Enabled +#define DRP 2 // DRDY signal active level control. 0 = ACTIVE LOW, 1 = ACTIVE HIGH (Default) +#define DTS 1 // Must be used default setting. 0 = (Default) +#define DOS 0 // Must be used default setting. 0 = (Default) + +/* + * Control 3 Register (CTRL3) + */ + +// - Bit control at the same time is prohibited. +// - Priority of this register is MSB. +// (Write/Read) +#define SRST 7 // Soft Reset Control Enable. 0 = No Action (Default), 1 = Soft Reset, Note: return to zero after soft reset. +#define FRC 6 // Start to Measure in Force State. 0 = No Action (Default), 1 = Measurement Start, Note: return to zero after measurement. +#define STC 4 // Self Test Control Enable. 0 = No Action (Default), 1 = Set parameters to Self Test Response (STB) register., Note: return to zero immediately. +#define TCS 1 // Start to Measure Temperature in Active Mode. 0 = No Action (Default), 1 = Measurement Start +#define OCL 0 // Start to Calibrate Offset in Active Mode. 0 = No Action (Default), 1 = Action + +/* + * Control 4 Register (CTRL4) + */ + +// - When a CTRL4 register value was changed during the measurement, +// The contents of the change are reflected after measurement. +#define MMD 6 // Must be used default setting. 10 = (Default) +#define RS 4 // Set Dynamic range of output data. 0 = 14 bit signed value (-8192 to +8191) (Default), 1 = 15 bit signed value (-16384 to +16383) +#define AS 3 // Must be used default setting. 0 = (Default) +// +// +#define OK 0 +#define ERROR -1 + +// Function prototypes +void printBinary(const unsigned char val); + +class HSCDTD008A +{ +public: + HSCDTD008A(PinName sda, PinName scl, PinName drdy = NC, uint8_t addr = 0x0C); + int16_t toInt16(uint16_t word); + void standbyMode(); + void normalMode(uint8_t odr = 0b01, bool enableDataReady = false); + void forcedMode(); + void softReset(); + uint8_t selftest(); + void calibrateOffsets(); + void setDriftOffsetX(uint16_t val); + void setDriftOffsetY(uint16_t val); + void setDriftOffsetZ(uint16_t val); + void compensateTemp(); + void enableFifo(); + void disableFifo(); + uint8_t getFifoPointer(); + bool isFifoFull(); + bool isFifoOverrun(); + bool isDataReady(); + bool getResolution(); + void setResolution(bool fifteen_bits); + uint8_t measure(); + void readData(); + float x(); + float y(); + float z(); + float bearing(); +private: + I2C* _i2c; + InterruptIn _drdy; + const uint8_t _addr; + uint16_t _x; + uint16_t _y; + uint16_t _z; + Thread _thread; + EventQueue _eventQueue; +}; +#endif // HSCDTD008A_H