Driver for the HSCDTD008A Geomagnetic Sensor.

Dependents:   HSCDTD008A_Hello

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HSCDTD008A.cpp Source File

HSCDTD008A.cpp

00001 /*
00002  * Copyright (c) 2020 Zoltan Hudak <hudakz@outlook.com>
00003  * All rights reserved.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include "mbed.h"
00020 #include "HSCDTD008A.h"
00021 
00022 /*$off*/
00023 const char STB      = 0x0C; // Self test response
00024 const char INFO1    = 0x0D; // More info version
00025 const char INFO2    = 0x0E; // More info ALPS
00026 const char WIA      = 0x0F; // Who I am
00027 const char OUTX_LSB = 0x10; // Output X LSB
00028 const char OUTX_MSB = 0x11; // Output X MSB
00029 const char OUTY_LSB = 0x12; // Output Y LSB
00030 const char OUTY_MSB = 0x13; // Output Y MSB
00031 const char OUTZ_LSB = 0x14; // Output Z LSB
00032 const char OUTZ_MSB = 0x15; // Output Z MSB
00033 const char STAT     = 0x18; // Status
00034 const char FFPT     = 0x19; // FIFO Pointer Status
00035 const char CTRL1    = 0x1B; // Control1
00036 const char CTRL2    = 0x1C; // Control2
00037 const char CTRL3    = 0x1D; // Control3
00038 const char CTRL4    = 0x1E; // Control4
00039 const char OFFX_LSB = 0x20; // Offset X LSB
00040 const char OFFX_MSB = 0x21; // Offset X MSB
00041 const char OFFY_LSB = 0x22; // Offset Y LSB
00042 const char OFFY_MSB = 0x23; // Offset Y MSB
00043 const char OFFZ_LSB = 0x24; // Offset Z LSB
00044 const char OFFZ_MSB = 0x25; // Offset Z MSB
00045 const char ITHR_LSB = 0x26; // Interrupt Threshold LSB. Comparison value.Note: Enabled if CTRL2.FCO
00046 const char ITHR_MSB = 0x27; // Interrupt Threshold MSB. Not Used (Read Only)
00047 const char TEMP     = 0x31; // Temperature Data, Signed Integer. LSB = 1°C, 1000 0000 = -128°C, 0000 0000 = 0°C, 0111 1111 = 127°C
00048 //
00049 /**
00050  * @brief
00051  * @note
00052  * @param
00053  * @retval
00054  */
00055 void printBinary(uint8_t val)
00056 {
00057     for (int i = 7; i >= 0; i--) {
00058         if (val & (1<< i))
00059             putc('1', stdout);
00060         else
00061             putc('0', stdout);
00062     }
00063 }
00064 /*$on*/
00065 
00066 /**
00067  * @brief   Constructor
00068  * @note
00069  * @param
00070  * @retval
00071  */
00072 HSCDTD008A::HSCDTD008A(PinName sda, PinName scl, uint8_t addr /*= 0x0C*/ ) :
00073     _i2c(new I2C(sda, scl)),
00074     _addr(addr << 1),           // convert to 8bit address
00075     _x(0),
00076     _y(0),
00077     _z(0)
00078 {
00079     _i2c->frequency(400000);    // select 400kHz clock
00080 }
00081 
00082 /**
00083  * @brief
00084  * @note
00085  * @param
00086  * @retval
00087  */
00088 int16_t HSCDTD008A::toInt16(uint16_t word)
00089 {
00090     if (word & (1 << 7))
00091         return(-(uint16_t(~word + 1)));
00092     else
00093         return(word);
00094 }
00095 
00096 /**
00097  * @brief
00098  * @note
00099  * @param
00100  * @retval
00101  */
00102 void HSCDTD008A::softReset()
00103 {
00104     const char  soft_reset[] = { CTRL3, (1 << SRST) };
00105     char        ret;
00106 
00107     _i2c->write(_addr, soft_reset, 2);
00108 
00109     while (true) {
00110         ThisThread::sleep_for(1ms);
00111 
00112         // read CTRL3 register
00113         _i2c->write(_addr, &CTRL3, 1);
00114         _i2c->read(_addr, &ret, 1);
00115 
00116         if (ret & (1 << SRST) == 0) {
00117             break;  // done
00118         }
00119     }
00120 }
00121 
00122 /**
00123  * @brief
00124  * @note
00125  * @param
00126  * @retval
00127  */
00128 uint8_t HSCDTD008A::selfTest()
00129 {
00130     const char  start_selftest[] = { CTRL3, (1 << STC) };
00131     char        ret;
00132 
00133     _i2c->write(_addr, start_selftest, 2);
00134 
00135     // read Status register
00136     _i2c->write(_addr, &STB, 1);
00137     _i2c->read(_addr, &ret, 1);
00138 
00139     if (ret == 0xAA) {
00140         ThisThread::sleep_for(1ms);
00141 
00142         // read Status register
00143         _i2c->write(_addr, &STB, 1);
00144         _i2c->read(_addr, &ret, 1);
00145         if (ret == 0x55) {
00146             return OK;
00147         }
00148     }
00149 
00150     return ERROR;
00151 }
00152 
00153 /**
00154  * @brief
00155  * @note
00156  * @param
00157  * @retval
00158  */
00159 void HSCDTD008A::calibrateOffsets()
00160 {
00161     const char  start_calibration[] = { CTRL3, (1 << OCL) };
00162     char        ret;
00163 
00164     _i2c->write(_addr, start_calibration, 2);
00165 
00166     while (true) {
00167         ThisThread::sleep_for(1ms);
00168 
00169         // read Control3 register
00170         _i2c->write(_addr, &CTRL3, 1);
00171         _i2c->read(_addr, &ret, 1);
00172 
00173         if ((ret & (1 << OCL)) == 0) {
00174             break;  // done
00175         }
00176     }
00177 }
00178 
00179 /**
00180  * @brief
00181  * @note
00182  * @param
00183  * @retval
00184  */
00185 void HSCDTD008A::setDriftOffsetX(uint16_t val)
00186 {
00187     const char  set_offx_lsb[] = { OFFX_LSB, (char)(val & 0xFF) };
00188     const char  set_offx_msb[] = { OFFX_MSB, (char)((val >> 8) & 0xFF) };
00189 
00190     _i2c->write(_addr, set_offx_lsb, 2);
00191     _i2c->write(_addr, set_offx_msb, 2);
00192 }
00193 
00194 /**
00195  * @brief
00196  * @note
00197  * @param
00198  * @retval
00199  */
00200 void HSCDTD008A::setDriftOffsetY(uint16_t val)
00201 {
00202     const char  set_offy_lsb[] = { OFFY_LSB, (char)(val & 0xFF) };
00203     const char  set_offy_msb[] = { OFFY_MSB, (char)((val >> 8) & 0xFF) };
00204 
00205     _i2c->write(_addr, set_offy_lsb, 2);
00206     _i2c->write(_addr, set_offy_msb, 2);
00207 }
00208 
00209 /**
00210  * @brief
00211  * @note
00212  * @param
00213  * @retval
00214  */
00215 void HSCDTD008A::setDriftOffsetZ(uint16_t val)
00216 {
00217     const char  set_offz_lsb[] = { OFFZ_LSB, (char)(val & 0xFF) };
00218     const char  set_offz_msb[] = { OFFZ_MSB, (char)((val >> 8) & 0xFF) };
00219 
00220     _i2c->write(_addr, set_offz_lsb, 2);
00221     _i2c->write(_addr, set_offz_msb, 2);
00222 }
00223 
00224 /**
00225  * @brief
00226  * @note
00227  * @param
00228  * @retval
00229  */
00230 void HSCDTD008A::compensateTemp()
00231 {
00232     const char  measure_temperature[] = { CTRL3, (1 << TCS) };
00233     char        ret;
00234 
00235     forcedMode();
00236     _i2c->write(_addr, measure_temperature, 2);
00237 
00238     while (true) {
00239         ThisThread::sleep_for(1ms);
00240 
00241         // read Status register
00242         _i2c->write(_addr, &STAT, 1);
00243         _i2c->read(_addr, &ret, 1);
00244 
00245         if (ret & (1 << TRDY)) {
00246             break;  // done
00247         }
00248     }
00249 
00250     standbyMode();
00251 }
00252 
00253 /**
00254  * @brief
00255  * @note
00256  * @param
00257  * @retval
00258  */
00259 void HSCDTD008A::enableFifo()
00260 {
00261     const char  enable_fifo[] = { CTRL2, (1 << FF) };
00262 
00263     _i2c->write(_addr, enable_fifo, 2);
00264 }
00265 
00266 /**
00267  * @brief
00268  * @note
00269  * @param
00270  * @retval
00271  */
00272 void HSCDTD008A::disableFifo()
00273 {
00274     const char  enable_fifo[] = { CTRL2, (0 << FF) };
00275 
00276     _i2c->write(_addr, enable_fifo, 2);
00277 }
00278 
00279 /**
00280  * @brief
00281  * @note
00282  * @param
00283  * @retval
00284  */
00285 uint8_t HSCDTD008A::getFifoPointer()
00286 {
00287     char    ret;
00288 
00289     // read FIFO pointer register
00290     _i2c->write(_addr, &FFPT, 1);
00291     _i2c->read(_addr, &ret, 1);
00292 
00293     return(ret & FP);
00294 }
00295 
00296 /**
00297  * @brief
00298  * @note
00299  * @param
00300  * @retval
00301  */
00302 bool HSCDTD008A::isFifoFull()
00303 {
00304     char    ret;
00305 
00306     // read Status register
00307     _i2c->write(_addr, &STAT, 1);
00308     _i2c->read(_addr, &ret, 1);
00309 
00310     if (ret & (1 << FFU))
00311         return true;
00312     else
00313         return false;
00314 }
00315 
00316 /**
00317  * @brief
00318  * @note
00319  * @param
00320  * @retval
00321  */
00322 bool HSCDTD008A::isFifoOverrun()
00323 {
00324     char    ret;
00325 
00326     // read Status register
00327     _i2c->write(_addr, &STAT, 1);
00328     _i2c->read(_addr, &ret, 1);
00329 
00330     if (ret & ((1 << FFU) | (1 << DOR)))
00331         return true;
00332     else
00333         return false;
00334 }
00335 
00336 /**
00337  * @brief
00338  * @note
00339  * @param
00340  * @retval
00341  */
00342 bool HSCDTD008A::isDataReady()
00343 {
00344     char    ret;
00345 
00346     // read Status register
00347     _i2c->write(_addr, &STAT, 1);
00348     _i2c->read(_addr, &ret, 1);
00349 
00350     if (ret & (1 << DRDY) == 0)
00351         return true;
00352     else
00353         return false;
00354 }
00355 
00356 /**
00357  * @brief
00358  * @note
00359  * @param
00360  * @retval
00361  */
00362 void HSCDTD008A::standbyMode()
00363 {
00364     const char  select_standby_mode[] = { CTRL1, (0 << PC) };
00365 
00366     _i2c->write(_addr, select_standby_mode, 2);
00367 }
00368 
00369 /**
00370  * @brief
00371  * @note
00372  * @param
00373  * @retval
00374  */
00375 void HSCDTD008A::normalMode(uint8_t odr /*= 0b01*/, bool enableDataReady /*= false*/ )
00376 {
00377     const char  enable_data_ready[] = { CTRL2, (1 << DEN) | (0 << DRP) };                   // enable Data Ready with  ACTIVE LOW control
00378     const char  select_normal_mode[] = { CTRL1, (1 << PC) | (0b11 << ODR) | (0 << FS) };    // set active mode to normal
00379 
00380     if (enableDataReady)
00381         _i2c->write(_addr, enable_data_ready, 2);
00382 
00383     _i2c->write(_addr, select_normal_mode, 2);
00384 }
00385 
00386 /**
00387  * @brief
00388  * @note
00389  * @param
00390  * @retval
00391  */
00392 void HSCDTD008A::forcedMode()
00393 {
00394     const char  select_forced_mode[] = { CTRL1, (1 << PC) | (1 << FS) };
00395 
00396     _i2c->write(_addr, select_forced_mode, 2);
00397 }
00398 
00399 /**
00400  * @brief
00401  * @note
00402  * @param
00403  * @retval
00404  */
00405 bool HSCDTD008A::getResolution()
00406 {
00407     char    ret;
00408 
00409     // read CTRL4 register
00410     _i2c->write(_addr, &CTRL4, 1);
00411     _i2c->read(_addr, &ret, 1);
00412 
00413     // check RS bit
00414     if (ret & (1 << RS))
00415         return true;    // 15bit output resolution
00416     else
00417         return false;   // 14bit output resolution
00418 }
00419 
00420 /**
00421  * @brief
00422  * @note
00423  * @param
00424  * @retval
00425  */
00426 void HSCDTD008A::setResolution(bool fifteen_bits)
00427 {
00428     char    ret;
00429     char    cmd[2] = { CTRL4, 0 };
00430 
00431     // read CTRL4 register
00432     _i2c->write(_addr, &CTRL4, 1);
00433     _i2c->read(_addr, &ret, 1);
00434 
00435     if (fifteen_bits)
00436         ret |= (1 << RS);   // set RS bit
00437     else
00438         ret &= ~(0 << RS);  // clear RS bit
00439     cmd[1] = RS;            // set output resolution
00440     _i2c->write(_addr, cmd, 2);
00441 }
00442 
00443 /**
00444  * @brief
00445  * @note    Shall be called in forced mode
00446  * @param
00447  * @retval
00448  */
00449 uint8_t HSCDTD008A::measure()
00450 {
00451     const char  start_measurement[] = { CTRL3, (1 << FRC) };    // Start measurement in force mode (returns to 0 when finished)
00452     char        ret;
00453     char        data[6];
00454 
00455     //  Start measurement in forced mode
00456     _i2c->write(_addr, &STAT, 1);
00457     _i2c->read(_addr, &ret, 1);
00458     if (!(ret & (1 << DRDY))) {
00459         _i2c->write(_addr, start_measurement, 2);
00460     }
00461 
00462     // Read Status register
00463     _i2c->write(_addr, &STAT, 1);
00464     _i2c->read(_addr, &ret, 1);
00465 
00466     // Is data ready?
00467     if (ret & (1 << DRDY)) {
00468         readData();
00469 
00470         return OK;
00471     }
00472 
00473     return ERROR;
00474 }
00475 
00476 /**
00477  * @brief
00478  * @note
00479  * @param
00480  * @retval
00481  */
00482 void HSCDTD008A::readData()
00483 {
00484     char    data[6];
00485 
00486     _i2c->write(_addr, &OUTX_LSB, 1);
00487     _i2c->read(_addr, data, 6);
00488 
00489     _x = *((uint16_t*) &data[0]);   // two bytes, LSB first
00490     _y = *((uint16_t*) &data[2]);   // two bytes, LSB first
00491     _z = *((uint16_t*) &data[4]);   // two bytes, LSB first
00492 
00493     // Debug print
00494     //printf("x = ");
00495     //printBinary(data[1]);
00496     //putc(' ', stdout);
00497     //printBinary(data[0]);
00498     //printf(" = %d \t= %f mT\r\n", _x, x());
00499     //printf("y = ");
00500     //printBinary(data[3]);
00501     //putc(' ', stdout);
00502     //printBinary(data[2]);
00503     //printf(" = %d \t= %f mT\r\n", _y, y());
00504     //printf("z = ");
00505     //printBinary(data[5]);
00506     //putc(' ', stdout);
00507     //printBinary(data[4]);
00508     //printf(" = %d \t= %f mT\r\n", _z, z());
00509 }
00510 
00511 /**
00512  * @brief
00513  * @note
00514  * @param
00515  * @retval
00516  */
00517 float HSCDTD008A::x()
00518 {
00519     return toInt16(_x) * RANGE / RESOL; // mT
00520 }
00521 
00522 /**
00523  * @brief
00524  * @note
00525  * @param
00526  * @retval
00527  */
00528 float HSCDTD008A::y()
00529 {
00530     return toInt16(_y) * RANGE / RESOL; // mT
00531 }
00532 
00533 /**
00534  * @brief
00535  * @note
00536  * @param
00537  * @retval
00538  */
00539 float HSCDTD008A::z()
00540 {
00541     return toInt16(_z) * RANGE / RESOL; // mT
00542 }