Enabling uarte0 causes i2c0 ssd1306 driver initialization failure

I'm using zephyr with a custom board (nrf52840) which has a ssd1306 OLED. I have it configured to use i2c0 and is working fine with lvgl.

When I try to enable uart0 (using the async API), the ssd1306 device initialization fails. I can confirm that both display and uart work as expected individually. When enabled together is when this issue occurs. I have triple checked that they don't have any overlapping pin configurations.

Is there anything else I should be checking?

Parents
  • Hi,

     

    Which GPIOs are you using for I2C0?

    I have triple checked that they don't have any overlapping pin configurations.

    Have you checked that the RTS/CTS does not overlap? That is by-default P0.05 to P0.08 on the nRF52840-DK.

     

    Kind regards,

    Håkon

  • I have my own board definition. In this board, I define the following:

    &pinctrl {
    	uart0_default: uart0_default {
    		group1 {
    			psels = <NRF_PSEL(UART_TX, 1, 11)>;
    		};
    		group2 {
    			psels = <NRF_PSEL(UART_RX, 1, 12)>;
    			bias-pull-up;
    		};
    	};
    
    	uart0_sleep: uart0_sleep {
    		group1 {
    			psels = <NRF_PSEL(UART_TX, 1, 11)>,
    				<NRF_PSEL(UART_RX, 1, 12)>;
    			low-power-enable;
    		};
    	};
    
    	i2c0_default: i2c0_default {
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
    				<NRF_PSEL(TWIM_SCL, 0, 5)>;
    		};
    	};
    
    	i2c0_sleep: i2c0_sleep {
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 4)>,
    				<NRF_PSEL(TWIM_SCL, 0, 5)>;
    			low-power-enable;
    		};
    	};
    };

    And the the ssd1306 entry:

    &i2c0 {
    	status = "okay";
    	ssd1306_128x64: ssd1306@3c {
    		compatible = "solomon,ssd1306fb";
    		reg = <0x3c>;
    		width = <128>;
    		height = <64>;
    		segment-offset = <0>;
    		page-offset = <0>;
    		display-offset = <0>;
    		multiplex-ratio = <63>;
    		segment-remap;
    		com-invdir;
    		prechargep = <0x22>;
    	};
    };

  • I have 4.7K external pull-ups on SCL and SDA lines. The clock speed was unset till now, but setting it to 100 kHZ (with `clock-frequency = <I2C_BITRATE_STANDARD>;` in the i2c0 node) has no effect.

    I can post scope screenshots and logs tomorrow when I get back to my desk.
    But I'm curious to understand your reasoning... Since i2c0 works if I disable uart0, I'd expect the issue to have something to the way they interact with each other. If it were a physical issue, that would still be the case with i2c0 alone no?
  • Hi,

     

    On the nRF52-series devices, the UART is not a shared resource, which it is on the nRF53-series and on-ward.

    This means that TWIM0 and UARTE0 can be used simultaneously without any issues, provided that they do not create a pin conflict for each other. Ie. there is no reason why these two peripherals cannot coexist on the nRF52840.

     

    sidcha said:
    I can post scope screenshots and logs tomorrow when I get back to my desk.

    perfect, this would be great.

     

    Kind regards,

    Håkon

  • Thanks for the response. I'm aware that those peripherals are non-shared. I was wondering if anything else was shared, such as IRQs, DMAs etc.,

    Here is a scope capture of the failure:

    I found Zephyr tries to send a single 0 byte to the OLED at address 0x3c. This can be seen in the capture as well.

  • Hi,

     

    There is no direct error in the image that you show. The sensor ACK, by keeping the line low at the LSBit.

    Do you have any runtime logs or similar? What happens in a normal scenario?

    Is this the very first transaction, or is it in the middle of the init? There is so much information that is not presented here. You need to provide traces/logs from boot-up.

     

    sidcha said:
    I was wondering if anything else was shared, such as IRQs, DMAs etc.,

    No, they are not shared between TWIM0 and UARTE0 on the nRF52840.

     

    Kind regards,

    Håkon

  • Another update: I switched to a bit-banged i2c for the OLED and again things work as expected.
    Tracking down the failure from software, I found that the I2C transfer takes longer than I2C_TRANSFER_TIMEOUT_MSEC and leads to a nrfx_twi_disable() + i2c_nrfx_twi_recover_bus() calls followed by -EIO return.

    Here is the call path that leads to this error:

    i2c_nrfx_twi_transfer()
        i2c_nrfx_twi_msg_transfer()
        ret = k_sem_take(&data->completion_sync, I2C_TRANSFER_TIMEOUT_MSEC);
        if ret != 0:
    	/* Whatever the frequency, completion_sync should have
    	* been given by the event handler.
    	*
    	* If it hasn't, it's probably due to an hardware issue
    	* on the I2C line, for example a short between SDA and
    	* GND.
    	* This is issue has also been when trying to use the
    	* I2C bus during MCU internal flash erase.
    	*
    	* In many situation, a retry is sufficient.
    	* However, some time the I2C device get stuck and need
    	* help to recover.
    	* Therefore we always call i2c_nrfx_twi_recover_bus()
    	* to make sure everything has been done to restore the
    	* bus from this error.
    	*/
    	nrfx_twi_disable(&config->twi);
    	(void)i2c_nrfx_twi_recover_bus(dev);
    	ret = -EIO;
    
    I cannot find any evidence to a hardware failure on the i2c bus when control reached this point. I have 2 prototypes which have the same failure so I'm reluctant to write it off as a board specific failure too :)
