From 13b417adedad0bc977f0024275ab191fe089de9b Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 26 May 2020 11:03:38 +0200 Subject: [PATCH 1/4] Detect and return I2C errors Close #100 --- src/i2c.rs | 2 +- src/i2c/peripheral.rs | 88 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/i2c.rs b/src/i2c.rs index 31eedd21f..c27d10ca7 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -60,5 +60,5 @@ mod peripheral; pub use self::{ clock::{Clock, ClockSource}, instances::Instance, - peripheral::I2C, + peripheral::{Error, I2C}, }; diff --git a/src/i2c/peripheral.rs b/src/i2c/peripheral.rs index 6a4992b1e..993aed9d8 100644 --- a/src/i2c/peripheral.rs +++ b/src/i2c/peripheral.rs @@ -1,5 +1,4 @@ use embedded_hal::blocking::i2c; -use void::Void; use crate::{init_state, swm, syscon}; @@ -125,7 +124,7 @@ impl i2c::Write for I2C where I: Instance, { - type Error = Void; + type Error = Error; /// Write to the I2C bus /// @@ -146,7 +145,11 @@ where for &b in data { // Wait until peripheral is ready to transmit - while self.i2c.stat.read().mstpending().is_in_progress() {} + while self.i2c.stat.read().mstpending().is_in_progress() { + if let Some(error) = Error::read(&self.i2c) { + return Err(error); + } + } // Write byte self.i2c.mstdat.write(|w| unsafe { w.data().bits(b) }); @@ -156,7 +159,11 @@ where } // Wait until peripheral is ready to transmit - while self.i2c.stat.read().mstpending().is_in_progress() {} + while self.i2c.stat.read().mstpending().is_in_progress() { + if let Some(error) = Error::read(&self.i2c) { + return Err(error); + } + } // Stop transmission self.i2c.mstctl.modify(|_, w| w.mststop().stop()); @@ -169,7 +176,7 @@ impl i2c::Read for I2C where I: Instance, { - type Error = Void; + type Error = Error; /// Read from the I2C bus /// @@ -182,7 +189,11 @@ where buffer: &mut [u8], ) -> Result<(), Self::Error> { // Wait until peripheral is idle - while !self.i2c.stat.read().mststate().is_idle() {} + while !self.i2c.stat.read().mststate().is_idle() { + if let Some(error) = Error::read(&self.i2c) { + return Err(error); + } + } // Write slave address with rw bit set to 1 self.i2c @@ -197,7 +208,11 @@ where self.i2c.mstctl.write(|w| w.mstcontinue().continue_()); // Wait until peripheral is ready to receive - while self.i2c.stat.read().mstpending().is_in_progress() {} + while self.i2c.stat.read().mstpending().is_in_progress() { + if let Some(error) = Error::read(&self.i2c) { + return Err(error); + } + } // Read received byte *b = self.i2c.mstdat.read().data().bits(); @@ -209,3 +224,62 @@ where Ok(()) } } + +/// I2C error +#[derive(Debug, Eq, PartialEq)] +pub enum Error { + /// Event Timeout + /// + /// Corresponds to the EVENTTIMEOUT flag in the STAT register. + EventTimeout, + + /// Master Arbitration Loss + /// + /// Corresponds to the MSTARBLOSS flag in the STAT register. + MasterArbitrationLoss, + + /// Master Start/Stop Error + /// + /// Corresponds to the MSTSTSTPERR flag in the STAT register. + MasterStartStopError, + + /// Monitor Overflow + /// + /// Corresponds to the MONOV flag in the STAT register. + MonitorOverflow, + + /// SCL Timeout + /// + /// Corresponds to the SCLTIMEOUT flag in the STAT register. + SclTimeout, +} + +impl Error { + fn read(i2c: &I) -> Option { + let stat = i2c.stat.read(); + + // Check for error flags. If one is set, clear it and return the error. + if stat.mstarbloss().bit_is_set() { + i2c.stat.write(|w| w.mstarbloss().set_bit()); + return Some(Self::MasterArbitrationLoss); + } + if stat.mstststperr().bit_is_set() { + i2c.stat.write(|w| w.mstststperr().set_bit()); + return Some(Self::MasterStartStopError); + } + if stat.monov().bit_is_set() { + i2c.stat.write(|w| w.monov().set_bit()); + return Some(Self::MonitorOverflow); + } + if stat.eventtimeout().bit_is_set() { + i2c.stat.write(|w| w.eventtimeout().set_bit()); + return Some(Self::EventTimeout); + } + if stat.scltimeout().bit_is_set() { + i2c.stat.write(|w| w.scltimeout().set_bit()); + return Some(Self::SclTimeout); + } + + None + } +} From fb311ba1445a227eff85ce36548fb0b6b9a21853 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 26 May 2020 11:22:06 +0200 Subject: [PATCH 2/4] Add method to read and clear I2C errors --- src/i2c/peripheral.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/i2c/peripheral.rs b/src/i2c/peripheral.rs index 993aed9d8..121870c61 100644 --- a/src/i2c/peripheral.rs +++ b/src/i2c/peripheral.rs @@ -99,6 +99,23 @@ where } } +impl I2C +where + I: Instance, +{ + /// Read and clear a detected error + /// + /// The read and write method will return an error, if one was detected. + /// However, only one error will be returned, even though, theoretically, + /// multiple could have been detected. + /// + /// This method can be used to read and clear all currently detected errors + /// before resuming normal operation. + pub fn read_error(&mut self) -> Option { + Error::read(&self.i2c) + } +} + impl I2C where I: Instance, From 8376fef1966f4f5662f9bec822f6acdde71fc6e6 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 26 May 2020 12:05:42 +0200 Subject: [PATCH 3/4] Add support for enabling/disabling I2C interrupts --- src/i2c.rs | 2 + src/i2c/interrupts.rs | 86 +++++++++++++++++++++++++++++++++++++++++++ src/i2c/peripheral.rs | 18 ++++++++- 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/i2c/interrupts.rs diff --git a/src/i2c.rs b/src/i2c.rs index c27d10ca7..89926530b 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -55,10 +55,12 @@ mod clock; mod instances; +mod interrupts; mod peripheral; pub use self::{ clock::{Clock, ClockSource}, instances::Instance, + interrupts::Interrupts, peripheral::{Error, I2C}, }; diff --git a/src/i2c/interrupts.rs b/src/i2c/interrupts.rs new file mode 100644 index 000000000..bc3d97e31 --- /dev/null +++ b/src/i2c/interrupts.rs @@ -0,0 +1,86 @@ +use super::Instance; + +macro_rules! interrupts { + ( + $( + $doc:expr, + $field:ident, + $enable:ident, + $disable:ident; + )* + ) => { + /// Used to enable or disable I2C interrupts + /// + /// See [`I2C::enable_interrupts`] or [`I2C::disable_interrupts`]. + /// + /// [`I2C::enable_interrupts`]: struct.I2C.html#method.enable_interrupts + /// [`I2C::disable_interrupts`]: struct.I2C.html#method.disable_interrupts + pub struct Interrupts { + $( + #[doc = $doc] + pub $field: bool, + )* + } + + impl Interrupts { + pub(super) fn enable(&self, i2c: &I) { + i2c.intenset.modify(|_, w| { + $( + if self.$field { + w.$enable().enabled(); + } + )* + + w + }) + } + + pub(super) fn disable(&self, i2c: &I) { + i2c.intenclr.write(|w| { + $( + if self.$field { + w.$disable().set_bit(); + } + )* + + w + }) + } + } + + impl Default for Interrupts { + fn default() -> Self { + Self { + $( + $field: false, + )* + } + } + } + }; +} + +interrupts!( + "Master Pending", master_pending, + mstpendingen, mstpendingclr; + "Master Arbitration Loss", master_arbitration_loss, + mstarblossen, mstarblossclr; + "Master Start/Stop Error", master_start_stop_error, + mstststperren, mstststperrclr; + "Slave Pending", slave_pending, + slvpendingen, slvpendingclr; + "Slave Not Stretching", slave_not_stretching, + slvnotstren, slvnotstrclr; + "Slave Deselect", slave_deselect, + slvdeselen, slvdeselclr; + "Monitor Ready", monitor_ready, + monrdyen, monrdyclr; + "Monitor Overrun", monitor_overrun, + monoven, monovclr; + "Monitor Idle", monitor_idle, + monidleen, monidleclr; + "Event Timeout", event_timeout, + eventtimeouten, eventtimeoutclr; + "SCL Timeout", scl_timeout, + scltimeouten, scltimeoutclr; +); diff --git a/src/i2c/peripheral.rs b/src/i2c/peripheral.rs index 121870c61..21ec3a24e 100644 --- a/src/i2c/peripheral.rs +++ b/src/i2c/peripheral.rs @@ -2,7 +2,7 @@ use embedded_hal::blocking::i2c; use crate::{init_state, swm, syscon}; -use super::{Clock, ClockSource, Instance}; +use super::{Clock, ClockSource, Instance, Interrupts}; /// Interface to an I2C peripheral /// @@ -103,6 +103,22 @@ impl I2C where I: Instance, { + /// Enable interrupts + /// + /// Enables all interrupts set to `true` in `interrupts`. Interrupts set to + /// `false` are not affected. + pub fn enable_interrupts(&mut self, interrupts: Interrupts) { + interrupts.enable(&self.i2c); + } + + /// Disable interrupts + /// + /// Disables all interrupts set to `true` in `interrupts`. Interrupts set to + /// `false` are not affected. + pub fn disable_interrupts(&mut self, interrupts: Interrupts) { + interrupts.disable(&self.i2c); + } + /// Read and clear a detected error /// /// The read and write method will return an error, if one was detected. From 14057a56e440ee03f3fd2174e8866a9f2b51feb2 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 27 May 2020 11:58:52 +0200 Subject: [PATCH 4/4] Improve wording in documentation Co-authored-by: david-sawatzke --- src/i2c/peripheral.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/i2c/peripheral.rs b/src/i2c/peripheral.rs index 21ec3a24e..cbcb503de 100644 --- a/src/i2c/peripheral.rs +++ b/src/i2c/peripheral.rs @@ -121,9 +121,9 @@ where /// Read and clear a detected error /// - /// The read and write method will return an error, if one was detected. - /// However, only one error will be returned, even though, theoretically, - /// multiple could have been detected. + /// The `read` and `write` methods will return an error and clear it, if one + /// was detected. However, if multiple errors occur, only one error will be + /// returned and cleared. /// /// This method can be used to read and clear all currently detected errors /// before resuming normal operation.