Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Comparator functionality #98

Merged
merged 12 commits into from
Aug 6, 2024
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion adafruit_ads1x15/ads1015.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines -72 to +73
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't change this. You'll change how read() works otherwise. Only .value needs to be the 16bit standard.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the ads1015 flow, I believe there are 2 unnecessary bit shifts. This is the right bit shift, which is immediately cancelled out by the left bit shift in the value property of analog_in.py. I removed both of these. There is no need to take in a 16-bit number, convert to 12, convert back to 16, and then report as 16. Removing both keeps .value at 16-bits for both ads1015 and ads1115.

1 change: 1 addition & 0 deletions adafruit_ads1x15/ads1115.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
89 changes: 87 additions & 2 deletions adafruit_ads1x15/ads1x15.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:param int value: 16-bit signed integer to write to register
:param int value: 17-bit signed integer to write to register. The lowest bit is dropped.

"""
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."""
Expand Down Expand Up @@ -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
Expand Down
27 changes: 23 additions & 4 deletions adafruit_ads1x15/analog_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
43 changes: 43 additions & 0 deletions examples/ads1x15_comparator_example.py
Original file line number Diff line number Diff line change
@@ -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)