Silicon Labs Si7210 device driver: I2C Hall Effect Magnetic Position and Temperature Sensor

Committer:
sm168j
Date:
Sat Oct 14 20:10:05 2017 -0500
Revision:
3:beca2e65b1b0
Parent:
2:d85076c18c80
Corrected temperature reading calculation

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ninensei 1:8cb452601311 1 /* mbed Microcontroller Library
ninensei 1:8cb452601311 2 * Copyright (c) 2017 AT&T, IIoT Foundry, Plano, TX, USA
ninensei 1:8cb452601311 3 *
ninensei 1:8cb452601311 4 * Licensed under the Apache License, Version 2.0 (the "License");
ninensei 1:8cb452601311 5 * you may not use this file except in compliance with the License.
ninensei 1:8cb452601311 6 * You may obtain a copy of the License at
ninensei 1:8cb452601311 7 *
ninensei 1:8cb452601311 8 * http://www.apache.org/licenses/LICENSE-2.0
ninensei 1:8cb452601311 9 *
ninensei 1:8cb452601311 10 * Unless required by applicable law or agreed to in writing, software
ninensei 1:8cb452601311 11 * distributed under the License is distributed on an "AS IS" BASIS,
ninensei 1:8cb452601311 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
ninensei 1:8cb452601311 13 * See the License for the specific language governing permissions and
ninensei 1:8cb452601311 14 * limitations under the License.
ninensei 1:8cb452601311 15 */
ninensei 1:8cb452601311 16
ninensei 1:8cb452601311 17 /** \addtogroup drivers */
ninensei 0:ee9d6b55bf85 18
ninensei 1:8cb452601311 19 /** Support for Silicon Labs SI7210: Magnetic field and temperature sensor
ninensei 1:8cb452601311 20 *
ninensei 1:8cb452601311 21 * Example:
ninensei 1:8cb452601311 22 * @code
ninensei 1:8cb452601311 23 *
ninensei 1:8cb452601311 24 * #include "mbed.h"
ninensei 1:8cb452601311 25 * #include "SI7210.h"
ninensei 1:8cb452601311 26 *
ninensei 1:8cb452601311 27 * I2C i2c(I2C_SDA, I2C_SCL);
ninensei 1:8cb452601311 28 * SI7210<I2C> si7210(&i2c, SI7210_BASE_ADDR_7BIT);
ninensei 1:8cb452601311 29 *
ninensei 1:8cb452601311 30 * int main() {
ninensei 1:8cb452601311 31 * si7210_measurements_t data;
ninensei 1:8cb452601311 32 * bool ok;
ninensei 1:8cb452601311 33 *
ninensei 1:8cb452601311 34 * ok = si7210.enable() &&
ninensei 1:8cb452601311 35 * si7210.read(&data) &&
ninensei 1:8cb452601311 36 * si7210.disable();
sm168j 3:beca2e65b1b0 37 *
ninensei 1:8cb452601311 38 * if (ok) {
ninensei 1:8cb452601311 39 printf("Mag T: %f\r\n", data.mag_T);
ninensei 1:8cb452601311 40 printf("temp C/F: %f/%f\r\n", data.temp_C, data.temp_C * 9 / 5 + 32);
ninensei 1:8cb452601311 41 * } else {
ninensei 1:8cb452601311 42 * printf("si7210 error!\r\n");
ninensei 1:8cb452601311 43 * }
ninensei 1:8cb452601311 44 * }
ninensei 1:8cb452601311 45 * @endcode
ninensei 1:8cb452601311 46 * @ingroup drivers
ninensei 1:8cb452601311 47 */
ninensei 1:8cb452601311 48
ninensei 1:8cb452601311 49 #pragma once
ninensei 1:8cb452601311 50
ninensei 1:8cb452601311 51 #define ARAUTOINC__ARAUTOINC_MASK 0x01
ninensei 1:8cb452601311 52 #define OTP_CTRL__OPT_BUSY_MASK 0x01
ninensei 1:8cb452601311 53 #define OTP_CTRL__OPT_READ_EN_MASK 0x02
ninensei 1:8cb452601311 54 #define POWER_CTRL__SLEEP_MASK 0x01
ninensei 1:8cb452601311 55 #define POWER_CTRL__STOP_MASK 0x02
ninensei 1:8cb452601311 56 #define POWER_CTRL__ONEBURST_MASK 0x04
ninensei 1:8cb452601311 57 #define POWER_CTRL__USESTORE_MASK 0x08
ninensei 1:8cb452601311 58 #define POWER_CTRL__MEAS_MASK 0x80
ninensei 1:8cb452601311 59 #define DSPSIGSEL__MAG_VAL_SEL 0
ninensei 1:8cb452601311 60 #define DSPSIGSEL__TEMP_VAL_SEL 1
ninensei 1:8cb452601311 61
ninensei 1:8cb452601311 62 /** I2C registers for Si72xx */
ninensei 1:8cb452601311 63 #define SI72XX_OTP_TEMP_OFFSET 0x1D
ninensei 1:8cb452601311 64 #define SI72XX_OTP_TEMP_GAIN 0x1E
ninensei 1:8cb452601311 65 #define SI72XX_HREVID 0xC0
ninensei 1:8cb452601311 66 #define SI72XX_DSPSIGM 0xC1
ninensei 1:8cb452601311 67 #define SI72XX_DSPSIGL 0xC2
ninensei 1:8cb452601311 68 #define SI72XX_DSPSIGSEL 0xC3
ninensei 1:8cb452601311 69 #define SI72XX_POWER_CTRL 0xC4
ninensei 1:8cb452601311 70 #define SI72XX_ARAUTOINC 0xC5
ninensei 1:8cb452601311 71 #define SI72XX_CTRL1 0xC6
ninensei 1:8cb452601311 72 #define SI72XX_CTRL2 0xC7
ninensei 1:8cb452601311 73 #define SI72XX_SLTIME 0xC8
ninensei 1:8cb452601311 74 #define SI72XX_CTRL3 0xC9
ninensei 1:8cb452601311 75 #define SI72XX_A0 0xCA
ninensei 1:8cb452601311 76 #define SI72XX_A1 0xCB
ninensei 1:8cb452601311 77 #define SI72XX_A2 0xCC
ninensei 1:8cb452601311 78 #define SI72XX_CTRL4 0xCD
ninensei 1:8cb452601311 79 #define SI72XX_A3 0xCE
ninensei 1:8cb452601311 80 #define SI72XX_A4 0xCF
ninensei 1:8cb452601311 81 #define SI72XX_A5 0xD0
ninensei 1:8cb452601311 82 #define SI72XX_OTP_ADDR 0xE1
ninensei 1:8cb452601311 83 #define SI72XX_OTP_DATA 0xE2
ninensei 1:8cb452601311 84 #define SI72XX_OTP_CTRL 0xE3
ninensei 1:8cb452601311 85 #define SI72XX_TM_FG 0xE4
ninensei 0:ee9d6b55bf85 86
ninensei 0:ee9d6b55bf85 87 #define SI7210_BASE_ADDR_7BIT 0x30
ninensei 0:ee9d6b55bf85 88
ninensei 0:ee9d6b55bf85 89 typedef struct {
ninensei 0:ee9d6b55bf85 90 float mag_T;
ninensei 0:ee9d6b55bf85 91 float temp_C;
ninensei 0:ee9d6b55bf85 92 } si7210_measurements_t;
ninensei 0:ee9d6b55bf85 93
ninensei 1:8cb452601311 94 template <class T>
ninensei 0:ee9d6b55bf85 95 class SI7210 {
ninensei 0:ee9d6b55bf85 96 public:
ninensei 0:ee9d6b55bf85 97 /**
ninensei 0:ee9d6b55bf85 98 * Constructor
ninensei 0:ee9d6b55bf85 99 *
ninensei 0:ee9d6b55bf85 100 * @param i2c I2C class servicing the strip
ninensei 0:ee9d6b55bf85 101 */
ninensei 1:8cb452601311 102 SI7210(T * i2c, uint8_t addr_7bit) : _i2c(i2c) {
ninensei 1:8cb452601311 103 _isTempOffsetAndGainValid = false;
ninensei 1:8cb452601311 104 _enabled = false;
ninensei 0:ee9d6b55bf85 105 _addr_8bit = ((addr_7bit & 0x3) + SI7210_BASE_ADDR_7BIT) << 1;
ninensei 0:ee9d6b55bf85 106 }
ninensei 1:8cb452601311 107
ninensei 1:8cb452601311 108 /**
ninensei 1:8cb452601311 109 * Activate the sensor (wake if sleeping).
ninensei 1:8cb452601311 110 *
ninensei 1:8cb452601311 111 * @returns true (success) or false (failure)
ninensei 1:8cb452601311 112 */
ninensei 1:8cb452601311 113 bool enable(void) {
ninensei 1:8cb452601311 114 bool ok = _i2c_transfer(_addr_8bit, NULL, 0, 0);
ninensei 1:8cb452601311 115 if (ok)
ninensei 1:8cb452601311 116 _enabled = true;
ninensei 1:8cb452601311 117 return ok;
ninensei 1:8cb452601311 118 }
ninensei 1:8cb452601311 119
ninensei 1:8cb452601311 120 /**
ninensei 1:8cb452601311 121 * Deactivate the sensor (puts it to sleep)
ninensei 1:8cb452601311 122 *
ninensei 1:8cb452601311 123 * @returns true (success) or false (failure)
ninensei 1:8cb452601311 124 */
ninensei 1:8cb452601311 125 bool disable(void) {
ninensei 1:8cb452601311 126 bool ok = _write_reg(SI72XX_POWER_CTRL, POWER_CTRL__SLEEP_MASK);
ninensei 1:8cb452601311 127 if (ok)
ninensei 1:8cb452601311 128 _enabled = ok;
ninensei 1:8cb452601311 129 return ok;
ninensei 1:8cb452601311 130 }
ninensei 1:8cb452601311 131
ninensei 0:ee9d6b55bf85 132 /**
ninensei 0:ee9d6b55bf85 133 * Read temperature and humidity
ninensei 0:ee9d6b55bf85 134 *
ninensei 0:ee9d6b55bf85 135 * @param data points to struct to store measurements in. Stucture is
ninensei 0:ee9d6b55bf85 136 * valid only when function returns success indication.
ninensei 0:ee9d6b55bf85 137 *
ninensei 0:ee9d6b55bf85 138 * @returns true (success) or false (failure)
ninensei 0:ee9d6b55bf85 139 */
ninensei 1:8cb452601311 140 bool read(si7210_measurements_t * data) {
ninensei 1:8cb452601311 141 uint16_t magRaw;
ninensei 1:8cb452601311 142 uint16_t tempRaw;
sm168j 3:beca2e65b1b0 143
ninensei 1:8cb452601311 144 bool ok = _write_reg(SI72XX_ARAUTOINC, ARAUTOINC__ARAUTOINC_MASK)
ninensei 1:8cb452601311 145 && _write_reg(SI72XX_DSPSIGSEL, DSPSIGSEL__MAG_VAL_SEL) //capture mag field measurement
ninensei 1:8cb452601311 146 && _write_reg(SI72XX_POWER_CTRL, POWER_CTRL__ONEBURST_MASK)
ninensei 1:8cb452601311 147 && _read_regs(SI72XX_DSPSIGM, 2, &magRaw)
ninensei 1:8cb452601311 148 && _write_reg(SI72XX_DSPSIGSEL, DSPSIGSEL__TEMP_VAL_SEL) //capture temp measurement
ninensei 1:8cb452601311 149 && _write_reg(SI72XX_POWER_CTRL, POWER_CTRL__ONEBURST_MASK)
ninensei 1:8cb452601311 150 && _read_regs(SI72XX_DSPSIGM, 2, &tempRaw);
ninensei 1:8cb452601311 151
ninensei 1:8cb452601311 152 if (ok && !_isTempOffsetAndGainValid) {
sm168j 3:beca2e65b1b0 153 signed char otpTempOffset;
sm168j 3:beca2e65b1b0 154 signed char otpTempGain;
sm168j 3:beca2e65b1b0 155
ninensei 1:8cb452601311 156 ok = _read_otp(SI72XX_OTP_TEMP_OFFSET, &otpTempOffset)
ninensei 1:8cb452601311 157 && _read_otp(SI72XX_OTP_TEMP_GAIN, &otpTempGain);
ninensei 1:8cb452601311 158 if (ok) {
ninensei 1:8cb452601311 159 _tempOffset = (float)otpTempOffset / 16;
ninensei 1:8cb452601311 160 _tempGain = 1 + (float)otpTempGain / 2048;
ninensei 1:8cb452601311 161 _isTempOffsetAndGainValid = true;
ninensei 1:8cb452601311 162 }
ninensei 1:8cb452601311 163 }
sm168j 3:beca2e65b1b0 164
ninensei 1:8cb452601311 165 if (ok) {
ninensei 1:8cb452601311 166 magRaw = ((magRaw >> 8) & 0xff) + ((magRaw & 0xff) << 8);
ninensei 1:8cb452601311 167 tempRaw = ((tempRaw >> 8) & 0xff) + ((tempRaw & 0xff) << 8);
ninensei 1:8cb452601311 168 ok = (magRaw & 0x8000) && (tempRaw & 0x8000);
ninensei 1:8cb452601311 169 data->mag_T = (float)(magRaw - 0xC000) * 0.00125F;
ninensei 1:8cb452601311 170 data->temp_C = (float)((tempRaw & ~0x8000) >> 3);
ninensei 1:8cb452601311 171 data->temp_C = _tempGain * (-3.83e-6F * data->temp_C * data->temp_C + 0.16094F * data->temp_C - 279.80F - 0.222F * 3.0F) + _tempOffset;
ninensei 1:8cb452601311 172 }
sm168j 3:beca2e65b1b0 173
ninensei 1:8cb452601311 174 return ok;
ninensei 1:8cb452601311 175 }
ninensei 0:ee9d6b55bf85 176
ninensei 0:ee9d6b55bf85 177 protected:
ninensei 1:8cb452601311 178
ninensei 0:ee9d6b55bf85 179 /**
ninensei 0:ee9d6b55bf85 180 * I2C read/write helper function
ninensei 0:ee9d6b55bf85 181 *
ninensei 0:ee9d6b55bf85 182 * @param address is the register to read/write
ninensei 0:ee9d6b55bf85 183 * @param buff holds the data to write and recieves the data to read
ninensei 0:ee9d6b55bf85 184 * @param writeSize is the number of bytes to write to register
ninensei 0:ee9d6b55bf85 185 * @param readSize is the number of bytes to retrieve from device
ninensei 0:ee9d6b55bf85 186 *
ninensei 0:ee9d6b55bf85 187 * @returns true (success) or false (failure)
ninensei 0:ee9d6b55bf85 188 */
ninensei 1:8cb452601311 189 bool _i2c_transfer(int address, void * buff, size_t writeSize, size_t readSize) {
ninensei 1:8cb452601311 190 bool ok;
ninensei 1:8cb452601311 191 bool expect_response = (readSize != 0);
sm168j 3:beca2e65b1b0 192
ninensei 1:8cb452601311 193 ok = !_i2c->write(address, (char*)buff, writeSize, expect_response);
ninensei 1:8cb452601311 194 if (ok && expect_response)
ninensei 1:8cb452601311 195 ok = !_i2c->read(address, (char*)buff, readSize);
sm168j 3:beca2e65b1b0 196
ninensei 1:8cb452601311 197 return ok;
ninensei 1:8cb452601311 198 }
ninensei 1:8cb452601311 199
ninensei 1:8cb452601311 200 /**
ninensei 1:8cb452601311 201 * Write to an I2C register
ninensei 1:8cb452601311 202 *
ninensei 1:8cb452601311 203 * @param reg sensor register to write
ninensei 1:8cb452601311 204 * @param val value to write
ninensei 1:8cb452601311 205 *
ninensei 1:8cb452601311 206 * @returns true (success) or false (failure)
ninensei 1:8cb452601311 207 */
ninensei 1:8cb452601311 208 bool _write_reg(char reg, char val) {
ninensei 1:8cb452601311 209 char out[2] = {reg, val};
ninensei 1:8cb452601311 210 return 0 == _i2c->write(_addr_8bit, out, 2);
ninensei 1:8cb452601311 211 }
ninensei 1:8cb452601311 212
ninensei 0:ee9d6b55bf85 213 /**
ninensei 1:8cb452601311 214 * Read multiple sensor registers
ninensei 1:8cb452601311 215 *
ninensei 1:8cb452601311 216 * @param start_reg first sensor register to be read
ninensei 1:8cb452601311 217 * @param count number of registers to be read
ninensei 1:8cb452601311 218 * @param buff pointer to buffer where to store the register values
ninensei 1:8cb452601311 219 *
ninensei 1:8cb452601311 220 * @returns true (success) or false (failure)
ninensei 0:ee9d6b55bf85 221 */
ninensei 1:8cb452601311 222 bool _read_regs(char start_reg, char count, void * buff) {
ninensei 1:8cb452601311 223 bool ok;
ninensei 1:8cb452601311 224 ok = (0 == _i2c->write(_addr_8bit, &start_reg, 1, true))
ninensei 1:8cb452601311 225 && (0 == _i2c->read(_addr_8bit, (char *)buff, count));
ninensei 1:8cb452601311 226 return ok;
ninensei 1:8cb452601311 227 }
ninensei 0:ee9d6b55bf85 228
ninensei 1:8cb452601311 229 /**
ninensei 1:8cb452601311 230 * Read sensor OTP
ninensei 1:8cb452601311 231 *
ninensei 1:8cb452601311 232 * @param otpAddr OTP address to be read
ninensei 1:8cb452601311 233 * @param *data where to store the OTP data
ninensei 1:8cb452601311 234 *
ninensei 1:8cb452601311 235 * @returns true (success) or false (failure)
ninensei 1:8cb452601311 236 */
ninensei 1:8cb452601311 237 bool _read_otp(uint8_t otpAddr, void *data) {
ninensei 1:8cb452601311 238 uint8_t optCtrl;
sm168j 3:beca2e65b1b0 239
ninensei 1:8cb452601311 240 bool ok = _read_regs(SI72XX_OTP_CTRL, 1, &optCtrl)
ninensei 1:8cb452601311 241 && !(optCtrl & OTP_CTRL__OPT_BUSY_MASK)
ninensei 1:8cb452601311 242 && _write_reg(SI72XX_OTP_ADDR, otpAddr)
ninensei 1:8cb452601311 243 && _write_reg(SI72XX_OTP_CTRL, OTP_CTRL__OPT_READ_EN_MASK)
ninensei 1:8cb452601311 244 && _read_regs(SI72XX_OTP_DATA, 1, data);
sm168j 3:beca2e65b1b0 245
ninensei 1:8cb452601311 246 return ok;
ninensei 1:8cb452601311 247 }
ninensei 1:8cb452601311 248
ninensei 1:8cb452601311 249 bool _isTempOffsetAndGainValid;
ninensei 1:8cb452601311 250 float _tempOffset;
ninensei 1:8cb452601311 251 float _tempGain;
ninensei 1:8cb452601311 252 bool _enabled;
ninensei 0:ee9d6b55bf85 253 int _addr_8bit;
ninensei 1:8cb452601311 254 T *_i2c;
ninensei 0:ee9d6b55bf85 255 };
ninensei 0:ee9d6b55bf85 256