adafruit / Adafruit_CircuitPython_TCA9548A

CircuitPython driver for the TCA9548A I2C Multiplexer.
MIT License
26 stars 15 forks source link

Support for 'write_then_readfrom' #7

Closed StevenBruinen closed 5 years ago

StevenBruinen commented 5 years ago

Added support for the busio 'write_then_readfrom' function as some i2c devices require this (e.g. the Adafruit i2c FRAM module)

ladyada commented 5 years ago

lgtm, @caternuson wanna try it out?

caternuson commented 5 years ago

I'm not seeing this used in the FRAM library: https://github.com/adafruit/Adafruit_CircuitPython_FRAM/blob/master/adafruit_fram.py Can you show where this is. Or, even better, provide an example showing how the TCA and FRAM are not working together?

ladyada commented 5 years ago

https://github.com/adafruit/Adafruit_CircuitPython_FRAM/blob/master/adafruit_fram.py#L210 ?

caternuson commented 5 years ago

Thought that might be the intention here. But PR is writeto_then_readfrom. Was it suppose to be write_then_readinto?

But also, I think my code comment here is wrong: https://github.com/adafruit/Adafruit_CircuitPython_TCA9548A/blob/master/adafruit_tca9548a.py#L53 The TCA is more of a passthru for busio.i2c, not i2c_device. The key bit being the override of try_lock to insert the channel switch: https://github.com/adafruit/Adafruit_CircuitPython_TCA9548A/blob/master/adafruit_tca9548a.py#L65 which gets called by i2c_device's context manager here: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice/blob/master/adafruit_bus_device/i2c_device.py#L175

Went ahead and wired up a TCA and I2C FRAM and it's currently working OK:

Adafruit CircuitPython 3.1.2 on 2019-01-07; Adafruit ItsyBitsy M4 Express with samd51g19
>>> import board, busio
>>> import adafruit_tca9548a
>>> import adafruit_fram
>>> i2c = busio.I2C(board.SCL, board.SDA)
>>> tca = adafruit_tca9548a.TCA9548A(i2c)
>>> fram = adafruit_fram.FRAM_I2C(tca[1])
>>> fram[0] = 1
>>> fram[0] 
bytearray(b'\x01')
>>> 
ladyada commented 5 years ago

maybe a typo? :) you hit the issue on raspberry pi more because it cant do repeated start in two commands

StevenBruinen commented 5 years ago

With the new code the FRAM module works ok on my RaspberryPi. Without it I get the error below when running:

try:
  import adafruit_fram
  fram = adafruit_fram.FRAM_I2C(tca[1])
  logging.info('FRAM memory: Module active.')
except Exception as e:
    logging.exception('FRAM memory: Module not responding.')
Traceback (most recent call last):
  File "/home/pi/MainProgram.py", line 50, in <module>
    fram = adafruit_fram.FRAM_I2C(tca[1])
  File "/usr/local/lib/python3.5/dist-packages/adafruit_fram.py", line 211, in __init__
    read_buf, stop=False)
  File "/usr/local/lib/python3.5/dist-packages/adafruit_bus_device/i2c_device.py", line 167, in write_then_readinto
    self.readinto(in_buffer, start=in_start, end=in_end)
  File "/usr/local/lib/python3.5/dist-packages/adafruit_bus_device/i2c_device.py", line 97, in readinto
    self.i2c.readfrom_into(self.device_address, buf, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/adafruit_tca9548a.py", line 76, in readfrom_into
    return self.tca.i2c.readfrom_into(address, buffer, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/busio.py", line 55, in readfrom_into
    return self._i2c.readfrom_into(address, buffer, stop=stop)
  File "/usr/local/lib/python3.5/dist-packages/adafruit_blinka/microcontroller/generic_linux/i2c.py", line 44, in readfrom_into
    readin = self._i2c_bus.read_bytes(address, end-start)
  File "/usr/local/lib/python3.5/dist-packages/Adafruit_PureIO/smbus.py", line 155, in read_bytes
    return self._device.read(number)
OSError: [Errno 121] Remote I/O error
caternuson commented 5 years ago

Oh, I see what's going on. It's to support this: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice/blob/master/adafruit_bus_device/i2c_device.py#L150 Things are conditionally different in write_then_readinto. It went down the other branch since there is no writeto_then_readfrom, which doesn't work as @ladyada pointed out. So gets the I/O Error.

caternuson commented 5 years ago

@StevenBruinen Thanks. Set this up on a Pi and got same error. Tested your fix and it works.

Can you add a code comment to the function similar to this: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice/blob/8a5b9974cda321771941f2e69513c9cee13e07fb/adafruit_bus_device/i2c_device.py#L154 which might help highlight this as a linuxy specific thing in the future.

StevenBruinen commented 5 years ago

Great! I've added the comment as requested.

StevenBruinen commented 5 years ago

Sorry, I think I did something wrong now but I don't know what. This is my first pull request ever... :-)

caternuson commented 5 years ago

Awesome! Thanks for finding this and submitting the fix.

Look ma, no I/O errors:

pi@raspberrypi:~ $ python3
Python 3.5.3 (default, Sep 27 2018, 17:25:39) 
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import board, busio
>>> import adafruit_tca9548a
>>> import adafruit_fram
>>> i2c = busio.I2C(board.SCL, board.SDA)
>>> tca = adafruit_tca9548a.TCA9548A(i2c)
>>> fram = adafruit_fram.FRAM_I2C(tca[1])
>>> fram[0] = 0x10
>>> fram[0]
bytearray(b'\x10')
StevenBruinen commented 5 years ago

Cool! Solved for everybody now \0/ Thank you all for your support!