From 3ddfc77d1f06f75117bd7f8e5ef8bab77473d085 Mon Sep 17 00:00:00 2001 From: Guinea Wheek Date: Mon, 4 Nov 2024 02:06:16 -0800 Subject: [PATCH] Add support for the error status register and error interrupts (#62) * add error interrupt support * add error_status * Add the other error flags * run cargo fmt * Update comment to reflect addition of error state handling --- src/interrupt.rs | 45 ++++++++++++++++++++ src/lib.rs | 105 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 142 insertions(+), 8 deletions(-) diff --git a/src/interrupt.rs b/src/interrupt.rs index 17aacf8..f02a8cf 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -64,6 +64,47 @@ pub enum Interrupt { /// Behavior is otherwise identical to [`Self::Fifo0Overrun`]. Fifo1Overrun = 1 << 6, + /// Fires the **SCE** interrupt when the error warning limit (receive or transmit error counter + /// >= 96) has been reached. + /// + /// [`Interrupt::Error`] must also be enabled for the interrupt to fire. + /// + /// The interrupt handler must clear the interrupt condition by calling + /// [`Can::clear_error_interrupt`]. + ErrorWarning = 1 << 8, + + /// Fires the **SCE** interrupt when the peripheral enters the error passive state. + /// + /// [`Interrupt::Error`] must also be enabled for the interrupt to fire. + /// + /// The interrupt handler must clear the interrupt condition by calling + /// [`Can::clear_error_interrupt`]. + ErrorPassive = 1 << 9, + + /// Fires the **SCE** interrupt when the peripheral has entered bus-off. + /// + /// [`Interrupt::Error`] must also be enabled for the interrupt to fire. + /// + /// The interrupt handler must clear the interrupt condition by calling + /// [`Can::clear_error_interrupt`]. + BusOff = 1 << 10, + + /// Fires the **SCE** interrupt when the peripheral updates the last error code. + /// + /// [`Interrupt::Error`] must also be enabled for the interrupt to fire. + /// + /// The interrupt handler must clear the interrupt condition by calling + /// [`Can::clear_error_interrupt`]. + LastErrorCode = 1 << 11, + + /// Fires the **SCE** interrupt when the peripheral enters an error state. + /// + /// The error states that will cause the interrupt to fire are determined by the subset of + /// [`Interrupt::ErrorWarning`], [`Interrupt::ErrorPassive`], [`Interrupt::BusOff`], and + /// [`Interrupt::LastErrorCode`] that are enabled along with this flag. + /// + /// The interrupt handler must clear the interrupt condition by calling + /// [`Can::clear_error_interrupt`]. Error = 1 << 15, /// Fires the **SCE** interrupt when an incoming CAN frame is detected while the peripheral is @@ -90,6 +131,10 @@ bitflags::bitflags! { const FIFO1_MESSAGE_PENDING = 1 << 4; const FIFO1_FULL = 1 << 5; const FIFO1_OVERRUN = 1 << 6; + const ERROR_WARNING = 1 << 8; + const ERROR_PASSIVE = 1 << 9; + const BUS_OFF = 1 << 10; + const LAST_ERROR_CODE = 1 << 11; const ERROR = 1 << 15; const WAKEUP = 1 << 16; const SLEEP = 1 << 17; diff --git a/src/lib.rs b/src/lib.rs index 255a097..080fd5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,10 +15,6 @@ //! - Implements the [`embedded-hal`] traits for interoperability. //! - Support for both RX FIFOs (as [`Rx0`] and [`Rx1`]). //! -//! # Limitations -//! -//! - Support for querying error states and handling error interrupts is incomplete. -//! //! # Cargo Features //! //! | Feature | Description | @@ -109,10 +105,11 @@ pub unsafe trait FilterOwner: Instance { /// This trait must only be implemented when there is actually an associated slave instance. pub unsafe trait MasterInstance: FilterOwner {} -// TODO: what to do with these? -/* -#[derive(Debug, Copy, Clone, Eq, PartialEq, Format)] +/// Enum of error status codes from the error status register. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] pub enum Error { + None, Stuff, Form, Acknowledgement, @@ -120,7 +117,63 @@ pub enum Error { BitDominant, Crc, Software, -}*/ +} + +/// The peripheral's current error status. +#[derive(Clone, PartialEq, Eq)] +#[cfg_attr(feature = "unstable-defmt", derive(defmt::Format))] +pub struct ErrorStatus { + pub(crate) recv_count: u8, + pub(crate) txmt_count: u8, + pub(crate) code: Error, + pub(crate) bus_off: bool, + pub(crate) err_passive: bool, + pub(crate) err_warning: bool, +} + +impl ErrorStatus { + /// The receive error counter. + #[inline] + pub fn receive_counter(&self) -> u8 { + self.recv_count + } + + /// The transmit error counter. + #[inline] + pub fn transmit_counter(&self) -> u8 { + self.recv_count + } + + /// The last error code. + #[inline] + pub fn last_error(&self) -> Error { + self.code + } + + /// Returns true if the peripheral is currently in bus-off. + /// + /// This occurs when the transmit error counter overflows past 255. + #[inline] + pub fn bus_off(&self) -> bool { + self.bus_off + } + + /// Returns true if the error passive limit has been reached. + /// + /// This occurs when the receive or transmit error counters exceed 127. + #[inline] + pub fn error_passive(&self) -> bool { + self.err_passive + } + + /// Returns true if the error warning limit has been reached. + /// + /// This occurs when the receive or transmit error counters are greater than or equal 96. + #[inline] + pub fn error_warning(&self) -> bool { + self.err_warning + } +} /// Error that indicates that an incoming message has been lost due to buffer overrun. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -650,6 +703,42 @@ where while self.clear_request_completed_flag().is_some() {} } + /// Clears the error interrupt flag ([`Interrupt::Error`]). + /// + /// To read the error status, use [`Can::error_status`] to get the [`ErrorStatus`] before + /// clearing the interrupt flag. + pub fn clear_error_interrupt(&mut self) { + let can = self.registers(); + can.msr.write(|w| w.erri().set_bit()); + } + + /// Reads the error status register's data. + /// + /// This does not clear the error interrupt flag. + pub fn error_status(&self) -> ErrorStatus { + let can = self.registers(); + let esr = can.esr.read(); + + ErrorStatus { + recv_count: esr.rec().bits, + txmt_count: esr.tec().bits, + code: (match esr.lec().bits { + 0b000 => Error::None, + 0b001 => Error::Stuff, + 0b010 => Error::Form, + 0b011 => Error::Acknowledgement, + 0b100 => Error::BitRecessive, + 0b101 => Error::BitDominant, + 0b110 => Error::Crc, + 0b111 => Error::Software, + _ => unreachable!(), + }), + bus_off: esr.boff().bits, + err_passive: esr.epvf().bits, + err_warning: esr.ewgf().bits, + } + } + /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. /// /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]).