diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70ade69..874bf5f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.17.4 + rev: v3.2.6 hooks: - id: pylint name: pylint (library code) diff --git a/adafruit_ads1x15/ads1015.py b/adafruit_ads1x15/ads1015.py index f1ecdbe..38bf7f7 100644 --- a/adafruit_ads1x15/ads1015.py +++ b/adafruit_ads1x15/ads1015.py @@ -65,8 +65,9 @@ def rate_config(self) -> Dict[int, int]: return _ADS1015_CONFIG_DR def _data_rate_default(self) -> Literal[1600]: + """Default data rate setting is 1600 samples per second""" return 1600 def _conversion_value(self, raw_adc: int) -> int: value = struct.unpack(">h", raw_adc.to_bytes(2, "big"))[0] - return value >> 4 + return value diff --git a/adafruit_ads1x15/ads1115.py b/adafruit_ads1x15/ads1115.py index a35149c..ca834f5 100644 --- a/adafruit_ads1x15/ads1115.py +++ b/adafruit_ads1x15/ads1115.py @@ -66,6 +66,7 @@ def rate_config(self) -> Dict[int, int]: return _ADS1115_CONFIG_DR def _data_rate_default(self) -> Literal[128]: + """Default data rate setting is 128 samples per second""" return 128 def _conversion_value(self, raw_adc: int) -> int: diff --git a/adafruit_ads1x15/ads1x15.py b/adafruit_ads1x15/ads1x15.py index 7f7ea11..fdd53cd 100644 --- a/adafruit_ads1x15/ads1x15.py +++ b/adafruit_ads1x15/ads1x15.py @@ -33,9 +33,17 @@ _ADS1X15_DEFAULT_ADDRESS = const(0x48) _ADS1X15_POINTER_CONVERSION = const(0x00) _ADS1X15_POINTER_CONFIG = const(0x01) +_ADS1X15_POINTER_LO_THRES = const(0x02) +_ADS1X15_POINTER_HI_THRES = const(0x03) + _ADS1X15_CONFIG_OS_SINGLE = const(0x8000) _ADS1X15_CONFIG_MUX_OFFSET = const(12) -_ADS1X15_CONFIG_COMP_QUE_DISABLE = const(0x0003) +_ADS1X15_CONFIG_COMP_QUEUE = { + 0: 0x0003, + 1: 0x0000, + 2: 0x0001, + 4: 0x0002, +} _ADS1X15_CONFIG_GAIN = { 2 / 3: 0x0000, 1: 0x0200, @@ -66,15 +74,28 @@ class ADS1x15: :param int data_rate: The data rate for ADC conversion in samples per second. Default value depends on the device. :param Mode mode: The conversion mode, defaults to `Mode.SINGLE`. + :param int comparator_queue_length: The number of successive conversions exceeding + the comparator threshold before asserting ALERT/RDY pin. + Defaults to 0 (comparator function disabled). + :param int comparator_low_threshold: Voltage limit under which comparator de-asserts + ALERT/RDY pin. Must be lower than high threshold to use comparator + function. See subclass for value range and default. + :param int comparator_high_threshold: Voltage limit over which comparator asserts + ALERT/RDY pin. Must be higher than low threshold to use comparator + function. See subclass for value range and default. :param int address: The I2C address of the device. """ + # pylint: disable=too-many-instance-attributes def __init__( self, i2c: I2C, gain: float = 1, data_rate: Optional[int] = None, mode: int = Mode.SINGLE, + comparator_queue_length: int = 0, + comparator_low_threshold: int = -32768, + comparator_high_threshold: int = 32767, address: int = _ADS1X15_DEFAULT_ADDRESS, ): # pylint: disable=too-many-arguments @@ -83,7 +104,10 @@ def __init__( self.gain = gain self.data_rate = self._data_rate_default() if data_rate is None else data_rate self.mode = mode + self.comparator_queue_length = comparator_queue_length self.i2c_device = I2CDevice(i2c, address) + self.comparator_low_threshold = comparator_low_threshold + self.comparator_high_threshold = comparator_high_threshold @property def bits(self) -> int: @@ -131,6 +155,67 @@ def gains(self) -> List[float]: g.sort() return g + @property + def comparator_queue_length(self) -> int: + """The ADC comparator queue length.""" + return self._comparator_queue_length + + @comparator_queue_length.setter + def comparator_queue_length(self, comparator_queue_length: int) -> None: + possible_comp_queue_lengths = self.comparator_queue_lengths + if comparator_queue_length not in possible_comp_queue_lengths: + raise ValueError( + "Comparator Queue must be one of: {}".format( + possible_comp_queue_lengths + ) + ) + self._comparator_queue_length = comparator_queue_length + + @property + def comparator_queue_lengths(self) -> List[int]: + """Possible comparator queue length settings.""" + g = list(_ADS1X15_CONFIG_COMP_QUEUE.keys()) + g.sort() + return g + + @property + def comparator_low_threshold(self) -> int: + """The ADC Comparator Lower Limit Threshold.""" + return self._comparator_low_threshold + + @property + def comparator_high_threshold(self) -> int: + """The ADC Comparator Higher Limit Threshold.""" + return self._comparator_high_threshold + + @comparator_low_threshold.setter + def comparator_low_threshold(self, value: int) -> None: + """Set comparator low threshold value for ADS1x15 ADC + + :param int value: 16-bit signed integer to write to register + """ + if value < -32768 or value > 32767: + raise ValueError( + "Comparator Threshold value must be between -32768 and 32767" + ) + + self._comparator_low_threshold = value + self._write_register(_ADS1X15_POINTER_LO_THRES, self.comparator_low_threshold) + + @comparator_high_threshold.setter + def comparator_high_threshold(self, value: int) -> None: + """Set comparator high threshold value for ADS1x15 ADC + + :param int value: 16-bit signed integer to write to register + """ + if value < -32768 or value > 32767: + raise ValueError( + "Comparator Threshold value must be between -32768 and 32767" + ) + + self._comparator_high_threshold = value + self._write_register(_ADS1X15_POINTER_HI_THRES, self.comparator_high_threshold) + @property def mode(self) -> int: """The ADC conversion mode.""" @@ -183,7 +268,7 @@ def _read(self, pin: Pin) -> int: config |= _ADS1X15_CONFIG_GAIN[self.gain] config |= self.mode config |= self.rate_config[self.data_rate] - config |= _ADS1X15_CONFIG_COMP_QUE_DISABLE + config |= _ADS1X15_CONFIG_COMP_QUEUE[self.comparator_queue_length] self._write_register(_ADS1X15_POINTER_CONFIG, config) # Wait for conversion to complete diff --git a/adafruit_ads1x15/analog_in.py b/adafruit_ads1x15/analog_in.py index e13e11e..726314c 100644 --- a/adafruit_ads1x15/analog_in.py +++ b/adafruit_ads1x15/analog_in.py @@ -55,12 +55,31 @@ def value(self) -> int: Even if the underlying analog to digital converter (ADC) is lower resolution, the value is 16-bit. """ - return self._ads.read( - self._pin_setting, is_differential=self.is_differential - ) << (16 - self._ads.bits) + return self._ads.read(self._pin_setting, is_differential=self.is_differential) @property def voltage(self) -> float: """Returns the voltage from the ADC pin as a floating point value.""" - volts = self.value * _ADS1X15_PGA_RANGE[self._ads.gain] / 32767 + volts = self.convert_to_voltage(self.value) + return volts + + def convert_to_value(self, volts: float) -> int: + """Calculates a standard 16-bit integer value for a given voltage""" + + lsb = _ADS1X15_PGA_RANGE[self._ads.gain] / (1 << (self._ads.bits - 1)) + value = int(volts / lsb) + + # Need to bit shift if value is only 12-bits + value <<= 16 - self._ads.bits + return value + + def convert_to_voltage(self, value_int: int) -> float: + """Calculates voltage from 16-bit ADC reading""" + + lsb = _ADS1X15_PGA_RANGE[self._ads.gain] / (1 << (self._ads.bits - 1)) + + # Need to bit shift if value is only 12-bits + value_int >>= 16 - self._ads.bits + volts = value_int * lsb + return volts diff --git a/examples/ads1x15_comparator_example.py b/examples/ads1x15_comparator_example.py new file mode 100644 index 0000000..25567ff --- /dev/null +++ b/examples/ads1x15_comparator_example.py @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import time +import board +import busio +import countio + +import adafruit_ads1x15.ads1015 as ADS + +# import adafruit_ads1x15.ads1115 as ADS +from adafruit_ads1x15.analog_in import AnalogIn + +# Create the I2C bus +i2c = busio.I2C(board.SCL, board.SDA) + +# Create the ADS object +ads = ADS.ADS1015(i2c) +# ads = ADS.ADS1115(i2c) + +# Create a single-ended channel on Pin 0 +# Max counts for ADS1015 = 2047 +# ADS1115 = 32767 +chan = AnalogIn(ads, ADS.P0) + +# Create Interrupt-driven input to track comparator changes +int_pin = countio.Counter(board.GP9, edge=countio.Edge.RISE) + +# Set comparator to assert after 1 ADC conversion +ads.comparator_queue_length = 1 + +# Set comparator low threshold to 2V +ads.comparator_low_threshold = chan.convert_to_value(2.000) +# Set comparator high threshold to 2.002V. High threshold must be above low threshold +ads.comparator_high_threshold = chan.convert_to_value(2.002) + +count = 0 +while True: + print(chan.value, chan.voltage) # This initiates new ADC reading + if int_pin.count > count: + print("Comparator Triggered") + count = int_pin.count + time.sleep(2)