-
Notifications
You must be signed in to change notification settings - Fork 221
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
base: main
Are you sure you want to change the base?
Conversation
…by the avr itself.
FYI i can read the sensor with a 100kHz bus and a 25ms delay in between. So it seems to work as expected. |
There was a problem hiding this 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()); |
There was a problem hiding this comment.
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() { }
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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:
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.
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/