Driver for the HSCDTD008A Geomagnetic Sensor.

Dependents:   HSCDTD008A_Hello

Files at this revision

API Documentation at this revision

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