Infinite loop in the `i2c (or Twim) implementation` of nrf52840_hal

I don't have one on me right now. But I'm looking for one. But from what I can tell, I don't think its transmitting at all and the reason for my suspicion is that both pins are set to INPUT before the write. (i.e. at the time of the write to TASKS_STARTTX register, both pins are in INPUT mode)

Shouldn't they be set to OUTPUT if we are going to set the state of a pin?

I can confirm that the ADDRESS register contains the correct slave address and it matches the one in the micropython case. Here's a screenshot of the micropython test. If you compare it with the earlier screenshot, we see the same addresses - (address 96 in decimal and x60 in hex)

I would still try 0xC0 instead of 0x60.

This doc says micropython's scan returns a 7-bit address and the nrf-hal crate's write doesn't seem to do shifting.

Ok, let me try that and get back to you.

I can confirm that changing the address to 0xC0 doesn't work. Here's the screenshot.

@trembel @lonesometraveler I was wondering if someone could test wiring-up an nrf52840 board and an i2c device (any type) and confirm if the current version (0.10.0) of nrf52840_hal actually works.

Sorry, I've at the moment no access to an nRF52, but I can get one in a week.

Maybe you should try building a minimal example (i.e. initialize only this two pin and make a write to i2c) to see if that works. Maybe you could also try it bare-metal without HAL, I2C is not that complicated on the nRF52 (take a look here: https://github.com/andenore/NordicSnippets/blob/master/examples/i2c_master/main.c, its in C but easily portable to Rust).

The registers can be found in the datasheet.

This one is a bare-metal binary but yeah, I'll probably try writing my own minimal i2c implementation. Although, I was hoping I didn't have to for my current need (which is just a simple write-wait-read). But thanks!

@nihalpasham
I only have a nRF52832. The code below works for me. I can read WHO_AM_I register of my sensor. I pass a 7 bit address.


fn main() -> ! {
    let p = pac::Peripherals::take().unwrap();
    let port0 = p0::Parts::new(p.P0);

    let scl = port0.p0_26.into_floating_input().degrade();
    let sda = port0.p0_27.into_floating_input().degrade();

    let pins = twim::Pins { scl, sda };

    let mut i2c = Twim::new(p.TWIM0, pins, twim::Frequency::K100);

    let bytes = [0x0F; 1];
    i2c.write(0x6B, &bytes);

    let mut bytes = [0; 1];
    i2c.read(0x6B, &mut bytes);

    loop {}
}

Thanks @lonesometraveler. Now, I think it kind of rules out a bug in the HAL implementation, considering both nRF52840 and nRF52832 use the same shared (i.e. common) HAL. So, that leaves me with 2 other things, I could try.

  1. Use an external pull-up resistor and
  2. Get my hands on a scope.

PS: which scope are you using?

I think the software and the logic analyzer is from saleae. You can also use a cheap saleae clone, if you have one around (~5$)

1 Like

So, it turns out I was dealing with buggy hardware.

I wired up my nRF to a different i2c based sensor (IMU -mpu6050) and it worked flawlessly (results in the screenshot below). I was able to wake the IMU sensor, examine the registers and confirm that the requisite 2 bytes were indeed transferred. So, I think I can junk my extension board and find another way to interface with the actual sensor I'm trying to talk to.

I'm including a link to the extension board just in case someone happens to run into this problem. (The extension board is made by microchip that somehow works with an esp32 but doesnt play well with other boards). https://www.microchip.com/developmenttools/ProductDetails/AT88CKSCKTSOIC-XPRO#additional-summary

Actually, I guess this is expected as the extension board was designed to specifically work with ATMEL dev boards only (that also magically happens to work with an esp32) :thinking:

I use a logic analyzer from Saleae.

1 Like

Finally managed to get my hands on a logic analyzer. Here are my findings -

  1. nRF52 does not get an ACK back and from what I know about i2c, it using the 7-bit addressing scheme.
  2. ESP32 on the other hand gets an ACK but it looks like its using i2c's 10-bit addressing scheme

Is my understanding correct? If yes, can we use 10-bit addresses on the nRF too.

It seems to me both pictures show a 7-bit address + R/W bit. How did you conclude ESP32 uses 10 bit addressing?

This page explains I2C slave addressing. It may be helpful.

It's more like a question than a conclusion but thanks for link. So, it's not an addressing issue.

With that out of the way, I see the number of clock pulses in each capture is different.

  1. ESP32 has 9 before SDA goes high
  2. nRF has 8 before SDA goes high

My understanding is that after transmitting a byte of data, the master releases the SDA line (pushes it to the high state) which is pulled down (after one clock pulse) by the receiver to be counted as an ACK.

Why do they look different?

I think I understand now. The slave isn't responding to the nRF but is responding to the ESP32 (and that shows in ESP32's logic capture, where the SDA line is pulled down to acknowledge each byte unlike that of the nRF).

  1. The question is why wont it respond to the nRF?

Yes, the master pulses 9 times. The first 8 pulses are for data and the 9th is for ACK/NACK.

Your device is not responding. Common mistakes are invalid address, missing pull-up resistors, clock too fast...

To learn more about the protocol, I recommend reading the I2C specification.

Finally figured this out with a ton of help from @henrik_alser. So, to close this one out, here's the

Solution: There's two parts to it

  1. First, the current implementation of TWIM (i.e. the common i2c interface included in nrf-hal) ends up in an infinite loop if no slave ACKs an address that's placed on the bus (including situations where slaves could be sleeping). A PR has been submitted by Henrik Alser to fix this issue here - https://github.com/nrf-rs/nrf-hal/pull/166.
  2. Second, this i2c slave requires a specific wake condition to be met before any actual data can be exchanged. The data line needed to be held down for at least 60 us followed by a high of at least 1500 us, before sending any data (including the slave's address).

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.