Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed 370: the hanging i2c because of a faulty stop the twint is set by the avr itself. #372

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

borissmidt
Copy link

@borissmidt borissmidt commented Nov 23, 2022

I had added the timouts that you requested for and i noticed that it always go stuck on start.

So i assumed that it was one of the bits set at the stop which caused the i2c to fail.
The twint is set by the avr itself. So when i disable the manual setting and now it works.
https://www.engineersgarage.com/avr-atmega32-twi-registers/

@borissmidt
Copy link
Author

FYI i can read the sensor with a 100kHz bus and a 25ms delay in between. So it seems to work as expected.

@Rahix Rahix added the bug Something isn't working label Nov 25, 2022
Copy link
Owner

@Rahix Rahix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR! Please check my comment below, I'm not sure whether the fix you provided is entirely correct...

@@ -538,7 +538,7 @@ macro_rules! impl_i2c_twi {
#[inline]
fn raw_stop(&mut self) -> Result<(), Error> {
self.twcr
.write(|w| w.twen().set_bit().twint().set_bit().twsto().set_bit());
.write(|w| w.twen().set_bit().twsto().set_bit());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is really the right thing to do. The datasheet clearly states that the TWINT bit should be set for generating the stop condition. And it makes sense, we do want to acknowledge any pending TWI interrupt and clear the flag to allow the hardware to continue doing its thing.

However, I wonder whether we are actually missing a wait after this. Right now we're not waiting for the stop condition getting sent. I see that Arduino's TWI driver does wait here: https://github.com/arduino/ArduinoCore-avr/blob/60f0d0b125e06dbf57b800192c80e5f60d681438/libraries/Wire/src/utility/twi.c#L414-L429

So maybe we just need to add the following wait afterwards?

// wait for the stop condition to complete
while self.twcr.read().twstop().bit_is_set() { }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.twcr
    .write(|w| w.twen().set_bit().twsto().set_bit());

while self.twcr.read().twsto().bit_is_clear() { }

this seems to work.
setting twint makes the next start hang

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, then it must be yet another problem I think. I don't have the time right now, but I suppose it would be good to look at the signals with an oscilloscope to see what the difference is. My guess it that we're either not setting another bit that we should set or that we're not waiting properly somewhere...

Another lead is comparing our implementation with the one from Arduino Core that I linked to above. Maybe there are other differences that we should investigate...

Interestingly, nobody else reported any similar issues so far...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see if i can get a logic analyser in my local hackerspace

Copy link

@blueted2 blueted2 Aug 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize I'm a little late to the party, I do believe that clearing twint is required. Without it it looks like the data and clock lines are held low after the transaction:

with_twint without_twint
With twint cleared Without twint cleared

Channel 1 (blue) is sda and channel 2 (yellow) is scl.

Copy link

@blueted2 blueted2 Aug 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I am still encountering a hanging I2C issue when performing either I2c::ping_device or I2c::i2cdetect.

let ping_res = i2c.ping_device(0x27, i2c::Direction::Read).unwrap(); // hangs
//---
i2c.i2cdetect(&mut serial, i2c::Direction::Read); // hangs when getting to device 0x27

When doing I2c::i2cdetect, it hangs after finding my device at address 0x27:

-    0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:       -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- 27

Now the data line is being held low for some reason:
i2cdetect

It looks like I2c::ping_device is the cause, as I get similar behavior when calling it directly.

I can duplicate this behavior by removing the raw_read in I2c::read and performing a normal read:

fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
    self.p.raw_start(address, Direction::Read)?;
    // self.p.raw_read(buffer)?;
    self.p.raw_stop()?;
    Ok(())
}
let mut buf: [u8; 1] = Default::default();
i2c.read(0x27, &mut buf).unwrap(); // now hangs
ufmt::uwriteln!(&mut serial, "buf[0]= {}", buf[0]).unwrap();

It seems that something goes wrong if raw_stop is called directly after raw_start without any data being sent or received.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants