Open jomoengineer opened 3 years ago
There were some recent I2C fixes made on the develop
branch of pico-sdk
, so could you try checking out that SDK branch and rebuild your examples against that?
I appreciate the reply. I have the latest changes as of this writing and it still hangs with the Grove LCD.
I'll see if I can dig deeper on this and find where it is hanging.
From what I have gathered, the 'bus_scan' script scans until it reaches the Grove LCD i2c address of 0x3e(62) and then does another read of the bus at address 0x3f but somehow the either the Grove LCD is not giving up the bus or something ese is causing the i2c bus to not appear to be available.
So, in bus_scan.c, it gets stuck at the 'i2c_read_blocking' call at line 69.
ret = i2c_read_blocking(i2c0, addr, &rxdata, 1, false);
The function 'i2c_read_blocking' is found in 'src/rp2_common/hardware_i2c/i2c.c' which then calls 'i2c_read_blocking_internal'. The do/while loop at lines 230-237 with the call to 'i2c_get_read_available' is where it is getting stuck in a loop.
The function 'i2c_read_blocking_internal'
do {
abort_reason = i2c->hw->tx_abrt_source;
abort = (bool) i2c->hw->clr_tx_abrt;
if (timeout_check) {
timeout = timeout_check(ts);
abort |= timeout;
}
} while (!abort && !i2c_get_read_available(i2c));
The function 'i2c_get_read_available' is found in 'src/rp2_common/hardware_i2c/include/hardware/i2c.h'. This calls 'i2c_get_hw' which then calls 'i2c_hw_index'.
static inline uint i2c_hw_index(i2c_inst_t *i2c) {
invalid_params_if(I2C, i2c != i2c0 && i2c != i2c1);
return i2c == i2c1 ? 1 : 0;
}
static inline i2c_hw_t *i2c_get_hw(i2c_inst_t *i2c) {
i2c_hw_index(i2c); // check it is a hw i2c
return i2c->hw;
}
For some reason these appear to be reporting that the bus is not available.
@fivdi Do you think this might be related to https://github.com/raspberrypi/pico-sdk/issues/336 ?
@fivdi Do you think this might be related to raspberrypi/pico-sdk#336 ?
It's possible, but I think it's unlikely. If I had to guess, I'd say it's more likely to be related to timing.
@jomoengineer
There are three PRs related to I2C that have not been released yet. That is, the PRs are are not part of pico-sdk v1.1.2. Two of these PRs are related to I2C timing. All three PRs make changes to the file src/rp2_common/hardware_i2c/i2c.c
and to this file only.
If you replace your version of src/rp2_common/hardware_i2c/i2c.c
with this version you would have the changes from all three PRs. If you haven't done this already, can you give it a try to see if it helps please? If this doesn't work, I have one more idea related to issue raspberrypi/pico-sdk#336 that @lurch mentioned that might help.
@fivdi I replaced the i2c.c with the one you referenced and it still does the same thing:
I2C Bus Scan
0 1 2 3 4 5 6 7 8 9 A B C D E F
00 . . . . . . . . . . . . . . . .
10 . . . . . . . . . . . . . . . .
20 . . . . . . . . . . . . . . . .
30 . . . . . . . . . . . . . . .
I did not see a change that could be added from issue #336 so I could be missing something there.
One other thing to note, the address for the Text part of the Grove LCD is 0x3e (62), so if the code identified this address then a "@" symbol should have been placed at the "e" of line 30. However, this is not the case and it is stopping at 3f. There is a RGB address at 0x62 but this does not get there so it is not clear if that would be picked up. I do believe the Grove LCD has 4 i2c addresses but the Text and RGB are the two most prominent. The MicroPython code I have picks up 3 though.
I did not see a change that could be added from issue #336 so I could be missing something there.
There is no PR or changes for fixing raspberrypi/pico-sdk#336 yet.
The MicroPython code I have picks up 3 though.
I'd say MicroPython is working because it uses bit-banging rather than the I2C peripheral for short reads and short reads are used to scan the I2C bus. This results in very different timing.
Here are some additional thing to try to see if it helps. (Keep using the modified version of i2c.c suggested above.)
1st thing to try I would be surprised if this helps, but drop the baud rate in the bus scan code from 100k to 10k to see if it helps. That is, change this line:
i2c_init(i2c_default, 100 * 1000);
to this:
i2c_init(i2c_default, 10 * 1000);
2nd thing to try
I would also be surprised if this helps, but do one of the fixes that are needed for raspberrypi/pico-sdk#336. That is, change this code in i2c_read_blocking_internal
:
abort_reason = i2c->hw->tx_abrt_source;
abort = (bool) i2c->hw->clr_tx_abrt;
to this:
abort_reason = i2c->hw->tx_abrt_source;
if (abort_reason) {
i2c->hw->clr_tx_abrt;
abort = true;
}
3rd thing to try
Set the SDA hold time during receive (IC_SDA_RX_HOLD) by changing this code in i2c_set_baudrate
:
hw_write_masked(&i2c->hw->sda_hold,
sda_tx_hold_count << I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_LSB,
I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_BITS);
to this:
uint sda_rx_hold_count = sda_tx_hold_count;
hw_write_masked(&i2c->hw->sda_hold,
sda_rx_hold_count << I2C_IC_SDA_HOLD_IC_SDA_RX_HOLD_LSB |
sda_tx_hold_count << I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_LSB,
I2C_IC_SDA_HOLD_IC_SDA_RX_HOLD_BITS |
I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_BITS);
@fivdi Thanks for the suggestions, however none made a change to the output of the bus_scan script.
I need to find a better way to debug this.
So, I have found something interesting, in that if I remove the power from the Grove LCD while the Pico is still connected to the Raspberry Pi 4, the i2c scan seems to complete. It is not picking up the Text Address at 0x3e, but it is showing the RGB at 0x62 and another at 0x70
I2C Bus Scan
0 1 2 3 4 5 6 7 8 9 A B C D E F
00 . . . . . . . . . . . . . . . .
10 . . . . . . . . . . . . . . . .
20 . . . . . . . . . . . . . . . .
30 . . . . . . . . . . . . . . . .
40 . . . . . . . . . . . . . . . .
50 . . . . . . . . . . . . . . . .
60 . . @ . . . . . . . . . . . . .
70 @ . . . . . . . . . . . . . . .
Done.
Also, I installed the Arduino IDE and Pico Board support and then ran the Wire i2c_scanner sketch from the UNO examples. This is picking up all 4 i2c address from the Grove LCD without issue:
Scanning...
I2C device found at address 0x03 !
I2C device found at address 0x3E !
I2C device found at address 0x62 !
I2C device found at address 0x70 !
done
@jomoengineer There are a few different Raspberry Pi Pico Arduino Cores available at the moment. Which one did you use?
The scan bus example in pico-examples doesn't scan I2C addresses less than 0x08 or greater than 0x77 so it won’t detect anything at address 0x03. Of course, this doesn't explain why it's not detecting anything at address 0x3e.
I have an actual Raspberry Pi Pico: https://www.raspberrypi.org/products/raspberry-pi-pico/
I have an actual Raspberry Pi Pico: https://www.raspberrypi.org/products/raspberry-pi-pico/
Yes. I meant something different. Above you mentioned you installed Arduino IDE and Pico Board support. Which Pico Board support did you install? There are a few available.
There this one https://github.com/earlephilhower/arduino-pico Arduino themselves also have one.
@fivdi This one: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
Following the example from Tom's Hardware: https://www.tomshardware.com/how-to/program-raspberry-pi-pico-with-arduino-ide
If I understand the code correctly, the Raspberry Pi Pico Arduino core linked to above uses a special bit-banged 0-byte write for scanning the I2C bus. See here and here.
The bus_scan in pico_examples uses a 1-byte read.
These are two very different approaches. Perhaps the Grove I2C LCD simply doesn't like the 1-byte read and gets a little messed up with the 1-byte read. Perhaps the 1-byte read even results in the Grove I2C LCD blocking the I2C bus.
Perhaps if you ignore the fact that the bus_scan in pico-examples doesn't work for the Grove I2C LCD and simply use the Grove I2C LCD using it's documented functionality it will work correctly.
If you would like to scan the I2C bus using a 1-byte write to see if it works you could give this a try. Note that this example assumes that the modified i2c.c from above is being used.
@fivdi The 'bus_scan_issue_278' example seems to be closer, however it still is not picking up the Text Address of 0x3e which is used to send text to the LCD.
I am using the 'i2c.c code that was posted previously and I have the '3rd thing to try' code implemented. Should I have all 3 suggestions implemented or does it really matter at this point?
Also, I had previously attempted to use the pico-examples 'lcd_1602_i2c' code to write to the LCD. But, it did not seem to be identifying the Grove LCD properly which led me to use the bus_scan to try to id the LCD. For some reason the Pico code does not seem to like the 0x3e address.
This is the output I see with the 'bus_scan_issue_278' code.
i2c_write_blocking test
0 1 2 3 4 5 6 7 8 9 A B C D E F
00 . . . . . . . . . . . . . . . .
10 . . . . . . . . . . . . . . . .
20 . . . . . . . . . . . . . . . .
30 . . . . . . . . . . . . . . . .
40 . . . . . . . . . . . . . . . .
50 . . . . . . . . . . . . . . . .
60 . . @ . . . . . . . . . . . . .
70 @ . . . . . . . . . . . . . . .
I am using the 'i2c.c code that was posted previously and I have the '3rd thing to try' code implemented.
This was the correct thing to do.
Should I have all 3 suggestions implemented or does it really matter at this point?
Only the "3rd thing to try" is needed to test "bus_scan_issue_278", the other suggestions don't matter here. I was hoping that "bus_scan_issue_278" would detect the Grove I2C LCD but it doesn't.
Also, I had previously attempted to use the pico-examples 'lcd_1602_i2c' code to write to the LCD. But, it did not seem to be identifying the Grove LCD properly which led me to use the bus_scan to try to id the LCD.
I wouldn't expect the pico-examples 'lcd_1602_i2c' code to function with the Grove I2C LCD. The pico-examples 'lcd_1602_i2c' code assumes a PCF8574 will be used to interface with the LCD. The Grove I2C LCD uses an AIP31068L (and a AIP31065) to interface with the LCD. These are not the same, the PCF8574 is a general purpose I/O expander, the AIP31068L is a LCD controller.
For some reason the Pico code does not seem to like the 0x3e address.
I think it's the other way around. The Grove I2C LCD doesn't like 1-byte reads or 1-byte writes to be used to detect it's presence on the I2C bus. It does like 0-byte writes, which is why the Arduino code can detect it. The Arduino code used 0-byte writes to detect devices on the I2C bus.
Don't worry about the fact that that the bus_scan pico-example can't detect the Grove I2C LCD. The Grove I2C LCD is there, because the Arduino code can detect it. The bus_scan pico-example isn't really that good. It uses 1-byte reads rather than 0-byte writes to try to detect devices on the I2C bus because the I2C peripheral on the RP2040 doesn't support 0-byte writes.
What I think you'll need to do is look a the Seed Studio code for the Grove I2C LCD here and migrate to pico-sdk for usage on the Pico. Also, now that you have the Arduino IDE with support for Pico installed, you could also try out the Seed Studio Arduino examples for the Grove I2C LCD to see it they function as expected.
@fivdi I appreciate the info. Yeah, the Grove LCD Arduino code from the Seeed Studio site and what you reference does seem to work with the Grove LCD connected to the Pico.
I'll have a look at the Arduino code and see if I can implement it to the pico-sdk.
Thanks.
I am now the proud owner of a Grove 16 x 2 LCD and did some tests with it. If an attempt is made to read a byte from the displays I2C controller it will block the I2C bus by holding SDA low forever.
A program to demonstrate this can be found here.
I would imagine that the bus_scan
example was intended to be a simple example of how to use i2c_read_blocking
rather than to be the most robust I2C bus scan ever written. If this is true, I'd say this issue can be closed.
Perhaps it would make sense to add a function to pico-sdk that attempts to unblock a blocked I2C bus. If an I2C device is blocking the I2C bus by holding SDA low, it's often possible to recover and unblock the bus by toggeling the SCL line a few times.
Cool. From looking at the Arduino code, when sending bytes to the LCD using Wire / TwoWire then use 'endTransmission' to end the communication with the i2c device. This basically just sends all parameters to the i2c device with zero values: https://github.com/arduino/ArduinoCore-avr/blob/master/libraries/Wire/src/Wire.cpp
The Arduino i2c_scanner does this for scanning the bus: Ref: https://playground.arduino.cc/Main/I2cScanner/
for (byte address = 1; address < 127; ++address) {
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
byte error = Wire.endTransmission();
So, it begins the transmission and then ends it to check for an error but not really pulling an address from the attached device.
Perhaps what is needed in the pico-sdk is to end or clear the transmission when communicating with an i2c device.
Perhaps it would make sense to add a function to pico-sdk that attempts to unblock a blocked I2C bus. If an I2C device is blocking the I2C bus by holding SDA low, it's often possible to recover and unblock the bus by toggeling the SCL line a few times.
Yes probably worth adding a function to bitbang the extra 9 clock cycles to get SDA unstuck, and letting the user call this following a timeout.
Also tempting to add a soft I2C implementation both for general use and for handling zero-length accesses, so we can have a more robust bus scan.
I've also ordered myself one of those displays so I can see how it behaves once SDA is stuck low
Just a note, the Grove LCD version I have is the Grove - LCD RGB Backlight v4.0.
Also, with the code changes that were suggested, a PCF8574 based LCD no longer worked with either the bus_scan or the lcd_1602_i2c example . I ended up re-cloning the pico-sdk and pico-examples repos to get it to work again.
Also, with the code changes that were suggested, a PCF8574 based LCD no longer worked with either the bus_scan or the lcd_1602_i2c example . I ended up re-cloning the pico-sdk and pico-examples repos to get it to work again.
@jomoengineer I can't manage to reproduce these problems.
With i2c.c from the develop branch this is what bus_scan from pico_examples displays for me with a PCF8574 based LCD on the I2C bus:
I2C Bus Scan
0 1 2 3 4 5 6 7 8 9 A B C D E F
00 . . . . . . . . . . . . . . . .
10 . . . . . . . . . . . . . . . .
20 . . . . . . . @ . . . . . . . .
30 . . . . . . . . . . . . . . . .
40 . . . . . . . . . . . . . . . .
50 . . . . . . . . . . . . . . . .
60 . . . . . . . . . . . . . . . .
70 . . . . . . . . . . . . . . . .
Done.
The lcd_1602_i2c example also functions correctly.
@Wren6991 said he had ordered a display, so assigning to him
It is a long shot, however this might be related to https://github.com/raspberrypi/pico-sdk/issues/708 even though in that case the hang in i2c_read_blocking
happens for any size of data transfer, not just the 1-byte ones like in bus scan.
Does this display work with other transfer sizes?
It is a long shot, however this might be related to raspberrypi/pico-sdk#708 even though in that case the hang in
i2c_read_blocking
happens for any size of data transfer, not just the 1-byte ones like in bus scan.
The problem described in this issues is related to the Grove 16 x 2 LCD. The displays LCD controller doesn't support one byte reads. If an attempt is made to read one byte from the displays I2C controller it will block the I2C bus and hold SDA low forever. The problem being discussed in this issue is unrelated to the issue you are experiencing with i2c_write_raw_blocking
.
Does this display work with other transfer sizes?
Yes, the display works fine.
I am attempting to run the 'bus_scan' pico-example from the i2c/bus_scan folder with a Grove i2c LCD connected to Pins 6,7 (i2c0) but the scan hangs at line '30' This is the LCD I am using: https://wiki.seeedstudio.com/Grove-16x2_LCD_Series/
Ex:
I have an OLED Display which the bus_scan code seems to pick up fine:
However, I have a Python script which scans the same bus and it seems to pick-up the Grove LCD addresses without issue:
Is there something I have to set differently for the Grove LCD vs another i2c device for the pico-example bus_scan code?