raspberrypi / pico-examples

BSD 3-Clause "New" or "Revised" License
2.84k stars 820 forks source link

pico-example bus_scan hangs when Grove I2C LCD is connected #102

Open jomoengineer opened 3 years ago

jomoengineer commented 3 years ago

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:

I2C Bus Scan
   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
00 .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
10 .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
20 .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
30 .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  

I have an OLED Display which the bus_scan code seems to pick up fine:

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.

However, I have a Python script which scans the same bus and it seems to pick-up the Grove LCD addresses without issue:

>>> %Run -c $EDITOR_CONTENT
I2C Scan:
[62, 98, 112]

Is there something I have to set differently for the Grove LCD vs another i2c device for the pico-example bus_scan code?

lurch commented 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?

jomoengineer commented 3 years ago

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.

jomoengineer commented 3 years ago

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.

lurch commented 3 years ago

@fivdi Do you think this might be related to https://github.com/raspberrypi/pico-sdk/issues/336 ?

fivdi commented 3 years ago

@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.

jomoengineer commented 3 years ago

@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.

fivdi commented 3 years ago

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);
jomoengineer commented 3 years ago

@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.

jomoengineer commented 3 years ago

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
fivdi commented 3 years ago

@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.

jomoengineer commented 3 years ago

I have an actual Raspberry Pi Pico: https://www.raspberrypi.org/products/raspberry-pi-pico/

fivdi commented 3 years ago

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.

jomoengineer commented 3 years ago

@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

fivdi commented 3 years ago

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.

fivdi commented 3 years ago

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.

jomoengineer commented 3 years ago

@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.

jomoengineer commented 3 years ago

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 @  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
fivdi commented 3 years ago

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.

jomoengineer commented 3 years ago

@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.

fivdi commented 3 years ago

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.

jomoengineer commented 3 years ago

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.

Wren6991 commented 3 years ago

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.

Wren6991 commented 3 years ago

I've also ordered myself one of those displays so I can see how it behaves once SDA is stuck low

jomoengineer commented 3 years ago

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.

fivdi commented 3 years ago

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.

kilograham commented 2 years ago

@Wren6991 said he had ordered a display, so assigning to him

davidedelvento commented 2 years ago

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?

fivdi commented 2 years ago

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.