Reply
  • Another update: I switched to a bit-banged i2c for the OLED and again things work as expected.
    Tracking down the failure from software, I found that the I2C transfer takes longer than I2C_TRANSFER_TIMEOUT_MSEC and leads to a nrfx_twi_disable() + i2c_nrfx_twi_recover_bus() calls followed by -EIO return.

    Here is the call path that leads to this error:

    i2c_nrfx_twi_transfer()
        i2c_nrfx_twi_msg_transfer()
        ret = k_sem_take(&data->completion_sync, I2C_TRANSFER_TIMEOUT_MSEC);
        if ret != 0:
    	/* Whatever the frequency, completion_sync should have
    	* been given by the event handler.
    	*
    	* If it hasn't, it's probably due to an hardware issue
    	* on the I2C line, for example a short between SDA and
    	* GND.
    	* This is issue has also been when trying to use the
    	* I2C bus during MCU internal flash erase.
    	*
    	* In many situation, a retry is sufficient.
    	* However, some time the I2C device get stuck and need
    	* help to recover.
    	* Therefore we always call i2c_nrfx_twi_recover_bus()
    	* to make sure everything has been done to restore the
    	* bus from this error.
    	*/
    	nrfx_twi_disable(&config->twi);
    	(void)i2c_nrfx_twi_recover_bus(dev);
    	ret = -EIO;
    
    I cannot find any evidence to a hardware failure on the i2c bus when control reached this point. I have 2 prototypes which have the same failure so I'm reluctant to write it off as a board specific failure too :)
