/ adafruit_ads1x15 / ads1x15.py
ads1x15.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2018 Carter Nelson for Adafruit Industries 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # The above copyright notice and this permission notice shall be included in 13 # all copies or substantial portions of the Software. 14 # 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 # THE SOFTWARE. 22 """ 23 `ads1x15` 24 ==================================================== 25 26 CircuitPython base class driver for ADS1015/1115 ADCs. 27 28 * Author(s): Carter Nelson 29 """ 30 31 __version__ = "0.0.0-auto.0" 32 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADS1x15.git" 33 34 import time 35 from micropython import const 36 from adafruit_bus_device.i2c_device import I2CDevice 37 38 # pylint: disable=bad-whitespace 39 _ADS1X15_DEFAULT_ADDRESS = const(0x48) 40 _ADS1X15_POINTER_CONVERSION = const(0x00) 41 _ADS1X15_POINTER_CONFIG = const(0x01) 42 _ADS1X15_CONFIG_OS_SINGLE = const(0x8000) 43 _ADS1X15_CONFIG_MUX_OFFSET = const(12) 44 _ADS1X15_CONFIG_COMP_QUE_DISABLE = const(0x0003) 45 _ADS1X15_CONFIG_GAIN = { 46 2 / 3: 0x0000, 47 1: 0x0200, 48 2: 0x0400, 49 4: 0x0600, 50 8: 0x0800, 51 16: 0x0A00, 52 } 53 # pylint: enable=bad-whitespace 54 55 56 class Mode: 57 """An enum-like class representing possible ADC operating modes.""" 58 59 # See datasheet "Operating Modes" section 60 # values here are masks for setting MODE bit in Config Register 61 # pylint: disable=too-few-public-methods 62 CONTINUOUS = 0x0000 63 SINGLE = 0x0100 64 65 66 class ADS1x15: 67 """Base functionality for ADS1x15 analog to digital converters.""" 68 69 def __init__( 70 self, 71 i2c, 72 gain=1, 73 data_rate=None, 74 mode=Mode.SINGLE, 75 address=_ADS1X15_DEFAULT_ADDRESS, 76 ): 77 # pylint: disable=too-many-arguments 78 self._last_pin_read = None 79 self.buf = bytearray(3) 80 self._data_rate = self._gain = self._mode = None 81 self.gain = gain 82 self.data_rate = self._data_rate_default() if data_rate is None else data_rate 83 self.mode = mode 84 self.i2c_device = I2CDevice(i2c, address) 85 86 @property 87 def data_rate(self): 88 """The data rate for ADC conversion in samples per second.""" 89 return self._data_rate 90 91 @data_rate.setter 92 def data_rate(self, rate): 93 possible_rates = self.rates 94 if rate not in possible_rates: 95 raise ValueError("Data rate must be one of: {}".format(possible_rates)) 96 self._data_rate = rate 97 98 @property 99 def rates(self): 100 """Possible data rate settings.""" 101 raise NotImplementedError("Subclass must implement rates property.") 102 103 @property 104 def rate_config(self): 105 """Rate configuration masks.""" 106 raise NotImplementedError("Subclass must implement rate_config property.") 107 108 @property 109 def gain(self): 110 """The ADC gain.""" 111 return self._gain 112 113 @gain.setter 114 def gain(self, gain): 115 possible_gains = self.gains 116 if gain not in possible_gains: 117 raise ValueError("Gain must be one of: {}".format(possible_gains)) 118 self._gain = gain 119 120 @property 121 def gains(self): 122 """Possible gain settings.""" 123 g = list(_ADS1X15_CONFIG_GAIN.keys()) 124 g.sort() 125 return g 126 127 @property 128 def mode(self): 129 """The ADC conversion mode.""" 130 return self._mode 131 132 @mode.setter 133 def mode(self, mode): 134 if mode not in (Mode.CONTINUOUS, Mode.SINGLE): 135 raise ValueError("Unsupported mode.") 136 self._mode = mode 137 138 def read(self, pin, is_differential=False): 139 """I2C Interface for ADS1x15-based ADCs reads. 140 141 params: 142 :param pin: individual or differential pin. 143 :param bool is_differential: single-ended or differential read. 144 """ 145 pin = pin if is_differential else pin + 0x04 146 return self._read(pin) 147 148 def _data_rate_default(self): 149 """Retrieve the default data rate for this ADC (in samples per second). 150 Should be implemented by subclasses. 151 """ 152 raise NotImplementedError("Subclasses must implement _data_rate_default!") 153 154 def _conversion_value(self, raw_adc): 155 """Subclasses should override this function that takes the 16 raw ADC 156 values of a conversion result and returns a signed integer value. 157 """ 158 raise NotImplementedError("Subclass must implement _conversion_value function!") 159 160 def _read(self, pin): 161 """Perform an ADC read. Returns the signed integer result of the read.""" 162 if self.mode == Mode.CONTINUOUS and self._last_pin_read == pin: 163 return self._conversion_value(self.get_last_result(True)) 164 self._last_pin_read = pin 165 if self.mode == Mode.SINGLE: 166 config = _ADS1X15_CONFIG_OS_SINGLE 167 else: 168 config = 0 169 config |= (pin & 0x07) << _ADS1X15_CONFIG_MUX_OFFSET 170 config |= _ADS1X15_CONFIG_GAIN[self.gain] 171 config |= self.mode 172 config |= self.rate_config[self.data_rate] 173 config |= _ADS1X15_CONFIG_COMP_QUE_DISABLE 174 self._write_register(_ADS1X15_POINTER_CONFIG, config) 175 176 if self.mode == Mode.SINGLE: 177 # poll conversion complete status bit 178 while not self._conversion_complete(): 179 pass 180 else: 181 # just sleep (can't poll in continuous) 182 time.sleep(2 / self.data_rate) 183 184 return self._conversion_value(self.get_last_result(False)) 185 186 def _conversion_complete(self): 187 """Return status of ADC conversion.""" 188 # OS is bit 15 189 # OS = 0: Device is currently performing a conversion 190 # OS = 1: Device is not currently performing a conversion 191 return self._read_register(_ADS1X15_POINTER_CONFIG) & 0x8000 192 193 def get_last_result(self, fast=False): 194 """Read the last conversion result when in continuous conversion mode. 195 Will return a signed integer value. If fast is True, the register 196 pointer is not updated as part of the read. This reduces I2C traffic 197 and increases possible read rate. 198 """ 199 return self._read_register(_ADS1X15_POINTER_CONVERSION, fast) 200 201 def _write_register(self, reg, value): 202 """Write 16 bit value to register.""" 203 self.buf[0] = reg 204 self.buf[1] = (value >> 8) & 0xFF 205 self.buf[2] = value & 0xFF 206 with self.i2c_device as i2c: 207 i2c.write(self.buf) 208 209 def _read_register(self, reg, fast=False): 210 """Read 16 bit register value. If fast is True, the pointer register 211 is not updated. 212 """ 213 with self.i2c_device as i2c: 214 if fast: 215 i2c.readinto(self.buf, end=2) 216 else: 217 i2c.write_then_readinto(bytearray([reg]), self.buf, in_end=2) 218 return self.buf[0] << 8 | self.buf[1]