Skip to content

Commit

Permalink
Add support for the error status register and error interrupts (#62)
Browse files Browse the repository at this point in the history
* add error interrupt support

* add error_status

* Add the other error flags

* run cargo fmt

* Update comment to reflect addition of error state handling
  • Loading branch information
guineawheek authored Nov 4, 2024
1 parent 638acda commit 3ddfc77
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 8 deletions.
45 changes: 45 additions & 0 deletions src/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
105 changes: 97 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -109,18 +105,75 @@ 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,
BitRecessive,
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)]
Expand Down Expand Up @@ -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`]).
Expand Down

0 comments on commit 3ddfc77

Please sign in to comment.