Children
  • > Do you have any runtime logs or similar? What happens in a normal scenario?

    Most of the logs I have are from Bluetooth layer in my app which is irrelevant to this issue. The only log line I get from Zephyr is this:

    [00:00:00.763,458] <dbg> i2c: i2c_dump_msgs_rw: I2C msg: i2c@40003000, addr=3c    
    [00:00:00.763,488] <dbg> i2c: i2c_dump_msgs_rw:    W      len=01: 00
    [00:00:00.763,549] <dbg> i2c: i2c_dump_msgs_rw:    W    P len=01: ae
    [00:00:00.763,549] <err> ssd1306: Failed to initialize device!
    *** Booting Zephyr OS build v4.2.0-2290-g617b71bc174b ***        
    [00:00:00.764,709] <err> lvgl: Display device 0 is not ready                                                                                            
    [00:00:00.764,892] <err> app: Device not ready, aborting test
    

    As you can see, that's not very helpful. Can you please be more specific about what kind of logs you care about so I can produce them for you? Such as which configs I should have enabled.

    > Is this the very first transaction, or is it in the middle of the init?

    This is soon after reset and then that's the end of all I2C activity on the bus in the failing case. When things work, there are ofc a lot more activity.

    Update: Added logs with debug level for I2C and SSD1306

  • Hi,

     

    Please share the full boot sequence, as asked for here:

    Håkon Alseth said:

    There is no direct error in the image that you show. The sensor ACK, by keeping the line low at the LSBit.

    Do you have any runtime logs or similar? What happens in a normal scenario?

    Is this the very first transaction, or is it in the middle of the init? There is so much information that is not presented here. You need to provide traces/logs from boot-up.

    sidcha said:
    Can you please be more specific about what kind of logs you care about so I can produce them for you? Such as which configs I should have enabled.

    The amount of debug logs in the driver is minimal, so you need to debug this.

    Enter debug mode, use break points to check where it returns EIO:

    https://github.com/zephyrproject-rtos/zephyr//blob/main/drivers/display/ssd1306.c#L441-L473

    sidcha said:
    I found that the I2C transfer takes longer than I2C_TRANSFER_TIMEOUT_MSEC

    Try to expand the value of that config. Again, I have no idea if the picture that you posted of the SDA/SCL is mid-transaction or if that is the very first one. Please confirm.

     

    Kind regards,

    Håkon

  • > Please share the full boot sequence, as asked for here:

    I have code that enabled the OLED and UART. On reset, I see the following logs (which I posted earlier) and then nothing after that. Those are all the things that happen at boot.
    [00:00:00.763,458] <dbg> i2c: i2c_dump_msgs_rw: I2C msg: i2c@40003000, addr=3c    
    [00:00:00.763,488] <dbg> i2c: i2c_dump_msgs_rw:    W      len=01: 00
    [00:00:00.763,549] <dbg> i2c: i2c_dump_msgs_rw:    W    P len=01: ae
    [00:00:00.763,549] <err> ssd1306: Failed to initialize device!
    *** Booting Zephyr OS build v4.2.0-2290-g617b71bc174b ***        
    [00:00:00.764,709] <err> lvgl: Display device 0 is not ready                                                                                            
    [00:00:00.764,892] <err> app: Device not ready, aborting test
    Physically on SCL/SDA lines, there is no other activity.

    > The amount of debug logs in the driver is minimal, so you need to debug this. Enter debug mode, use break points to check where it returns EIO:

    I did just that and posted where it returns -EIO: the very first I2C transaction; in driver, that is at the call to ssd1306_suspend() here.

    > Try to expand the value of that config. Again, I have no idea if the picture that you posted of the SDA/SCL is mid-transaction or if that is the very first one. Please confirm.

    The posted scope capture is soon after reset after that there is no activity on the bus.

    I suspect that you missed at-least one of my earlier replies?

  • Hi,

     

    sidcha said:

    The posted scope capture is soon after reset after that there is no activity on the bus.

    I suspect that you missed at-least one of my earlier replies?

    Thank you for confirming this.

    In your log, it shows 3 rw transactions:

    [00:00:00.763,458] <dbg> i2c: i2c_dump_msgs_rw: I2C msg: i2c@40003000, addr=3c
    [00:00:00.763,488] <dbg> i2c: i2c_dump_msgs_rw: W len=01: 00
    [00:00:00.763,549] <dbg> i2c: i2c_dump_msgs_rw: W P len=01: ae

     

    However, the transaction on the SDA/SCL pins only show two. Is there a third transaction shifted out on the pins?

    sidcha said:
    I did just that and posted where it returns -EIO: the very first I2C transaction; in driver, that is at the call to ssd1306_suspend() here.

    Good. This command corresponds with the "0xae" that is sent, ie define SSD1306_DISPLAY_OFF, which is sent with a write and a stop condition (indicated with W and P).

     

    You mention that you have a scenario where this works as intended. Can you please share plots (of the SDA/SCL via a logic analyzer), so I can compare the transactions in the beginning?

     

    Kind regards,

    Håkon

  • > However, the transaction on the SDA/SCL pins only show two. Is there a third transaction shifted out on the pins?

    No, what I posted was everything that happened during the failure case.

    After some more trial and errors, I noticed that the uart0 was configured to do DMA while the i2c0 was not. First I tried removing DMA from uart0 -- it did not help. I then added DMA to i2c0 and things start working.

    For now this setup (both DMA) works but I would still like to understand how this is a problem. Can you help explaining?

Related