-
Notifications
You must be signed in to change notification settings - Fork 76
Fix I2C init error on BeagleBone Black #22
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
Conversation
This init method verifies that a device exists at the given address. It was writing a zero byte value to the bus. This triggered a bug in the Linux kernel I2C bus OMAP2 driver for the BeagleBone. The driver returns an invalid write error when msg->len is 0: https://github.com/beagleboard/linux/blob/4.14/drivers/i2c/busses/i2c-omap.c#L665 The solution is to write the value 'x' instead of ''. Refer to Adafruit_Blinka PR adafruit#42 for more information: adafruit/Adafruit_Blinka#42 (comment)
How does Linux do the scan? Does it send an x value? Seems bad to transmit a random byte to an unknown device. |
@tannewt good point. I'm looking at i2cdetect and it looks like reading a byte might be a better method to determine if there is a device at a given address: /* Probe this address */
switch (cmd) {
default: /* MODE_QUICK */
/* This is known to corrupt the Atmel AT24RF08
EEPROM */
res = i2c_smbus_write_quick(file,
I2C_SMBUS_WRITE);
break;
case MODE_READ:
/* This is known to lock SMBus on various
write-only chips (mainly clock chips) */
res = i2c_smbus_read_byte(file);
break;
} |
Alternative approach is to use a read instead of a write. This looks useful: def scan(self):
"""Try to read a byte from each address, if you get an OSError it means the device isnt there"""
found = []
for addr in range(0,0x80):
try:
self._i2c_bus.read_byte(addr)
except OSError:
continue
found.append(addr)
return found |
instead of writing a zero byte, try to read a byte from an address if you get an OSError it means the device is not there this fixes issue for BealgeBone Black in Adafruit_Blinka adafruit/Adafruit_Blinka#42
for def __init__(self, i2c, device_address):
"""
Try to read a byte from an address,
if you get an OSError it means the device is not there
"""
while not i2c.try_lock():
pass
try:
result = bytearray(2)
i2c.readfrom_into(device_address, result)
except OSError:
raise ValueError("No I2C device at address: %x" % device_address)
finally:
i2c.unlock()
self.i2c = i2c
self.device_address = device_address instead of writing a zero byte, try to read a byte from an address. if you get an OSError it means the device is not there this fixes issue for BealgeBone Black in Adafruit_Blinka: adafruit/Adafruit_Blinka#42 |
it is not needed to read more than 1 byte just to verify that the i2c device is present
@ladyada good point. I have change it to read just a single byte and it still works OK. |
fyi - this is need for adafruit/Adafruit_Blinka#42 |
pardon - gonna test this on m0 and m4 and merge if they both work! |
ok tested with a plethora of sensors and the VEML6075 hates this so turns out you actually need something like....
@pdp7 can you try this on BBB? it should work because it will fail at first but succeed on retry. the veml6075 will still be unhappy but maybe its better than nothing? |
@ladyada thanks for the feedback. I'm currently travelling post-supercon but I will try this out on Beagle once I return home. |
@pdp7 ping! |
@ladyada thank you for the ping. I will test on BBB this morning |
@ladyada the example you provided does work OK on the BBB: diff --git a/adafruit_bus_device/i2c_device.py b/adafruit_bus_device/i2c_device.py
index 1310769..5dea936 100644
--- a/adafruit_bus_device/i2c_device.py
+++ b/adafruit_bus_device/i2c_device.py
@@ -56,6 +56,7 @@ class I2CDevice:
with device:
device.write(bytes_read)
"""
+
def __init__(self, i2c, device_address):
"""
Try to read a byte from an address,
@@ -64,10 +65,15 @@ class I2CDevice:
while not i2c.try_lock():
pass
try:
- result = bytearray(1)
- i2c.readfrom_into(device_address, result)
+ i2c.writeto(device_address, b'')
except OSError:
- raise ValueError("No I2C device at address: %x" % device_address)
+ # some OS's dont like writing an empty bytesting...
+ # Retry by reading a byte
+ try:
+ result = bytearray(1)
+ i2c.readfrom_into(device_address, result)
+ except OSError:
+ raise ValueError("No I2C device at address: %x" % device_address)
finally:
i2c.unlock() I added some debug output and verified that on the BeagleBone the write fails, and the fall back to read works OK:
Example of BME280 working OK:
|
I'll add a commit with this change. |
kk! |
This approach was suggested by ladyada in Adafruit_CircuitPython_BusDevice PR adafruit#22: adafruit#22 (comment) > ok tested with a plethora of sensors and the VEML6075 hates this > so turns out you actually need something like.... def __init__(self, i2c, device_address): """ Try to read a byte from an address, if you get an OSError it means the device is not there """ while not i2c.try_lock(): pass try: i2c.writeto(device_address, b'') except OSError: # some OS's dont like writing an empty bytesting... # Retry by reading a byte try: result = bytearray(1) i2c.readfrom_into(device_address, result) except OSError: raise ValueError("No I2C device at address: %x" % device_address) finally: i2c.unlock() self.i2c = i2c self.device_address = device_address
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! You could rewrite
result = bytearray(1)
i2c.readfrom_into(device_address, result)
as
i2c.readfrom_into(device_address, bytearray(1))
because result
is discarded, but that's just stylistic. No big deal.
@dhalbert i know its correct but that looks odd to me, i'd like to keep as is if that's ok :) |
sure! |
Updating https://github.com/adafruit/Adafruit_CircuitPython_74HC595 to 1.0.1 from 1.0.0: > Merge pull request adafruit/Adafruit_CircuitPython_74HC595#2 from kattni/pypi-setup Updating https://github.com/adafruit/Adafruit_CircuitPython_MPR121 to 2.0.0 from 1.2.1: > Merge pull request adafruit/Adafruit_CircuitPython_MPR121#14 from caternuson/iss12 Updating https://github.com/adafruit/Adafruit_CircuitPython_RFM69 to 1.2.3 from 1.2.2: > Merge pull request adafruit/Adafruit_CircuitPython_RFM69#12 from jerryneedell/jerryn_header Updating https://github.com/adafruit/Adafruit_CircuitPython_BusDevice to 2.2.7 from 2.2.6: > Merge pull request adafruit/Adafruit_CircuitPython_BusDevice#22 from pdp7/master Updating https://github.com/adafruit/Adafruit_CircuitPython_Bundle/circuitpython_library_list.md to NA from NA: > Added the following libraries: Adafruit_CircuitPython_74HC595
This init method verifies that a device exists at the given address.
It was writing a zero byte value to the bus. This triggered a bug in the Linux kernel I2C bus OMAP2 driver for the BeagleBone. The driver returns an invalid write error when msg->len is 0:
https://github.com/beagleboard/linux/blob/4.14/drivers/i2c/busses/i2c-omap.c#L665
The solution is to write the value 'x' instead of ''.
Refer to Adafruit_Blinka PR #42 for more information:
adafruit/Adafruit_Blinka#42 (comment)