SELKIELogger  1.0.0
I2C-ADS1015.c
1 /*
2  * Copyright (C) 2023 Swansea University
3  *
4  * This file is part of the SELKIELogger suite of tools.
5  *
6  * SELKIELogger is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, either version 3 of the License, or (at your option)
9  * any later version.
10  *
11  * SELKIELogger is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this SELKIELogger product.
18  * If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include <math.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 
26 #include <i2c/smbus.h>
27 #include <linux/i2c-dev.h>
28 #include <sys/ioctl.h>
29 
30 #include "I2C-ADS1015.h"
31 #include "I2CConnection.h"
32 
40 uint16_t i2c_ads1015_read_configuration(const int busHandle, const int devAddr) {
41  if (ioctl(busHandle, I2C_SLAVE, devAddr) < 0) { return false; }
42 
43  int32_t res = i2c_smbus_read_word_data(busHandle, ADS1015_REG_CONFIG);
44  if (res < 0) { return -1; }
45  return ((uint16_t)i2c_swapbytes(res));
46 }
47 
55 float i2c_ads1015_pga_to_scale_factor(const uint16_t config) {
56  uint16_t pga = (config & ADS1015_CONFIG_PGA_SELECT);
57  if (pga == ADS1015_CONFIG_PGA_6144MV) {
58  return 3;
59  } else if (pga == ADS1015_CONFIG_PGA_4096MV) {
60  return 2;
61  } else if (pga == ADS1015_CONFIG_PGA_2048MV) {
62  return 1;
63  } else if (pga == ADS1015_CONFIG_PGA_1024MV) {
64  return 0.5;
65  } else if (pga == ADS1015_CONFIG_PGA_0512MV) {
66  return 0.25;
67  } else if (pga == ADS1015_CONFIG_PGA_0256MV) {
68  return 0.125;
69  } else if (pga == ADS1015_CONFIG_PGA_0256MV2) {
70  return 0.125;
71  } else if (pga == ADS1015_CONFIG_PGA_0256MV3) {
72  return 0.125;
73  } else {
74  return NAN;
75  }
76  return NAN;
77 }
78 
91 float i2c_ads1015_read_mux(const int busHandle, const int devAddr, const uint16_t mux,
92  const i2c_ads1015_options *opts) {
93  if (ioctl(busHandle, I2C_SLAVE, devAddr) < 0) { return NAN; }
94 
95  uint16_t pga = ADS1015_CONFIG_PGA_DEFAULT;
96  float min = -INFINITY;
97  float max = INFINITY;
98  float scale = 1.0;
99  float offset = 0;
100 
101  if (opts) {
102  min = opts->min;
103  max = opts->max;
104  scale = opts->scale;
105  offset = opts->offset;
106  pga = opts->pga;
107  }
108 
109  while (!(i2c_ads1015_read_configuration(busHandle, devAddr) & ADS1015_CONFIG_STATE_CONVERT)) {
110  usleep(100);
111  }
112 
113  uint16_t confClear = (ADS1015_CONFIG_DEFAULT & ADS1015_CONFIG_MUX_CLEAR & ADS1015_CONFIG_PGA_CLEAR);
114  uint16_t muxToSet = (mux & ADS1015_CONFIG_MUX_SELECT);
115  uint16_t pgaToSet = (pga & ADS1015_CONFIG_PGA_SELECT);
116 
117  /*
118  * Default configuration with MUX and PGA cleared, OR'd with the MUX
119  * bits of mux and PGA bits of pga
120  *
121  * Finally, the top bit is set (using ADS1015_CONFIG_STATE_CONVERT) to
122  * trigger the measurement
123  */
124  if (i2c_smbus_write_word_data(
125  busHandle, ADS1015_REG_CONFIG,
126  (uint16_t)i2c_swapbytes(confClear | muxToSet | pgaToSet | ADS1015_CONFIG_STATE_CONVERT)) < 0) {
127  return NAN;
128  }
129  while (i2c_ads1015_read_configuration(busHandle, devAddr) & ADS1015_CONFIG_STATE_CONVERT) {
130  usleep(100);
131  }
132 
133  int32_t res = i2c_smbus_read_word_data(busHandle, ADS1015_REG_RESULT);
134  if (res < 0) { return NAN; }
135 
136  uint16_t sres = i2c_swapbytes(res);
137  sres = (sres & 0xFFF0) >> 4;
138 
139  float sv = i2c_ads1015_pga_to_scale_factor(pga);
140 
141  float adcV = 0;
142  if ((sres & 0x800)) {
143  uint16_t t = (sres & 0x7FF) + 1;
144  adcV = ~t * sv;
145  } else {
146  adcV = (sres & 0x7FF) * sv;
147  }
148 
149  adcV = scale * adcV + offset;
150  if ((adcV < min) || (adcV > max)) { adcV = NAN; }
151 
152  return adcV;
153 }
154 
163 float i2c_ads1015_read_ch0(const int busHandle, const int devAddr, const void *opts) {
164  i2c_ads1015_options *o = NULL;
165  if (opts) { o = (i2c_ads1015_options *)opts; }
166  return i2c_ads1015_read_mux(busHandle, devAddr, ADS1015_CONFIG_MUX_SINGLE_0, o);
167 }
168 
177 float i2c_ads1015_read_ch1(const int busHandle, const int devAddr, const void *opts) {
178  i2c_ads1015_options *o = NULL;
179  if (opts) { o = (i2c_ads1015_options *)opts; }
180  return i2c_ads1015_read_mux(busHandle, devAddr, ADS1015_CONFIG_MUX_SINGLE_1, o);
181 }
182 
191 float i2c_ads1015_read_ch2(const int busHandle, const int devAddr, const void *opts) {
192  i2c_ads1015_options *o = NULL;
193  if (opts) { o = (i2c_ads1015_options *)opts; }
194  return i2c_ads1015_read_mux(busHandle, devAddr, ADS1015_CONFIG_MUX_SINGLE_2, o);
195 }
196 
198 
206 float i2c_ads1015_read_ch3(const int busHandle, const int devAddr, const void *opts) {
207  i2c_ads1015_options *o = NULL;
208  if (opts) { o = (i2c_ads1015_options *)opts; }
209  return i2c_ads1015_read_mux(busHandle, devAddr, ADS1015_CONFIG_MUX_SINGLE_3, o);
210 }
211 
220 float i2c_ads1015_read_diff_ch0_ch1(const int busHandle, const int devAddr, const void *opts) {
221  i2c_ads1015_options *o = NULL;
222  if (opts) { o = (i2c_ads1015_options *)opts; }
223  return i2c_ads1015_read_mux(busHandle, devAddr, ADS1015_CONFIG_MUX_DIFF_0_1, o);
224 }
225 
234 float i2c_ads1015_read_diff_ch0_ch3(const int busHandle, const int devAddr, const void *opts) {
235  i2c_ads1015_options *o = NULL;
236  if (opts) { o = (i2c_ads1015_options *)opts; }
237  return i2c_ads1015_read_mux(busHandle, devAddr, ADS1015_CONFIG_MUX_DIFF_0_3, o);
238 }
239 
248 float i2c_ads1015_read_diff_ch1_ch3(const int busHandle, const int devAddr, const void *opts) {
249  i2c_ads1015_options *o = NULL;
250  if (opts) { o = (i2c_ads1015_options *)opts; }
251  return i2c_ads1015_read_mux(busHandle, devAddr, ADS1015_CONFIG_MUX_DIFF_1_3, o);
252 }
253 
262 float i2c_ads1015_read_diff_ch2_ch3(const int busHandle, const int devAddr, const void *opts) {
263  i2c_ads1015_options *o = NULL;
264  if (opts) { o = (i2c_ads1015_options *)opts; }
265  return i2c_ads1015_read_mux(busHandle, devAddr, ADS1015_CONFIG_MUX_DIFF_2_3, o);
266 }
int16_t i2c_swapbytes(const int16_t in)
Swap word byte order.
Definition: I2CConnection.c:58
float i2c_ads1015_read_ch1(const int busHandle, const int devAddr, const void *opts)
Get single-ended voltage measurement from channel 1.
Definition: I2C-ADS1015.c:177
float i2c_ads1015_read_ch2(const int busHandle, const int devAddr, const void *opts)
Get single-ended voltage measurement from channel 2.
Definition: I2C-ADS1015.c:191
float i2c_ads1015_read_mux(const int busHandle, const int devAddr, const uint16_t mux, const i2c_ads1015_options *opts)
Generic ADS1015 read function.
Definition: I2C-ADS1015.c:91
float i2c_ads1015_pga_to_scale_factor(const uint16_t config)
Get number of millivolts represented by LSB at a given PGA value.
Definition: I2C-ADS1015.c:55
float i2c_ads1015_read_diff_ch0_ch3(const int busHandle, const int devAddr, const void *opts)
Get differential voltage measurement between channels 0 and 3.
Definition: I2C-ADS1015.c:234
float i2c_ads1015_read_diff_ch0_ch1(const int busHandle, const int devAddr, const void *opts)
Get differential voltage measurement between channels 0 and 1.
Definition: I2C-ADS1015.c:220
float i2c_ads1015_read_ch0(const int busHandle, const int devAddr, const void *opts)
Get single-ended voltage measurement from channel 0.
Definition: I2C-ADS1015.c:163
float i2c_ads1015_read_ch3(const int busHandle, const int devAddr, const void *opts)
Get single-ended voltage measurement from channel 3.
Definition: I2C-ADS1015.c:206
float i2c_ads1015_read_diff_ch2_ch3(const int busHandle, const int devAddr, const void *opts)
Get differential voltage measurement between channels 2 and 3.
Definition: I2C-ADS1015.c:262
uint16_t i2c_ads1015_read_configuration(const int busHandle, const int devAddr)
Read configuration from device.
Definition: I2C-ADS1015.c:40
float i2c_ads1015_read_diff_ch1_ch3(const int busHandle, const int devAddr, const void *opts)
Get differential voltage measurement between channels 1 and 3.
Definition: I2C-ADS1015.c:248
float offset
Add this amount to received value.
float scale
Scale received value by this quantity.
float max
If not NaN, largest value considered valid.
float min
If not NaN, smallest value considered valid.
uint16_t pga
PGA setting.