From 2afe1a758d7e037f2f52119ed5eae36d9b683165 Mon Sep 17 00:00:00 2001 From: djstrickland <96876452+dstric-aqueduct@users.noreply.github.com> Date: Sun, 18 Dec 2022 16:08:01 -0500 Subject: [PATCH 01/13] Prototype a FlexCAN peripheral Implementation of the FlexCAN1 and FlexCAN2 peripherals for the NXP i.MX RT 1060 processor. Includes APIs for initializing the peripherals, setting the clock source, setting mailbox receive filters, setting mailbox FIFO functionality, and receiving and transmitting frames. The FlexCAN3 peripheral is not implemented. --- .gitignore | 1 + imxrt-hal/src/can/embedded_hal.rs | 104 ++++ imxrt-hal/src/can/filter.rs | 27 + imxrt-hal/src/can/frame.rs | 451 ++++++++++++++ imxrt-hal/src/can/frame/tests.rs | 78 +++ imxrt-hal/src/can/id.rs | 126 ++++ imxrt-hal/src/can/mod.rs | 998 ++++++++++++++++++++++++++++++ imxrt-hal/src/ccm.rs | 159 +++++ imxrt-hal/src/lib.rs | 10 + 9 files changed, 1954 insertions(+) create mode 100644 imxrt-hal/src/can/embedded_hal.rs create mode 100644 imxrt-hal/src/can/filter.rs create mode 100644 imxrt-hal/src/can/frame.rs create mode 100644 imxrt-hal/src/can/frame/tests.rs create mode 100644 imxrt-hal/src/can/id.rs create mode 100644 imxrt-hal/src/can/mod.rs diff --git a/.gitignore b/.gitignore index a36d0931..7268cb79 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target +.idea .vscode Cargo.lock diff --git a/imxrt-hal/src/can/embedded_hal.rs b/imxrt-hal/src/can/embedded_hal.rs new file mode 100644 index 00000000..4d6a918c --- /dev/null +++ b/imxrt-hal/src/can/embedded_hal.rs @@ -0,0 +1,104 @@ +//! `embedded_hal` trait impls. + +use super::{Data, ExtendedId, Frame, Id, NoDataError, StandardId, CAN}; + +use crate::iomuxc::consts::Unsigned; +use embedded_hal::can; + +impl can::Can for CAN +where + M: Unsigned, +{ + type Frame = Frame; + + type Error = NoDataError; + + fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error> { + match self.transmit(frame) { + Ok(_status) => Ok(Some(frame.clone())), + Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), + Err(nb::Error::Other(e)) => match e {}, + } + } + + fn receive(&mut self) -> nb::Result { + match self.read_mailboxes() { + Some(d) => Ok(d.frame), + None => Err(nb::Error::Other(NoDataError { _priv: () })), + } + } +} + +impl can::Error for NoDataError { + fn kind(&self) -> can::ErrorKind { + can::ErrorKind::Overrun + } +} + +impl can::Frame for Frame { + fn new(id: impl Into, data: &[u8]) -> Option { + let id = match id.into() { + can::Id::Standard(id) => unsafe { + Id::Standard(StandardId::new_unchecked(id.as_raw())) + }, + can::Id::Extended(id) => unsafe { + Id::Extended(ExtendedId::new_unchecked(id.as_raw())) + }, + }; + + let data = Data::new(data)?; + Some(Frame::new_data(id, data)) + } + + fn new_remote(id: impl Into, dlc: usize) -> Option { + let id = match id.into() { + can::Id::Standard(id) => unsafe { + Id::Standard(StandardId::new_unchecked(id.as_raw())) + }, + can::Id::Extended(id) => unsafe { + Id::Extended(ExtendedId::new_unchecked(id.as_raw())) + }, + }; + + if dlc <= 8 { + Some(Frame::new_remote(id, dlc as u8)) + } else { + None + } + } + + #[inline] + fn is_extended(&self) -> bool { + self.is_extended() + } + + #[inline] + fn is_remote_frame(&self) -> bool { + self.is_remote_frame() + } + + #[inline] + fn id(&self) -> can::Id { + match self.id() { + Id::Standard(id) => unsafe { + can::Id::Standard(can::StandardId::new_unchecked(id.as_raw())) + }, + Id::Extended(id) => unsafe { + can::Id::Extended(can::ExtendedId::new_unchecked(id.as_raw())) + }, + } + } + + #[inline] + fn dlc(&self) -> usize { + self.dlc().into() + } + + fn data(&self) -> &[u8] { + if let Some(data) = self.data() { + data + } else { + &[] + } + } +} diff --git a/imxrt-hal/src/can/filter.rs b/imxrt-hal/src/can/filter.rs new file mode 100644 index 00000000..947c3b26 --- /dev/null +++ b/imxrt-hal/src/can/filter.rs @@ -0,0 +1,27 @@ +//! Filter bank API. + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum FlexCanIde { + #[default] + None = 0, + Ext = 1, + Rtr = 2, + Std = 3, + Inactive +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum FlexCanFlten { + AcceptAll = 0, + #[default] + RejectAll = 1, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub struct FlexCanFilter { + pub filter_id: u8, + pub id: u32, + pub ide: FlexCanIde, + pub remote: FlexCanIde +} + diff --git a/imxrt-hal/src/can/frame.rs b/imxrt-hal/src/can/frame.rs new file mode 100644 index 00000000..1c3ebbc3 --- /dev/null +++ b/imxrt-hal/src/can/frame.rs @@ -0,0 +1,451 @@ +#[cfg(test)] +mod tests; + +use core::convert::TryFrom; +use core::ops::{Deref, DerefMut}; + +use super::id::{ExtendedId, Id, StandardId}; + +/// A CAN data or remote frame. +#[derive(Clone, Debug, Eq)] +pub struct Frame { + pub(crate) code: CodeReg, + pub(crate) id: IdReg, + pub(crate) data: Data, +} + +impl Frame { + /// Creates a new data frame. + pub fn new_data(id: impl Into, data: impl Into) -> Self { + let (code, id) = match id.into() { + Id::Standard(id) => (CodeReg::new_standard(), IdReg::new_standard(id)), + Id::Extended(id) => (CodeReg::new_extended(), IdReg::new_extended(id)), + }; + + Self { + code, + id, + data: data.into(), + } + } + + /// Creates a new data frame. + #[inline(always)] + pub fn new_from_raw(code: u32, id: u32, data: impl Into) -> Self { + Self { + code: CodeReg::new(code), + id: IdReg::new(id), + data: data.into(), + } + } + + /// Creates a new remote frame with configurable data length code (DLC). + /// + /// # Panics + /// + /// This function will panic if `dlc` is not inside the valid range `0..=8`. + pub fn new_remote(id: impl Into, dlc: u8) -> Self { + assert!(dlc <= 8); + + let mut frame = Self::new_data(id, []); + // Just extend the data length, even with no data present. The API does not hand out this + // `Data` object. + frame.data.len = dlc; + frame.code = frame.code.with_rtr(true); + frame + } + + /// Returns true if this frame is an extended frame. + #[inline(always)] + pub fn is_extended(&self) -> bool { + self.code.is_extended() + } + + /// Returns true if this frame is a standard frame. + #[inline(always)] + pub fn is_standard(&self) -> bool { + self.code.is_standard() + } + + /// Returns true if this frame is a remote frame. + #[inline(always)] + pub fn is_remote_frame(&self) -> bool { + self.code.rtr() + } + + /// Returns true if this frame is a data frame. + #[inline(always)] + pub fn is_data_frame(&self) -> bool { + !self.is_remote_frame() + } + + /// Returns the frame identifier. + #[inline(always)] + pub fn id(&self) -> Id { + if self.is_extended() { + self.id.to_extended() + } else { + self.id.to_standard() + } + } + + /// Returns the data length code (DLC) which is in the range 0..8. + /// + /// For data frames the DLC value always matches the length of the data. + /// Remote frames do not carry any data, yet the DLC can be greater than 0. + #[inline(always)] + pub fn dlc(&self) -> u8 { + self.data.len() as u8 + } + + #[inline(always)] + pub fn to_tx_once_code(&self) -> u32 { + ((self.dlc() as u32) << CodeReg::DLC_SHIFT) | FlexCanMailboxCSCode::TxOnce.to_code_reg() + } + + /// Returns the frame data (0..8 bytes in length) if this is a data frame. + /// + /// If this is a remote frame, returns `None`. + pub fn data(&self) -> Option<&Data> { + if self.is_data_frame() { + Some(&self.data) + } else { + None + } + } +} + +impl PartialEq for Frame { + fn eq(&self, other: &Self) -> bool { + match (self.data(), other.data()) { + (None, None) => self.id.eq(&other.id), + (Some(a), Some(b)) => self.id.eq(&other.id) && a.eq(b), + (None, Some(_)) | (Some(_), None) => false, + } + } +} + +#[derive(Debug, PartialEq)] +#[repr(u8)] +pub enum FlexCanMailboxCSCode { + RxInactive = 0b0000, + RxEmpty = 0b0100, + RxFull = 0b0010, + RxOverrun = 0b0110, + RxAnswer = 0b1010, + RxBusy = 0b0001, + + TxInactive = 0b1000, + TxAbort = 0b1001, + TxOnce = 0b1100, + TxAnswer = 0b1110, + + Unknown, +} + +impl ::core::convert::TryFrom for FlexCanMailboxCSCode { + type Error = (); + + #[inline(always)] + fn try_from(value: u8) -> Result { + match value { + v if v == FlexCanMailboxCSCode::RxInactive as u8 => { + Ok(FlexCanMailboxCSCode::RxInactive) + } + v if v == FlexCanMailboxCSCode::RxEmpty as u8 => Ok(FlexCanMailboxCSCode::RxEmpty), + v if v == FlexCanMailboxCSCode::RxFull as u8 => Ok(FlexCanMailboxCSCode::RxFull), + v if v == FlexCanMailboxCSCode::RxOverrun as u8 => Ok(FlexCanMailboxCSCode::RxOverrun), + v if v == FlexCanMailboxCSCode::RxAnswer as u8 => Ok(FlexCanMailboxCSCode::RxAnswer), + v if v == FlexCanMailboxCSCode::RxBusy as u8 => Ok(FlexCanMailboxCSCode::RxBusy), + v if v == FlexCanMailboxCSCode::TxInactive as u8 => { + Ok(FlexCanMailboxCSCode::TxInactive) + } + v if v == FlexCanMailboxCSCode::TxAbort as u8 => Ok(FlexCanMailboxCSCode::TxAbort), + v if v == FlexCanMailboxCSCode::TxOnce as u8 => Ok(FlexCanMailboxCSCode::TxOnce), + v if v == FlexCanMailboxCSCode::TxAnswer as u8 => Ok(FlexCanMailboxCSCode::TxAnswer), + _ => Ok(FlexCanMailboxCSCode::Unknown), + } + } +} + +impl FlexCanMailboxCSCode { + #[inline(always)] + pub fn to_code_reg(&self) -> u32 { + (*self as u32) << CodeReg::CODE_SHIFT + } + + #[inline(always)] + pub fn from_code_reg(reg: u32) -> Result { + Self::try_from(((reg & CodeReg::CODE_MASK) >> CodeReg::CODE_SHIFT) as u8) + } + + #[inline(always)] + pub fn is_tx_mailbox(&self) -> bool { + (*self as u8) >> 3 != 0 + } +} + +/// Code Register of a FlexCAN mailbox. +/// +/// Contains info relating to the frame's remote status (RTR), whether +/// the frams is standard os extended (IDE), the length of the data +/// content (DLC), and the timestamp. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CodeReg(u32); + +impl CodeReg { + pub(crate) const CODE_SHIFT: u32 = 24; + pub(crate) const CODE_MASK: u32 = 0b1111_u32 << Self::CODE_SHIFT; + + #[allow(dead_code)] + const SRR_SHIFT: u32 = 22; + #[allow(dead_code)] + const SRR_MASK: u32 = 0b1_u32 << Self::SRR_SHIFT; + + const IDE_SHIFT: u32 = 21; + const IDE_MASK: u32 = 0b1_u32 << Self::IDE_SHIFT; + + const RTR_SHIFT: u32 = 20; + const RTR_MASK: u32 = 0b1_u32 << Self::RTR_SHIFT; + + const DLC_SHIFT: u32 = 16; + const DLC_MASK: u32 = 0b111_u32 << Self::DLC_SHIFT; + + const TIMESTAMP_SHIFT: u32 = 0; + const TIMESTAMP_MASK: u32 = 0xFFFF_u32 << Self::TIMESTAMP_SHIFT; + + /// Creates a new code reg. + pub fn new(code: u32) -> Self { + Self(code) + } + + /// Creates a new code reg for a standard identifier. + pub fn new_standard() -> Self { + Self::new(0x00) + } + + /// Creates a new code reg for an extended identifier. + pub fn new_extended() -> Self { + Self::new(Self::IDE_MASK) + } + + /// Returns the register value. + #[inline(always)] + pub fn to_code_reg(&self) -> u32 { + self.0 + } + + /// Set the 4 bit code content for a CodeReg + #[inline(always)] + pub fn set_code(&mut self, code: FlexCanMailboxCSCode) -> Self { + Self::new(self.0 & ((code as u32) << Self::CODE_SHIFT)) + } + + /// Get the 4 bit code content for a CodeReg. + /// + /// # Panics + /// + /// This function will panic if a matching [`FlexCanMailboxCSCode`] is not found. + #[inline(always)] + pub fn code(&self) -> FlexCanMailboxCSCode { + FlexCanMailboxCSCode::try_from(((self.0 & Self::CODE_MASK) >> Self::CODE_SHIFT) as u8) + .unwrap() + } + + pub fn timestamp(&self) -> u16 { + ((self.0 & Self::TIMESTAMP_MASK) >> Self::TIMESTAMP_SHIFT) as u16 + } + + pub fn dlc(&self) -> u8 { + ((self.0 & Self::DLC_MASK) >> Self::DLC_SHIFT) as u8 + } + + /// Returns `true` if the code reg is an extended identifier. + #[inline(always)] + fn is_extended(self) -> bool { + self.0 & Self::IDE_MASK != 0 + } + + /// Returns `true` if the code reg is a standard identifier. + #[inline(always)] + fn is_standard(self) -> bool { + !self.is_extended() + } + + /// Sets the remote transmission (RTR) flag. This marks the identifier as + /// being part of a remote frame. + #[must_use = "returns a new IdReg without modifying `self`"] + pub fn with_rtr(self, rtr: bool) -> Self { + if rtr { + Self::new(self.0 | Self::RTR_MASK) + } else { + Self::new(self.0 & !Self::RTR_MASK) + } + } + + /// Returns `true` if the identifer is part of a remote frame (RTR bit set). + #[inline(always)] + pub fn rtr(self) -> bool { + self.0 & Self::RTR_MASK != 0 + } +} + +/// Identifier of a CAN message. +/// +/// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a +/// extendended identifier (29bit , Range: 0..0x1FFFFFFF). +/// +/// The `Ord` trait can be used to determine the frame’s priority this ID +/// belongs to. +/// Lower identifier values have a higher priority. Additionally standard frames +/// have a higher priority than extended frames and data frames have a higher +/// priority than remote frames. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct IdReg(u32); + +impl IdReg { + const STANDARD_SHIFT: u32 = 18; + const EXTENDED_SHIFT: u32 = 0; + + /// Creates a new standard identifier (11bit, Range: 0..0x7FF) + /// + /// Panics for IDs outside the allowed range. + #[inline(always)] + pub fn new(id: u32) -> Self { + Self(id) + } + + /// Returns the register value. + #[inline(always)] + pub fn to_id_reg(&self) -> u32 { + self.0 + } + + /// Creates a new standard identifier (11bit, Range: 0..0x7FF) + /// + /// Panics for IDs outside the allowed range. + #[inline(always)] + pub fn new_standard(id: StandardId) -> Self { + let id = u32::from(id.as_raw()) << Self::STANDARD_SHIFT; + Self::new(id) + } + + /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). + /// + /// Panics for IDs outside the allowed range. + #[inline(always)] + pub fn new_extended(id: ExtendedId) -> Self { + let id = u32::from(id.as_raw()) << Self::EXTENDED_SHIFT; + Self::new(id) + } + + #[inline(always)] + pub fn to_standard(&self) -> Id { + Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::STANDARD_SHIFT) }) + } + + #[inline(always)] + pub fn to_extended(&self) -> Id { + Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::EXTENDED_SHIFT) as u16) }) + } +} + +/// Payload of a CAN data frame. +/// +/// Contains 0 to 8 Bytes of data. +/// +/// `Data` implements `From<[u8; N]>` for all `N` up to 8, which provides a convenient lossless +/// conversion from fixed-length arrays. +#[derive(Debug, Copy, Clone)] +pub struct Data { + pub(crate) len: u8, + pub(crate) bytes: [u8; 8], +} + +impl Data { + /// Creates a data payload from a raw byte slice. + /// + /// Returns `None` if `data` contains more than 8 Bytes (which is the maximum). + /// + /// `Data` can also be constructed from fixed-length arrays up to length 8 via `From`/`Into`. + pub fn new(data: &[u8]) -> Option { + if data.len() > 8 { + return None; + } + + let mut bytes = [0; 8]; + bytes[..data.len()].copy_from_slice(data); + + Some(Self { + len: data.len() as u8, + bytes, + }) + } + + /// Creates an empty data payload containing 0 bytes. + #[inline(always)] + pub const fn empty() -> Self { + Self { + len: 0, + bytes: [0; 8], + } + } +} + +impl Deref for Data { + type Target = [u8]; + + #[inline(always)] + fn deref(&self) -> &[u8] { + &self.bytes[..usize::from(self.len)] + } +} + +impl DerefMut for Data { + #[inline(always)] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.bytes[..usize::from(self.len)] + } +} + +impl AsRef<[u8]> for Data { + #[inline(always)] + fn as_ref(&self) -> &[u8] { + self.deref() + } +} + +impl AsMut<[u8]> for Data { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u8] { + self.deref_mut() + } +} + +impl PartialEq for Data { + fn eq(&self, other: &Self) -> bool { + self.as_ref() == other.as_ref() + } +} + +impl Eq for Data {} + +macro_rules! data_from_array { + ( $($len:literal),+ ) => { + $( + impl From<[u8; $len]> for Data { + #[inline(always)] + fn from(arr: [u8; $len]) -> Self { + let mut bytes = [0; 8]; + bytes[..$len].copy_from_slice(&arr); + Self { + len: $len, + bytes, + } + } + } + )+ + }; +} + +data_from_array!(0, 1, 2, 3, 4, 5, 6, 7, 8); diff --git a/imxrt-hal/src/can/frame/tests.rs b/imxrt-hal/src/can/frame/tests.rs new file mode 100644 index 00000000..e8b43d1f --- /dev/null +++ b/imxrt-hal/src/can/frame/tests.rs @@ -0,0 +1,78 @@ +use crate::can::{ + frame::Frame, + id::{ExtendedId, StandardId}, +}; + +#[test] +fn data_greater_remote() { + let id = StandardId::new(0).unwrap(); + + let data_frame = Frame::new_data(id, []); + let remote_frame = Frame::new_remote(id, 0); + assert!(data_frame.is_data_frame()); + assert!(remote_frame.is_remote_frame()); +} + +#[test] +fn lower_ids_win_arbitration() { + let zero = Frame::new_data(StandardId::new(0).unwrap(), []); + let one = Frame::new_data(StandardId::new(1).unwrap(), []); + assert!(zero.is_standard()); + assert!(!zero.is_extended()); + assert!(one.is_standard()); + assert!(!one.is_extended()); + + // Standard IDs have priority over Extended IDs if the Base ID matches. + let ext_one = Frame::new_data( + ExtendedId::new(0b00000000001_000000000000000000).unwrap(), + [], + ); + assert!(!ext_one.is_standard()); + assert!(ext_one.is_extended()); + + // Ext. ID with Base ID 0 has priority over Standard ID 1. + let ext_zero = Frame::new_data( + ExtendedId::new(0b00000000000_100000000000000000).unwrap(), + [], + ); + assert!(!ext_zero.is_standard()); + assert!(ext_zero.is_extended()); +} + +#[test] +fn highest_standard_higher_prio_than_highest_ext() { + let std = Frame::new_data(StandardId::MAX, []); + let ext = Frame::new_data(ExtendedId::MAX, []); + + assert!(std.is_standard()); + assert!(!std.is_extended()); + assert!(!ext.is_standard()); + assert!(ext.is_extended()); +} + +#[test] +fn data_neq_remote() { + let id = StandardId::new(0).unwrap(); + + let data_frame = Frame::new_data(id, []); + let remote_frame = Frame::new_remote(id, 0); + + assert_ne!(data_frame, remote_frame); +} + +#[test] +fn remote_eq_remote_ignores_data() { + let mut remote1 = Frame::new_remote(StandardId::MAX, 7); + let mut remote2 = Frame::new_remote(StandardId::MAX, 7); + + remote1.data.bytes = [0xAA; 8]; + remote2.data.bytes = [0x55; 8]; + + assert_eq!(remote1, remote2); +} + +#[test] +fn max_len() { + Frame::new_data(StandardId::MAX, [0; 8]); + Frame::new_remote(StandardId::MAX, 8); +} diff --git a/imxrt-hal/src/can/id.rs b/imxrt-hal/src/can/id.rs new file mode 100644 index 00000000..3a4c3f5d --- /dev/null +++ b/imxrt-hal/src/can/id.rs @@ -0,0 +1,126 @@ +/// Standard 11-bit CAN Identifier (`0..=0x7FF`). +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct StandardId(u16); + +impl StandardId { + /// CAN ID `0`, the highest priority. + pub const ZERO: Self = Self(0); + + /// CAN ID `0x7FF`, the lowest priority. + pub const MAX: Self = Self(0x7FF); + + /// Tries to create a `StandardId` from a raw 16-bit integer. + /// + /// This will return `None` if `raw` is out of range of an 11-bit integer (`> 0x7FF`). + #[inline] + pub const fn new(raw: u16) -> Option { + if raw <= 0x7FF { + Some(Self(raw)) + } else { + None + } + } + + /// Creates a new `StandardId` without checking if it is inside the valid range. + /// + /// # Safety + /// + /// The caller must ensure that `raw` is in the valid range, otherwise the behavior is + /// undefined. + #[inline] + pub const unsafe fn new_unchecked(raw: u16) -> Self { + Self(raw) + } + + /// Returns this CAN Identifier as a raw 16-bit integer. + #[inline] + pub fn as_raw(&self) -> u16 { + self.0 + } +} + +/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`). +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ExtendedId(u32); + +impl ExtendedId { + /// CAN ID `0`, the highest priority. + pub const ZERO: Self = Self(0); + + /// CAN ID `0x1FFFFFFF`, the lowest priority. + pub const MAX: Self = Self(0x1FFF_FFFF); + + /// Tries to create a `ExtendedId` from a raw 32-bit integer. + /// + /// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`). + #[inline] + pub const fn new(raw: u32) -> Option { + if raw <= 0x1FFF_FFFF { + Some(Self(raw)) + } else { + None + } + } + + /// Creates a new `ExtendedId` without checking if it is inside the valid range. + /// + /// # Safety + /// + /// The caller must ensure that `raw` is in the valid range, otherwise the behavior is + /// undefined. + #[inline] + pub const unsafe fn new_unchecked(raw: u32) -> Self { + Self(raw) + } + + /// Returns this CAN Identifier as a raw 32-bit integer. + #[inline] + pub fn as_raw(&self) -> u32 { + self.0 + } + + /// Returns the Base ID part of this extended identifier. + pub fn standard_id(&self) -> StandardId { + // ID-28 to ID-18 + StandardId((self.0 >> 18) as u16) + } +} + +/// A CAN Identifier (standard or extended). +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Id { + /// Standard 11-bit Identifier (`0..=0x7FF`). + Standard(StandardId), + + /// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`). + Extended(ExtendedId), +} + +impl Default for Id { + fn default() -> Self { + Id::Standard(StandardId::new(0).unwrap()) + } +} + +impl From for Id { + #[inline] + fn from(id: StandardId) -> Self { + Id::Standard(id) + } +} + +impl From for Id { + #[inline] + fn from(id: ExtendedId) -> Self { + Id::Extended(id) + } +} + +impl Id { + pub fn as_raw(&self) -> u32 { + match self { + Self::Standard(id) => id.as_raw() as u32, + Self::Extended(id) => id.as_raw(), + } + } +} diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs new file mode 100644 index 00000000..a812945d --- /dev/null +++ b/imxrt-hal/src/can/mod.rs @@ -0,0 +1,998 @@ +mod embedded_hal; +pub mod filter; +mod frame; +mod id; + +pub use frame::{CodeReg, Data, FlexCanMailboxCSCode, Frame, IdReg}; +pub use id::{ExtendedId, Id, StandardId}; +use ral::{modify_reg, read_reg, write_reg}; + +use crate::ccm; +use crate::iomuxc::consts::{Unsigned, U1, U2}; +use crate::iomuxc::can; +use crate::ral; + +use core::convert::Infallible; +use core::marker::PhantomData; + +/// Error that indicates that no received frames are available +/// in the FlexCAN mailboxes +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NoDataError { + _priv: (), +} + +/// Unclocked Can modules +/// +/// The `Unclocked` struct represents the unconfigured Can peripherals. +/// Once clocked, you'll have the ability to build Can peripherals from the +/// compatible processor pins. +pub struct Unclocked { + pub(crate) can1: ral::can::Instance, + pub(crate) can2: ral::can::Instance, +} + +impl Unclocked { + /// Enable clocks to all Can modules, returning a builder for the two Can modules. + pub fn clock( + self, + handle: &mut ccm::Handle, + clock_select: ccm::can::ClockSelect, + divider: ccm::can::PrescalarSelect, + ) -> (Builder, Builder) { + let (ccm, _) = handle.raw(); + // First, disable clocks + ral::modify_reg!( + ral::ccm, + ccm, + CCGR1, + CG0: 0, + CG1: 0, + CG2: 0, + CG3: 0 + ); + + let clk_sel = match clock_select { + ccm::can::ClockSelect::OSC => ral::ccm::CSCMR2::CAN_CLK_SEL::RW::CAN_CLK_SEL_1, + }; + + // Select clock, and commit prescalar + ral::modify_reg!( + ral::ccm, + ccm, + CSCMR2, + CAN_CLK_PODF: ral::ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_1, + CAN_CLK_SEL: clk_sel + ); + + // Enable clocks + ral::modify_reg!( + ral::ccm, + ccm, + CCGR1, + CG0: 0b11, + CG1: 0b11, + CG2: 0b11, + CG3: 0b11 + ); + + let source_clock = ccm::Frequency::from(clock_select) / ccm::Divider::from(divider); + ( + Builder::new(source_clock, self.can1), + Builder::new(source_clock, self.can2), + ) + } +} + +/// A Can builder that can build a Can peripheral +pub struct Builder { + _module: PhantomData, + reg: ral::can::Instance, + source_clock: ccm::Frequency, +} + +impl Builder +where + M: Unsigned, +{ + fn new(source_clock: ccm::Frequency, reg: ral::can::Instance) -> Self { + Builder { + _module: PhantomData, + reg, + source_clock, + } + } + + /// Builds a Can peripheral. The return + /// is a configured FlexCan peripheral running at 24MHz. + pub fn build(self, mut tx: TX, mut rx: RX) -> CAN + where + TX: can::Pin, + RX: can::Pin, + { + crate::iomuxc::can::prepare(&mut tx); + crate::iomuxc::can::prepare(&mut rx); + + CAN::new(self.source_clock, self.reg) + } +} + +/// A Can master +/// +pub struct CAN { + reg: ral::can::Instance, + _module: PhantomData, + source_clock: ccm::Frequency, + _mailbox_reader_index: u8, +} + +#[derive(Debug)] +pub struct MailboxData { + pub frame: Frame, + pub mailbox_number: u8, +} + +impl CAN +where + M: Unsigned, +{ + pub const NUMBER_FIFO_RX_MAILBOXES: u32 = 6; + + fn new(source_clock: ccm::Frequency, reg: ral::can::Instance) -> Self { + let mut can = CAN { + reg, + _module: PhantomData, + source_clock, + _mailbox_reader_index: 0, + }; + can.begin(); + can + } + + pub fn instance(&mut self) -> &mut ral::can::Instance { + &mut self.reg + } + + pub fn print_registers(&self) { + log::info!("MCR: {:X}", ral::read_reg!(ral::can, self.reg, MCR)); + log::info!("CTRL1: {:X}", ral::read_reg!(ral::can, self.reg, CTRL1)); + log::info!("CTRL2: {:X}", ral::read_reg!(ral::can, self.reg, CTRL2)); + log::info!( + "RXMGMASK: {:X}", + ral::read_reg!(ral::can, self.reg, RXMGMASK) + ); + log::info!( + "RXFGMASK: {:X}", + ral::read_reg!(ral::can, self.reg, RXFGMASK) + ); + + let max_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 8; + + for mailbox_number in 0..max_fifo_filters { + let mailbox_idflt_tab_addr = + self.mailbox_number_to_idflt_tab_address(mailbox_number as u8); + let idflt_tab = + unsafe { core::ptr::read_volatile((mailbox_idflt_tab_addr) as *mut u32) }; + log::info!( + "IDFLT_TAB[{}, {:X}]: {:X}", + mailbox_number, + mailbox_idflt_tab_addr, + idflt_tab + ); + } + } + + fn while_frozen R, R>(&mut self, mut act: F) -> R { + let frz_flag_negate = ral::read_reg!(ral::can, self.reg, MCR, FRZACK == FRZACK_0); + self.enter_freeze_mode(); + let res = act(self); + if frz_flag_negate { + self.exit_freeze_mode(); + } + res + } + + pub fn begin(&mut self) { + self.set_ccm_ccg(); + + ral::modify_reg!(ral::can, self.reg, MCR, MDIS: MDIS_0); + + self.enter_freeze_mode(); + + ral::modify_reg!(ral::can, self.reg, CTRL1, LOM: LOM_1); + ral::modify_reg!(ral::can, self.reg, MCR, FRZ: FRZ_1); + while ral::read_reg!(ral::can, self.reg, MCR, LPMACK == LPMACK_1) {} + + self.soft_reset(); + + while ral::read_reg!(ral::can, self.reg, MCR, FRZACK != FRZACK_1) {} + + ral::modify_reg!( + ral::can, + self.reg, + MCR, + SRXDIS: SRXDIS_1, + IRMQ: IRMQ_1, + AEN: AEN_1, + LPRIOEN: LPRIOEN_1, + SLFWAK: SLFWAK_1, + WAKSRC: WAKSRC_1, + WRNEN: WRNEN_1, + WAKMSK: WAKMSK_1 + ); + + ral::modify_reg!(ral::can, self.reg, MCR, |reg| reg & !0x8800); + + ral::modify_reg!( + ral::can, + self.reg, + CTRL2, + RRS: RRS_1, + EACEN: EACEN_1, + MRP: MRP_1 + ); + + self.disable_fifo(); + self.exit_freeze_mode(); + } + + pub fn instance_number(&self) -> usize { + M::USIZE + } + + pub fn is_can1(&self) -> bool { + M::USIZE == 1 + } + + pub fn is_can2(&self) -> bool { + M::USIZE == 2 + } + + pub fn base_address(&self) -> u32 { + let addr: *const ral::can::RegisterBlock = &*self.reg; + addr as u32 + } + + pub fn free(self) -> ral::can::Instance { + self.reg + } + + fn soft_reset(&mut self) { + ral::modify_reg!(ral::can, self.reg, MCR, SOFTRST: SOFTRST_1); + while ral::read_reg!(ral::can, self.reg, MCR, SOFTRST == SOFTRST_1) {} + } + + fn enter_freeze_mode(&mut self) { + ral::modify_reg!(ral::can, self.reg, MCR, FRZ: FRZ_1); + ral::modify_reg!(ral::can, self.reg, MCR, HALT: HALT_1); + while ral::read_reg!(ral::can, self.reg, MCR, FRZACK != FRZACK_1) {} + } + + fn exit_freeze_mode(&mut self) { + ral::modify_reg!(ral::can, self.reg, MCR, HALT: HALT_0); + while ral::read_reg!(ral::can, self.reg, MCR, FRZACK != FRZACK_0) {} + } + + pub fn set_mrp(&mut self, mrp: bool) { + ral::modify_reg!(ral::can, self.reg, CTRL2, MRP: mrp as u32) + } + + pub fn set_rrs(&mut self, rrs: bool) { + ral::modify_reg!(ral::can, self.reg, CTRL2, RRS: rrs as u32) + } + + fn set_ccm_ccg(&mut self) { + match self.instance_number() { + 1 => { + unsafe { modify_reg!(ral::ccm, CCM, CCGR0, |reg| reg | 0x3C000) }; + } + 2 => { + unsafe { modify_reg!(ral::ccm, CCM, CCGR0, |reg| reg | 0x3C0000) }; + } + u => { + log::error!("Invalid Can instance (set_ccm_ccg): {:?}", u); + } + } + } + + pub fn get_clock(&self) -> u32 { + self.source_clock.0 + } + + fn result_to_bit_table(&self, result: u8) -> Option<[u32; 3]> { + match result { + 0 => Some([0, 0, 1]), + 1 => Some([1, 0, 1]), + 2 => Some([1, 1, 1]), + 3 => Some([2, 1, 1]), + 4 => Some([2, 2, 1]), + 5 => Some([2, 3, 1]), + 6 => Some([2, 3, 2]), + 7 => Some([2, 4, 2]), + 8 => Some([2, 5, 2]), + 9 => Some([2, 5, 3]), + 10 => Some([2, 6, 3]), + 11 => Some([2, 7, 3]), + 12 => Some([2, 7, 4]), + 13 => Some([3, 7, 4]), + 14 => Some([3, 7, 5]), + 15 => Some([4, 7, 5]), + 16 => Some([4, 7, 6]), + 17 => Some([5, 7, 6]), + 18 => Some([6, 7, 6]), + 19 => Some([6, 7, 7]), + 20 => Some([7, 7, 7]), + _ => None, + } + } + + pub fn set_baud_rate(&mut self, baud: u32) { + let clock_freq = 24_000_000; + + let mut divisor = 0; + let mut best_divisor: u32 = 0; + let mut result: u32 = clock_freq / baud / (divisor + 1); + let mut error: i16 = (baud - (clock_freq / (result * (divisor + 1)))) as i16; + let mut best_error = error; + + self.while_frozen(|this| { + while result > 5 { + divisor += 1; + result = clock_freq / baud / (divisor + 1); + if result <= 25 { + error = (baud - (clock_freq / (result * (divisor + 1)))) as i16; + if error < 0 { + error = -1 * error; + } + if error < best_error { + best_error = error; + best_divisor = divisor; + } + if (error == best_error) && (result > 11) && (result < 19) { + best_error = error; + best_divisor = divisor; + } + } + } + + divisor = best_divisor; + result = clock_freq / baud / (divisor + 1); + + if !(result < 5) || (result > 25) || (best_error > 300) { + result -= 5; + + match this.result_to_bit_table(result as u8) { + Some(t) => { + modify_reg!( + ral::can, + this.reg, + CTRL1, + PROPSEG: t[0], + RJW: 1, + PSEG1: t[1], + PSEG2: t[2], + ERRMSK: ERRMSK_1, + LOM: LOM_0, + PRESDIV: divisor) + } + _ => {} + } + } + }); + } + + pub fn set_max_mailbox(&mut self, last: u8) { + let last = last.clamp(1, 64) - 1; + self.while_frozen(|this| { + let fifo_cleared = this.fifo_enabled(); + this.disable_fifo(); + this.write_iflag(this.read_iflag()); + ral::modify_reg!(ral::can, this.reg, MCR, MAXMB: last as u32); + if fifo_cleared { + this.set_fifo(true); + } + }); + } + + fn get_max_mailbox(&self) -> u8 { + ral::read_reg!(ral::can, self.reg, MCR, MAXMB) as u8 + } + + fn write_iflag(&mut self, value: u64) { + write_reg!(ral::can, self.reg, IFLAG1, value as u32); + write_reg!(ral::can, self.reg, IFLAG2, (value >> 32) as u32); + } + + fn write_iflag_bit(&mut self, mailbox_number: u8) { + if mailbox_number < 32 { + modify_reg!(ral::can, self.reg, IFLAG1, |reg| reg + | 1_u32 << mailbox_number) + } else { + modify_reg!(ral::can, self.reg, IFLAG2, |reg| reg + | 1_u32 << (mailbox_number - 32)) + } + } + + fn write_imask_bit(&mut self, mailbox_number: u8, value: bool) { + if mailbox_number < 32 { + modify_reg!(ral::can, self.reg, IMASK1, |reg| reg + | (value as u32) << mailbox_number) + } else { + modify_reg!(ral::can, self.reg, IMASK2, |reg| reg + | (value as u32) << (mailbox_number - 32)) + } + } + + fn read_iflag(&self) -> u64 { + (ral::read_reg!(ral::can, self.reg, IFLAG2) as u64) << 32 + | ral::read_reg!(ral::can, self.reg, IFLAG1) as u64 + } + + fn read_imask(&self) -> u64 { + (ral::read_reg!(ral::can, self.reg, IMASK2) as u64) << 32 + | ral::read_reg!(ral::can, self.reg, IMASK1) as u64 + } + + fn write_imask(&mut self, value: u64) { + write_reg!(ral::can, self.reg, IMASK1, value as u32); + write_reg!(ral::can, self.reg, IMASK2, (value >> 32) as u32); + } + + pub fn set_fifo(&mut self, enabled: bool) { + self.while_frozen(|this| { + modify_reg!(ral::can, this.reg, MCR, RFEN: RFEN_0); + this.write_imask(0); + + for i in 0..this.get_max_mailbox() { + this.write_mailbox(i, Some(0), Some(0), Some([0x00; 8])); + this.write_mailbox_rximr(i, Some(0x00)); + } + + write_reg!(ral::can, this.reg, RXMGMASK, 0); + write_reg!(ral::can, this.reg, RXFGMASK, 0); + + this.write_iflag(this.read_iflag()); + + let max_mailbox = this.get_max_mailbox(); + if enabled { + modify_reg!(ral::can, this.reg, MCR, RFEN: RFEN_1); + while ral::read_reg!(ral::can, this.reg, MCR, RFEN != RFEN_1) {} + for i in this.mailbox_offset()..max_mailbox { + this.write_mailbox( + i, + Some(FlexCanMailboxCSCode::TxInactive.to_code_reg()), + None, + None, + ); + this.enable_mailbox_interrupt(i, true); + } + } else { + for i in 0..max_mailbox { + if i < max_mailbox / 2 { + let code = FlexCanMailboxCSCode::RxEmpty.to_code_reg() | 0x00400000 | { + if i < max_mailbox / 4 { + 0 + } else { + 0x00200000 + } + }; + this.write_mailbox(i, Some(code), None, None); + let eacen = read_reg!(ral::can, this.reg, CTRL2, EACEN == EACEN_1); + let rximr = 0_32 | { + if eacen { + 1_u32 << 30 + } else { + 0 + } + }; + this.write_mailbox_rximr(i, Some(rximr)); + } else { + // set inactive + this.write_mailbox( + i, + Some(FlexCanMailboxCSCode::TxInactive.to_code_reg()), + Some(0), + Some([0x00; 8]), + ); + this.enable_mailbox_interrupt(i, true); + } + } + } + }) + } + + pub fn enable_fifo(&mut self) { + self.set_fifo(true); + } + + pub fn disable_fifo(&mut self) { + self.set_fifo(false); + } + + pub fn set_fifo_reject_all(&mut self) { + self.set_fifo_filter_mask(filter::FlexCanFlten::RejectAll) + } + + pub fn set_fifo_accept_all(&mut self) { + self.set_fifo_filter_mask(filter::FlexCanFlten::AcceptAll) + } + + fn set_fifo_filter_mask(&mut self, flten: filter::FlexCanFlten) { + if !self.fifo_enabled() { + return; + } + let max_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 8; + self.while_frozen(|this| match flten { + filter::FlexCanFlten::AcceptAll => { + let offset = this.mailbox_offset(); + for i in 0..max_fifo_filters { + this.write_mailbox_idflt_tab(i as u8, Some(0x00)); + if i < offset.clamp(0, 32) as u32 { + this.write_mailbox_rximr(i as u8, Some(0x00)); + } + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x00); + } + filter::FlexCanFlten::RejectAll => match read_reg!(ral::can, this.reg, MCR, IDAM) { + ral::can::MCR::IDAM::RW::IDAM_0 => { + let offset = this.mailbox_offset(); + for i in 0..max_fifo_filters { + this.write_mailbox_idflt_tab(i as u8, Some(0xFFFFFFFF)); + if i < offset.clamp(0, 32) as u32 { + this.write_mailbox_rximr(i as u8, Some(0x3FFFFFFF)); + } + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x3FFFFFFF); + } + ral::can::MCR::IDAM::RW::IDAM_1 => { + let offset = this.mailbox_offset(); + for i in 0..max_fifo_filters { + this.write_mailbox_idflt_tab(i as u8, Some(0xFFFFFFFF)); + if i < offset.clamp(0, 32) as u32 { + this.write_mailbox_rximr(i as u8, Some(0x7FFF7FFF)); + } + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x7FFF7FFF); + } + ral::can::MCR::IDAM::RW::IDAM_2 => { + // not implemented + } + ral::can::MCR::IDAM::RW::IDAM_3 => { + // not implemented + } + _ => {} + }, + }) + } + + pub fn set_fifo_filter( + &mut self, + filter_id: u8, + id: u32, + ide: filter::FlexCanIde, + remote: filter::FlexCanIde, + ) { + if !self.fifo_enabled() { + return; + } + let max_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 8; + if filter_id as u32 >= max_fifo_filters { + return; + } + self.while_frozen(|this| match read_reg!(ral::can, this.reg, MCR, IDAM) { + ral::can::MCR::IDAM::RW::IDAM_0 => { + let mask: u32 = if ide != filter::FlexCanIde::Ext { + ((((id) ^ (id)) ^ 0x7FF) << 19) | 0xC0000001 + } else { + ((((id) ^ (id)) ^ 0x1FFFFFFF) << 1) | 0xC0000001 + }; + let mut filter: u32 = (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 30); + filter |= if remote == filter::FlexCanIde::Rtr { + 1 + } else { + 0 + } << 31; + filter |= if ide == filter::FlexCanIde::Ext { + (id & 0x1FFFFFFF) << 1 + } else { + ((id & 0x000007FF) << 18) << 1 + }; + this.write_mailbox_idflt_tab(filter_id, Some(filter)); + let offset = this.mailbox_offset(); + if filter_id < offset.clamp(0, 32) as u8 { + this.write_mailbox_rximr(filter_id, Some(mask)); + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x3FFFFFFF); + } + ral::can::MCR::IDAM::RW::IDAM_1 => { + let mut mask: u32 = if ide != filter::FlexCanIde::Ext { + (((id) ^ (id)) ^ 0x7FF) << 19 + } else { + (((id) ^ (id)) ^ 0x1FFFFFFF) << 16 + } | if remote == filter::FlexCanIde::Rtr { + 1 << 31 + } else { + 0 + }; + mask |= if ide != filter::FlexCanIde::Ext { + (((id) ^ (id)) ^ 0x7FF) << 3 + } else { + (((id) ^ (id)) ^ 0x1FFFFFFF) << 0 + } | if remote == filter::FlexCanIde::Rtr { + 1 << 15 + } else { + 0 + } & 0xFFFF; + mask |= (1 << 30) | (1 << 14); + let filter: u32 = (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 30) + | (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 14) + | (if remote == filter::FlexCanIde::Rtr { + 1 + } else { + 0 + } << 31) + | (if remote == filter::FlexCanIde::Rtr { + 1 + } else { + 0 + } << 15) + | (if ide == filter::FlexCanIde::Ext { + (id >> (29 - 14)) << 16 + } else { + (id & 0x7FF) << 19 + }) + | (if ide == filter::FlexCanIde::Ext { + (id >> (29 - 14)) << 0 + } else { + (id & 0x7FF) << 3 + }); + this.write_mailbox_idflt_tab(filter_id, Some(filter)); + let offset = this.mailbox_offset(); + if filter_id < offset.clamp(0, 32) as u8 { + this.write_mailbox_rximr(filter_id, Some(mask)); + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x7FFF7FFF); + } + ral::can::MCR::IDAM::RW::IDAM_2 => { + // not implemented + } + ral::can::MCR::IDAM::RW::IDAM_3 => { + // not implemented + } + _ => {} + }) + } + + pub fn set_fifo_filter_2(&mut self, filter: filter::FlexCanFilter) { + self.set_fifo_filter(filter.filter_id, filter.id, filter.ide, filter.remote) + } + + pub fn set_fifo_interrupt(&mut self, enabled: bool) { + /* FIFO must be enabled first */ + if !self.fifo_enabled() { + return; + }; + /* FIFO interrupts already enabled */ + const FLEXCAN_IMASK1_BUF5M: u32 = 0x00000020; + if (ral::read_reg!(ral::can, self.reg, IMASK1, BUFLM) & FLEXCAN_IMASK1_BUF5M) != 0 { + return; + }; + /* disable FIFO interrupt flags */ + modify_reg!(ral::can, self.reg, IMASK1, |reg| reg & !0xFF); + /* enable FIFO interrupt */ + if enabled { + const FLEXCAN_IMASK1_BUF5M: u32 = 0x00000020; + modify_reg!(ral::can, self.reg, IMASK1, BUFLM: FLEXCAN_IMASK1_BUF5M); + } + } + + fn fifo_enabled(&self) -> bool { + ral::read_reg!(ral::can, self.reg, MCR, RFEN == RFEN_1) + } + + fn mailbox_offset(&self) -> u8 { + if self.fifo_enabled() { + let max_mailbox = self.get_max_mailbox() as u32; + let num_rx_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 2; + let remaining_mailboxes = max_mailbox - 6_u32 - num_rx_fifo_filters; + /* return offset MB position after FIFO area */ + if max_mailbox < max_mailbox - remaining_mailboxes { + max_mailbox as u8 + } else { + (max_mailbox - remaining_mailboxes) as u8 + } + } else { + /* return offset 0 since FIFO is disabled */ + 0 + } + } + + #[allow(dead_code)] + fn read_fifo(&self) -> Option<()> { + // if FIFO enabled and interrupt not enabled + if !self.fifo_enabled() { + return None; + }; + // FIFO interrupt enabled, polling blocked + if (ral::read_reg!(ral::can, self.reg, IMASK1) & (0x00000020 as u32)) != 0 { + return None; + } + // message available + if (ral::read_reg!(ral::can, self.reg, IFLAG1) & (0x00000020 as u32)) != 0 { + return None; + } + Some(()) + } + + #[inline(always)] + fn mailbox_number_to_address(&self, mailbox_number: u8) -> u32 { + self.base_address() + 0x80_u32 + (mailbox_number as u32 * 0x10_u32) + } + + #[inline(always)] + fn mailbox_number_to_rximr_address(&self, mailbox_number: u8) -> u32 { + self.base_address() + 0x880_u32 + (mailbox_number as u32 * 0x4_u32) + } + + #[inline(always)] + fn mailbox_number_to_idflt_tab_address(&self, mailbox_number: u8) -> u32 { + self.base_address() + 0xE0_u32 + (mailbox_number as u32 * 0x4_u32) + } + + #[inline(always)] + fn read_mailbox_code(&mut self, mailbox_number: u8) -> u32 { + let mailbox_addr = self.mailbox_number_to_address(mailbox_number); + unsafe { core::ptr::read_volatile(mailbox_addr as *const u32) } + } + + #[inline(always)] + fn read_mailbox(&mut self, mailbox_number: u8) -> Option { + if (self.read_imask() & (1_u64 << mailbox_number)) != 0 { + return None; + } + + let code = self.read_mailbox_code(mailbox_number); + let c = match FlexCanMailboxCSCode::from_code_reg(code) { + Ok(c) => Some(c), + Err(_e) => None, + }; + + let mailbox_addr = self.mailbox_number_to_address(mailbox_number); + + match c { + // return None from a transmit mailbox + Some(c) if c.is_tx_mailbox() => None, + // full or overrun + Some(c) + if (c == FlexCanMailboxCSCode::RxFull) | (c == FlexCanMailboxCSCode::RxOverrun) => + { + let id = + unsafe { core::ptr::read_volatile((mailbox_addr + 0x4_u32) as *const u32) }; + let data0 = + unsafe { core::ptr::read_volatile((mailbox_addr + 0x8_u32) as *const u32) }; + let data1 = + unsafe { core::ptr::read_volatile((mailbox_addr + 0xC_u32) as *const u32) }; + + let mut data: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + for i in 0..4 { + data[3 - i] = (data0 >> (8 * i)) as u8; + } + for i in 0..4 { + data[7 - i] = (data1 >> (8 * i)) as u8; + } + + self.write_mailbox( + mailbox_number, + Some(FlexCanMailboxCSCode::RxEmpty.to_code_reg()), + None, + None, + ); + read_reg!(ral::can, self.reg, TIMER); + self.write_iflag_bit(mailbox_number); + + let frame = Frame::new_from_raw(code, id, data); + + Some(MailboxData { + frame, + mailbox_number, + }) + } + _ => None, + } + } + + /// Write the registers of a mailbox. + #[inline(always)] + fn write_mailbox( + &self, + mailbox_number: u8, + code: Option, + id: Option, + data: Option<[u8; 8]>, + ) { + let mailbox_addr = self.mailbox_number_to_address(mailbox_number); + if let Some(code) = code { + unsafe { core::ptr::write_volatile((mailbox_addr + 0_u32) as *mut u32, code) }; + } + if let Some(id) = id { + unsafe { core::ptr::write_volatile((mailbox_addr + 0x4_u32) as *mut u32, id) }; + } + if let Some(data) = data { + let word0 = u32::from_be_bytes([data[0], data[1], data[2], data[3]]); + let word1 = u32::from_be_bytes([data[4], data[5], data[6], data[7]]); + unsafe { core::ptr::write_volatile((mailbox_addr + 0x8_u32) as *mut u32, word0) }; + unsafe { core::ptr::write_volatile((mailbox_addr + 0xC_u32) as *mut u32, word1) }; + } + } + + /// Write data to an available TX Mailbox. + /// + /// This will accept both standard and extended data and remote frames with any ID. + /// + /// In order to transmit a Can frame, the CPU must prepare a Message Buffer for + /// transmission by executing the procedure found here. + /// + /// 1. Check if the respective interruption bit is set and clear it. + /// + /// 2. If the MB is active (transmission pending), write the ABORT code (0b1001) to the + /// CODE field of the Control and Status word to request an abortion of the + /// transmission. Wait for the corresponding IFLAG to be asserted by polling the IFLAG + /// register or by the interrupt request if enabled by the respective IMASK. Then read + /// back the CODE field to check if the transmission was aborted or transmitted (see + /// Transmission Abort Mechanism). If backwards compatibility is desired (MCR[AEN] + /// bit negated), just write the INACTIVE code (0b1000) to the CODE field to inactivate + /// the MB but then the pending frame may be transmitted without notification (see + /// Message Buffer Inactivation). + /// + /// 3. Write the ID word. + /// + /// 4. Write the data bytes. + /// + /// 5. Write the DLC, Control and Code fields of the Control and Status word to activate + /// the MB. + /// + /// Once the MB is activated, it will participate into the arbitration process and eventually be + /// transmitted according to its priority. + #[inline(always)] + fn write_tx_mailbox(&mut self, mailbox_number: u8, frame: &Frame) { + // Check if the respective interruption bit is set and clear it. + self.write_iflag_bit(mailbox_number); + self.write_mailbox( + mailbox_number, + Some(FlexCanMailboxCSCode::TxInactive.to_code_reg()), + None, + None, + ); + self.write_mailbox( + mailbox_number, + None, + Some(frame.id.to_id_reg()), + Some(frame.data.bytes), + ); + self.write_mailbox(mailbox_number, Some(frame.to_tx_once_code()), None, None); + } + + fn write_mailbox_rximr(&self, mailbox_number: u8, rximr: Option) { + let mailbox_rximr_addr = self.mailbox_number_to_rximr_address(mailbox_number); + if let Some(rximr) = rximr { + unsafe { core::ptr::write_volatile((mailbox_rximr_addr) as *mut u32, rximr) }; + } + } + + fn write_mailbox_idflt_tab(&self, mailbox_number: u8, idflt_tab: Option) { + let mailbox_idflt_tab_addr = self.mailbox_number_to_idflt_tab_address(mailbox_number); + if let Some(idflt_tab) = idflt_tab { + unsafe { core::ptr::write_volatile((mailbox_idflt_tab_addr) as *mut u32, idflt_tab) }; + } + } + + fn enable_mailbox_interrupt(&mut self, mailbox_number: u8, enabled: bool) { + /* mailbox not available */ + if mailbox_number < self.mailbox_offset() { + return; + } + if enabled { + /* enable mailbox interrupt */ + self.write_imask_bit(mailbox_number, true); + return; + } else { + match self.read_mailbox(mailbox_number) { + Some(d) => { + if (d.frame.code.to_code_reg() & 0x0F000000) >> 3 != 0 { + /* transmit interrupt keeper */ + self.write_imask_bit(mailbox_number, true); + return; + } + } + _ => {} + } + } + /* disable mailbox interrupt */ + self.write_imask_bit(mailbox_number, false); + return; + } + + pub fn read_mailboxes(&mut self) -> Option { + let mut iflag: u64; + let mut cycle_limit: u8 = 3; + let offset = self.mailbox_offset(); + let max_mailbox = self.get_max_mailbox(); + while self._mailbox_reader_index <= max_mailbox { + iflag = self.read_iflag(); + if iflag != 0 && (self._mailbox_reader_index >= (64 - iflag.leading_zeros() as u8)) { + /* break from MSB's if unset, add 1 to prevent undefined behaviour in clz for 0 check */ + self._mailbox_reader_index = self.mailbox_offset(); + cycle_limit -= 1; + if cycle_limit == 0 { + return None; + } + } + if self.fifo_enabled() { + /* FIFO is enabled, get only remaining RX (if any) */ + if self._mailbox_reader_index < offset { + /* go back to position end of fifo+filter region */ + self._mailbox_reader_index = offset; + } + } + if self._mailbox_reader_index >= max_mailbox { + self._mailbox_reader_index = self.mailbox_offset(); + cycle_limit -= 1; + if cycle_limit == 0 { + return None; + } + } + if (self.read_imask() & (1_u64 << self._mailbox_reader_index)) != 0 { + self._mailbox_reader_index += 1; + continue; /* don't read interrupt enabled mailboxes */ + } + match self.read_mailbox(self._mailbox_reader_index) { + Some(mailbox_data) => { + return Some(mailbox_data); + } + _ => {} + } + self._mailbox_reader_index += 1; + } + None + } + + #[inline(always)] + pub fn handle_interrupt(&mut self) -> Option { + let imask = self.read_imask(); + let iflag = self.read_iflag(); + + /* if DMA is disabled, ONLY THEN can you handle FIFO in ISR */ + if self.fifo_enabled() & (imask & 0x00000020 != 0) & (iflag & 0x00000020 != 0) { + if let Some(mailbox_data) = self.read_mailbox(0) { + self.write_iflag_bit(5); + if iflag & 0x00000040 != 0 { + self.write_iflag_bit(6); + } + if iflag & 0x00000080 != 0 { + self.write_iflag_bit(7); + } + return Some(mailbox_data); + } + } + + None + } + + #[inline(always)] + pub fn transmit(&mut self, frame: &Frame) -> nb::Result<(), Infallible> { + for i in self.mailbox_offset()..self.get_max_mailbox() { + if let Ok(FlexCanMailboxCSCode::TxInactive) = + FlexCanMailboxCSCode::from_code_reg(self.read_mailbox_code(i)) + { + self.write_tx_mailbox(i, frame); + return Ok(()); + } + } + Ok(()) + } +} + +/// Interface to the Can transmitter part. +pub struct Tx { + _can: PhantomData, +} diff --git a/imxrt-hal/src/ccm.rs b/imxrt-hal/src/ccm.rs index f71507ba..55573ae7 100644 --- a/imxrt-hal/src/ccm.rs +++ b/imxrt-hal/src/ccm.rs @@ -965,3 +965,162 @@ pub mod spi { } } } + +/// Timing configurations for FlexCAN peripherals +pub mod can { + use super::{ral::ccm, Divider, Frequency, OSCILLATOR_FREQUENCY}; + + #[derive(Clone, Copy)] + #[non_exhaustive] // Not all variants added + pub enum ClockSelect { + OSC, + } + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + #[allow(non_camel_case_types)] // Easier mapping if the names are consistent + #[repr(u32)] + pub enum PrescalarSelect { + /// 0b000000: Divide by 1 + DIVIDE_1 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_1, + /// 0b000001: Divide by 2 + DIVIDE_2 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_2, + /// 0b000010: Divide by 3 + DIVIDE_3 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_3, + /// 0b000011: Divide by 4 + DIVIDE_4 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_4, + /// 0b000100: Divide by 5 + DIVIDE_5 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_5, + /// 0b000101: Divide by 6 + DIVIDE_6 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_6, + /// 0b000110: Divide by 7 + DIVIDE_7 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_7, + /// 0b000111: Divide by 8 + DIVIDE_8 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_8, + /// 0b001000: Divide by 9 + DIVIDE_9 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_9, + /// 0b001001: Divide by 10 + DIVIDE_10 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_10, + /// 0b001010: Divide by 11 + DIVIDE_11 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_11, + /// 0b001011: Divide by 12 + DIVIDE_12 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_12, + /// 0b001100: Divide by 13 + DIVIDE_13 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_13, + /// 0b001101: Divide by 14 + DIVIDE_14 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_14, + /// 0b001110: Divide by 15 + DIVIDE_15 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_15, + /// 0b001111: Divide by 16 + DIVIDE_16 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_16, + /// 0b010000: Divide by 17 + DIVIDE_17 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_17, + /// 0b010001: Divide by 18 + DIVIDE_18 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_18, + /// 0b010010: Divide by 19 + DIVIDE_19 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_19, + /// 0b010011: Divide by 20 + DIVIDE_20 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_20, + /// 0b010100: Divide by 21 + DIVIDE_21 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_21, + /// 0b010101: Divide by 22 + DIVIDE_22 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_22, + /// 0b010110: Divide by 23 + DIVIDE_23 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_23, + /// 0b010111: Divide by 24 + DIVIDE_24 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_24, + /// 0b011000: Divide by 25 + DIVIDE_25 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_25, + /// 0b011001: Divide by 26 + DIVIDE_26 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_26, + /// 0b011010: Divide by 27 + DIVIDE_27 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_27, + /// 0b011011: Divide by 28 + DIVIDE_28 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_28, + /// 0b011100: Divide by 29 + DIVIDE_29 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_29, + /// 0b011101: Divide by 30 + DIVIDE_30 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_30, + /// 0b011110: Divide by 31 + DIVIDE_31 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_31, + /// 0b011111: Divide by 32 + DIVIDE_32 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_32, + /// 0b100000: Divide by 33 + DIVIDE_33 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_33, + /// 0b100001: Divide by 34 + DIVIDE_34 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_34, + /// 0b100010: Divide by 35 + DIVIDE_35 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_35, + /// 0b100011: Divide by 36 + DIVIDE_36 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_36, + /// 0b100100: Divide by 37 + DIVIDE_37 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_37, + /// 0b100101: Divide by 38 + DIVIDE_38 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_38, + /// 0b100110: Divide by 39 + DIVIDE_39 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_39, + /// 0b100111: Divide by 40 + DIVIDE_40 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_40, + /// 0b101000: Divide by 41 + DIVIDE_41 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_41, + /// 0b101001: Divide by 42 + DIVIDE_42 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_42, + /// 0b101010: Divide by 43 + DIVIDE_43 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_43, + /// 0b101011: Divide by 44 + DIVIDE_44 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_44, + /// 0b101100: Divide by 45 + DIVIDE_45 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_45, + /// 0b101101: Divide by 46 + DIVIDE_46 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_46, + /// 0b101110: Divide by 47 + DIVIDE_47 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_47, + /// 0b101111: Divide by 48 + DIVIDE_48 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_48, + /// 0b110000: Divide by 49 + DIVIDE_49 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_49, + /// 0b110001: Divide by 50 + DIVIDE_50 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_50, + /// 0b110010: Divide by 51 + DIVIDE_51 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_51, + /// 0b110011: Divide by 52 + DIVIDE_52 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_52, + /// 0b110100: Divide by 53 + DIVIDE_53 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_53, + /// 0b110101: Divide by 54 + DIVIDE_54 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_54, + /// 0b110110: Divide by 55 + DIVIDE_55 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_55, + /// 0b110111: Divide by 56 + DIVIDE_56 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_56, + /// 0b111000: Divide by 57 + DIVIDE_57 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_57, + /// 0b111001: Divide by 58 + DIVIDE_58 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_58, + /// 0b111010: Divide by 59 + DIVIDE_59 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_59, + /// 0b111011: Divide by 60 + DIVIDE_60 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_60, + /// 0b111100: Divide by 61 + DIVIDE_61 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_61, + /// 0b111101: Divide by 62 + DIVIDE_62 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_62, + /// 0b111110: Divide by 63 + DIVIDE_63 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_63, + /// 0b111111: Divide by 64 + DIVIDE_64 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_64, + } + + impl From for Frequency { + fn from(clock_select: ClockSelect) -> Self { + match clock_select { + ClockSelect::OSC => OSCILLATOR_FREQUENCY, + } + } + } + + impl From for Divider { + fn from(prescalar_select: PrescalarSelect) -> Self { + Divider((prescalar_select as u32) + 1) + } + } +} diff --git a/imxrt-hal/src/lib.rs b/imxrt-hal/src/lib.rs index 9a00548d..ad8ceed5 100644 --- a/imxrt-hal/src/lib.rs +++ b/imxrt-hal/src/lib.rs @@ -28,6 +28,7 @@ pub mod iomuxc { } pub mod adc; +pub mod can; pub mod ccm; pub mod dma; pub mod gpio; @@ -55,6 +56,7 @@ pub mod dcdc { pub struct Peripherals { pub adc: adc::Unclocked, pub iomuxc: iomuxc::Pads, + pub can: can::Unclocked, pub ccm: ccm::CCM, pub pit: pit::UnclockedPIT, pub dcdc: dcdc::DCDC, @@ -89,6 +91,10 @@ impl Peripherals { adc2: ral::adc::ADC2::steal(), }, iomuxc: iomuxc::pads(ral::iomuxc::IOMUXC::steal()), + can: can::Unclocked { + can1: ral::can::CAN1::steal(), + can2: ral::can::CAN2::steal(), + }, ccm: ccm::CCM::new(ral::ccm::CCM::steal(), ral::ccm_analog::CCM_ANALOG::steal()), pit: pit::UnclockedPIT::new(ral::pit::PIT::steal()), dcdc: dcdc::DCDC(ral::dcdc::DCDC::steal()), @@ -139,6 +145,10 @@ impl Peripherals { adc2: ral::adc::ADC2::take()?, }, iomuxc: iomuxc::pads(ral::iomuxc::IOMUXC::take()?), + can: can::Unclocked { + can1: ral::can::CAN1::take()?, + can2: ral::can::CAN2::take()?, + }, ccm: ccm::CCM::new(ral::ccm::CCM::take()?, ral::ccm_analog::CCM_ANALOG::take()?), pit: pit::UnclockedPIT::new(ral::pit::PIT::take()?), dcdc: dcdc::DCDC(ral::dcdc::DCDC::take()?), From fd8e1ae5a71a62f3de3641be91aebe37808d93a1 Mon Sep 17 00:00:00 2001 From: djstrickland <96876452+dstric-aqueduct@users.noreply.github.com> Date: Sun, 18 Dec 2022 15:50:58 -0500 Subject: [PATCH 02/13] Remove StandardId and ExtendedId structs in favor of embedded-hal - Remove StandardId and ExtendedId structs in favor of embedded-hal - convert TryFrom to From for FlexCanMailboxCSCode --- imxrt-hal/Cargo.toml | 8 +- imxrt-hal/src/can/embedded_hal.rs | 5 +- imxrt-hal/src/can/frame.rs | 38 ++++----- imxrt-hal/src/can/frame/tests.rs | 5 +- imxrt-hal/src/can/id.rs | 126 ------------------------------ imxrt-hal/src/can/mod.rs | 20 ++--- 6 files changed, 31 insertions(+), 171 deletions(-) delete mode 100644 imxrt-hal/src/can/id.rs diff --git a/imxrt-hal/Cargo.toml b/imxrt-hal/Cargo.toml index 1397ee22..07779758 100644 --- a/imxrt-hal/Cargo.toml +++ b/imxrt-hal/Cargo.toml @@ -22,9 +22,9 @@ log = "0.4.8" rand_core = { version = "0.5", default-features = false, optional = true } [dependencies.imxrt-iomuxc] -version = "0.1.2" -git = "https://github.com/imxrt-rs/imxrt-iomuxc.git" -branch = "v0.1" +features = ["imxrt106x"] +git = "https://github.com/dstric-aqueduct/imxrt-iomuxc.git" +tag = "v0.1.5-can" [dev-dependencies.cortex-m-rt] version = "0.6" @@ -34,7 +34,7 @@ features = ["device"] bench = false [features] -default = ["embedded-hal/unproven"] # Allows us to access the new digital pin traits +default = ["embedded-hal/unproven", "imxrt1062"] # Allows us to access the new digital pin traits # Device specific feature flags # these need fixes and conditional sections in CCM diff --git a/imxrt-hal/src/can/embedded_hal.rs b/imxrt-hal/src/can/embedded_hal.rs index 4d6a918c..b10046f3 100644 --- a/imxrt-hal/src/can/embedded_hal.rs +++ b/imxrt-hal/src/can/embedded_hal.rs @@ -1,9 +1,10 @@ //! `embedded_hal` trait impls. -use super::{Data, ExtendedId, Frame, Id, NoDataError, StandardId, CAN}; - +use super::{Data, Frame, NoDataError, CAN}; use crate::iomuxc::consts::Unsigned; + use embedded_hal::can; +pub use embedded_hal::can::{ExtendedId, Id, StandardId}; impl can::Can for CAN where diff --git a/imxrt-hal/src/can/frame.rs b/imxrt-hal/src/can/frame.rs index 1c3ebbc3..2e546438 100644 --- a/imxrt-hal/src/can/frame.rs +++ b/imxrt-hal/src/can/frame.rs @@ -4,7 +4,7 @@ mod tests; use core::convert::TryFrom; use core::ops::{Deref, DerefMut}; -use super::id::{ExtendedId, Id, StandardId}; +use super::{ExtendedId, Id, StandardId}; /// A CAN data or remote frame. #[derive(Clone, Debug, Eq)] @@ -143,27 +143,21 @@ pub enum FlexCanMailboxCSCode { Unknown, } -impl ::core::convert::TryFrom for FlexCanMailboxCSCode { - type Error = (); - +impl ::core::convert::From for FlexCanMailboxCSCode { #[inline(always)] - fn try_from(value: u8) -> Result { + fn from(value: u8) -> Self { match value { - v if v == FlexCanMailboxCSCode::RxInactive as u8 => { - Ok(FlexCanMailboxCSCode::RxInactive) - } - v if v == FlexCanMailboxCSCode::RxEmpty as u8 => Ok(FlexCanMailboxCSCode::RxEmpty), - v if v == FlexCanMailboxCSCode::RxFull as u8 => Ok(FlexCanMailboxCSCode::RxFull), - v if v == FlexCanMailboxCSCode::RxOverrun as u8 => Ok(FlexCanMailboxCSCode::RxOverrun), - v if v == FlexCanMailboxCSCode::RxAnswer as u8 => Ok(FlexCanMailboxCSCode::RxAnswer), - v if v == FlexCanMailboxCSCode::RxBusy as u8 => Ok(FlexCanMailboxCSCode::RxBusy), - v if v == FlexCanMailboxCSCode::TxInactive as u8 => { - Ok(FlexCanMailboxCSCode::TxInactive) - } - v if v == FlexCanMailboxCSCode::TxAbort as u8 => Ok(FlexCanMailboxCSCode::TxAbort), - v if v == FlexCanMailboxCSCode::TxOnce as u8 => Ok(FlexCanMailboxCSCode::TxOnce), - v if v == FlexCanMailboxCSCode::TxAnswer as u8 => Ok(FlexCanMailboxCSCode::TxAnswer), - _ => Ok(FlexCanMailboxCSCode::Unknown), + v if v == FlexCanMailboxCSCode::RxInactive as u8 => FlexCanMailboxCSCode::RxInactive, + v if v == FlexCanMailboxCSCode::RxEmpty as u8 => FlexCanMailboxCSCode::RxEmpty, + v if v == FlexCanMailboxCSCode::RxFull as u8 => FlexCanMailboxCSCode::RxFull, + v if v == FlexCanMailboxCSCode::RxOverrun as u8 => FlexCanMailboxCSCode::RxOverrun, + v if v == FlexCanMailboxCSCode::RxAnswer as u8 => FlexCanMailboxCSCode::RxAnswer, + v if v == FlexCanMailboxCSCode::RxBusy as u8 => FlexCanMailboxCSCode::RxBusy, + v if v == FlexCanMailboxCSCode::TxInactive as u8 => FlexCanMailboxCSCode::TxInactive, + v if v == FlexCanMailboxCSCode::TxAbort as u8 => FlexCanMailboxCSCode::TxAbort, + v if v == FlexCanMailboxCSCode::TxOnce as u8 => FlexCanMailboxCSCode::TxOnce, + v if v == FlexCanMailboxCSCode::TxAnswer as u8 => FlexCanMailboxCSCode::TxAnswer, + _ => FlexCanMailboxCSCode::Unknown, } } } @@ -175,8 +169,8 @@ impl FlexCanMailboxCSCode { } #[inline(always)] - pub fn from_code_reg(reg: u32) -> Result { - Self::try_from(((reg & CodeReg::CODE_MASK) >> CodeReg::CODE_SHIFT) as u8) + pub fn from_code_reg(reg: u32) -> Self { + Self::from(((reg & CodeReg::CODE_MASK) >> CodeReg::CODE_SHIFT) as u8) } #[inline(always)] diff --git a/imxrt-hal/src/can/frame/tests.rs b/imxrt-hal/src/can/frame/tests.rs index e8b43d1f..8e84bed2 100644 --- a/imxrt-hal/src/can/frame/tests.rs +++ b/imxrt-hal/src/can/frame/tests.rs @@ -1,7 +1,4 @@ -use crate::can::{ - frame::Frame, - id::{ExtendedId, StandardId}, -}; +use crate::can::{frame::Frame, ExtendedId, StandardId}; #[test] fn data_greater_remote() { diff --git a/imxrt-hal/src/can/id.rs b/imxrt-hal/src/can/id.rs deleted file mode 100644 index 3a4c3f5d..00000000 --- a/imxrt-hal/src/can/id.rs +++ /dev/null @@ -1,126 +0,0 @@ -/// Standard 11-bit CAN Identifier (`0..=0x7FF`). -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct StandardId(u16); - -impl StandardId { - /// CAN ID `0`, the highest priority. - pub const ZERO: Self = Self(0); - - /// CAN ID `0x7FF`, the lowest priority. - pub const MAX: Self = Self(0x7FF); - - /// Tries to create a `StandardId` from a raw 16-bit integer. - /// - /// This will return `None` if `raw` is out of range of an 11-bit integer (`> 0x7FF`). - #[inline] - pub const fn new(raw: u16) -> Option { - if raw <= 0x7FF { - Some(Self(raw)) - } else { - None - } - } - - /// Creates a new `StandardId` without checking if it is inside the valid range. - /// - /// # Safety - /// - /// The caller must ensure that `raw` is in the valid range, otherwise the behavior is - /// undefined. - #[inline] - pub const unsafe fn new_unchecked(raw: u16) -> Self { - Self(raw) - } - - /// Returns this CAN Identifier as a raw 16-bit integer. - #[inline] - pub fn as_raw(&self) -> u16 { - self.0 - } -} - -/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`). -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct ExtendedId(u32); - -impl ExtendedId { - /// CAN ID `0`, the highest priority. - pub const ZERO: Self = Self(0); - - /// CAN ID `0x1FFFFFFF`, the lowest priority. - pub const MAX: Self = Self(0x1FFF_FFFF); - - /// Tries to create a `ExtendedId` from a raw 32-bit integer. - /// - /// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`). - #[inline] - pub const fn new(raw: u32) -> Option { - if raw <= 0x1FFF_FFFF { - Some(Self(raw)) - } else { - None - } - } - - /// Creates a new `ExtendedId` without checking if it is inside the valid range. - /// - /// # Safety - /// - /// The caller must ensure that `raw` is in the valid range, otherwise the behavior is - /// undefined. - #[inline] - pub const unsafe fn new_unchecked(raw: u32) -> Self { - Self(raw) - } - - /// Returns this CAN Identifier as a raw 32-bit integer. - #[inline] - pub fn as_raw(&self) -> u32 { - self.0 - } - - /// Returns the Base ID part of this extended identifier. - pub fn standard_id(&self) -> StandardId { - // ID-28 to ID-18 - StandardId((self.0 >> 18) as u16) - } -} - -/// A CAN Identifier (standard or extended). -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum Id { - /// Standard 11-bit Identifier (`0..=0x7FF`). - Standard(StandardId), - - /// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`). - Extended(ExtendedId), -} - -impl Default for Id { - fn default() -> Self { - Id::Standard(StandardId::new(0).unwrap()) - } -} - -impl From for Id { - #[inline] - fn from(id: StandardId) -> Self { - Id::Standard(id) - } -} - -impl From for Id { - #[inline] - fn from(id: ExtendedId) -> Self { - Id::Extended(id) - } -} - -impl Id { - pub fn as_raw(&self) -> u32 { - match self { - Self::Standard(id) => id.as_raw() as u32, - Self::Extended(id) => id.as_raw(), - } - } -} diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index a812945d..7edb25a6 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -1,15 +1,14 @@ mod embedded_hal; pub mod filter; mod frame; -mod id; +pub use self::embedded_hal::{ExtendedId, Id, StandardId}; pub use frame::{CodeReg, Data, FlexCanMailboxCSCode, Frame, IdReg}; -pub use id::{ExtendedId, Id, StandardId}; use ral::{modify_reg, read_reg, write_reg}; use crate::ccm; -use crate::iomuxc::consts::{Unsigned, U1, U2}; use crate::iomuxc::can; +use crate::iomuxc::consts::{Unsigned, U1, U2}; use crate::ral; use core::convert::Infallible; @@ -112,7 +111,7 @@ where { crate::iomuxc::can::prepare(&mut tx); crate::iomuxc::can::prepare(&mut rx); - + CAN::new(self.source_clock, self.reg) } } @@ -752,20 +751,15 @@ where } let code = self.read_mailbox_code(mailbox_number); - let c = match FlexCanMailboxCSCode::from_code_reg(code) { - Ok(c) => Some(c), - Err(_e) => None, - }; + let c = FlexCanMailboxCSCode::from_code_reg(code); let mailbox_addr = self.mailbox_number_to_address(mailbox_number); match c { // return None from a transmit mailbox - Some(c) if c.is_tx_mailbox() => None, + c if c.is_tx_mailbox() => None, // full or overrun - Some(c) - if (c == FlexCanMailboxCSCode::RxFull) | (c == FlexCanMailboxCSCode::RxOverrun) => - { + c if (c == FlexCanMailboxCSCode::RxFull) | (c == FlexCanMailboxCSCode::RxOverrun) => { let id = unsafe { core::ptr::read_volatile((mailbox_addr + 0x4_u32) as *const u32) }; let data0 = @@ -981,7 +975,7 @@ where #[inline(always)] pub fn transmit(&mut self, frame: &Frame) -> nb::Result<(), Infallible> { for i in self.mailbox_offset()..self.get_max_mailbox() { - if let Ok(FlexCanMailboxCSCode::TxInactive) = + if let FlexCanMailboxCSCode::TxInactive = FlexCanMailboxCSCode::from_code_reg(self.read_mailbox_code(i)) { self.write_tx_mailbox(i, frame); From df488228ae1652a1101d9bddacd75b7c20812e46 Mon Sep 17 00:00:00 2001 From: djstrickland <96876452+dstric-aqueduct@users.noreply.github.com> Date: Sun, 18 Dec 2022 15:59:46 -0500 Subject: [PATCH 03/13] use get_clock() instead of hard code freq for baud rate calc --- imxrt-hal/src/can/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index 7edb25a6..b37e66fd 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -326,7 +326,7 @@ where } pub fn set_baud_rate(&mut self, baud: u32) { - let clock_freq = 24_000_000; + let clock_freq = self.get_clock(); let mut divisor = 0; let mut best_divisor: u32 = 0; From f73b2b8318cf38a66197b003745135f6a6107f05 Mon Sep 17 00:00:00 2001 From: djstrickland <96876452+dstric-aqueduct@users.noreply.github.com> Date: Wed, 21 Dec 2022 07:12:36 -0500 Subject: [PATCH 04/13] PR updates - added bounds checks on StandardID and ExtendedID for creation of IdReg - improved Error descriptiveness - added docs to Frame timestamp field - Update Cargo.toml --- imxrt-hal/Cargo.toml | 2 +- imxrt-hal/src/can/embedded_hal.rs | 17 ++++++--- imxrt-hal/src/can/frame.rs | 36 +++++++++++------- imxrt-hal/src/can/mod.rs | 62 +++++++++++++++++++++++++++---- 4 files changed, 90 insertions(+), 27 deletions(-) diff --git a/imxrt-hal/Cargo.toml b/imxrt-hal/Cargo.toml index 07779758..77a48e7b 100644 --- a/imxrt-hal/Cargo.toml +++ b/imxrt-hal/Cargo.toml @@ -34,7 +34,7 @@ features = ["device"] bench = false [features] -default = ["embedded-hal/unproven", "imxrt1062"] # Allows us to access the new digital pin traits +default = ["embedded-hal/unproven"] # Allows us to access the new digital pin traits # Device specific feature flags # these need fixes and conditional sections in CCM diff --git a/imxrt-hal/src/can/embedded_hal.rs b/imxrt-hal/src/can/embedded_hal.rs index b10046f3..353a55a2 100644 --- a/imxrt-hal/src/can/embedded_hal.rs +++ b/imxrt-hal/src/can/embedded_hal.rs @@ -1,10 +1,11 @@ //! `embedded_hal` trait impls. -use super::{Data, Frame, NoDataError, CAN}; +use super::{Data, Frame, Error, CAN}; use crate::iomuxc::consts::Unsigned; use embedded_hal::can; pub use embedded_hal::can::{ExtendedId, Id, StandardId}; +pub(crate) use embedded_hal::can::ErrorKind; impl can::Can for CAN where @@ -12,27 +13,31 @@ where { type Frame = Frame; - type Error = NoDataError; + type Error = Error; fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error> { match self.transmit(frame) { Ok(_status) => Ok(Some(frame.clone())), Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), - Err(nb::Error::Other(e)) => match e {}, + Err(nb::Error::Other(e)) => Err(nb::Error::Other(e)), } } fn receive(&mut self) -> nb::Result { match self.read_mailboxes() { Some(d) => Ok(d.frame), - None => Err(nb::Error::Other(NoDataError { _priv: () })), + None => Err(nb::Error::Other(Error::NoRxData)), } } } -impl can::Error for NoDataError { +impl can::Error for Error { fn kind(&self) -> can::ErrorKind { - can::ErrorKind::Overrun + match self { + Self::NoRxData => can::ErrorKind::Other, + Self::NoTxMailbox => can::ErrorKind::Other, + Self::EmbeddedHal(e) => e.kind() + } } } diff --git a/imxrt-hal/src/can/frame.rs b/imxrt-hal/src/can/frame.rs index 2e546438..c85c09c5 100644 --- a/imxrt-hal/src/can/frame.rs +++ b/imxrt-hal/src/can/frame.rs @@ -1,7 +1,6 @@ #[cfg(test)] mod tests; -use core::convert::TryFrom; use core::ops::{Deref, DerefMut}; use super::{ExtendedId, Id, StandardId}; @@ -236,16 +235,27 @@ impl CodeReg { } /// Get the 4 bit code content for a CodeReg. - /// - /// # Panics - /// - /// This function will panic if a matching [`FlexCanMailboxCSCode`] is not found. + /// + /// This may return the variant [`FlexCanMailboxCSCode::Unknown`], + /// which must be handled appropriately for the intended usage. #[inline(always)] pub fn code(&self) -> FlexCanMailboxCSCode { - FlexCanMailboxCSCode::try_from(((self.0 & Self::CODE_MASK) >> Self::CODE_SHIFT) as u8) - .unwrap() + FlexCanMailboxCSCode::from(((self.0 & Self::CODE_MASK) >> Self::CODE_SHIFT) as u8) } + /// The frame timestamp, captured from the peripheral's free-running timer. + /// + /// The timestamp represents the time at which a TX or RX frame appears on + /// the bus. The counter increments at the CAN bus baud rate. The counter pauses + /// when the driver is frozen, or the processor is in debug mode. + /// + /// A frame's timestamp resets to zero when either + /// + /// - the counter wraps around at the 16 bit boundary. + /// - a message is received in mailbox 0. This happens if time sync is enabled, + /// and if the message passed the mailbox filter. + /// + /// Users may also override the timestamp to any specific value. pub fn timestamp(&self) -> u16 { ((self.0 & Self::TIMESTAMP_MASK) >> Self::TIMESTAMP_SHIFT) as u16 } @@ -301,11 +311,9 @@ impl IdReg { const STANDARD_SHIFT: u32 = 18; const EXTENDED_SHIFT: u32 = 0; - /// Creates a new standard identifier (11bit, Range: 0..0x7FF) - /// - /// Panics for IDs outside the allowed range. + /// Creates a new `IdReg` #[inline(always)] - pub fn new(id: u32) -> Self { + fn new(id: u32) -> Self { Self(id) } @@ -315,20 +323,22 @@ impl IdReg { self.0 } - /// Creates a new standard identifier (11bit, Range: 0..0x7FF) + /// Creates a new standard identifier (11bit, Range: 0..=0x7FF) /// /// Panics for IDs outside the allowed range. #[inline(always)] pub fn new_standard(id: StandardId) -> Self { + assert!(id.as_raw() <= StandardId::MAX.as_raw()); let id = u32::from(id.as_raw()) << Self::STANDARD_SHIFT; Self::new(id) } - /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). + /// Creates a new extendended identifier (29bit , Range: 0..=0x1FFFFFFF). /// /// Panics for IDs outside the allowed range. #[inline(always)] pub fn new_extended(id: ExtendedId) -> Self { + assert!(id.as_raw() <= ExtendedId::MAX.as_raw()); let id = u32::from(id.as_raw()) << Self::EXTENDED_SHIFT; Self::new(id) } diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index b37e66fd..81666fb0 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -1,3 +1,44 @@ +//! Flexible Controller Area Network (FlexCAN) +//! +//! The FlexCAN module provides a CanBus peripheral that implements +//! the `embedded_hal::can` traits. +//!//! +//! # Example +//! +//! ```no_run +//! use imxrt_hal; +//! use embedded_hal::serial::{Read, Write}; +//! +//! let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); +//! +//! let (can1_builder, _) = peripherals.can.clock( +//! &mut peripherals.ccm.handle, +//! bsp::hal::ccm::can::ClockSelect::OSC, +//! bsp::hal::ccm::can::PrescalarSelect::DIVIDE_1, +//! ); +//! +//! let mut can1 = can1_builder.build(pins.p22, pins.p23); +//! +//! can1.set_baud_rate(1_000_000); +//! can1.set_max_mailbox(16); +//! can1.enable_fifo(); +//! can1.set_fifo_interrupt(true); +//! can1.set_fifo_accept_all(); +//! +//! // create a `Frame` with `StandardID` 0x00 +//! // and `Data` [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07] +//! let id = imxrt_hal::can::Id::from(imxrt_hal::can::StandardId::new(0x00).unwrap()); +//! let data: [u8; 8] = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; +//! let frame = imxrt_hal::can::Frame::new_data(id, data); +//! +//! // read all available mailboxes for any available frames +//! can1.read_mailboxes(); +//! +//! // transmit the frame +//! can1.transmit(&frame); +//! ``` + + mod embedded_hal; pub mod filter; mod frame; @@ -14,11 +55,18 @@ use crate::ral; use core::convert::Infallible; use core::marker::PhantomData; -/// Error that indicates that no received frames are available -/// in the FlexCAN mailboxes +/// Error from the FlexCan peripheral. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NoDataError { - _priv: (), +pub enum Error { + /// Error indicating that no received data is + /// available in the mailboxes + NoRxData, + /// Error indicating that no transmit mailboxes + /// are available + NoTxMailbox, + /// A wrapper around the [`embedded_hal::ErrorKind`] + /// enum + EmbeddedHal(embedded_hal::ErrorKind), } /// Unclocked Can modules @@ -247,7 +295,7 @@ where M::USIZE == 2 } - pub fn base_address(&self) -> u32 { + pub(crate) fn base_address(&self) -> u32 { let addr: *const ral::can::RegisterBlock = &*self.reg; addr as u32 } @@ -973,7 +1021,7 @@ where } #[inline(always)] - pub fn transmit(&mut self, frame: &Frame) -> nb::Result<(), Infallible> { + pub fn transmit(&mut self, frame: &Frame) -> nb::Result<(), Error> { for i in self.mailbox_offset()..self.get_max_mailbox() { if let FlexCanMailboxCSCode::TxInactive = FlexCanMailboxCSCode::from_code_reg(self.read_mailbox_code(i)) @@ -982,7 +1030,7 @@ where return Ok(()); } } - Ok(()) + Err(nb::Error::Other(Error::NoTxMailbox)) } } From 228a0002780ef7f05400faf73d583c16adf758f6 Mon Sep 17 00:00:00 2001 From: dstric-aqueduct <96876452+dstric-aqueduct@users.noreply.github.com> Date: Sat, 24 Dec 2022 06:27:49 -0500 Subject: [PATCH 05/13] remove unused import, added derives to FlexCanMailboxCSCode --- imxrt-hal/src/can/frame.rs | 8 ++++---- imxrt-hal/src/can/mod.rs | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/imxrt-hal/src/can/frame.rs b/imxrt-hal/src/can/frame.rs index c85c09c5..3b88af2c 100644 --- a/imxrt-hal/src/can/frame.rs +++ b/imxrt-hal/src/can/frame.rs @@ -124,7 +124,7 @@ impl PartialEq for Frame { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone, Copy)] #[repr(u8)] pub enum FlexCanMailboxCSCode { RxInactive = 0b0000, @@ -235,8 +235,8 @@ impl CodeReg { } /// Get the 4 bit code content for a CodeReg. - /// - /// This may return the variant [`FlexCanMailboxCSCode::Unknown`], + /// + /// This may return the variant [`FlexCanMailboxCSCode::Unknown`], /// which must be handled appropriately for the intended usage. #[inline(always)] pub fn code(&self) -> FlexCanMailboxCSCode { @@ -311,7 +311,7 @@ impl IdReg { const STANDARD_SHIFT: u32 = 18; const EXTENDED_SHIFT: u32 = 0; - /// Creates a new `IdReg` + /// Creates a new `IdReg` #[inline(always)] fn new(id: u32) -> Self { Self(id) diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index 81666fb0..a65b977f 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -52,7 +52,6 @@ use crate::iomuxc::can; use crate::iomuxc::consts::{Unsigned, U1, U2}; use crate::ral; -use core::convert::Infallible; use core::marker::PhantomData; /// Error from the FlexCan peripheral. From c6f055e944cd5677c93e116c9548cd55ab1ad326 Mon Sep 17 00:00:00 2001 From: dstric-aqueduct <96876452+dstric-aqueduct@users.noreply.github.com> Date: Mon, 26 Dec 2022 18:08:08 -0500 Subject: [PATCH 06/13] fix to mod.rs docs --- imxrt-hal/src/can/mod.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index a65b977f..2befb898 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -1,31 +1,33 @@ //! Flexible Controller Area Network (FlexCAN) //! //! The FlexCAN module provides a CanBus peripheral that implements -//! the `embedded_hal::can` traits. +//! the `embedded_hal::can` traits. //!//! //! # Example //! //! ```no_run //! use imxrt_hal; -//! use embedded_hal::serial::{Read, Write}; //! //! let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); -//! +//! //! let (can1_builder, _) = peripherals.can.clock( //! &mut peripherals.ccm.handle, -//! bsp::hal::ccm::can::ClockSelect::OSC, -//! bsp::hal::ccm::can::PrescalarSelect::DIVIDE_1, +//! imxrt_hal::ccm::can::ClockSelect::OSC, +//! imxrt_hal::ccm::can::PrescalarSelect::DIVIDE_1, +//! ); +//! +//! let mut can1 = can1_builder.build( +//! peripherals.iomuxc.ad_b1.p08, +//! peripherals.iomuxc.ad_b1.p09 //! ); -//! -//! let mut can1 = can1_builder.build(pins.p22, pins.p23); -//! +//! //! can1.set_baud_rate(1_000_000); //! can1.set_max_mailbox(16); //! can1.enable_fifo(); //! can1.set_fifo_interrupt(true); //! can1.set_fifo_accept_all(); -//! -//! // create a `Frame` with `StandardID` 0x00 +//! +//! // create a `Frame` with `StandardID` 0x00 //! // and `Data` [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07] //! let id = imxrt_hal::can::Id::from(imxrt_hal::can::StandardId::new(0x00).unwrap()); //! let data: [u8; 8] = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; @@ -33,12 +35,11 @@ //! //! // read all available mailboxes for any available frames //! can1.read_mailboxes(); -//! +//! //! // transmit the frame //! can1.transmit(&frame); //! ``` - mod embedded_hal; pub mod filter; mod frame; @@ -57,12 +58,12 @@ use core::marker::PhantomData; /// Error from the FlexCan peripheral. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Error { - /// Error indicating that no received data is + /// Error indicating that no received data is /// available in the mailboxes NoRxData, /// Error indicating that no transmit mailboxes /// are available - NoTxMailbox, + NoTxMailbox, /// A wrapper around the [`embedded_hal::ErrorKind`] /// enum EmbeddedHal(embedded_hal::ErrorKind), From 11daf65bfbd078c5abc142d68c854706b8df35c5 Mon Sep 17 00:00:00 2001 From: dstric-aqueduct <96876452+dstric-aqueduct@users.noreply.github.com> Date: Wed, 28 Dec 2022 11:53:57 -0500 Subject: [PATCH 07/13] PR updates - fixed clock configuration in builder method and removed set_ccm_ccg method - removed unused `instance` method - removed duplicated `set_fifo_filter` method in favor of single method that accepts a `FlexCanFilter` as arg - removed dead code --- imxrt-hal/src/can/filter.rs | 7 +++ imxrt-hal/src/can/mod.rs | 90 +++++++++---------------------------- 2 files changed, 29 insertions(+), 68 deletions(-) diff --git a/imxrt-hal/src/can/filter.rs b/imxrt-hal/src/can/filter.rs index 947c3b26..4010cc58 100644 --- a/imxrt-hal/src/can/filter.rs +++ b/imxrt-hal/src/can/filter.rs @@ -25,3 +25,10 @@ pub struct FlexCanFilter { pub remote: FlexCanIde } +impl FlexCanFilter { + /// Create a new [`FlexCanFilter`]. + pub fn new(filter_id: u8, id: u32, ide: FlexCanIde, remote: FlexCanIde) -> Self { + Self { filter_id, id, ide, remote } + } +} + diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index 2befb898..c2d2c10b 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -17,7 +17,7 @@ //! ); //! //! let mut can1 = can1_builder.build( -//! peripherals.iomuxc.ad_b1.p08, +//! peripherals.iomuxc.ad_b1.p08, //! peripherals.iomuxc.ad_b1.p09 //! ); //! @@ -88,15 +88,16 @@ impl Unclocked { divider: ccm::can::PrescalarSelect, ) -> (Builder, Builder) { let (ccm, _) = handle.raw(); - // First, disable clocks + + // First, disable the clocks for Can1 and Can2 ral::modify_reg!( ral::ccm, ccm, - CCGR1, - CG0: 0, - CG1: 0, - CG2: 0, - CG3: 0 + CCGR0, + CG7: 0b00, + CG8: 0b00, + CG9: 0b00, + CG10: 0b00 ); let clk_sel = match clock_select { @@ -112,15 +113,15 @@ impl Unclocked { CAN_CLK_SEL: clk_sel ); - // Enable clocks + // Enable the clocks for Can1 and Can2 ral::modify_reg!( ral::ccm, ccm, - CCGR1, - CG0: 0b11, - CG1: 0b11, - CG2: 0b11, - CG3: 0b11 + CCGR0, + CG7: 0b11, + CG8: 0b11, + CG9: 0b11, + CG10: 0b11 ); let source_clock = ccm::Frequency::from(clock_select) / ccm::Divider::from(divider); @@ -150,8 +151,7 @@ where } } - /// Builds a Can peripheral. The return - /// is a configured FlexCan peripheral running at 24MHz. + /// Builds a Can peripheral. pub fn build(self, mut tx: TX, mut rx: RX) -> CAN where TX: can::Pin, @@ -196,10 +196,6 @@ where can } - pub fn instance(&mut self) -> &mut ral::can::Instance { - &mut self.reg - } - pub fn print_registers(&self) { log::info!("MCR: {:X}", ral::read_reg!(ral::can, self.reg, MCR)); log::info!("CTRL1: {:X}", ral::read_reg!(ral::can, self.reg, CTRL1)); @@ -240,8 +236,6 @@ where } pub fn begin(&mut self) { - self.set_ccm_ccg(); - ral::modify_reg!(ral::can, self.reg, MCR, MDIS: MDIS_0); self.enter_freeze_mode(); @@ -328,20 +322,6 @@ where ral::modify_reg!(ral::can, self.reg, CTRL2, RRS: rrs as u32) } - fn set_ccm_ccg(&mut self) { - match self.instance_number() { - 1 => { - unsafe { modify_reg!(ral::ccm, CCM, CCGR0, |reg| reg | 0x3C000) }; - } - 2 => { - unsafe { modify_reg!(ral::ccm, CCM, CCGR0, |reg| reg | 0x3C0000) }; - } - u => { - log::error!("Invalid Can instance (set_ccm_ccg): {:?}", u); - } - } - } - pub fn get_clock(&self) -> u32 { self.source_clock.0 } @@ -612,16 +592,16 @@ where }) } - pub fn set_fifo_filter( - &mut self, - filter_id: u8, - id: u32, - ide: filter::FlexCanIde, - remote: filter::FlexCanIde, - ) { + pub fn set_fifo_filter(&mut self, filter: filter::FlexCanFilter) { if !self.fifo_enabled() { return; } + let filter::FlexCanFilter { + filter_id, + id, + ide, + remote, + } = filter; let max_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 8; if filter_id as u32 >= max_fifo_filters { return; @@ -710,10 +690,6 @@ where }) } - pub fn set_fifo_filter_2(&mut self, filter: filter::FlexCanFilter) { - self.set_fifo_filter(filter.filter_id, filter.id, filter.ide, filter.remote) - } - pub fn set_fifo_interrupt(&mut self, enabled: bool) { /* FIFO must be enabled first */ if !self.fifo_enabled() { @@ -754,23 +730,6 @@ where } } - #[allow(dead_code)] - fn read_fifo(&self) -> Option<()> { - // if FIFO enabled and interrupt not enabled - if !self.fifo_enabled() { - return None; - }; - // FIFO interrupt enabled, polling blocked - if (ral::read_reg!(ral::can, self.reg, IMASK1) & (0x00000020 as u32)) != 0 { - return None; - } - // message available - if (ral::read_reg!(ral::can, self.reg, IFLAG1) & (0x00000020 as u32)) != 0 { - return None; - } - Some(()) - } - #[inline(always)] fn mailbox_number_to_address(&self, mailbox_number: u8) -> u32 { self.base_address() + 0x80_u32 + (mailbox_number as u32 * 0x10_u32) @@ -1033,8 +992,3 @@ where Err(nb::Error::Other(Error::NoTxMailbox)) } } - -/// Interface to the Can transmitter part. -pub struct Tx { - _can: PhantomData, -} From a87433cc990f608f0c940133de0f037dd2159d61 Mon Sep 17 00:00:00 2001 From: djstrickland <96876452+dstric-aqueduct@users.noreply.github.com> Date: Sun, 18 Dec 2022 16:08:01 -0500 Subject: [PATCH 08/13] Prototype a FlexCAN peripheral Implementation of the FlexCAN1 and FlexCAN2 peripherals for the NXP i.MX RT 1060 processor. Includes APIs for initializing the peripherals, setting the clock source, setting mailbox receive filters, setting mailbox FIFO functionality, and receiving and transmitting frames. The FlexCAN3 peripheral is not implemented. --- .gitignore | 1 + imxrt-hal/Cargo.toml | 6 +- imxrt-hal/src/can/embedded_hal.rs | 110 ++++ imxrt-hal/src/can/filter.rs | 38 ++ imxrt-hal/src/can/frame.rs | 455 ++++++++++++++ imxrt-hal/src/can/frame/tests.rs | 75 +++ imxrt-hal/src/can/mod.rs | 994 ++++++++++++++++++++++++++++++ imxrt-hal/src/ccm.rs | 159 +++++ imxrt-hal/src/lib.rs | 10 + 9 files changed, 1845 insertions(+), 3 deletions(-) create mode 100644 imxrt-hal/src/can/embedded_hal.rs create mode 100644 imxrt-hal/src/can/filter.rs create mode 100644 imxrt-hal/src/can/frame.rs create mode 100644 imxrt-hal/src/can/frame/tests.rs create mode 100644 imxrt-hal/src/can/mod.rs diff --git a/.gitignore b/.gitignore index a36d0931..7268cb79 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target +.idea .vscode Cargo.lock diff --git a/imxrt-hal/Cargo.toml b/imxrt-hal/Cargo.toml index 1397ee22..77a48e7b 100644 --- a/imxrt-hal/Cargo.toml +++ b/imxrt-hal/Cargo.toml @@ -22,9 +22,9 @@ log = "0.4.8" rand_core = { version = "0.5", default-features = false, optional = true } [dependencies.imxrt-iomuxc] -version = "0.1.2" -git = "https://github.com/imxrt-rs/imxrt-iomuxc.git" -branch = "v0.1" +features = ["imxrt106x"] +git = "https://github.com/dstric-aqueduct/imxrt-iomuxc.git" +tag = "v0.1.5-can" [dev-dependencies.cortex-m-rt] version = "0.6" diff --git a/imxrt-hal/src/can/embedded_hal.rs b/imxrt-hal/src/can/embedded_hal.rs new file mode 100644 index 00000000..1e5ec66c --- /dev/null +++ b/imxrt-hal/src/can/embedded_hal.rs @@ -0,0 +1,110 @@ +//! `embedded_hal` trait impls. + +use super::{Data, Error, Frame, CAN}; +use crate::iomuxc::consts::Unsigned; + +use embedded_hal::can; +pub(crate) use embedded_hal::can::ErrorKind; +pub use embedded_hal::can::{ExtendedId, Id, StandardId}; + +impl can::Can for CAN +where + M: Unsigned, +{ + type Frame = Frame; + + type Error = Error; + + fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error> { + match self.transmit(frame) { + Ok(_status) => Ok(Some(frame.clone())), + Err(nb::Error::WouldBlock) => Err(nb::Error::WouldBlock), + Err(nb::Error::Other(e)) => Err(nb::Error::Other(e)), + } + } + + fn receive(&mut self) -> nb::Result { + match self.read_mailboxes() { + Some(d) => Ok(d.frame), + None => Err(nb::Error::Other(Error::NoRxData)), + } + } +} + +impl can::Error for Error { + fn kind(&self) -> can::ErrorKind { + match self { + Self::NoRxData => can::ErrorKind::Other, + Self::NoTxMailbox => can::ErrorKind::Other, + Self::EmbeddedHal(e) => e.kind(), + } + } +} + +impl can::Frame for Frame { + fn new(id: impl Into, data: &[u8]) -> Option { + let id = match id.into() { + can::Id::Standard(id) => unsafe { + Id::Standard(StandardId::new_unchecked(id.as_raw())) + }, + can::Id::Extended(id) => unsafe { + Id::Extended(ExtendedId::new_unchecked(id.as_raw())) + }, + }; + + let data = Data::new(data)?; + Some(Frame::new_data(id, data)) + } + + fn new_remote(id: impl Into, dlc: usize) -> Option { + let id = match id.into() { + can::Id::Standard(id) => unsafe { + Id::Standard(StandardId::new_unchecked(id.as_raw())) + }, + can::Id::Extended(id) => unsafe { + Id::Extended(ExtendedId::new_unchecked(id.as_raw())) + }, + }; + + if dlc <= 8 { + Some(Frame::new_remote(id, dlc as u8)) + } else { + None + } + } + + #[inline] + fn is_extended(&self) -> bool { + self.is_extended() + } + + #[inline] + fn is_remote_frame(&self) -> bool { + self.is_remote_frame() + } + + #[inline] + fn id(&self) -> can::Id { + match self.id() { + Id::Standard(id) => unsafe { + can::Id::Standard(can::StandardId::new_unchecked(id.as_raw())) + }, + Id::Extended(id) => unsafe { + can::Id::Extended(can::ExtendedId::new_unchecked(id.as_raw())) + }, + } + } + + #[inline] + fn dlc(&self) -> usize { + self.dlc().into() + } + + fn data(&self) -> &[u8] { + if let Some(data) = self.data() { + data + } else { + &[] + } + } +} diff --git a/imxrt-hal/src/can/filter.rs b/imxrt-hal/src/can/filter.rs new file mode 100644 index 00000000..5dee705d --- /dev/null +++ b/imxrt-hal/src/can/filter.rs @@ -0,0 +1,38 @@ +//! Filter bank API. + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum FlexCanIde { + #[default] + None = 0, + Ext = 1, + Rtr = 2, + Std = 3, + Inactive, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum FlexCanFlten { + AcceptAll = 0, + #[default] + RejectAll = 1, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub struct FlexCanFilter { + pub filter_id: u8, + pub id: u32, + pub ide: FlexCanIde, + pub remote: FlexCanIde, +} + +impl FlexCanFilter { + /// Create a new [`FlexCanFilter`]. + pub fn new(filter_id: u8, id: u32, ide: FlexCanIde, remote: FlexCanIde) -> Self { + Self { + filter_id, + id, + ide, + remote, + } + } +} diff --git a/imxrt-hal/src/can/frame.rs b/imxrt-hal/src/can/frame.rs new file mode 100644 index 00000000..3b88af2c --- /dev/null +++ b/imxrt-hal/src/can/frame.rs @@ -0,0 +1,455 @@ +#[cfg(test)] +mod tests; + +use core::ops::{Deref, DerefMut}; + +use super::{ExtendedId, Id, StandardId}; + +/// A CAN data or remote frame. +#[derive(Clone, Debug, Eq)] +pub struct Frame { + pub(crate) code: CodeReg, + pub(crate) id: IdReg, + pub(crate) data: Data, +} + +impl Frame { + /// Creates a new data frame. + pub fn new_data(id: impl Into, data: impl Into) -> Self { + let (code, id) = match id.into() { + Id::Standard(id) => (CodeReg::new_standard(), IdReg::new_standard(id)), + Id::Extended(id) => (CodeReg::new_extended(), IdReg::new_extended(id)), + }; + + Self { + code, + id, + data: data.into(), + } + } + + /// Creates a new data frame. + #[inline(always)] + pub fn new_from_raw(code: u32, id: u32, data: impl Into) -> Self { + Self { + code: CodeReg::new(code), + id: IdReg::new(id), + data: data.into(), + } + } + + /// Creates a new remote frame with configurable data length code (DLC). + /// + /// # Panics + /// + /// This function will panic if `dlc` is not inside the valid range `0..=8`. + pub fn new_remote(id: impl Into, dlc: u8) -> Self { + assert!(dlc <= 8); + + let mut frame = Self::new_data(id, []); + // Just extend the data length, even with no data present. The API does not hand out this + // `Data` object. + frame.data.len = dlc; + frame.code = frame.code.with_rtr(true); + frame + } + + /// Returns true if this frame is an extended frame. + #[inline(always)] + pub fn is_extended(&self) -> bool { + self.code.is_extended() + } + + /// Returns true if this frame is a standard frame. + #[inline(always)] + pub fn is_standard(&self) -> bool { + self.code.is_standard() + } + + /// Returns true if this frame is a remote frame. + #[inline(always)] + pub fn is_remote_frame(&self) -> bool { + self.code.rtr() + } + + /// Returns true if this frame is a data frame. + #[inline(always)] + pub fn is_data_frame(&self) -> bool { + !self.is_remote_frame() + } + + /// Returns the frame identifier. + #[inline(always)] + pub fn id(&self) -> Id { + if self.is_extended() { + self.id.to_extended() + } else { + self.id.to_standard() + } + } + + /// Returns the data length code (DLC) which is in the range 0..8. + /// + /// For data frames the DLC value always matches the length of the data. + /// Remote frames do not carry any data, yet the DLC can be greater than 0. + #[inline(always)] + pub fn dlc(&self) -> u8 { + self.data.len() as u8 + } + + #[inline(always)] + pub fn to_tx_once_code(&self) -> u32 { + ((self.dlc() as u32) << CodeReg::DLC_SHIFT) | FlexCanMailboxCSCode::TxOnce.to_code_reg() + } + + /// Returns the frame data (0..8 bytes in length) if this is a data frame. + /// + /// If this is a remote frame, returns `None`. + pub fn data(&self) -> Option<&Data> { + if self.is_data_frame() { + Some(&self.data) + } else { + None + } + } +} + +impl PartialEq for Frame { + fn eq(&self, other: &Self) -> bool { + match (self.data(), other.data()) { + (None, None) => self.id.eq(&other.id), + (Some(a), Some(b)) => self.id.eq(&other.id) && a.eq(b), + (None, Some(_)) | (Some(_), None) => false, + } + } +} + +#[derive(Debug, PartialEq, Clone, Copy)] +#[repr(u8)] +pub enum FlexCanMailboxCSCode { + RxInactive = 0b0000, + RxEmpty = 0b0100, + RxFull = 0b0010, + RxOverrun = 0b0110, + RxAnswer = 0b1010, + RxBusy = 0b0001, + + TxInactive = 0b1000, + TxAbort = 0b1001, + TxOnce = 0b1100, + TxAnswer = 0b1110, + + Unknown, +} + +impl ::core::convert::From for FlexCanMailboxCSCode { + #[inline(always)] + fn from(value: u8) -> Self { + match value { + v if v == FlexCanMailboxCSCode::RxInactive as u8 => FlexCanMailboxCSCode::RxInactive, + v if v == FlexCanMailboxCSCode::RxEmpty as u8 => FlexCanMailboxCSCode::RxEmpty, + v if v == FlexCanMailboxCSCode::RxFull as u8 => FlexCanMailboxCSCode::RxFull, + v if v == FlexCanMailboxCSCode::RxOverrun as u8 => FlexCanMailboxCSCode::RxOverrun, + v if v == FlexCanMailboxCSCode::RxAnswer as u8 => FlexCanMailboxCSCode::RxAnswer, + v if v == FlexCanMailboxCSCode::RxBusy as u8 => FlexCanMailboxCSCode::RxBusy, + v if v == FlexCanMailboxCSCode::TxInactive as u8 => FlexCanMailboxCSCode::TxInactive, + v if v == FlexCanMailboxCSCode::TxAbort as u8 => FlexCanMailboxCSCode::TxAbort, + v if v == FlexCanMailboxCSCode::TxOnce as u8 => FlexCanMailboxCSCode::TxOnce, + v if v == FlexCanMailboxCSCode::TxAnswer as u8 => FlexCanMailboxCSCode::TxAnswer, + _ => FlexCanMailboxCSCode::Unknown, + } + } +} + +impl FlexCanMailboxCSCode { + #[inline(always)] + pub fn to_code_reg(&self) -> u32 { + (*self as u32) << CodeReg::CODE_SHIFT + } + + #[inline(always)] + pub fn from_code_reg(reg: u32) -> Self { + Self::from(((reg & CodeReg::CODE_MASK) >> CodeReg::CODE_SHIFT) as u8) + } + + #[inline(always)] + pub fn is_tx_mailbox(&self) -> bool { + (*self as u8) >> 3 != 0 + } +} + +/// Code Register of a FlexCAN mailbox. +/// +/// Contains info relating to the frame's remote status (RTR), whether +/// the frams is standard os extended (IDE), the length of the data +/// content (DLC), and the timestamp. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CodeReg(u32); + +impl CodeReg { + pub(crate) const CODE_SHIFT: u32 = 24; + pub(crate) const CODE_MASK: u32 = 0b1111_u32 << Self::CODE_SHIFT; + + #[allow(dead_code)] + const SRR_SHIFT: u32 = 22; + #[allow(dead_code)] + const SRR_MASK: u32 = 0b1_u32 << Self::SRR_SHIFT; + + const IDE_SHIFT: u32 = 21; + const IDE_MASK: u32 = 0b1_u32 << Self::IDE_SHIFT; + + const RTR_SHIFT: u32 = 20; + const RTR_MASK: u32 = 0b1_u32 << Self::RTR_SHIFT; + + const DLC_SHIFT: u32 = 16; + const DLC_MASK: u32 = 0b111_u32 << Self::DLC_SHIFT; + + const TIMESTAMP_SHIFT: u32 = 0; + const TIMESTAMP_MASK: u32 = 0xFFFF_u32 << Self::TIMESTAMP_SHIFT; + + /// Creates a new code reg. + pub fn new(code: u32) -> Self { + Self(code) + } + + /// Creates a new code reg for a standard identifier. + pub fn new_standard() -> Self { + Self::new(0x00) + } + + /// Creates a new code reg for an extended identifier. + pub fn new_extended() -> Self { + Self::new(Self::IDE_MASK) + } + + /// Returns the register value. + #[inline(always)] + pub fn to_code_reg(&self) -> u32 { + self.0 + } + + /// Set the 4 bit code content for a CodeReg + #[inline(always)] + pub fn set_code(&mut self, code: FlexCanMailboxCSCode) -> Self { + Self::new(self.0 & ((code as u32) << Self::CODE_SHIFT)) + } + + /// Get the 4 bit code content for a CodeReg. + /// + /// This may return the variant [`FlexCanMailboxCSCode::Unknown`], + /// which must be handled appropriately for the intended usage. + #[inline(always)] + pub fn code(&self) -> FlexCanMailboxCSCode { + FlexCanMailboxCSCode::from(((self.0 & Self::CODE_MASK) >> Self::CODE_SHIFT) as u8) + } + + /// The frame timestamp, captured from the peripheral's free-running timer. + /// + /// The timestamp represents the time at which a TX or RX frame appears on + /// the bus. The counter increments at the CAN bus baud rate. The counter pauses + /// when the driver is frozen, or the processor is in debug mode. + /// + /// A frame's timestamp resets to zero when either + /// + /// - the counter wraps around at the 16 bit boundary. + /// - a message is received in mailbox 0. This happens if time sync is enabled, + /// and if the message passed the mailbox filter. + /// + /// Users may also override the timestamp to any specific value. + pub fn timestamp(&self) -> u16 { + ((self.0 & Self::TIMESTAMP_MASK) >> Self::TIMESTAMP_SHIFT) as u16 + } + + pub fn dlc(&self) -> u8 { + ((self.0 & Self::DLC_MASK) >> Self::DLC_SHIFT) as u8 + } + + /// Returns `true` if the code reg is an extended identifier. + #[inline(always)] + fn is_extended(self) -> bool { + self.0 & Self::IDE_MASK != 0 + } + + /// Returns `true` if the code reg is a standard identifier. + #[inline(always)] + fn is_standard(self) -> bool { + !self.is_extended() + } + + /// Sets the remote transmission (RTR) flag. This marks the identifier as + /// being part of a remote frame. + #[must_use = "returns a new IdReg without modifying `self`"] + pub fn with_rtr(self, rtr: bool) -> Self { + if rtr { + Self::new(self.0 | Self::RTR_MASK) + } else { + Self::new(self.0 & !Self::RTR_MASK) + } + } + + /// Returns `true` if the identifer is part of a remote frame (RTR bit set). + #[inline(always)] + pub fn rtr(self) -> bool { + self.0 & Self::RTR_MASK != 0 + } +} + +/// Identifier of a CAN message. +/// +/// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a +/// extendended identifier (29bit , Range: 0..0x1FFFFFFF). +/// +/// The `Ord` trait can be used to determine the frame’s priority this ID +/// belongs to. +/// Lower identifier values have a higher priority. Additionally standard frames +/// have a higher priority than extended frames and data frames have a higher +/// priority than remote frames. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct IdReg(u32); + +impl IdReg { + const STANDARD_SHIFT: u32 = 18; + const EXTENDED_SHIFT: u32 = 0; + + /// Creates a new `IdReg` + #[inline(always)] + fn new(id: u32) -> Self { + Self(id) + } + + /// Returns the register value. + #[inline(always)] + pub fn to_id_reg(&self) -> u32 { + self.0 + } + + /// Creates a new standard identifier (11bit, Range: 0..=0x7FF) + /// + /// Panics for IDs outside the allowed range. + #[inline(always)] + pub fn new_standard(id: StandardId) -> Self { + assert!(id.as_raw() <= StandardId::MAX.as_raw()); + let id = u32::from(id.as_raw()) << Self::STANDARD_SHIFT; + Self::new(id) + } + + /// Creates a new extendended identifier (29bit , Range: 0..=0x1FFFFFFF). + /// + /// Panics for IDs outside the allowed range. + #[inline(always)] + pub fn new_extended(id: ExtendedId) -> Self { + assert!(id.as_raw() <= ExtendedId::MAX.as_raw()); + let id = u32::from(id.as_raw()) << Self::EXTENDED_SHIFT; + Self::new(id) + } + + #[inline(always)] + pub fn to_standard(&self) -> Id { + Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::STANDARD_SHIFT) }) + } + + #[inline(always)] + pub fn to_extended(&self) -> Id { + Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::EXTENDED_SHIFT) as u16) }) + } +} + +/// Payload of a CAN data frame. +/// +/// Contains 0 to 8 Bytes of data. +/// +/// `Data` implements `From<[u8; N]>` for all `N` up to 8, which provides a convenient lossless +/// conversion from fixed-length arrays. +#[derive(Debug, Copy, Clone)] +pub struct Data { + pub(crate) len: u8, + pub(crate) bytes: [u8; 8], +} + +impl Data { + /// Creates a data payload from a raw byte slice. + /// + /// Returns `None` if `data` contains more than 8 Bytes (which is the maximum). + /// + /// `Data` can also be constructed from fixed-length arrays up to length 8 via `From`/`Into`. + pub fn new(data: &[u8]) -> Option { + if data.len() > 8 { + return None; + } + + let mut bytes = [0; 8]; + bytes[..data.len()].copy_from_slice(data); + + Some(Self { + len: data.len() as u8, + bytes, + }) + } + + /// Creates an empty data payload containing 0 bytes. + #[inline(always)] + pub const fn empty() -> Self { + Self { + len: 0, + bytes: [0; 8], + } + } +} + +impl Deref for Data { + type Target = [u8]; + + #[inline(always)] + fn deref(&self) -> &[u8] { + &self.bytes[..usize::from(self.len)] + } +} + +impl DerefMut for Data { + #[inline(always)] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.bytes[..usize::from(self.len)] + } +} + +impl AsRef<[u8]> for Data { + #[inline(always)] + fn as_ref(&self) -> &[u8] { + self.deref() + } +} + +impl AsMut<[u8]> for Data { + #[inline(always)] + fn as_mut(&mut self) -> &mut [u8] { + self.deref_mut() + } +} + +impl PartialEq for Data { + fn eq(&self, other: &Self) -> bool { + self.as_ref() == other.as_ref() + } +} + +impl Eq for Data {} + +macro_rules! data_from_array { + ( $($len:literal),+ ) => { + $( + impl From<[u8; $len]> for Data { + #[inline(always)] + fn from(arr: [u8; $len]) -> Self { + let mut bytes = [0; 8]; + bytes[..$len].copy_from_slice(&arr); + Self { + len: $len, + bytes, + } + } + } + )+ + }; +} + +data_from_array!(0, 1, 2, 3, 4, 5, 6, 7, 8); diff --git a/imxrt-hal/src/can/frame/tests.rs b/imxrt-hal/src/can/frame/tests.rs new file mode 100644 index 00000000..8e84bed2 --- /dev/null +++ b/imxrt-hal/src/can/frame/tests.rs @@ -0,0 +1,75 @@ +use crate::can::{frame::Frame, ExtendedId, StandardId}; + +#[test] +fn data_greater_remote() { + let id = StandardId::new(0).unwrap(); + + let data_frame = Frame::new_data(id, []); + let remote_frame = Frame::new_remote(id, 0); + assert!(data_frame.is_data_frame()); + assert!(remote_frame.is_remote_frame()); +} + +#[test] +fn lower_ids_win_arbitration() { + let zero = Frame::new_data(StandardId::new(0).unwrap(), []); + let one = Frame::new_data(StandardId::new(1).unwrap(), []); + assert!(zero.is_standard()); + assert!(!zero.is_extended()); + assert!(one.is_standard()); + assert!(!one.is_extended()); + + // Standard IDs have priority over Extended IDs if the Base ID matches. + let ext_one = Frame::new_data( + ExtendedId::new(0b00000000001_000000000000000000).unwrap(), + [], + ); + assert!(!ext_one.is_standard()); + assert!(ext_one.is_extended()); + + // Ext. ID with Base ID 0 has priority over Standard ID 1. + let ext_zero = Frame::new_data( + ExtendedId::new(0b00000000000_100000000000000000).unwrap(), + [], + ); + assert!(!ext_zero.is_standard()); + assert!(ext_zero.is_extended()); +} + +#[test] +fn highest_standard_higher_prio_than_highest_ext() { + let std = Frame::new_data(StandardId::MAX, []); + let ext = Frame::new_data(ExtendedId::MAX, []); + + assert!(std.is_standard()); + assert!(!std.is_extended()); + assert!(!ext.is_standard()); + assert!(ext.is_extended()); +} + +#[test] +fn data_neq_remote() { + let id = StandardId::new(0).unwrap(); + + let data_frame = Frame::new_data(id, []); + let remote_frame = Frame::new_remote(id, 0); + + assert_ne!(data_frame, remote_frame); +} + +#[test] +fn remote_eq_remote_ignores_data() { + let mut remote1 = Frame::new_remote(StandardId::MAX, 7); + let mut remote2 = Frame::new_remote(StandardId::MAX, 7); + + remote1.data.bytes = [0xAA; 8]; + remote2.data.bytes = [0x55; 8]; + + assert_eq!(remote1, remote2); +} + +#[test] +fn max_len() { + Frame::new_data(StandardId::MAX, [0; 8]); + Frame::new_remote(StandardId::MAX, 8); +} diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs new file mode 100644 index 00000000..c2d2c10b --- /dev/null +++ b/imxrt-hal/src/can/mod.rs @@ -0,0 +1,994 @@ +//! Flexible Controller Area Network (FlexCAN) +//! +//! The FlexCAN module provides a CanBus peripheral that implements +//! the `embedded_hal::can` traits. +//!//! +//! # Example +//! +//! ```no_run +//! use imxrt_hal; +//! +//! let mut peripherals = imxrt_hal::Peripherals::take().unwrap(); +//! +//! let (can1_builder, _) = peripherals.can.clock( +//! &mut peripherals.ccm.handle, +//! imxrt_hal::ccm::can::ClockSelect::OSC, +//! imxrt_hal::ccm::can::PrescalarSelect::DIVIDE_1, +//! ); +//! +//! let mut can1 = can1_builder.build( +//! peripherals.iomuxc.ad_b1.p08, +//! peripherals.iomuxc.ad_b1.p09 +//! ); +//! +//! can1.set_baud_rate(1_000_000); +//! can1.set_max_mailbox(16); +//! can1.enable_fifo(); +//! can1.set_fifo_interrupt(true); +//! can1.set_fifo_accept_all(); +//! +//! // create a `Frame` with `StandardID` 0x00 +//! // and `Data` [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07] +//! let id = imxrt_hal::can::Id::from(imxrt_hal::can::StandardId::new(0x00).unwrap()); +//! let data: [u8; 8] = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]; +//! let frame = imxrt_hal::can::Frame::new_data(id, data); +//! +//! // read all available mailboxes for any available frames +//! can1.read_mailboxes(); +//! +//! // transmit the frame +//! can1.transmit(&frame); +//! ``` + +mod embedded_hal; +pub mod filter; +mod frame; + +pub use self::embedded_hal::{ExtendedId, Id, StandardId}; +pub use frame::{CodeReg, Data, FlexCanMailboxCSCode, Frame, IdReg}; +use ral::{modify_reg, read_reg, write_reg}; + +use crate::ccm; +use crate::iomuxc::can; +use crate::iomuxc::consts::{Unsigned, U1, U2}; +use crate::ral; + +use core::marker::PhantomData; + +/// Error from the FlexCan peripheral. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + /// Error indicating that no received data is + /// available in the mailboxes + NoRxData, + /// Error indicating that no transmit mailboxes + /// are available + NoTxMailbox, + /// A wrapper around the [`embedded_hal::ErrorKind`] + /// enum + EmbeddedHal(embedded_hal::ErrorKind), +} + +/// Unclocked Can modules +/// +/// The `Unclocked` struct represents the unconfigured Can peripherals. +/// Once clocked, you'll have the ability to build Can peripherals from the +/// compatible processor pins. +pub struct Unclocked { + pub(crate) can1: ral::can::Instance, + pub(crate) can2: ral::can::Instance, +} + +impl Unclocked { + /// Enable clocks to all Can modules, returning a builder for the two Can modules. + pub fn clock( + self, + handle: &mut ccm::Handle, + clock_select: ccm::can::ClockSelect, + divider: ccm::can::PrescalarSelect, + ) -> (Builder, Builder) { + let (ccm, _) = handle.raw(); + + // First, disable the clocks for Can1 and Can2 + ral::modify_reg!( + ral::ccm, + ccm, + CCGR0, + CG7: 0b00, + CG8: 0b00, + CG9: 0b00, + CG10: 0b00 + ); + + let clk_sel = match clock_select { + ccm::can::ClockSelect::OSC => ral::ccm::CSCMR2::CAN_CLK_SEL::RW::CAN_CLK_SEL_1, + }; + + // Select clock, and commit prescalar + ral::modify_reg!( + ral::ccm, + ccm, + CSCMR2, + CAN_CLK_PODF: ral::ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_1, + CAN_CLK_SEL: clk_sel + ); + + // Enable the clocks for Can1 and Can2 + ral::modify_reg!( + ral::ccm, + ccm, + CCGR0, + CG7: 0b11, + CG8: 0b11, + CG9: 0b11, + CG10: 0b11 + ); + + let source_clock = ccm::Frequency::from(clock_select) / ccm::Divider::from(divider); + ( + Builder::new(source_clock, self.can1), + Builder::new(source_clock, self.can2), + ) + } +} + +/// A Can builder that can build a Can peripheral +pub struct Builder { + _module: PhantomData, + reg: ral::can::Instance, + source_clock: ccm::Frequency, +} + +impl Builder +where + M: Unsigned, +{ + fn new(source_clock: ccm::Frequency, reg: ral::can::Instance) -> Self { + Builder { + _module: PhantomData, + reg, + source_clock, + } + } + + /// Builds a Can peripheral. + pub fn build(self, mut tx: TX, mut rx: RX) -> CAN + where + TX: can::Pin, + RX: can::Pin, + { + crate::iomuxc::can::prepare(&mut tx); + crate::iomuxc::can::prepare(&mut rx); + + CAN::new(self.source_clock, self.reg) + } +} + +/// A Can master +/// +pub struct CAN { + reg: ral::can::Instance, + _module: PhantomData, + source_clock: ccm::Frequency, + _mailbox_reader_index: u8, +} + +#[derive(Debug)] +pub struct MailboxData { + pub frame: Frame, + pub mailbox_number: u8, +} + +impl CAN +where + M: Unsigned, +{ + pub const NUMBER_FIFO_RX_MAILBOXES: u32 = 6; + + fn new(source_clock: ccm::Frequency, reg: ral::can::Instance) -> Self { + let mut can = CAN { + reg, + _module: PhantomData, + source_clock, + _mailbox_reader_index: 0, + }; + can.begin(); + can + } + + pub fn print_registers(&self) { + log::info!("MCR: {:X}", ral::read_reg!(ral::can, self.reg, MCR)); + log::info!("CTRL1: {:X}", ral::read_reg!(ral::can, self.reg, CTRL1)); + log::info!("CTRL2: {:X}", ral::read_reg!(ral::can, self.reg, CTRL2)); + log::info!( + "RXMGMASK: {:X}", + ral::read_reg!(ral::can, self.reg, RXMGMASK) + ); + log::info!( + "RXFGMASK: {:X}", + ral::read_reg!(ral::can, self.reg, RXFGMASK) + ); + + let max_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 8; + + for mailbox_number in 0..max_fifo_filters { + let mailbox_idflt_tab_addr = + self.mailbox_number_to_idflt_tab_address(mailbox_number as u8); + let idflt_tab = + unsafe { core::ptr::read_volatile((mailbox_idflt_tab_addr) as *mut u32) }; + log::info!( + "IDFLT_TAB[{}, {:X}]: {:X}", + mailbox_number, + mailbox_idflt_tab_addr, + idflt_tab + ); + } + } + + fn while_frozen R, R>(&mut self, mut act: F) -> R { + let frz_flag_negate = ral::read_reg!(ral::can, self.reg, MCR, FRZACK == FRZACK_0); + self.enter_freeze_mode(); + let res = act(self); + if frz_flag_negate { + self.exit_freeze_mode(); + } + res + } + + pub fn begin(&mut self) { + ral::modify_reg!(ral::can, self.reg, MCR, MDIS: MDIS_0); + + self.enter_freeze_mode(); + + ral::modify_reg!(ral::can, self.reg, CTRL1, LOM: LOM_1); + ral::modify_reg!(ral::can, self.reg, MCR, FRZ: FRZ_1); + while ral::read_reg!(ral::can, self.reg, MCR, LPMACK == LPMACK_1) {} + + self.soft_reset(); + + while ral::read_reg!(ral::can, self.reg, MCR, FRZACK != FRZACK_1) {} + + ral::modify_reg!( + ral::can, + self.reg, + MCR, + SRXDIS: SRXDIS_1, + IRMQ: IRMQ_1, + AEN: AEN_1, + LPRIOEN: LPRIOEN_1, + SLFWAK: SLFWAK_1, + WAKSRC: WAKSRC_1, + WRNEN: WRNEN_1, + WAKMSK: WAKMSK_1 + ); + + ral::modify_reg!(ral::can, self.reg, MCR, |reg| reg & !0x8800); + + ral::modify_reg!( + ral::can, + self.reg, + CTRL2, + RRS: RRS_1, + EACEN: EACEN_1, + MRP: MRP_1 + ); + + self.disable_fifo(); + self.exit_freeze_mode(); + } + + pub fn instance_number(&self) -> usize { + M::USIZE + } + + pub fn is_can1(&self) -> bool { + M::USIZE == 1 + } + + pub fn is_can2(&self) -> bool { + M::USIZE == 2 + } + + pub(crate) fn base_address(&self) -> u32 { + let addr: *const ral::can::RegisterBlock = &*self.reg; + addr as u32 + } + + pub fn free(self) -> ral::can::Instance { + self.reg + } + + fn soft_reset(&mut self) { + ral::modify_reg!(ral::can, self.reg, MCR, SOFTRST: SOFTRST_1); + while ral::read_reg!(ral::can, self.reg, MCR, SOFTRST == SOFTRST_1) {} + } + + fn enter_freeze_mode(&mut self) { + ral::modify_reg!(ral::can, self.reg, MCR, FRZ: FRZ_1); + ral::modify_reg!(ral::can, self.reg, MCR, HALT: HALT_1); + while ral::read_reg!(ral::can, self.reg, MCR, FRZACK != FRZACK_1) {} + } + + fn exit_freeze_mode(&mut self) { + ral::modify_reg!(ral::can, self.reg, MCR, HALT: HALT_0); + while ral::read_reg!(ral::can, self.reg, MCR, FRZACK != FRZACK_0) {} + } + + pub fn set_mrp(&mut self, mrp: bool) { + ral::modify_reg!(ral::can, self.reg, CTRL2, MRP: mrp as u32) + } + + pub fn set_rrs(&mut self, rrs: bool) { + ral::modify_reg!(ral::can, self.reg, CTRL2, RRS: rrs as u32) + } + + pub fn get_clock(&self) -> u32 { + self.source_clock.0 + } + + fn result_to_bit_table(&self, result: u8) -> Option<[u32; 3]> { + match result { + 0 => Some([0, 0, 1]), + 1 => Some([1, 0, 1]), + 2 => Some([1, 1, 1]), + 3 => Some([2, 1, 1]), + 4 => Some([2, 2, 1]), + 5 => Some([2, 3, 1]), + 6 => Some([2, 3, 2]), + 7 => Some([2, 4, 2]), + 8 => Some([2, 5, 2]), + 9 => Some([2, 5, 3]), + 10 => Some([2, 6, 3]), + 11 => Some([2, 7, 3]), + 12 => Some([2, 7, 4]), + 13 => Some([3, 7, 4]), + 14 => Some([3, 7, 5]), + 15 => Some([4, 7, 5]), + 16 => Some([4, 7, 6]), + 17 => Some([5, 7, 6]), + 18 => Some([6, 7, 6]), + 19 => Some([6, 7, 7]), + 20 => Some([7, 7, 7]), + _ => None, + } + } + + pub fn set_baud_rate(&mut self, baud: u32) { + let clock_freq = self.get_clock(); + + let mut divisor = 0; + let mut best_divisor: u32 = 0; + let mut result: u32 = clock_freq / baud / (divisor + 1); + let mut error: i16 = (baud - (clock_freq / (result * (divisor + 1)))) as i16; + let mut best_error = error; + + self.while_frozen(|this| { + while result > 5 { + divisor += 1; + result = clock_freq / baud / (divisor + 1); + if result <= 25 { + error = (baud - (clock_freq / (result * (divisor + 1)))) as i16; + if error < 0 { + error = -1 * error; + } + if error < best_error { + best_error = error; + best_divisor = divisor; + } + if (error == best_error) && (result > 11) && (result < 19) { + best_error = error; + best_divisor = divisor; + } + } + } + + divisor = best_divisor; + result = clock_freq / baud / (divisor + 1); + + if !(result < 5) || (result > 25) || (best_error > 300) { + result -= 5; + + match this.result_to_bit_table(result as u8) { + Some(t) => { + modify_reg!( + ral::can, + this.reg, + CTRL1, + PROPSEG: t[0], + RJW: 1, + PSEG1: t[1], + PSEG2: t[2], + ERRMSK: ERRMSK_1, + LOM: LOM_0, + PRESDIV: divisor) + } + _ => {} + } + } + }); + } + + pub fn set_max_mailbox(&mut self, last: u8) { + let last = last.clamp(1, 64) - 1; + self.while_frozen(|this| { + let fifo_cleared = this.fifo_enabled(); + this.disable_fifo(); + this.write_iflag(this.read_iflag()); + ral::modify_reg!(ral::can, this.reg, MCR, MAXMB: last as u32); + if fifo_cleared { + this.set_fifo(true); + } + }); + } + + fn get_max_mailbox(&self) -> u8 { + ral::read_reg!(ral::can, self.reg, MCR, MAXMB) as u8 + } + + fn write_iflag(&mut self, value: u64) { + write_reg!(ral::can, self.reg, IFLAG1, value as u32); + write_reg!(ral::can, self.reg, IFLAG2, (value >> 32) as u32); + } + + fn write_iflag_bit(&mut self, mailbox_number: u8) { + if mailbox_number < 32 { + modify_reg!(ral::can, self.reg, IFLAG1, |reg| reg + | 1_u32 << mailbox_number) + } else { + modify_reg!(ral::can, self.reg, IFLAG2, |reg| reg + | 1_u32 << (mailbox_number - 32)) + } + } + + fn write_imask_bit(&mut self, mailbox_number: u8, value: bool) { + if mailbox_number < 32 { + modify_reg!(ral::can, self.reg, IMASK1, |reg| reg + | (value as u32) << mailbox_number) + } else { + modify_reg!(ral::can, self.reg, IMASK2, |reg| reg + | (value as u32) << (mailbox_number - 32)) + } + } + + fn read_iflag(&self) -> u64 { + (ral::read_reg!(ral::can, self.reg, IFLAG2) as u64) << 32 + | ral::read_reg!(ral::can, self.reg, IFLAG1) as u64 + } + + fn read_imask(&self) -> u64 { + (ral::read_reg!(ral::can, self.reg, IMASK2) as u64) << 32 + | ral::read_reg!(ral::can, self.reg, IMASK1) as u64 + } + + fn write_imask(&mut self, value: u64) { + write_reg!(ral::can, self.reg, IMASK1, value as u32); + write_reg!(ral::can, self.reg, IMASK2, (value >> 32) as u32); + } + + pub fn set_fifo(&mut self, enabled: bool) { + self.while_frozen(|this| { + modify_reg!(ral::can, this.reg, MCR, RFEN: RFEN_0); + this.write_imask(0); + + for i in 0..this.get_max_mailbox() { + this.write_mailbox(i, Some(0), Some(0), Some([0x00; 8])); + this.write_mailbox_rximr(i, Some(0x00)); + } + + write_reg!(ral::can, this.reg, RXMGMASK, 0); + write_reg!(ral::can, this.reg, RXFGMASK, 0); + + this.write_iflag(this.read_iflag()); + + let max_mailbox = this.get_max_mailbox(); + if enabled { + modify_reg!(ral::can, this.reg, MCR, RFEN: RFEN_1); + while ral::read_reg!(ral::can, this.reg, MCR, RFEN != RFEN_1) {} + for i in this.mailbox_offset()..max_mailbox { + this.write_mailbox( + i, + Some(FlexCanMailboxCSCode::TxInactive.to_code_reg()), + None, + None, + ); + this.enable_mailbox_interrupt(i, true); + } + } else { + for i in 0..max_mailbox { + if i < max_mailbox / 2 { + let code = FlexCanMailboxCSCode::RxEmpty.to_code_reg() | 0x00400000 | { + if i < max_mailbox / 4 { + 0 + } else { + 0x00200000 + } + }; + this.write_mailbox(i, Some(code), None, None); + let eacen = read_reg!(ral::can, this.reg, CTRL2, EACEN == EACEN_1); + let rximr = 0_32 | { + if eacen { + 1_u32 << 30 + } else { + 0 + } + }; + this.write_mailbox_rximr(i, Some(rximr)); + } else { + // set inactive + this.write_mailbox( + i, + Some(FlexCanMailboxCSCode::TxInactive.to_code_reg()), + Some(0), + Some([0x00; 8]), + ); + this.enable_mailbox_interrupt(i, true); + } + } + } + }) + } + + pub fn enable_fifo(&mut self) { + self.set_fifo(true); + } + + pub fn disable_fifo(&mut self) { + self.set_fifo(false); + } + + pub fn set_fifo_reject_all(&mut self) { + self.set_fifo_filter_mask(filter::FlexCanFlten::RejectAll) + } + + pub fn set_fifo_accept_all(&mut self) { + self.set_fifo_filter_mask(filter::FlexCanFlten::AcceptAll) + } + + fn set_fifo_filter_mask(&mut self, flten: filter::FlexCanFlten) { + if !self.fifo_enabled() { + return; + } + let max_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 8; + self.while_frozen(|this| match flten { + filter::FlexCanFlten::AcceptAll => { + let offset = this.mailbox_offset(); + for i in 0..max_fifo_filters { + this.write_mailbox_idflt_tab(i as u8, Some(0x00)); + if i < offset.clamp(0, 32) as u32 { + this.write_mailbox_rximr(i as u8, Some(0x00)); + } + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x00); + } + filter::FlexCanFlten::RejectAll => match read_reg!(ral::can, this.reg, MCR, IDAM) { + ral::can::MCR::IDAM::RW::IDAM_0 => { + let offset = this.mailbox_offset(); + for i in 0..max_fifo_filters { + this.write_mailbox_idflt_tab(i as u8, Some(0xFFFFFFFF)); + if i < offset.clamp(0, 32) as u32 { + this.write_mailbox_rximr(i as u8, Some(0x3FFFFFFF)); + } + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x3FFFFFFF); + } + ral::can::MCR::IDAM::RW::IDAM_1 => { + let offset = this.mailbox_offset(); + for i in 0..max_fifo_filters { + this.write_mailbox_idflt_tab(i as u8, Some(0xFFFFFFFF)); + if i < offset.clamp(0, 32) as u32 { + this.write_mailbox_rximr(i as u8, Some(0x7FFF7FFF)); + } + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x7FFF7FFF); + } + ral::can::MCR::IDAM::RW::IDAM_2 => { + // not implemented + } + ral::can::MCR::IDAM::RW::IDAM_3 => { + // not implemented + } + _ => {} + }, + }) + } + + pub fn set_fifo_filter(&mut self, filter: filter::FlexCanFilter) { + if !self.fifo_enabled() { + return; + } + let filter::FlexCanFilter { + filter_id, + id, + ide, + remote, + } = filter; + let max_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 8; + if filter_id as u32 >= max_fifo_filters { + return; + } + self.while_frozen(|this| match read_reg!(ral::can, this.reg, MCR, IDAM) { + ral::can::MCR::IDAM::RW::IDAM_0 => { + let mask: u32 = if ide != filter::FlexCanIde::Ext { + ((((id) ^ (id)) ^ 0x7FF) << 19) | 0xC0000001 + } else { + ((((id) ^ (id)) ^ 0x1FFFFFFF) << 1) | 0xC0000001 + }; + let mut filter: u32 = (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 30); + filter |= if remote == filter::FlexCanIde::Rtr { + 1 + } else { + 0 + } << 31; + filter |= if ide == filter::FlexCanIde::Ext { + (id & 0x1FFFFFFF) << 1 + } else { + ((id & 0x000007FF) << 18) << 1 + }; + this.write_mailbox_idflt_tab(filter_id, Some(filter)); + let offset = this.mailbox_offset(); + if filter_id < offset.clamp(0, 32) as u8 { + this.write_mailbox_rximr(filter_id, Some(mask)); + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x3FFFFFFF); + } + ral::can::MCR::IDAM::RW::IDAM_1 => { + let mut mask: u32 = if ide != filter::FlexCanIde::Ext { + (((id) ^ (id)) ^ 0x7FF) << 19 + } else { + (((id) ^ (id)) ^ 0x1FFFFFFF) << 16 + } | if remote == filter::FlexCanIde::Rtr { + 1 << 31 + } else { + 0 + }; + mask |= if ide != filter::FlexCanIde::Ext { + (((id) ^ (id)) ^ 0x7FF) << 3 + } else { + (((id) ^ (id)) ^ 0x1FFFFFFF) << 0 + } | if remote == filter::FlexCanIde::Rtr { + 1 << 15 + } else { + 0 + } & 0xFFFF; + mask |= (1 << 30) | (1 << 14); + let filter: u32 = (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 30) + | (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 14) + | (if remote == filter::FlexCanIde::Rtr { + 1 + } else { + 0 + } << 31) + | (if remote == filter::FlexCanIde::Rtr { + 1 + } else { + 0 + } << 15) + | (if ide == filter::FlexCanIde::Ext { + (id >> (29 - 14)) << 16 + } else { + (id & 0x7FF) << 19 + }) + | (if ide == filter::FlexCanIde::Ext { + (id >> (29 - 14)) << 0 + } else { + (id & 0x7FF) << 3 + }); + this.write_mailbox_idflt_tab(filter_id, Some(filter)); + let offset = this.mailbox_offset(); + if filter_id < offset.clamp(0, 32) as u8 { + this.write_mailbox_rximr(filter_id, Some(mask)); + } + write_reg!(ral::can, this.reg, RXFGMASK, 0x7FFF7FFF); + } + ral::can::MCR::IDAM::RW::IDAM_2 => { + // not implemented + } + ral::can::MCR::IDAM::RW::IDAM_3 => { + // not implemented + } + _ => {} + }) + } + + pub fn set_fifo_interrupt(&mut self, enabled: bool) { + /* FIFO must be enabled first */ + if !self.fifo_enabled() { + return; + }; + /* FIFO interrupts already enabled */ + const FLEXCAN_IMASK1_BUF5M: u32 = 0x00000020; + if (ral::read_reg!(ral::can, self.reg, IMASK1, BUFLM) & FLEXCAN_IMASK1_BUF5M) != 0 { + return; + }; + /* disable FIFO interrupt flags */ + modify_reg!(ral::can, self.reg, IMASK1, |reg| reg & !0xFF); + /* enable FIFO interrupt */ + if enabled { + const FLEXCAN_IMASK1_BUF5M: u32 = 0x00000020; + modify_reg!(ral::can, self.reg, IMASK1, BUFLM: FLEXCAN_IMASK1_BUF5M); + } + } + + fn fifo_enabled(&self) -> bool { + ral::read_reg!(ral::can, self.reg, MCR, RFEN == RFEN_1) + } + + fn mailbox_offset(&self) -> u8 { + if self.fifo_enabled() { + let max_mailbox = self.get_max_mailbox() as u32; + let num_rx_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 2; + let remaining_mailboxes = max_mailbox - 6_u32 - num_rx_fifo_filters; + /* return offset MB position after FIFO area */ + if max_mailbox < max_mailbox - remaining_mailboxes { + max_mailbox as u8 + } else { + (max_mailbox - remaining_mailboxes) as u8 + } + } else { + /* return offset 0 since FIFO is disabled */ + 0 + } + } + + #[inline(always)] + fn mailbox_number_to_address(&self, mailbox_number: u8) -> u32 { + self.base_address() + 0x80_u32 + (mailbox_number as u32 * 0x10_u32) + } + + #[inline(always)] + fn mailbox_number_to_rximr_address(&self, mailbox_number: u8) -> u32 { + self.base_address() + 0x880_u32 + (mailbox_number as u32 * 0x4_u32) + } + + #[inline(always)] + fn mailbox_number_to_idflt_tab_address(&self, mailbox_number: u8) -> u32 { + self.base_address() + 0xE0_u32 + (mailbox_number as u32 * 0x4_u32) + } + + #[inline(always)] + fn read_mailbox_code(&mut self, mailbox_number: u8) -> u32 { + let mailbox_addr = self.mailbox_number_to_address(mailbox_number); + unsafe { core::ptr::read_volatile(mailbox_addr as *const u32) } + } + + #[inline(always)] + fn read_mailbox(&mut self, mailbox_number: u8) -> Option { + if (self.read_imask() & (1_u64 << mailbox_number)) != 0 { + return None; + } + + let code = self.read_mailbox_code(mailbox_number); + let c = FlexCanMailboxCSCode::from_code_reg(code); + + let mailbox_addr = self.mailbox_number_to_address(mailbox_number); + + match c { + // return None from a transmit mailbox + c if c.is_tx_mailbox() => None, + // full or overrun + c if (c == FlexCanMailboxCSCode::RxFull) | (c == FlexCanMailboxCSCode::RxOverrun) => { + let id = + unsafe { core::ptr::read_volatile((mailbox_addr + 0x4_u32) as *const u32) }; + let data0 = + unsafe { core::ptr::read_volatile((mailbox_addr + 0x8_u32) as *const u32) }; + let data1 = + unsafe { core::ptr::read_volatile((mailbox_addr + 0xC_u32) as *const u32) }; + + let mut data: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + for i in 0..4 { + data[3 - i] = (data0 >> (8 * i)) as u8; + } + for i in 0..4 { + data[7 - i] = (data1 >> (8 * i)) as u8; + } + + self.write_mailbox( + mailbox_number, + Some(FlexCanMailboxCSCode::RxEmpty.to_code_reg()), + None, + None, + ); + read_reg!(ral::can, self.reg, TIMER); + self.write_iflag_bit(mailbox_number); + + let frame = Frame::new_from_raw(code, id, data); + + Some(MailboxData { + frame, + mailbox_number, + }) + } + _ => None, + } + } + + /// Write the registers of a mailbox. + #[inline(always)] + fn write_mailbox( + &self, + mailbox_number: u8, + code: Option, + id: Option, + data: Option<[u8; 8]>, + ) { + let mailbox_addr = self.mailbox_number_to_address(mailbox_number); + if let Some(code) = code { + unsafe { core::ptr::write_volatile((mailbox_addr + 0_u32) as *mut u32, code) }; + } + if let Some(id) = id { + unsafe { core::ptr::write_volatile((mailbox_addr + 0x4_u32) as *mut u32, id) }; + } + if let Some(data) = data { + let word0 = u32::from_be_bytes([data[0], data[1], data[2], data[3]]); + let word1 = u32::from_be_bytes([data[4], data[5], data[6], data[7]]); + unsafe { core::ptr::write_volatile((mailbox_addr + 0x8_u32) as *mut u32, word0) }; + unsafe { core::ptr::write_volatile((mailbox_addr + 0xC_u32) as *mut u32, word1) }; + } + } + + /// Write data to an available TX Mailbox. + /// + /// This will accept both standard and extended data and remote frames with any ID. + /// + /// In order to transmit a Can frame, the CPU must prepare a Message Buffer for + /// transmission by executing the procedure found here. + /// + /// 1. Check if the respective interruption bit is set and clear it. + /// + /// 2. If the MB is active (transmission pending), write the ABORT code (0b1001) to the + /// CODE field of the Control and Status word to request an abortion of the + /// transmission. Wait for the corresponding IFLAG to be asserted by polling the IFLAG + /// register or by the interrupt request if enabled by the respective IMASK. Then read + /// back the CODE field to check if the transmission was aborted or transmitted (see + /// Transmission Abort Mechanism). If backwards compatibility is desired (MCR[AEN] + /// bit negated), just write the INACTIVE code (0b1000) to the CODE field to inactivate + /// the MB but then the pending frame may be transmitted without notification (see + /// Message Buffer Inactivation). + /// + /// 3. Write the ID word. + /// + /// 4. Write the data bytes. + /// + /// 5. Write the DLC, Control and Code fields of the Control and Status word to activate + /// the MB. + /// + /// Once the MB is activated, it will participate into the arbitration process and eventually be + /// transmitted according to its priority. + #[inline(always)] + fn write_tx_mailbox(&mut self, mailbox_number: u8, frame: &Frame) { + // Check if the respective interruption bit is set and clear it. + self.write_iflag_bit(mailbox_number); + self.write_mailbox( + mailbox_number, + Some(FlexCanMailboxCSCode::TxInactive.to_code_reg()), + None, + None, + ); + self.write_mailbox( + mailbox_number, + None, + Some(frame.id.to_id_reg()), + Some(frame.data.bytes), + ); + self.write_mailbox(mailbox_number, Some(frame.to_tx_once_code()), None, None); + } + + fn write_mailbox_rximr(&self, mailbox_number: u8, rximr: Option) { + let mailbox_rximr_addr = self.mailbox_number_to_rximr_address(mailbox_number); + if let Some(rximr) = rximr { + unsafe { core::ptr::write_volatile((mailbox_rximr_addr) as *mut u32, rximr) }; + } + } + + fn write_mailbox_idflt_tab(&self, mailbox_number: u8, idflt_tab: Option) { + let mailbox_idflt_tab_addr = self.mailbox_number_to_idflt_tab_address(mailbox_number); + if let Some(idflt_tab) = idflt_tab { + unsafe { core::ptr::write_volatile((mailbox_idflt_tab_addr) as *mut u32, idflt_tab) }; + } + } + + fn enable_mailbox_interrupt(&mut self, mailbox_number: u8, enabled: bool) { + /* mailbox not available */ + if mailbox_number < self.mailbox_offset() { + return; + } + if enabled { + /* enable mailbox interrupt */ + self.write_imask_bit(mailbox_number, true); + return; + } else { + match self.read_mailbox(mailbox_number) { + Some(d) => { + if (d.frame.code.to_code_reg() & 0x0F000000) >> 3 != 0 { + /* transmit interrupt keeper */ + self.write_imask_bit(mailbox_number, true); + return; + } + } + _ => {} + } + } + /* disable mailbox interrupt */ + self.write_imask_bit(mailbox_number, false); + return; + } + + pub fn read_mailboxes(&mut self) -> Option { + let mut iflag: u64; + let mut cycle_limit: u8 = 3; + let offset = self.mailbox_offset(); + let max_mailbox = self.get_max_mailbox(); + while self._mailbox_reader_index <= max_mailbox { + iflag = self.read_iflag(); + if iflag != 0 && (self._mailbox_reader_index >= (64 - iflag.leading_zeros() as u8)) { + /* break from MSB's if unset, add 1 to prevent undefined behaviour in clz for 0 check */ + self._mailbox_reader_index = self.mailbox_offset(); + cycle_limit -= 1; + if cycle_limit == 0 { + return None; + } + } + if self.fifo_enabled() { + /* FIFO is enabled, get only remaining RX (if any) */ + if self._mailbox_reader_index < offset { + /* go back to position end of fifo+filter region */ + self._mailbox_reader_index = offset; + } + } + if self._mailbox_reader_index >= max_mailbox { + self._mailbox_reader_index = self.mailbox_offset(); + cycle_limit -= 1; + if cycle_limit == 0 { + return None; + } + } + if (self.read_imask() & (1_u64 << self._mailbox_reader_index)) != 0 { + self._mailbox_reader_index += 1; + continue; /* don't read interrupt enabled mailboxes */ + } + match self.read_mailbox(self._mailbox_reader_index) { + Some(mailbox_data) => { + return Some(mailbox_data); + } + _ => {} + } + self._mailbox_reader_index += 1; + } + None + } + + #[inline(always)] + pub fn handle_interrupt(&mut self) -> Option { + let imask = self.read_imask(); + let iflag = self.read_iflag(); + + /* if DMA is disabled, ONLY THEN can you handle FIFO in ISR */ + if self.fifo_enabled() & (imask & 0x00000020 != 0) & (iflag & 0x00000020 != 0) { + if let Some(mailbox_data) = self.read_mailbox(0) { + self.write_iflag_bit(5); + if iflag & 0x00000040 != 0 { + self.write_iflag_bit(6); + } + if iflag & 0x00000080 != 0 { + self.write_iflag_bit(7); + } + return Some(mailbox_data); + } + } + + None + } + + #[inline(always)] + pub fn transmit(&mut self, frame: &Frame) -> nb::Result<(), Error> { + for i in self.mailbox_offset()..self.get_max_mailbox() { + if let FlexCanMailboxCSCode::TxInactive = + FlexCanMailboxCSCode::from_code_reg(self.read_mailbox_code(i)) + { + self.write_tx_mailbox(i, frame); + return Ok(()); + } + } + Err(nb::Error::Other(Error::NoTxMailbox)) + } +} diff --git a/imxrt-hal/src/ccm.rs b/imxrt-hal/src/ccm.rs index e75f07c3..d1727eea 100644 --- a/imxrt-hal/src/ccm.rs +++ b/imxrt-hal/src/ccm.rs @@ -965,3 +965,162 @@ pub mod spi { } } } + +/// Timing configurations for FlexCAN peripherals +pub mod can { + use super::{ral::ccm, Divider, Frequency, OSCILLATOR_FREQUENCY}; + + #[derive(Clone, Copy)] + #[non_exhaustive] // Not all variants added + pub enum ClockSelect { + OSC, + } + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + #[allow(non_camel_case_types)] // Easier mapping if the names are consistent + #[repr(u32)] + pub enum PrescalarSelect { + /// 0b000000: Divide by 1 + DIVIDE_1 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_1, + /// 0b000001: Divide by 2 + DIVIDE_2 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_2, + /// 0b000010: Divide by 3 + DIVIDE_3 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_3, + /// 0b000011: Divide by 4 + DIVIDE_4 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_4, + /// 0b000100: Divide by 5 + DIVIDE_5 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_5, + /// 0b000101: Divide by 6 + DIVIDE_6 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_6, + /// 0b000110: Divide by 7 + DIVIDE_7 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_7, + /// 0b000111: Divide by 8 + DIVIDE_8 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_8, + /// 0b001000: Divide by 9 + DIVIDE_9 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_9, + /// 0b001001: Divide by 10 + DIVIDE_10 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_10, + /// 0b001010: Divide by 11 + DIVIDE_11 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_11, + /// 0b001011: Divide by 12 + DIVIDE_12 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_12, + /// 0b001100: Divide by 13 + DIVIDE_13 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_13, + /// 0b001101: Divide by 14 + DIVIDE_14 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_14, + /// 0b001110: Divide by 15 + DIVIDE_15 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_15, + /// 0b001111: Divide by 16 + DIVIDE_16 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_16, + /// 0b010000: Divide by 17 + DIVIDE_17 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_17, + /// 0b010001: Divide by 18 + DIVIDE_18 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_18, + /// 0b010010: Divide by 19 + DIVIDE_19 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_19, + /// 0b010011: Divide by 20 + DIVIDE_20 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_20, + /// 0b010100: Divide by 21 + DIVIDE_21 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_21, + /// 0b010101: Divide by 22 + DIVIDE_22 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_22, + /// 0b010110: Divide by 23 + DIVIDE_23 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_23, + /// 0b010111: Divide by 24 + DIVIDE_24 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_24, + /// 0b011000: Divide by 25 + DIVIDE_25 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_25, + /// 0b011001: Divide by 26 + DIVIDE_26 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_26, + /// 0b011010: Divide by 27 + DIVIDE_27 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_27, + /// 0b011011: Divide by 28 + DIVIDE_28 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_28, + /// 0b011100: Divide by 29 + DIVIDE_29 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_29, + /// 0b011101: Divide by 30 + DIVIDE_30 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_30, + /// 0b011110: Divide by 31 + DIVIDE_31 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_31, + /// 0b011111: Divide by 32 + DIVIDE_32 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_32, + /// 0b100000: Divide by 33 + DIVIDE_33 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_33, + /// 0b100001: Divide by 34 + DIVIDE_34 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_34, + /// 0b100010: Divide by 35 + DIVIDE_35 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_35, + /// 0b100011: Divide by 36 + DIVIDE_36 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_36, + /// 0b100100: Divide by 37 + DIVIDE_37 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_37, + /// 0b100101: Divide by 38 + DIVIDE_38 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_38, + /// 0b100110: Divide by 39 + DIVIDE_39 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_39, + /// 0b100111: Divide by 40 + DIVIDE_40 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_40, + /// 0b101000: Divide by 41 + DIVIDE_41 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_41, + /// 0b101001: Divide by 42 + DIVIDE_42 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_42, + /// 0b101010: Divide by 43 + DIVIDE_43 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_43, + /// 0b101011: Divide by 44 + DIVIDE_44 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_44, + /// 0b101100: Divide by 45 + DIVIDE_45 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_45, + /// 0b101101: Divide by 46 + DIVIDE_46 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_46, + /// 0b101110: Divide by 47 + DIVIDE_47 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_47, + /// 0b101111: Divide by 48 + DIVIDE_48 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_48, + /// 0b110000: Divide by 49 + DIVIDE_49 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_49, + /// 0b110001: Divide by 50 + DIVIDE_50 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_50, + /// 0b110010: Divide by 51 + DIVIDE_51 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_51, + /// 0b110011: Divide by 52 + DIVIDE_52 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_52, + /// 0b110100: Divide by 53 + DIVIDE_53 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_53, + /// 0b110101: Divide by 54 + DIVIDE_54 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_54, + /// 0b110110: Divide by 55 + DIVIDE_55 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_55, + /// 0b110111: Divide by 56 + DIVIDE_56 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_56, + /// 0b111000: Divide by 57 + DIVIDE_57 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_57, + /// 0b111001: Divide by 58 + DIVIDE_58 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_58, + /// 0b111010: Divide by 59 + DIVIDE_59 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_59, + /// 0b111011: Divide by 60 + DIVIDE_60 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_60, + /// 0b111100: Divide by 61 + DIVIDE_61 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_61, + /// 0b111101: Divide by 62 + DIVIDE_62 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_62, + /// 0b111110: Divide by 63 + DIVIDE_63 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_63, + /// 0b111111: Divide by 64 + DIVIDE_64 = ccm::CSCMR2::CAN_CLK_PODF::RW::DIVIDE_64, + } + + impl From for Frequency { + fn from(clock_select: ClockSelect) -> Self { + match clock_select { + ClockSelect::OSC => OSCILLATOR_FREQUENCY, + } + } + } + + impl From for Divider { + fn from(prescalar_select: PrescalarSelect) -> Self { + Divider((prescalar_select as u32) + 1) + } + } +} diff --git a/imxrt-hal/src/lib.rs b/imxrt-hal/src/lib.rs index 9a00548d..ad8ceed5 100644 --- a/imxrt-hal/src/lib.rs +++ b/imxrt-hal/src/lib.rs @@ -28,6 +28,7 @@ pub mod iomuxc { } pub mod adc; +pub mod can; pub mod ccm; pub mod dma; pub mod gpio; @@ -55,6 +56,7 @@ pub mod dcdc { pub struct Peripherals { pub adc: adc::Unclocked, pub iomuxc: iomuxc::Pads, + pub can: can::Unclocked, pub ccm: ccm::CCM, pub pit: pit::UnclockedPIT, pub dcdc: dcdc::DCDC, @@ -89,6 +91,10 @@ impl Peripherals { adc2: ral::adc::ADC2::steal(), }, iomuxc: iomuxc::pads(ral::iomuxc::IOMUXC::steal()), + can: can::Unclocked { + can1: ral::can::CAN1::steal(), + can2: ral::can::CAN2::steal(), + }, ccm: ccm::CCM::new(ral::ccm::CCM::steal(), ral::ccm_analog::CCM_ANALOG::steal()), pit: pit::UnclockedPIT::new(ral::pit::PIT::steal()), dcdc: dcdc::DCDC(ral::dcdc::DCDC::steal()), @@ -139,6 +145,10 @@ impl Peripherals { adc2: ral::adc::ADC2::take()?, }, iomuxc: iomuxc::pads(ral::iomuxc::IOMUXC::take()?), + can: can::Unclocked { + can1: ral::can::CAN1::take()?, + can2: ral::can::CAN2::take()?, + }, ccm: ccm::CCM::new(ral::ccm::CCM::take()?, ral::ccm_analog::CCM_ANALOG::take()?), pit: pit::UnclockedPIT::new(ral::pit::PIT::take()?), dcdc: dcdc::DCDC(ral::dcdc::DCDC::take()?), From 3350979576fd1ceb000e7372a593667561195062 Mon Sep 17 00:00:00 2001 From: dstric-aqueduct <96876452+dstric-aqueduct@users.noreply.github.com> Date: Sun, 1 Jan 2023 06:54:25 -0500 Subject: [PATCH 09/13] fix clippy warnings --- imxrt-hal/src/can/frame.rs | 4 +- imxrt-hal/src/can/mod.rs | 86 ++++++++++++++++---------------------- 2 files changed, 39 insertions(+), 51 deletions(-) diff --git a/imxrt-hal/src/can/frame.rs b/imxrt-hal/src/can/frame.rs index 3b88af2c..d2ae9c7b 100644 --- a/imxrt-hal/src/can/frame.rs +++ b/imxrt-hal/src/can/frame.rs @@ -124,7 +124,7 @@ impl PartialEq for Frame { } } -#[derive(Debug, PartialEq, Clone, Copy)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] #[repr(u8)] pub enum FlexCanMailboxCSCode { RxInactive = 0b0000, @@ -339,7 +339,7 @@ impl IdReg { #[inline(always)] pub fn new_extended(id: ExtendedId) -> Self { assert!(id.as_raw() <= ExtendedId::MAX.as_raw()); - let id = u32::from(id.as_raw()) << Self::EXTENDED_SHIFT; + let id = id.as_raw() << Self::EXTENDED_SHIFT; Self::new(id) } diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index c2d2c10b..c12cc946 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -369,7 +369,7 @@ where if result <= 25 { error = (baud - (clock_freq / (result * (divisor + 1)))) as i16; if error < 0 { - error = -1 * error; + error *= -1; } if error < best_error { best_error = error; @@ -385,26 +385,23 @@ where divisor = best_divisor; result = clock_freq / baud / (divisor + 1); - if !(result < 5) || (result > 25) || (best_error > 300) { - result -= 5; - - match this.result_to_bit_table(result as u8) { - Some(t) => { - modify_reg!( - ral::can, - this.reg, - CTRL1, - PROPSEG: t[0], - RJW: 1, - PSEG1: t[1], - PSEG2: t[2], - ERRMSK: ERRMSK_1, - LOM: LOM_0, - PRESDIV: divisor) - } - _ => {} - } - } + if (result < 5) || (result > 25) || (best_error > 300) { return }; + + result -= 5; + + if let Some(t) = this.result_to_bit_table(result as u8) { + modify_reg!( + ral::can, + this.reg, + CTRL1, + PROPSEG: t[0], + RJW: 1, + PSEG1: t[1], + PSEG2: t[2], + ERRMSK: ERRMSK_1, + LOM: LOM_0, + PRESDIV: divisor) + }; }); } @@ -505,7 +502,7 @@ where }; this.write_mailbox(i, Some(code), None, None); let eacen = read_reg!(ral::can, this.reg, CTRL2, EACEN == EACEN_1); - let rximr = 0_32 | { + let rximr = { if eacen { 1_u32 << 30 } else { @@ -609,9 +606,9 @@ where self.while_frozen(|this| match read_reg!(ral::can, this.reg, MCR, IDAM) { ral::can::MCR::IDAM::RW::IDAM_0 => { let mask: u32 = if ide != filter::FlexCanIde::Ext { - ((((id) ^ (id)) ^ 0x7FF) << 19) | 0xC0000001 + ((id ^ 0x7FF) << 19) | 0xC0000001 } else { - ((((id) ^ (id)) ^ 0x1FFFFFFF) << 1) | 0xC0000001 + ((id ^ 0x1FFFFFFF) << 1) | 0xC0000001 }; let mut filter: u32 = (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 30); filter |= if remote == filter::FlexCanIde::Rtr { @@ -633,18 +630,18 @@ where } ral::can::MCR::IDAM::RW::IDAM_1 => { let mut mask: u32 = if ide != filter::FlexCanIde::Ext { - (((id) ^ (id)) ^ 0x7FF) << 19 + (id ^ 0x7FF) << 19 } else { - (((id) ^ (id)) ^ 0x1FFFFFFF) << 16 + (id ^ 0x1FFFFFFF) << 16 } | if remote == filter::FlexCanIde::Rtr { 1 << 31 } else { 0 }; mask |= if ide != filter::FlexCanIde::Ext { - (((id) ^ (id)) ^ 0x7FF) << 3 + (id ^ 0x7FF) << 3 } else { - (((id) ^ (id)) ^ 0x1FFFFFFF) << 0 + id ^ 0x1FFFFFFF } | if remote == filter::FlexCanIde::Rtr { 1 << 15 } else { @@ -669,7 +666,7 @@ where (id & 0x7FF) << 19 }) | (if ide == filter::FlexCanIde::Ext { - (id >> (29 - 14)) << 0 + id >> (29 - 14) } else { (id & 0x7FF) << 3 }); @@ -717,12 +714,12 @@ where if self.fifo_enabled() { let max_mailbox = self.get_max_mailbox() as u32; let num_rx_fifo_filters = (read_reg!(ral::can, self.reg, CTRL2, RFFN) + 1) * 2; - let remaining_mailboxes = max_mailbox - 6_u32 - num_rx_fifo_filters; + let remaining_mailboxes = (max_mailbox - 6_u32 - num_rx_fifo_filters) as i32; /* return offset MB position after FIFO area */ - if max_mailbox < max_mailbox - remaining_mailboxes { + if remaining_mailboxes > max_mailbox as i32 { max_mailbox as u8 } else { - (max_mailbox - remaining_mailboxes) as u8 + (max_mailbox as i32 - remaining_mailboxes) as u8 } } else { /* return offset 0 since FIFO is disabled */ @@ -813,7 +810,7 @@ where ) { let mailbox_addr = self.mailbox_number_to_address(mailbox_number); if let Some(code) = code { - unsafe { core::ptr::write_volatile((mailbox_addr + 0_u32) as *mut u32, code) }; + unsafe { core::ptr::write_volatile((mailbox_addr) as *mut u32, code) }; } if let Some(id) = id { unsafe { core::ptr::write_volatile((mailbox_addr + 0x4_u32) as *mut u32, id) }; @@ -896,21 +893,15 @@ where /* enable mailbox interrupt */ self.write_imask_bit(mailbox_number, true); return; - } else { - match self.read_mailbox(mailbox_number) { - Some(d) => { - if (d.frame.code.to_code_reg() & 0x0F000000) >> 3 != 0 { - /* transmit interrupt keeper */ - self.write_imask_bit(mailbox_number, true); - return; - } - } - _ => {} + } else if let Some(d) = self.read_mailbox(mailbox_number) { + if (d.frame.code.to_code_reg() & 0x0F000000) >> 3 != 0 { + /* transmit interrupt keeper */ + self.write_imask_bit(mailbox_number, true); + return; } } /* disable mailbox interrupt */ self.write_imask_bit(mailbox_number, false); - return; } pub fn read_mailboxes(&mut self) -> Option { @@ -946,11 +937,8 @@ where self._mailbox_reader_index += 1; continue; /* don't read interrupt enabled mailboxes */ } - match self.read_mailbox(self._mailbox_reader_index) { - Some(mailbox_data) => { - return Some(mailbox_data); - } - _ => {} + if let Some(mailbox_data) = self.read_mailbox(self._mailbox_reader_index) { + return Some(mailbox_data); } self._mailbox_reader_index += 1; } From 2ca7328154a324ec739f1e8e5db0e0f02075cf8f Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Sun, 1 Jan 2023 15:01:29 -0500 Subject: [PATCH 10/13] Fix formatting We need properly-formmated code to pass CI. --- imxrt-hal/src/can/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index c12cc946..fd2e5594 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -385,7 +385,9 @@ where divisor = best_divisor; result = clock_freq / baud / (divisor + 1); - if (result < 5) || (result > 25) || (best_error > 300) { return }; + if (result < 5) || (result > 25) || (best_error > 300) { + return; + }; result -= 5; @@ -401,7 +403,7 @@ where ERRMSK: ERRMSK_1, LOM: LOM_0, PRESDIV: divisor) - }; + }; }); } From 18ca5f92f34202cd7ae058f12e0086eb4f9cbe8f Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Sun, 1 Jan 2023 15:07:28 -0500 Subject: [PATCH 11/13] Run 'cargo clippy --fix' to resolve some warnings It can't automatically fix all the issues, but the remaining fixes are straightforward. --- imxrt-hal/src/can/frame/tests.rs | 4 ++-- imxrt-hal/src/can/mod.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/imxrt-hal/src/can/frame/tests.rs b/imxrt-hal/src/can/frame/tests.rs index 8e84bed2..08e43a53 100644 --- a/imxrt-hal/src/can/frame/tests.rs +++ b/imxrt-hal/src/can/frame/tests.rs @@ -21,7 +21,7 @@ fn lower_ids_win_arbitration() { // Standard IDs have priority over Extended IDs if the Base ID matches. let ext_one = Frame::new_data( - ExtendedId::new(0b00000000001_000000000000000000).unwrap(), + ExtendedId::new(0b0_0000_0000_0100_0000_0000_0000_0000).unwrap(), [], ); assert!(!ext_one.is_standard()); @@ -29,7 +29,7 @@ fn lower_ids_win_arbitration() { // Ext. ID with Base ID 0 has priority over Standard ID 1. let ext_zero = Frame::new_data( - ExtendedId::new(0b00000000000_100000000000000000).unwrap(), + ExtendedId::new(0b0_0000_0000_0010_0000_0000_0000_0000).unwrap(), [], ); assert!(!ext_zero.is_standard()); diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index fd2e5594..31debfb4 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -385,7 +385,7 @@ where divisor = best_divisor; result = clock_freq / baud / (divisor + 1); - if (result < 5) || (result > 25) || (best_error > 300) { + if !(5..=25).contains(&result) || (best_error > 300) { return; }; @@ -625,7 +625,7 @@ where }; this.write_mailbox_idflt_tab(filter_id, Some(filter)); let offset = this.mailbox_offset(); - if filter_id < offset.clamp(0, 32) as u8 { + if filter_id < offset.clamp(0, 32) { this.write_mailbox_rximr(filter_id, Some(mask)); } write_reg!(ral::can, this.reg, RXFGMASK, 0x3FFFFFFF); @@ -674,7 +674,7 @@ where }); this.write_mailbox_idflt_tab(filter_id, Some(filter)); let offset = this.mailbox_offset(); - if filter_id < offset.clamp(0, 32) as u8 { + if filter_id < offset.clamp(0, 32) { this.write_mailbox_rximr(filter_id, Some(mask)); } write_reg!(ral::can, this.reg, RXFGMASK, 0x7FFF7FFF); From 5ad3713f14fbc3ac5e9479989115be9dc7757461 Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Sun, 1 Jan 2023 15:23:03 -0500 Subject: [PATCH 12/13] Simplify conditional expressions with u32::from Use the fact that a bool is either a zero or a one to simplify the filter value. Warned by clippy, and implemented by hand. --- imxrt-hal/src/can/mod.rs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index 31debfb4..6a1453ab 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -612,12 +612,8 @@ where } else { ((id ^ 0x1FFFFFFF) << 1) | 0xC0000001 }; - let mut filter: u32 = (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 30); - filter |= if remote == filter::FlexCanIde::Rtr { - 1 - } else { - 0 - } << 31; + let mut filter: u32 = u32::from(ide == filter::FlexCanIde::Ext) << 30; + filter |= u32::from(remote == filter::FlexCanIde::Rtr) << 31; filter |= if ide == filter::FlexCanIde::Ext { (id & 0x1FFFFFFF) << 1 } else { @@ -650,18 +646,10 @@ where 0 } & 0xFFFF; mask |= (1 << 30) | (1 << 14); - let filter: u32 = (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 30) - | (if ide == filter::FlexCanIde::Ext { 1 } else { 0 } << 14) - | (if remote == filter::FlexCanIde::Rtr { - 1 - } else { - 0 - } << 31) - | (if remote == filter::FlexCanIde::Rtr { - 1 - } else { - 0 - } << 15) + let filter: u32 = u32::from(ide == filter::FlexCanIde::Ext) << 30 + | u32::from(ide == filter::FlexCanIde::Ext) << 14 + | u32::from(remote == filter::FlexCanIde::Rtr) << 31 + | u32::from(remote == filter::FlexCanIde::Rtr) << 15 | (if ide == filter::FlexCanIde::Ext { (id >> (29 - 14)) << 16 } else { From 87c27f5af2820f10a1e6a128dac2850ce809fc28 Mon Sep 17 00:00:00 2001 From: djstrickland <96876452+dstric-aqueduct@users.noreply.github.com> Date: Wed, 11 Jan 2023 09:01:37 -0500 Subject: [PATCH 13/13] update to address overflow in baud rate calc See PR Issue: https://github.com/imxrt-rs/imxrt-hal/pull/122#discussion_r1060083938 --- imxrt-hal/src/can/mod.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/imxrt-hal/src/can/mod.rs b/imxrt-hal/src/can/mod.rs index 6a1453ab..5e64b64d 100644 --- a/imxrt-hal/src/can/mod.rs +++ b/imxrt-hal/src/can/mod.rs @@ -354,23 +354,31 @@ where } pub fn set_baud_rate(&mut self, baud: u32) { + + fn calc_result(baud: u32, clock_freq: u32, divisor: u32) -> u32 { + clock_freq / baud / (divisor + 1) + } + + fn calc_error(baud: u32, clock_freq: u32, result: u32, divisor: u32) -> u32 { + baud.abs_diff(clock_freq / (result * (divisor + 1))) + } + let clock_freq = self.get_clock(); let mut divisor = 0; let mut best_divisor: u32 = 0; - let mut result: u32 = clock_freq / baud / (divisor + 1); - let mut error: i16 = (baud - (clock_freq / (result * (divisor + 1)))) as i16; + + let mut result: u32 = calc_result(baud, clock_freq, divisor); + + let mut error = calc_error(baud, clock_freq, result, divisor); let mut best_error = error; self.while_frozen(|this| { while result > 5 { divisor += 1; - result = clock_freq / baud / (divisor + 1); + result = calc_result(baud, clock_freq, divisor); if result <= 25 { - error = (baud - (clock_freq / (result * (divisor + 1)))) as i16; - if error < 0 { - error *= -1; - } + error = calc_error(baud, clock_freq, result, divisor); if error < best_error { best_error = error; best_divisor = divisor;