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

various i2c fixes #282

Merged
merged 5 commits into from
Nov 6, 2020
Merged

various i2c fixes #282

merged 5 commits into from
Nov 6, 2020

Conversation

sjoerdsimons
Copy link
Contributor

Fixes required to work with an AM2320 sensor; This sensor doesn't acknowledge the first i2c message sent to it, but it triggers it to wake up. This caused the i2c state to stay in an error state and the bus not to go idle. This is fixed in the first two patches

Later on a multi (>2) byte read is necessary to actually get the data; However the data didn't get properly acknowledged, fixed in the last patch.

clear the error flags after detecting them as the hardware does not
autoclear on read.

Signed-off-by: Sjoerd Simons <sjoerd@luon.net>
If the remote device fails to acknowledge send a stop. This allows the
bus to go back to idle again.

Signed-off-by: Sjoerd Simons <sjoerd@luon.net>
Enable the ack bit on multibyte (>2) reads otherwise bytes from the
remote device are not acknowledged.

Signed-off-by: Sjoerd Simons <sjoerd@luon.net>
Copy link
Member

@TheZoq2 TheZoq2 left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, that sounds like it must have been an annoying bug to track down 😅

I added a thought about the random status register reads which I don't know the purpose of.

Would you mind adding an entry to the changelog about this? Other than that, it looks great!

@@ -436,12 +436,17 @@ where
last_ret
}

fn write_without_stop(&mut self, addr: u8, bytes: &[u8]) -> NbResult<(), Error> {
self.send_start_and_wait()?;
fn send_addr_and_wait(&mut self, addr: u8, read: bool) -> NbResult<(), Error> {
self.nb.i2c.sr1.read();
Copy link
Member

Choose a reason for hiding this comment

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

Not directly related to the PR, but I'm now becomming unsure about what this is doing here. My best guess is that it tries to clear the status registers, but clearly that's not done since the first commit in the PR is needed...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm; looking at the datasheet i'm not entirely sure if it's needed here either; Fwiw the relevant parts are:

Then the master waits for a read of the SR1 register followed by a write in the DR register
with the Slave address (see Figure 273 and Figure 274 Transfer sequencing EV5).

with EV5 being EV5: SB=1, cleared by reading SR1 register followed by writing DR register with Address.

But ofcourse the send_start_and_wait is already reading SR1 while polling for the flag; So it's probably not needed here.

Overall it seems some bits of sr1 are persistents (the error bits in particular) while others gets cleared on read

Copy link
Member

Choose a reason for hiding this comment

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

Right. I think the best course of action here is to break this into its own issue and maybe fix it later since doing the wrong thing could break things subtly.

@TheZoq2 TheZoq2 merged commit a100426 into stm32-rs:master Nov 6, 2020
@freiguy1
Copy link

Thanks for this fix! I was beating my head against the wall trying to figure out why I couldn't use a library to read from a real time clock module. It was panicking when trying to get the date & time because it read 8 bytes through I2C.

Comment on lines +223 to +232
$i2c.sr1.write(|w| w.berr().clear_bit());
Err(Other(Error::Bus))
} else if sr1.arlo().bit_is_set() {
$i2c.sr1.write(|w| w.arlo().clear_bit());
Err(Other(Error::Arbitration))
} else if sr1.af().bit_is_set() {
$i2c.sr1.write(|w| w.af().clear_bit());
Err(Other(Error::Acknowledge))
} else if sr1.ovr().bit_is_set() {
$i2c.sr1.write(|w| w.ovr().clear_bit());
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, is write correct here? These look like no-op to me.

Copy link
Member

@TheZoq2 TheZoq2 Dec 23, 2020

Choose a reason for hiding this comment

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

I could be wrong, but I believe these are there to reset the flags.

Edit: Reference manual says

Cleared by software writing 0, or by hardware when PE=0.

And I don't believe we have PE set

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but sr1.write(|w| w.any_field().clear_bit()); basically writes 0 to the whole register, regardless of the field name. That's why this looks like a mistake.

Copy link
Member

Choose a reason for hiding this comment

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

Oh right, I guess that should be modify instead. Good catch, I'll go ahead and fix that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants