From a4fdaa9768153b7d8b879aaaa858eed90d952312 Mon Sep 17 00:00:00 2001 From: Jim Morris Date: Thu, 18 Mar 2021 16:56:13 +0000 Subject: [PATCH 1/3] Fix clock timing issues, more in line with the builtin i2c bitbang. Fix writeto_then_readfrom sending a stop at the end of the write (this upsets some chips) --- adafruit_bitbangio.py | 93 +++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/adafruit_bitbangio.py b/adafruit_bitbangio.py index 4d6c607..18a2718 100644 --- a/adafruit_bitbangio.py +++ b/adafruit_bitbangio.py @@ -24,7 +24,7 @@ """ # imports -from time import monotonic, sleep +from time import monotonic from digitalio import DigitalInOut __version__ = "0.0.0-auto.0" @@ -84,15 +84,15 @@ def __init__(self, scl, sda, *, frequency=400000, timeout=1): # Set pins as outputs/inputs. self._scl = DigitalInOut(scl) - self._scl.switch_to_output() - self._scl.value = 1 + # rpi gpio does not support OPEN_DRAIN, so we have to emulate it + # by setting the pin to input for high and output 0 for low + self._scl.switch_to_input() # SDA flips between being input and output self._sda = DigitalInOut(sda) - self._sda.switch_to_output() - self._sda.value = 1 + self._sda.switch_to_input() - self._delay = 1 / frequency / 2 + self._delay = (1 / frequency) / 2 # half period self._timeout = timeout def deinit(self): @@ -100,6 +100,11 @@ def deinit(self): self._sda.deinit() self._scl.deinit() + def _wait(self): + end = monotonic() + self._delay # half period + while end > monotonic(): + pass + def scan(self): """Perform an I2C Device Scan""" found = [] @@ -109,12 +114,12 @@ def scan(self): found.append(address) return found - def writeto(self, address, buffer, *, start=0, end=None): + def writeto(self, address, buffer, *, start=0, end=None, stop=True): """Write data from the buffer to an address""" if end is None: end = len(buffer) if self._check_lock(): - self._write(address, buffer[start:end], True) + self._write(address, buffer[start:end], stop) def readfrom_into(self, address, buffer, *, start=0, end=None): """Read data from an address and into the buffer""" @@ -145,100 +150,100 @@ def writeto_then_readfrom( if in_end is None: in_end = len(buffer_in) if self._check_lock(): - self.writeto(address, buffer_out, start=out_start, end=out_end) + self.writeto(address, buffer_out, start=out_start, end=out_end, stop=False) self.readfrom_into(address, buffer_in, start=in_start, end=in_end) def _scl_low(self): - self._scl.value = 0 + self._scl.switch_to_output(value=False) def _sda_low(self): - self._sda.value = 0 + self._sda.switch_to_output(value=False) def _scl_release(self): """Release and let the pullups lift""" # Use self._timeout to add clock stretching - self._scl.value = 1 + self._scl.switch_to_input() def _sda_release(self): """Release and let the pullups lift""" # Use self._timeout to add clock stretching - self._sda.value = 1 + self._sda.switch_to_input() def _start(self): self._sda_release() self._scl_release() - sleep(self._delay) + self._wait() self._sda_low() - sleep(self._delay) + self._wait() def _stop(self): self._scl_low() - sleep(self._delay) + self._wait() self._sda_low() - sleep(self._delay) + self._wait() self._scl_release() - sleep(self._delay) + self._wait() self._sda_release() - sleep(self._delay) + self._wait() def _repeated_start(self): self._scl_low() - sleep(self._delay) + self._wait() self._sda_release() - sleep(self._delay) + self._wait() self._scl_release() - sleep(self._delay) + self._wait() self._sda_low() - sleep(self._delay) + self._wait() def _write_byte(self, byte): for bit_position in range(8): self._scl_low() - sleep(self._delay) + if byte & (0x80 >> bit_position): self._sda_release() else: self._sda_low() - sleep(self._delay) + self._wait() self._scl_release() - sleep(self._delay) + self._wait() + self._scl_low() - sleep(self._delay * 2) + self._sda.switch_to_input() # SDA may go high, but SCL is low + self._wait() self._scl_release() - sleep(self._delay) - - self._sda.switch_to_input() - ack = self._sda.value - self._sda.switch_to_output() - sleep(self._delay) + self._wait() + ack = self._sda.value # read the ack self._scl_low() + self._sda_release() + self._wait() return not ack def _read_byte(self, ack=False): self._scl_low() - sleep(self._delay) - + self._wait() + # sda will already be an input as we are simulating open drain data = 0 - self._sda.switch_to_input() for _ in range(8): self._scl_release() - sleep(self._delay) + self._wait() data = (data << 1) | int(self._sda.value) - sleep(self._delay) self._scl_low() - sleep(self._delay) - self._sda.switch_to_output() + self._wait() if ack: self._sda_low() - else: - self._sda_release() - sleep(self._delay) + # else sda will already be in release (open drain) mode + + self._wait() self._scl_release() - sleep(self._delay) + self._wait() + self._scl_low() + self._sda_release() + return data & 0xFF def _probe(self, address): From 8662a8157515a1b7c68a94f3b824d8111ff411d5 Mon Sep 17 00:00:00 2001 From: Jim Morris Date: Sun, 21 Mar 2021 16:25:59 +0000 Subject: [PATCH 2/3] Fixed the writeto_then_readfrom() in a better way. I notice that the stop was removed from writeto() for a reason, so instead of adding it back I just call _write() instead. --- adafruit_bitbangio.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_bitbangio.py b/adafruit_bitbangio.py index 18a2718..ab85dc0 100644 --- a/adafruit_bitbangio.py +++ b/adafruit_bitbangio.py @@ -114,12 +114,12 @@ def scan(self): found.append(address) return found - def writeto(self, address, buffer, *, start=0, end=None, stop=True): + def writeto(self, address, buffer, *, start=0, end=None): """Write data from the buffer to an address""" if end is None: end = len(buffer) if self._check_lock(): - self._write(address, buffer[start:end], stop) + self._write(address, buffer[start:end], True) def readfrom_into(self, address, buffer, *, start=0, end=None): """Read data from an address and into the buffer""" @@ -150,7 +150,7 @@ def writeto_then_readfrom( if in_end is None: in_end = len(buffer_in) if self._check_lock(): - self.writeto(address, buffer_out, start=out_start, end=out_end, stop=False) + self._write(address, buffer[out_start:out_end], False) self.readfrom_into(address, buffer_in, start=in_start, end=in_end) def _scl_low(self): From 496b0d6fd69f1ed0de159ff91ea3b81037c80a74 Mon Sep 17 00:00:00 2001 From: Jim Morris Date: Sun, 21 Mar 2021 16:30:33 +0000 Subject: [PATCH 3/3] fixed typo --- adafruit_bitbangio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_bitbangio.py b/adafruit_bitbangio.py index ab85dc0..92d2b17 100644 --- a/adafruit_bitbangio.py +++ b/adafruit_bitbangio.py @@ -150,7 +150,7 @@ def writeto_then_readfrom( if in_end is None: in_end = len(buffer_in) if self._check_lock(): - self._write(address, buffer[out_start:out_end], False) + self._write(address, buffer_out[out_start:out_end], False) self.readfrom_into(address, buffer_in, start=in_start, end=in_end) def _scl_low(self):