/ 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]