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

Have you some documentation about Pins, since I can't find anything about it.
If I look to the TWI sample of the hal, it seems that the pins should be p0_p26 and p0_27 instead of p26 and p27 - but I don't know if the Pins::new() makes some abstraction to it.
See: https://github.com/nrf-rs/nrf-hal/blob/master/examples/twi-ssd1306/src/main.rs#L38

Sure, Pins is just another macro defined in Rust-SDK for the nrf52840-mdk development board.

Here's the GitHub repo for it - https://github.com/nrf-rs/nrf52840-mdk-rs

Ok, that seems correct. Have you tried looking at it with a logic analyzer to see if something is sent and that you get a ACK package?

A common mistake is passing a wrong address. Are you sure your address is correct? In 7 bit addressing, you may need to shift the address to left by one bit. (The least significant bit is Read/Write bit.) Perhaps micropython automatically does that for you and the nrf52 hal doesn't? (Or the other way around?)

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?