diff --git a/adafruit_ads1x15/ads1x15.py b/adafruit_ads1x15/ads1x15.py index ccb3a88..b406bfe 100644 --- a/adafruit_ads1x15/ads1x15.py +++ b/adafruit_ads1x15/ads1x15.py @@ -157,9 +157,16 @@ def _conversion_value(self, raw_adc): def _read(self, pin): """Perform an ADC read. Returns the signed integer result of the read.""" + # Immediately return conversion register result if in CONTINUOUS mode + # and pin has not changed if self.mode == Mode.CONTINUOUS and self._last_pin_read == pin: return self._conversion_value(self.get_last_result(True)) + + # Assign last pin read if in SINGLE mode or first sample in CONTINUOUS mode on this pin self._last_pin_read = pin + + # Configure ADC every time before a conversion in SINGLE mode + # or changing channels in CONTINUOUS mode if self.mode == Mode.SINGLE: config = _ADS1X15_CONFIG_OS_SINGLE else: @@ -171,12 +178,15 @@ def _read(self, pin): config |= _ADS1X15_CONFIG_COMP_QUE_DISABLE self._write_register(_ADS1X15_POINTER_CONFIG, config) + # Wait for conversion to complete + # ADS1x1x devices settle within a single conversion cycle if self.mode == Mode.SINGLE: - # poll conversion complete status bit + # Continuously poll conversion complete status bit while not self._conversion_complete(): pass else: - # just sleep (can't poll in continuous) + # Can't poll registers in CONTINUOUS mode + # Wait expected time for two conversions to complete time.sleep(2 / self.data_rate) return self._conversion_value(self.get_last_result(False)) diff --git a/examples/ads1x15_fast_read.py b/examples/ads1x15_fast_read.py index 1e499b8..e53b4c1 100644 --- a/examples/ads1x15_fast_read.py +++ b/examples/ads1x15_fast_read.py @@ -10,6 +10,9 @@ SAMPLES = 1000 # Create the I2C bus with a fast frequency +# NOTE: Your device may not respect the frequency setting +# Raspberry Pis must change this in /boot/config.txt + i2c = busio.I2C(board.SCL, board.SDA, frequency=1000000) # Create the ADC object using the I2C bus @@ -22,16 +25,57 @@ ads.mode = Mode.CONTINUOUS ads.data_rate = RATE +# First ADC channel read in continuous mode configures device +# and waits 2 conversion cycles +_ = chan0.value + +sample_interval = 1.0 / ads.data_rate + +repeats = 0 +skips = 0 + data = [None] * SAMPLES start = time.monotonic() +time_next_sample = start + sample_interval # Read the same channel over and over for i in range(SAMPLES): + # Wait for expected conversion finish time + while time.monotonic() < (time_next_sample): + pass + + # Read conversion value for ADC channel data[i] = chan0.value + # Loop timing + time_last_sample = time.monotonic() + time_next_sample = time_next_sample + sample_interval + if time_last_sample > (time_next_sample + sample_interval): + skips += 1 + time_next_sample = time.monotonic() + sample_interval + + # Detect repeated values due to over polling + if data[i] == data[i - 1]: + repeats += 1 + end = time.monotonic() total_time = end - start -print("Time of capture: {}s".format(total_time)) -print("Sample rate requested={} actual={}".format(RATE, SAMPLES / total_time)) +rate_reported = SAMPLES / total_time +rate_actual = (SAMPLES - repeats) / total_time +# NOTE: leave input floating to pickup some random noise +# This cannot estimate conversion rates higher than polling rate + +print("Took {:5.3f} s to acquire {:d} samples.".format(total_time, SAMPLES)) +print("") +print("Configured:") +print(" Requested = {:5d} sps".format(RATE)) +print(" Reported = {:5d} sps".format(ads.data_rate)) +print("") +print("Actual:") +print(" Polling Rate = {:8.2f} sps".format(rate_reported)) +print(" {:9.2%}".format(rate_reported / RATE)) +print(" Skipped = {:5d}".format(skips)) +print(" Repeats = {:5d}".format(repeats)) +print(" Conversion Rate = {:8.2f} sps (estimated)".format(rate_actual))