diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ba22ba0b6..d3626610e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,6 @@ critical-section = "1.1" #stm32-metapac = { version = "15" } stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c8b32ecae7d70cea2705095c4fc6bd5f59d238d5" } vcell = "0.1.3" -bxcan = "0.7.0" nb = "1.0.0" stm32-fmc = "0.3.0" cfg-if = "1.0.0" @@ -84,6 +83,7 @@ document-features = "0.2.7" static_assertions = { version = "1.1" } volatile-register = { version = "0.2.1" } +bitflags = "2.4.2" @@ -104,7 +104,7 @@ default = ["rt"] rt = ["stm32-metapac/rt"] ## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async/defmt-03", "embassy-usb-driver/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async/defmt-03", "embassy-usb-driver/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] exti = [] low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] diff --git a/embassy-stm32/src/can/bx/filter.rs b/embassy-stm32/src/can/bx/filter.rs new file mode 100644 index 000000000..51766aa31 --- /dev/null +++ b/embassy-stm32/src/can/bx/filter.rs @@ -0,0 +1,475 @@ +//! Filter bank API. + +use core::marker::PhantomData; + +use crate::can::bx::{ExtendedId, Fifo, FilterOwner, Id, Instance, MasterInstance, StandardId}; + +const F32_RTR: u32 = 0b010; // set the RTR bit to match remote frames +const F32_IDE: u32 = 0b100; // set the IDE bit to match extended identifiers +const F16_RTR: u16 = 0b10000; +const F16_IDE: u16 = 0b01000; + +/// A 16-bit filter list entry. +/// +/// This can match data and remote frames using standard IDs. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ListEntry16(u16); + +/// A 32-bit filter list entry. +/// +/// This can match data and remote frames using extended or standard IDs. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ListEntry32(u32); + +/// A 16-bit identifier mask. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Mask16 { + id: u16, + mask: u16, +} + +/// A 32-bit identifier mask. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Mask32 { + id: u32, + mask: u32, +} + +impl ListEntry16 { + /// Creates a filter list entry that accepts data frames with the given standard ID. + /// + /// This entry will *not* accept remote frames with the same ID. + pub fn data_frames_with_id(id: StandardId) -> Self { + Self(id.as_raw() << 5) + } + + /// Creates a filter list entry that accepts remote frames with the given standard ID. + pub fn remote_frames_with_id(id: StandardId) -> Self { + Self(id.as_raw() << 5 | F16_RTR) + } +} + +impl ListEntry32 { + /// Creates a filter list entry that accepts data frames with the given ID. + /// + /// This entry will *not* accept remote frames with the same ID. + /// + /// The filter will only accept *either* standard *or* extended frames, depending on `id`. + pub fn data_frames_with_id(id: impl Into) -> Self { + match id.into() { + Id::Standard(id) => Self(u32::from(id.as_raw()) << 21), + Id::Extended(id) => Self(id.as_raw() << 3 | F32_IDE), + } + } + + /// Creates a filter list entry that accepts remote frames with the given ID. + pub fn remote_frames_with_id(id: impl Into) -> Self { + match id.into() { + Id::Standard(id) => Self(u32::from(id.as_raw()) << 21 | F32_RTR), + Id::Extended(id) => Self(id.as_raw() << 3 | F32_IDE | F32_RTR), + } + } +} + +impl Mask16 { + /// Creates a 16-bit identifier mask that accepts all frames. + /// + /// This will accept both standard and extended data and remote frames with any ID. + pub fn accept_all() -> Self { + Self { id: 0, mask: 0 } + } + + /// Creates a 16-bit identifier mask that accepts all frames with the given standard + /// ID and mask combination. + /// + /// Filter logic: `frame_accepted = (incoming_id & mask) == (id & mask)` + /// + /// A mask of all all ones (`0x7FF`) matches an exact ID, a mask of 0 matches all IDs. + /// + /// Both data and remote frames with `id` will be accepted. Any extended frames will be + /// rejected. + pub fn frames_with_std_id(id: StandardId, mask: StandardId) -> Self { + Self { + id: id.as_raw() << 5, + mask: mask.as_raw() << 5 | F16_IDE, // also require IDE = 0 + } + } + + /// Make the filter accept data frames only. + pub fn data_frames_only(&mut self) -> &mut Self { + self.id &= !F16_RTR; // RTR = 0 + self.mask |= F16_RTR; + self + } + + /// Make the filter accept remote frames only. + pub fn remote_frames_only(&mut self) -> &mut Self { + self.id |= F16_RTR; // RTR = 1 + self.mask |= F16_RTR; + self + } +} + +impl Mask32 { + /// Creates a 32-bit identifier mask that accepts all frames. + /// + /// This will accept both standard and extended data and remote frames with any ID. + pub fn accept_all() -> Self { + Self { id: 0, mask: 0 } + } + + /// Creates a 32-bit identifier mask that accepts all frames with the given extended + /// ID and mask combination. + /// + /// Filter logic: `frame_accepted = (incoming_id & mask) == (id & mask)` + /// + /// A mask of all all ones (`0x1FFF_FFFF`) matches an exact ID, a mask of 0 matches all IDs. + /// + /// Both data and remote frames with `id` will be accepted. Standard frames will be rejected. + pub fn frames_with_ext_id(id: ExtendedId, mask: ExtendedId) -> Self { + Self { + id: id.as_raw() << 3 | F32_IDE, + mask: mask.as_raw() << 3 | F32_IDE, // also require IDE = 1 + } + } + + /// Creates a 32-bit identifier mask that accepts all frames with the given standard + /// ID and mask combination. + /// + /// Filter logic: `frame_accepted = (incoming_id & mask) == (id & mask)` + /// + /// A mask of all all ones (`0x7FF`) matches the exact ID, a mask of 0 matches all IDs. + /// + /// Both data and remote frames with `id` will be accepted. Extended frames will be rejected. + pub fn frames_with_std_id(id: StandardId, mask: StandardId) -> Self { + Self { + id: u32::from(id.as_raw()) << 21, + mask: u32::from(mask.as_raw()) << 21 | F32_IDE, // also require IDE = 0 + } + } + + /// Make the filter accept data frames only. + pub fn data_frames_only(&mut self) -> &mut Self { + self.id &= !F32_RTR; // RTR = 0 + self.mask |= F32_RTR; + self + } + + /// Make the filter accept remote frames only. + pub fn remote_frames_only(&mut self) -> &mut Self { + self.id |= F32_RTR; // RTR = 1 + self.mask |= F32_RTR; + self + } +} + +/// The configuration of a filter bank. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum BankConfig { + /// Specify up to 4 exact standard CAN ID's. + List16([ListEntry16; 4]), + /// Specify up to 2 exact standard or extended CAN ID's. + List32([ListEntry32; 2]), + /// Specify up to 2 standard ID's with masks. + Mask16([Mask16; 2]), + /// Specify a single extended ID with mask. + Mask32(Mask32), +} + +impl From<[ListEntry16; 4]> for BankConfig { + #[inline] + fn from(entries: [ListEntry16; 4]) -> Self { + Self::List16(entries) + } +} + +impl From<[ListEntry32; 2]> for BankConfig { + #[inline] + fn from(entries: [ListEntry32; 2]) -> Self { + Self::List32(entries) + } +} + +impl From<[Mask16; 2]> for BankConfig { + #[inline] + fn from(entries: [Mask16; 2]) -> Self { + Self::Mask16(entries) + } +} + +impl From for BankConfig { + #[inline] + fn from(filter: Mask32) -> Self { + Self::Mask32(filter) + } +} + +/// Interface to the filter banks of a CAN peripheral. +pub struct MasterFilters<'a, I: FilterOwner> { + /// Number of assigned filter banks. + /// + /// On chips with splittable filter banks, this value can be dynamic. + bank_count: u8, + _can: PhantomData<&'a mut I>, + canregs: crate::pac::can::Can, +} + +// NOTE: This type mutably borrows the CAN instance and has unique access to the registers while it +// exists. +impl MasterFilters<'_, I> { + pub(crate) unsafe fn new(canregs: crate::pac::can::Can) -> Self { + // Enable initialization mode. + canregs.fmr().modify(|reg| reg.set_finit(true)); + + // Read the filter split value. + let bank_count = canregs.fmr().read().can2sb(); + + // (Reset value of CAN2SB is 0x0E, 14, which, in devices with 14 filter banks, assigns all + // of them to the master peripheral, and in devices with 28, assigns them 50/50 to + // master/slave instances) + + Self { + bank_count, + _can: PhantomData, + canregs, + } + } + + fn banks_imm(&self) -> FilterBanks { + FilterBanks { + start_idx: 0, + bank_count: self.bank_count, + canregs: self.canregs, + } + } + + /// Returns the number of filter banks currently assigned to this instance. + /// + /// Chips with splittable filter banks may start out with some banks assigned to the master + /// instance and some assigned to the slave instance. + pub fn num_banks(&self) -> u8 { + self.bank_count + } + + /// Disables all enabled filter banks. + /// + /// This causes all incoming frames to be disposed. + pub fn clear(&mut self) -> &mut Self { + self.banks_imm().clear(); + self + } + + /// Disables a filter bank. + /// + /// If `index` is out of bounds, this will panic. + pub fn disable_bank(&mut self, index: u8) -> &mut Self { + self.banks_imm().disable(index); + self + } + + /// Configures a filter bank according to `config` and enables it. + /// + /// Each filter bank is associated with one of the two RX FIFOs, configured by the [`Fifo`] + /// passed to this function. In the event that both FIFOs are configured to accept an incoming + /// frame, the accepting filter bank with the lowest index wins. The FIFO state is ignored, so + /// if the FIFO is full, it will overflow, even if the other FIFO is also configured to accept + /// the frame. + /// + /// # Parameters + /// + /// - `index`: the filter index. + /// - `fifo`: the receive FIFO the filter should pass accepted messages to. + /// - `config`: the filter configuration. + pub fn enable_bank(&mut self, index: u8, fifo: Fifo, config: impl Into) -> &mut Self { + self.banks_imm().enable(index, fifo, config.into()); + self + } +} + +impl MasterFilters<'_, I> { + /// Sets the index at which the filter banks owned by the slave peripheral start. + pub fn set_split(&mut self, split_index: u8) -> &mut Self { + assert!(split_index <= I::NUM_FILTER_BANKS); + self.canregs.fmr().modify(|reg| reg.set_can2sb(split_index)); + self.bank_count = split_index; + self + } + + /// Accesses the filters assigned to the slave peripheral. + pub fn slave_filters(&mut self) -> SlaveFilters<'_, I> { + // NB: This mutably borrows `self`, so it has full access to the filter bank registers. + SlaveFilters { + start_idx: self.bank_count, + bank_count: I::NUM_FILTER_BANKS - self.bank_count, + _can: PhantomData, + canregs: self.canregs, + } + } +} + +impl Drop for MasterFilters<'_, I> { + #[inline] + fn drop(&mut self) { + // Leave initialization mode. + self.canregs.fmr().modify(|regs| regs.set_finit(false)); + } +} + +/// Interface to the filter banks assigned to a slave peripheral. +pub struct SlaveFilters<'a, I: Instance> { + start_idx: u8, + bank_count: u8, + _can: PhantomData<&'a mut I>, + canregs: crate::pac::can::Can, +} + +impl SlaveFilters<'_, I> { + fn banks_imm(&self) -> FilterBanks { + FilterBanks { + start_idx: self.start_idx, + bank_count: self.bank_count, + canregs: self.canregs, + } + } + + /// Returns the number of filter banks currently assigned to this instance. + /// + /// Chips with splittable filter banks may start out with some banks assigned to the master + /// instance and some assigned to the slave instance. + pub fn num_banks(&self) -> u8 { + self.bank_count + } + + /// Disables all enabled filter banks. + /// + /// This causes all incoming frames to be disposed. + pub fn clear(&mut self) -> &mut Self { + self.banks_imm().clear(); + self + } + + /// Disables a filter bank. + /// + /// If `index` is out of bounds, this will panic. + pub fn disable_bank(&mut self, index: u8) -> &mut Self { + self.banks_imm().disable(index); + self + } + + /// Configures a filter bank according to `config` and enables it. + /// + /// # Parameters + /// + /// - `index`: the filter index. + /// - `fifo`: the receive FIFO the filter should pass accepted messages to. + /// - `config`: the filter configuration. + pub fn enable_bank(&mut self, index: u8, fifo: Fifo, config: impl Into) -> &mut Self { + self.banks_imm().enable(index, fifo, config.into()); + self + } +} + +struct FilterBanks { + start_idx: u8, + bank_count: u8, + canregs: crate::pac::can::Can, +} + +impl FilterBanks { + fn clear(&mut self) { + let mask = filter_bitmask(self.start_idx, self.bank_count); + + self.canregs.fa1r().modify(|reg| { + for i in 0..28usize { + if (0x01u32 << i) & mask != 0 { + reg.set_fact(i, false); + } + } + }); + } + + fn assert_bank_index(&self, index: u8) { + assert!((self.start_idx..self.start_idx + self.bank_count).contains(&index)); + } + + fn disable(&mut self, index: u8) { + self.assert_bank_index(index); + self.canregs.fa1r().modify(|reg| reg.set_fact(index as usize, false)) + } + + fn enable(&mut self, index: u8, fifo: Fifo, config: BankConfig) { + self.assert_bank_index(index); + + // Configure mode. + let mode = matches!(config, BankConfig::List16(_) | BankConfig::List32(_)); + self.canregs.fm1r().modify(|reg| reg.set_fbm(index as usize, mode)); + + // Configure scale. + let scale = matches!(config, BankConfig::List32(_) | BankConfig::Mask32(_)); + self.canregs.fs1r().modify(|reg| reg.set_fsc(index as usize, scale)); + + // Configure filter register. + let (fxr1, fxr2); + match config { + BankConfig::List16([a, b, c, d]) => { + fxr1 = (u32::from(b.0) << 16) | u32::from(a.0); + fxr2 = (u32::from(d.0) << 16) | u32::from(c.0); + } + BankConfig::List32([a, b]) => { + fxr1 = a.0; + fxr2 = b.0; + } + BankConfig::Mask16([a, b]) => { + fxr1 = (u32::from(a.mask) << 16) | u32::from(a.id); + fxr2 = (u32::from(b.mask) << 16) | u32::from(b.id); + } + BankConfig::Mask32(a) => { + fxr1 = a.id; + fxr2 = a.mask; + } + }; + let bank = self.canregs.fb(index as usize); + bank.fr1().write(|w| w.0 = fxr1); + bank.fr2().write(|w| w.0 = fxr2); + + // Assign to the right FIFO + self.canregs.ffa1r().modify(|reg| { + reg.set_ffa( + index as usize, + match fifo { + Fifo::Fifo0 => false, + Fifo::Fifo1 => true, + }, + ) + }); + + // Set active. + self.canregs.fa1r().modify(|reg| reg.set_fact(index as usize, true)) + } +} + +/// Computes a bitmask for per-filter-bank registers that only includes filters in the given range. +fn filter_bitmask(start_idx: u8, bank_count: u8) -> u32 { + let count_mask = (1 << bank_count) - 1; // `bank_count` 1-bits + count_mask << start_idx +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_filter_bitmask() { + assert_eq!(filter_bitmask(0, 1), 0x1); + assert_eq!(filter_bitmask(1, 1), 0b10); + assert_eq!(filter_bitmask(0, 4), 0xf); + assert_eq!(filter_bitmask(1, 3), 0xe); + assert_eq!(filter_bitmask(8, 1), 0x100); + assert_eq!(filter_bitmask(8, 4), 0xf00); + } +} diff --git a/embassy-stm32/src/can/bx/frame.rs b/embassy-stm32/src/can/bx/frame.rs new file mode 100644 index 000000000..828f375be --- /dev/null +++ b/embassy-stm32/src/can/bx/frame.rs @@ -0,0 +1,248 @@ +#[cfg(test)] +use core::cmp::Ordering; +use core::ops::{Deref, DerefMut}; + +use crate::can::bx::{Id, IdReg}; + +/// A CAN data or remote frame. +#[derive(Clone, Debug, Eq)] +//#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Frame { + 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 id = match id.into() { + Id::Standard(id) => IdReg::new_standard(id), + Id::Extended(id) => IdReg::new_extended(id), + }; + + Self { 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.id = frame.id.with_rtr(true); + frame + } + + /// Returns true if this frame is an extended frame. + #[inline] + pub fn is_extended(&self) -> bool { + self.id.is_extended() + } + + /// Returns true if this frame is a standard frame. + #[inline] + pub fn is_standard(&self) -> bool { + self.id.is_standard() + } + + /// Returns true if this frame is a remote frame. + #[inline] + pub fn is_remote_frame(&self) -> bool { + self.id.rtr() + } + + /// Returns true if this frame is a data frame. + #[inline] + pub fn is_data_frame(&self) -> bool { + !self.is_remote_frame() + } + + /// Returns the frame identifier. + #[inline] + pub fn id(&self) -> Id { + self.id.to_id() + } + + /// Returns the priority of this frame. + #[inline] + pub fn priority(&self) -> FramePriority { + FramePriority(self.id) + } + + /// 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] + pub fn dlc(&self) -> u8 { + self.data.len() as u8 + } + + /// 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, + } + } +} + +/// Priority of a CAN frame. +/// +/// Returned by [`Frame::priority`]. +/// +/// The priority of a frame is determined by the bits that are part of the *arbitration field*. +/// These consist of the frame identifier bits (including the *IDE* bit, which is 0 for extended +/// frames and 1 for standard frames), as well as the *RTR* bit, which determines whether a frame +/// is a data or remote frame. Lower values of the *arbitration field* have higher priority. +/// +/// This struct wraps the *arbitration field* and implements `PartialOrd` and `Ord` accordingly, +/// ordering higher priorities greater than lower ones. +#[derive(Debug, Copy, Clone)] +pub struct FramePriority(IdReg); + +/// Ordering is based on the Identifier and frame type (data vs. remote) and can be used to sort +/// frames by priority. +impl Ord for FramePriority { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl PartialOrd for FramePriority { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for FramePriority { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == core::cmp::Ordering::Equal + } +} + +impl Eq for FramePriority {} + +/// 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] + pub const fn empty() -> Self { + Self { len: 0, bytes: [0; 8] } + } +} + +impl Deref for Data { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.bytes[..usize::from(self.len)] + } +} + +impl DerefMut for Data { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.bytes[..usize::from(self.len)] + } +} + +impl AsRef<[u8]> for Data { + #[inline] + fn as_ref(&self) -> &[u8] { + self.deref() + } +} + +impl AsMut<[u8]> for Data { + #[inline] + 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 {} + +#[cfg(feature = "defmt")] +impl defmt::Format for Data { + fn format(&self, fmt: defmt::Formatter<'_>) { + self.as_ref().format(fmt) + } +} + +macro_rules! data_from_array { + ( $($len:literal),+ ) => { + $( + impl From<[u8; $len]> for Data { + #[inline] + 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/embassy-stm32/src/can/bx/id.rs b/embassy-stm32/src/can/bx/id.rs new file mode 100644 index 000000000..9fdcd8319 --- /dev/null +++ b/embassy-stm32/src/can/bx/id.rs @@ -0,0 +1,113 @@ +//! CAN Identifiers. + +/// 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 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) + } +} diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs new file mode 100644 index 000000000..f639260a1 --- /dev/null +++ b/embassy-stm32/src/can/bx/mod.rs @@ -0,0 +1,956 @@ +//! Driver for the STM32 bxCAN peripheral. +//! +//! This crate provides a reusable driver for the bxCAN peripheral found in many low- to middle-end +//! STM32 microcontrollers. HALs for compatible chips can reexport this crate and implement its +//! traits to easily expose a featureful CAN driver. +//! +//! # Features +//! +//! - Supports both single- and dual-peripheral configurations (where one bxCAN instance manages the +//! filters of a secondary instance). +//! - Handles standard and extended frames, and data and remote frames. +//! - Support for interrupts emitted by the bxCAN peripheral. +//! - Transmission respects CAN IDs and protects against priority inversion (a lower-priority frame +//! may be dequeued when enqueueing a higher-priority one). +//! - Implements the [`embedded-hal`] traits for interoperability. +//! - Support for both RX FIFOs (as [`Rx0`] and [`Rx1`]). +//! +//! # Limitations +//! +//! - Support for querying error states and handling error interrupts is incomplete. +//! + +// Deny a few warnings in doctests, since rustdoc `allow`s many warnings by default +#![allow(clippy::unnecessary_operation)] // lint is bugged + +//mod embedded_hal; +pub mod filter; +mod frame; +mod id; + +#[allow(clippy::all)] // generated code +use core::cmp::{Ord, Ordering}; +use core::convert::{Infallible, TryInto}; +use core::marker::PhantomData; +use core::mem; + +pub use id::{ExtendedId, Id, StandardId}; + +use crate::can::bx::filter::MasterFilters; +pub use crate::can::bx::frame::{Data, Frame, FramePriority}; + +/// A bxCAN peripheral instance. +/// +/// This trait is meant to be implemented for a HAL-specific type that represent ownership of +/// the CAN peripheral (and any pins required by it, although that is entirely up to the HAL). +/// +/// # Safety +/// +/// It is only safe to implement this trait, when: +/// +/// * The implementing type has ownership of the peripheral, preventing any other accesses to the +/// register block. +/// * `REGISTERS` is a pointer to that peripheral's register block and can be safely accessed for as +/// long as ownership or a borrow of the implementing type is present. +pub unsafe trait Instance {} + +/// A bxCAN instance that owns filter banks. +/// +/// In master-slave-instance setups, only the master instance owns the filter banks, and needs to +/// split some of them off for use by the slave instance. In that case, the master instance should +/// implement [`FilterOwner`] and [`MasterInstance`], while the slave instance should only implement +/// [`Instance`]. +/// +/// In single-instance configurations, the instance owns all filter banks and they can not be split +/// off. In that case, the instance should implement [`Instance`] and [`FilterOwner`]. +/// +/// # Safety +/// +/// This trait must only be implemented if the instance does, in fact, own its associated filter +/// banks, and `NUM_FILTER_BANKS` must be correct. +pub unsafe trait FilterOwner: Instance { + /// The total number of filter banks available to the instance. + /// + /// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet. + const NUM_FILTER_BANKS: u8; +} + +/// A bxCAN master instance that shares filter banks with a slave instance. +/// +/// In master-slave-instance setups, this trait should be implemented for the master instance. +/// +/// # Safety +/// +/// This trait must only be implemented when there is actually an associated slave instance. +pub unsafe trait MasterInstance: FilterOwner {} + +// TODO: what to do with these? +/* +#[derive(Debug, Copy, Clone, Eq, PartialEq, Format)] +pub enum Error { + Stuff, + Form, + Acknowledgement, + BitRecessive, + BitDominant, + Crc, + Software, +}*/ + +/// Error that indicates that an incoming message has been lost due to buffer overrun. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct OverrunError { + _priv: (), +} + +/// 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)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) struct IdReg(u32); + +impl IdReg { + const STANDARD_SHIFT: u32 = 21; + + const EXTENDED_SHIFT: u32 = 3; + + const IDE_MASK: u32 = 0x0000_0004; + + const RTR_MASK: u32 = 0x0000_0002; + + /// Creates a new standard identifier (11bit, Range: 0..0x7FF) + /// + /// Panics for IDs outside the allowed range. + fn new_standard(id: StandardId) -> Self { + Self(u32::from(id.as_raw()) << Self::STANDARD_SHIFT) + } + + /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). + /// + /// Panics for IDs outside the allowed range. + fn new_extended(id: ExtendedId) -> IdReg { + Self(id.as_raw() << Self::EXTENDED_SHIFT | Self::IDE_MASK) + } + + fn from_register(reg: u32) -> IdReg { + Self(reg & 0xFFFF_FFFE) + } + + /// 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`"] + fn with_rtr(self, rtr: bool) -> IdReg { + if rtr { + Self(self.0 | Self::RTR_MASK) + } else { + Self(self.0 & !Self::RTR_MASK) + } + } + + /// Returns the identifier. + fn to_id(self) -> Id { + if self.is_extended() { + Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::EXTENDED_SHIFT) }) + } else { + Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::STANDARD_SHIFT) as u16) }) + } + } + + /// Returns `true` if the identifier is an extended identifier. + fn is_extended(self) -> bool { + self.0 & Self::IDE_MASK != 0 + } + + /// Returns `true` if the identifier is a standard identifier. + fn is_standard(self) -> bool { + !self.is_extended() + } + + /// Returns `true` if the identifer is part of a remote frame (RTR bit set). + fn rtr(self) -> bool { + self.0 & Self::RTR_MASK != 0 + } +} + +/// `IdReg` is ordered by priority. +impl Ord for IdReg { + fn cmp(&self, other: &Self) -> Ordering { + // When the IDs match, data frames have priority over remote frames. + let rtr = self.rtr().cmp(&other.rtr()).reverse(); + + let id_a = self.to_id(); + let id_b = other.to_id(); + match (id_a, id_b) { + (Id::Standard(a), Id::Standard(b)) => { + // Lower IDs have priority over higher IDs. + a.as_raw().cmp(&b.as_raw()).reverse().then(rtr) + } + (Id::Extended(a), Id::Extended(b)) => a.as_raw().cmp(&b.as_raw()).reverse().then(rtr), + (Id::Standard(a), Id::Extended(b)) => { + // Standard frames have priority over extended frames if their Base IDs match. + a.as_raw() + .cmp(&b.standard_id().as_raw()) + .reverse() + .then(Ordering::Greater) + } + (Id::Extended(a), Id::Standard(b)) => { + a.standard_id().as_raw().cmp(&b.as_raw()).reverse().then(Ordering::Less) + } + } + } +} + +impl PartialOrd for IdReg { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Configuration proxy returned by [`Can::modify_config`]. +#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"] +pub struct CanConfig<'a, I: Instance> { + can: &'a mut Can, +} + +impl CanConfig<'_, I> { + /// Configures the bit timings. + /// + /// You can use to calculate the `btr` parameter. Enter + /// parameters as follows: + /// + /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). + /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). + /// - *Sample Point*: Should normally be left at the default value of 87.5%. + /// - *SJW*: Should normally be left at the default value of 1. + /// + /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` + /// parameter to this method. + pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self { + self.can.set_bit_timing(bt); + self + } + + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(self, enabled: bool) -> Self { + self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); + self + } + + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(self, enabled: bool) -> Self { + let mode = match enabled { + false => stm32_metapac::can::vals::Silm::NORMAL, + true => stm32_metapac::can::vals::Silm::SILENT, + }; + self.can.canregs.btr().modify(|reg| reg.set_silm(mode)); + self + } + + /// Enables or disables automatic retransmission of messages. + /// + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(self, enabled: bool) -> Self { + self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled)); + self + } + + /// Leaves initialization mode and enables the peripheral. + /// + /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected + /// on the bus. + /// + /// If you want to finish configuration without enabling the peripheral, you can call + /// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead. + pub fn enable(mut self) { + self.leave_init_mode(); + + match nb::block!(self.can.enable_non_blocking()) { + Ok(()) => {} + Err(void) => match void {}, + } + + // Don't run the destructor. + mem::forget(self); + } + + /// Leaves initialization mode, but keeps the peripheral in sleep mode. + /// + /// Before the [`Can`] instance can be used, you have to enable it by calling + /// [`Can::enable_non_blocking`]. + pub fn leave_disabled(mut self) { + self.leave_init_mode(); + } + + /// Leaves initialization mode, enters sleep mode. + fn leave_init_mode(&mut self) { + self.can.canregs.mcr().modify(|reg| { + reg.set_sleep(true); + reg.set_inrq(false); + }); + loop { + let msr = self.can.canregs.msr().read(); + if msr.slak() && !msr.inak() { + break; + } + } + } +} + +impl Drop for CanConfig<'_, I> { + #[inline] + fn drop(&mut self) { + self.leave_init_mode(); + } +} + +/// Builder returned by [`Can::builder`]. +#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"] +pub struct CanBuilder { + can: Can, +} + +impl CanBuilder { + /// Configures the bit timings. + /// + /// You can use to calculate the `btr` parameter. Enter + /// parameters as follows: + /// + /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). + /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). + /// - *Sample Point*: Should normally be left at the default value of 87.5%. + /// - *SJW*: Should normally be left at the default value of 1. + /// + /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` + /// parameter to this method. + pub fn set_bit_timing(mut self, bt: crate::can::util::NominalBitTiming) -> Self { + self.can.set_bit_timing(bt); + self + } + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(self, enabled: bool) -> Self { + self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); + self + } + + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(self, enabled: bool) -> Self { + let mode = match enabled { + false => stm32_metapac::can::vals::Silm::NORMAL, + true => stm32_metapac::can::vals::Silm::SILENT, + }; + self.can.canregs.btr().modify(|reg| reg.set_silm(mode)); + self + } + + /// Enables or disables automatic retransmission of messages. + /// + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(self, enabled: bool) -> Self { + self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled)); + self + } + + /// Leaves initialization mode and enables the peripheral. + /// + /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected + /// on the bus. + /// + /// If you want to finish configuration without enabling the peripheral, you can call + /// [`CanBuilder::leave_disabled`] instead. + pub fn enable(mut self) -> Can { + self.leave_init_mode(); + + match nb::block!(self.can.enable_non_blocking()) { + Ok(()) => self.can, + Err(void) => match void {}, + } + } + + /// Returns the [`Can`] interface without enabling it. + /// + /// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling + /// it. + /// + /// Before the [`Can`] instance can be used, you have to enable it by calling + /// [`Can::enable_non_blocking`]. + pub fn leave_disabled(mut self) -> Can { + self.leave_init_mode(); + self.can + } + + /// Leaves initialization mode, enters sleep mode. + fn leave_init_mode(&mut self) { + self.can.canregs.mcr().modify(|reg| { + reg.set_sleep(true); + reg.set_inrq(false); + }); + loop { + let msr = self.can.canregs.msr().read(); + if msr.slak() && !msr.inak() { + break; + } + } + } +} + +/// Interface to a bxCAN peripheral. +pub struct Can { + instance: I, + canregs: crate::pac::can::Can, +} + +impl Can +where + I: Instance, +{ + /// Creates a [`CanBuilder`] for constructing a CAN interface. + pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder { + let can_builder = CanBuilder { + can: Can { instance, canregs }, + }; + + canregs.mcr().modify(|reg| { + reg.set_sleep(false); + reg.set_inrq(true); + }); + loop { + let msr = canregs.msr().read(); + if !msr.slak() && msr.inak() { + break; + } + } + + can_builder + } + + fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) { + let prescaler = u16::from(bt.prescaler) & 0x1FF; + let seg1 = u8::from(bt.seg1); + let seg2 = u8::from(bt.seg2) & 0x7F; + let sync_jump_width = u8::from(bt.sync_jump_width) & 0x7F; + self.canregs.btr().modify(|reg| { + reg.set_brp(prescaler - 1); + reg.set_ts(0, seg1 - 1); + reg.set_ts(1, seg2 - 1); + reg.set_sjw(sync_jump_width - 1); + }); + } + + /// Returns a reference to the peripheral instance. + /// + /// This allows accessing HAL-specific data stored in the instance type. + pub fn instance(&mut self) -> &mut I { + &mut self.instance + } + + /// Disables the CAN interface and returns back the raw peripheral it was created from. + /// + /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to + /// enter sleep mode. + pub fn free(self) -> I { + self.canregs.mcr().write(|reg| reg.set_reset(true)); + self.instance + } + + /// Configure bit timings and silent/loop-back mode. + /// + /// Calling this method will enter initialization mode. + pub fn modify_config(&mut self) -> CanConfig<'_, I> { + self.canregs.mcr().modify(|reg| { + reg.set_sleep(false); + reg.set_inrq(true); + }); + loop { + let msr = self.canregs.msr().read(); + if !msr.slak() && msr.inak() { + break; + } + } + + CanConfig { can: self } + } + + /// Configures the automatic wake-up feature. + /// + /// This is turned off by default. + /// + /// When turned on, an incoming frame will cause the peripheral to wake up from sleep and + /// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming + /// frame. + pub fn set_automatic_wakeup(&mut self, enabled: bool) { + self.canregs.mcr().modify(|reg| reg.set_awum(enabled)); + } + + /// Leaves initialization mode and enables the peripheral (non-blocking version). + /// + /// Usually, it is recommended to call [`CanConfig::enable`] instead. This method is only needed + /// if you want non-blocking initialization. + /// + /// If this returns [`WouldBlock`][nb::Error::WouldBlock], the peripheral will enable itself + /// in the background. The peripheral is enabled and ready to use when this method returns + /// successfully. + pub fn enable_non_blocking(&mut self) -> nb::Result<(), Infallible> { + let msr = self.canregs.msr().read(); + if msr.slak() { + self.canregs.mcr().modify(|reg| { + reg.set_abom(true); + reg.set_sleep(false); + }); + Err(nb::Error::WouldBlock) + } else { + Ok(()) + } + } + + /// Puts the peripheral in a sleep mode to save power. + /// + /// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled. + pub fn sleep(&mut self) { + self.canregs.mcr().modify(|reg| { + reg.set_sleep(true); + reg.set_inrq(false); + }); + loop { + let msr = self.canregs.msr().read(); + if msr.slak() && !msr.inak() { + break; + } + } + } + + /// Wakes up from sleep mode. + /// + /// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN + /// frame will cause that interrupt. + pub fn wakeup(&mut self) { + self.canregs.mcr().modify(|reg| { + reg.set_sleep(false); + reg.set_inrq(false); + }); + loop { + let msr = self.canregs.msr().read(); + if !msr.slak() && !msr.inak() { + break; + } + } + } + + /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. + /// + /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). + /// Transmit order is preserved for frames with identical priority. + /// + /// If all transmit mailboxes are full, and `frame` has a higher priority than the + /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is + /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as + /// [`TransmitStatus::dequeued_frame`]. + pub fn transmit(&mut self, frame: &Frame) -> nb::Result { + // Safety: We have a `&mut self` and have unique access to the peripheral. + unsafe { Tx::::conjure(self.canregs).transmit(frame) } + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_transmitter_idle(&self) -> bool { + // Safety: Read-only operation. + unsafe { Tx::::conjure(self.canregs).is_idle() } + } + + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + // Safety: We have a `&mut self` and have unique access to the peripheral. + unsafe { Tx::::conjure(self.canregs).abort(mailbox) } + } + + /// Returns a received frame if available. + /// + /// This will first check FIFO 0 for a message or error. If none are available, FIFO 1 is + /// checked. + /// + /// Returns `Err` when a frame was lost due to buffer overrun. + pub fn receive(&mut self) -> nb::Result { + // Safety: We have a `&mut self` and have unique access to the peripheral. + let mut rx0 = unsafe { Rx0::::conjure(self.canregs) }; + let mut rx1 = unsafe { Rx1::::conjure(self.canregs) }; + + match rx0.receive() { + Err(nb::Error::WouldBlock) => rx1.receive(), + result => result, + } + } + + /// Returns a reference to the RX FIFO 0. + pub fn rx0(&mut self) -> Rx0 { + // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. + unsafe { Rx0::conjure(self.canregs) } + } + + /// Returns a reference to the RX FIFO 1. + pub fn rx1(&mut self) -> Rx1 { + // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. + unsafe { Rx1::conjure(self.canregs) } + } + + pub(crate) fn split_by_ref(&mut self) -> (Tx, Rx0, Rx1) { + // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. + let tx = unsafe { Tx::conjure(self.canregs) }; + let rx0 = unsafe { Rx0::conjure(self.canregs) }; + let rx1 = unsafe { Rx1::conjure(self.canregs) }; + (tx, rx0, rx1) + } + + /// Consumes this `Can` instance and splits it into transmitting and receiving halves. + pub fn split(self) -> (Tx, Rx0, Rx1) { + // Safety: `Self` is not `Copy` and is destroyed by moving it into this method. + unsafe { + ( + Tx::conjure(self.canregs), + Rx0::conjure(self.canregs), + Rx1::conjure(self.canregs), + ) + } + } +} + +impl Can { + /// Accesses the filter banks owned by this CAN peripheral. + /// + /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master + /// peripheral instead. + pub fn modify_filters(&mut self) -> MasterFilters<'_, I> { + unsafe { MasterFilters::new(self.canregs) } + } +} + +/// Interface to the CAN transmitter part. +pub struct Tx { + _can: PhantomData, + canregs: crate::pac::can::Can, +} + +impl Tx +where + I: Instance, +{ + unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { + Self { + _can: PhantomData, + canregs, + } + } + + /// Puts a CAN frame in a transmit mailbox for transmission on the bus. + /// + /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). + /// Transmit order is preserved for frames with identical priority. + /// + /// If all transmit mailboxes are full, and `frame` has a higher priority than the + /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is + /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as + /// [`TransmitStatus::dequeued_frame`]. + pub fn transmit(&mut self, frame: &Frame) -> nb::Result { + // Get the index of the next free mailbox or the one with the lowest priority. + let tsr = self.canregs.tsr().read(); + let idx = tsr.code() as usize; + + let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2); + let pending_frame = if frame_is_pending { + // High priority frames are transmitted first by the mailbox system. + // Frames with identical identifier shall be transmitted in FIFO order. + // The controller schedules pending frames of same priority based on the + // mailbox index instead. As a workaround check all pending mailboxes + // and only accept higher priority frames. + self.check_priority(0, frame.id)?; + self.check_priority(1, frame.id)?; + self.check_priority(2, frame.id)?; + + let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2); + if all_frames_are_pending { + // No free mailbox is available. This can only happen when three frames with + // ascending priority (descending IDs) were requested for transmission and all + // of them are blocked by bus traffic with even higher priority. + // To prevent a priority inversion abort and replace the lowest priority frame. + self.read_pending_mailbox(idx) + } else { + // There was a free mailbox. + None + } + } else { + // All mailboxes are available: Send frame without performing any checks. + None + }; + + self.write_mailbox(idx, frame); + + let mailbox = match idx { + 0 => Mailbox::Mailbox0, + 1 => Mailbox::Mailbox1, + 2 => Mailbox::Mailbox2, + _ => unreachable!(), + }; + Ok(TransmitStatus { + dequeued_frame: pending_frame, + mailbox, + }) + } + + /// Returns `Ok` when the mailbox is free or if it contains pending frame with a + /// lower priority (higher ID) than the identifier `id`. + fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> { + // Read the pending frame's id to check its priority. + assert!(idx < 3); + let tir = &self.canregs.tx(idx).tir().read(); + //let tir = &can.tx[idx].tir.read(); + + // Check the priority by comparing the identifiers. But first make sure the + // frame has not finished the transmission (`TXRQ` == 0) in the meantime. + if tir.txrq() && id <= IdReg::from_register(tir.0) { + // There's a mailbox whose priority is higher or equal + // the priority of the new frame. + return Err(nb::Error::WouldBlock); + } + + Ok(()) + } + + fn write_mailbox(&mut self, idx: usize, frame: &Frame) { + debug_assert!(idx < 3); + + let mb = self.canregs.tx(idx); + mb.tdtr().write(|w| w.set_dlc(frame.dlc() as u8)); + + mb.tdlr() + .write(|w| w.0 = u32::from_ne_bytes(frame.data.bytes[0..4].try_into().unwrap())); + mb.tdhr() + .write(|w| w.0 = u32::from_ne_bytes(frame.data.bytes[4..8].try_into().unwrap())); + mb.tir().write(|w| { + w.0 = frame.id.0; + w.set_txrq(true); + }); + } + + fn read_pending_mailbox(&mut self, idx: usize) -> Option { + if self.abort_by_index(idx) { + debug_assert!(idx < 3); + + let mb = self.canregs.tx(idx); + // Read back the pending frame. + let mut pending_frame = Frame { + id: IdReg(mb.tir().read().0), + data: Data::empty(), + }; + pending_frame.data.bytes[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes()); + pending_frame.data.bytes[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes()); + pending_frame.data.len = mb.tdtr().read().dlc(); + + Some(pending_frame) + } else { + // Abort request failed because the frame was already sent (or being sent) on + // the bus. All mailboxes are now free. This can happen for small prescaler + // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR + // has preempted the execution. + None + } + } + + /// Tries to abort a pending frame. Returns `true` when aborted. + fn abort_by_index(&mut self, idx: usize) -> bool { + self.canregs.tsr().write(|reg| reg.set_abrq(idx, true)); + + // Wait for the abort request to be finished. + loop { + let tsr = self.canregs.tsr().read(); + if false == tsr.abrq(idx) { + break tsr.txok(idx) == false; + } + } + } + + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + // If the mailbox is empty, the value of TXOKx depends on what happened with the previous + // frame in that mailbox. Only call abort_by_index() if the mailbox is not empty. + let tsr = self.canregs.tsr().read(); + let mailbox_empty = match mailbox { + Mailbox::Mailbox0 => tsr.tme(0), + Mailbox::Mailbox1 => tsr.tme(1), + Mailbox::Mailbox2 => tsr.tme(2), + }; + if mailbox_empty { + false + } else { + self.abort_by_index(mailbox as usize) + } + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_idle(&self) -> bool { + let tsr = self.canregs.tsr().read(); + tsr.tme(0) && tsr.tme(1) && tsr.tme(2) + } + + /// Clears the request complete flag for all mailboxes. + pub fn clear_interrupt_flags(&mut self) { + self.canregs.tsr().write(|reg| { + reg.set_rqcp(0, true); + reg.set_rqcp(1, true); + reg.set_rqcp(2, true); + }); + } +} + +/// Interface to receiver FIFO 0. +pub struct Rx0 { + _can: PhantomData, + canregs: crate::pac::can::Can, +} + +impl Rx0 +where + I: Instance, +{ + unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { + Self { + _can: PhantomData, + canregs, + } + } + + /// Returns a received frame if available. + /// + /// Returns `Err` when a frame was lost due to buffer overrun. + pub fn receive(&mut self) -> nb::Result { + receive_fifo(self.canregs, 0) + } +} + +/// Interface to receiver FIFO 1. +pub struct Rx1 { + _can: PhantomData, + canregs: crate::pac::can::Can, +} + +impl Rx1 +where + I: Instance, +{ + unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { + Self { + _can: PhantomData, + canregs, + } + } + + /// Returns a received frame if available. + /// + /// Returns `Err` when a frame was lost due to buffer overrun. + pub fn receive(&mut self) -> nb::Result { + receive_fifo(self.canregs, 1) + } +} + +fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result { + assert!(fifo_nr < 2); + let rfr = canregs.rfr(fifo_nr); + let rx = canregs.rx(fifo_nr); + + //let rfr = &can.rfr[fifo_nr]; + //let rx = &can.rx[fifo_nr]; + + // Check if a frame is available in the mailbox. + let rfr_read = rfr.read(); + if rfr_read.fmp() == 0 { + return Err(nb::Error::WouldBlock); + } + + // Check for RX FIFO overrun. + if rfr_read.fovr() { + rfr.write(|w| w.set_fovr(true)); + return Err(nb::Error::Other(OverrunError { _priv: () })); + } + + // Read the frame. + let mut frame = Frame { + id: IdReg(rx.rir().read().0), + data: [0; 8].into(), + }; + frame.data[0..4].copy_from_slice(&rx.rdlr().read().0.to_ne_bytes()); + frame.data[4..8].copy_from_slice(&rx.rdhr().read().0.to_ne_bytes()); + frame.data.len = rx.rdtr().read().dlc(); + + // Release the mailbox. + rfr.write(|w| w.set_rfom(true)); + + Ok(frame) +} + +/// Identifies one of the two receive FIFOs. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Fifo { + /// First receive FIFO + Fifo0 = 0, + /// Second receive FIFO + Fifo1 = 1, +} + +/// Identifies one of the three transmit mailboxes. +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Mailbox { + /// Transmit mailbox 0 + Mailbox0 = 0, + /// Transmit mailbox 1 + Mailbox1 = 1, + /// Transmit mailbox 2 + Mailbox2 = 2, +} + +/// Contains information about a frame enqueued for transmission via [`Can::transmit`] or +/// [`Tx::transmit`]. +pub struct TransmitStatus { + dequeued_frame: Option, + mailbox: Mailbox, +} + +impl TransmitStatus { + /// Returns the lower-priority frame that was dequeued to make space for the new frame. + #[inline] + pub fn dequeued_frame(&self) -> Option<&Frame> { + self.dequeued_frame.as_ref() + } + + /// Returns the [`Mailbox`] the frame was enqueued in. + #[inline] + pub fn mailbox(&self) -> Mailbox { + self.mailbox + } +} diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 7e00eca6f..acd831937 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -4,8 +4,9 @@ use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; -pub use bxcan; -use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; +pub mod bx; + +pub use bx::{filter, Data, ExtendedId, Fifo, Frame, Id, StandardId}; use embassy_hal_internal::{into_ref, PeripheralRef}; use futures::FutureExt; @@ -29,7 +30,7 @@ pub struct Envelope { #[cfg(feature = "time")] pub ts: embassy_time::Instant, /// The actual CAN frame. - pub frame: bxcan::Frame, + pub frame: crate::can::bx::Frame, } /// Interrupt handler. @@ -93,7 +94,7 @@ impl interrupt::typelevel::Handler for SceInterrup /// CAN driver pub struct Can<'d, T: Instance> { - can: bxcan::Can>, + can: crate::can::bx::Can>, } /// Error returned by `try_read` @@ -166,21 +167,14 @@ impl<'d, T: Instance> Can<'d, T> { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(); + let can = crate::can::bx::Can::builder(BxcanInstance(peri), T::regs()).leave_disabled(); Self { can } } /// Set CAN bit rate. pub fn set_bitrate(&mut self, bitrate: u32) { let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); - let sjw = u8::from(bit_timing.sync_jump_width) as u32; - let seg1 = u8::from(bit_timing.seg1) as u32; - let seg2 = u8::from(bit_timing.seg2) as u32; - let prescaler = u16::from(bit_timing.prescaler) as u32; - self.can - .modify_config() - .set_bit_timing((sjw - 1) << 24 | (seg1 - 1) << 16 | (seg2 - 1) << 20 | (prescaler - 1)) - .leave_disabled(); + self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); } /// Enables the peripheral and synchronizes with the bus. @@ -198,19 +192,19 @@ impl<'d, T: Instance> Can<'d, T> { /// Queues the message to be sent. /// /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. - pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { + pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus { self.split().0.write(frame).await } /// Attempts to transmit a frame without blocking. /// /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. - pub fn try_write(&mut self, frame: &Frame) -> Result { + pub fn try_write(&mut self, frame: &Frame) -> Result { self.split().0.try_write(frame) } /// Waits for a specific transmit mailbox to become empty - pub async fn flush(&self, mb: bxcan::Mailbox) { + pub async fn flush(&self, mb: crate::can::bx::Mailbox) { CanTx::::flush_inner(mb).await } @@ -298,29 +292,29 @@ impl<'d, T: Instance> Can<'d, T> { /// Split the CAN driver into transmit and receive halves. /// /// Useful for doing separate transmit/receive tasks. - pub fn split<'c>(&'c mut self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) { + pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) { let (tx, rx0, rx1) = self.can.split_by_ref(); (CanTx { tx }, CanRx { rx0, rx1 }) } } -impl<'d, T: Instance> AsMut>> for Can<'d, T> { +impl<'d, T: Instance> AsMut>> for Can<'d, T> { /// Get mutable access to the lower-level driver from the `bxcan` crate. - fn as_mut(&mut self) -> &mut bxcan::Can> { + fn as_mut(&mut self) -> &mut crate::can::bx::Can> { &mut self.can } } /// CAN driver, transmit half. -pub struct CanTx<'c, 'd, T: Instance> { - tx: &'c mut bxcan::Tx>, +pub struct CanTx<'d, T: Instance> { + tx: crate::can::bx::Tx>, } -impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { +impl<'d, T: Instance> CanTx<'d, T> { /// Queues the message to be sent. /// /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. - pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { + pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); if let Ok(status) = self.tx.transmit(frame) { @@ -335,11 +329,11 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { /// Attempts to transmit a frame without blocking. /// /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. - pub fn try_write(&mut self, frame: &Frame) -> Result { + pub fn try_write(&mut self, frame: &Frame) -> Result { self.tx.transmit(frame).map_err(|_| TryWriteError::Full) } - async fn flush_inner(mb: bxcan::Mailbox) { + async fn flush_inner(mb: crate::can::bx::Mailbox) { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); if T::regs().tsr().read().tme(mb.index()) { @@ -352,7 +346,7 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { } /// Waits for a specific transmit mailbox to become empty - pub async fn flush(&self, mb: bxcan::Mailbox) { + pub async fn flush(&self, mb: crate::can::bx::Mailbox) { Self::flush_inner(mb).await } @@ -361,9 +355,9 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { T::state().tx_waker.register(cx.waker()); let tsr = T::regs().tsr().read(); - if tsr.tme(bxcan::Mailbox::Mailbox0.index()) - || tsr.tme(bxcan::Mailbox::Mailbox1.index()) - || tsr.tme(bxcan::Mailbox::Mailbox2.index()) + if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) + || tsr.tme(crate::can::bx::Mailbox::Mailbox1.index()) + || tsr.tme(crate::can::bx::Mailbox::Mailbox2.index()) { return Poll::Ready(()); } @@ -383,9 +377,9 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { T::state().tx_waker.register(cx.waker()); let tsr = T::regs().tsr().read(); - if tsr.tme(bxcan::Mailbox::Mailbox0.index()) - && tsr.tme(bxcan::Mailbox::Mailbox1.index()) - && tsr.tme(bxcan::Mailbox::Mailbox2.index()) + if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) + && tsr.tme(crate::can::bx::Mailbox::Mailbox1.index()) + && tsr.tme(crate::can::bx::Mailbox::Mailbox2.index()) { return Poll::Ready(()); } @@ -403,12 +397,12 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { /// CAN driver, receive half. #[allow(dead_code)] -pub struct CanRx<'c, 'd, T: Instance> { - rx0: &'c mut bxcan::Rx0>, - rx1: &'c mut bxcan::Rx1>, +pub struct CanRx<'d, T: Instance> { + rx0: crate::can::bx::Rx0>, + rx1: crate::can::bx::Rx1>, } -impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { +impl<'d, T: Instance> CanRx<'d, T> { /// Read a CAN frame. /// /// If no CAN frame is in the RX buffer, this will wait until there is one. @@ -478,7 +472,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> { } impl<'d, T: Instance> Deref for Can<'d, T> { - type Target = bxcan::Can>; + type Target = crate::can::bx::Can>; fn deref(&self) -> &Self::Target { &self.can @@ -515,8 +509,6 @@ pub(crate) mod sealed { } pub trait Instance { - const REGISTERS: *mut bxcan::RegisterBlock; - fn regs() -> crate::pac::can::Can; fn state() -> &'static State; } @@ -537,14 +529,11 @@ pub trait Instance: sealed::Instance + RccPeripheral + 'static { /// BXCAN instance newtype. pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>); -unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> { - const REGISTERS: *mut bxcan::RegisterBlock = T::REGISTERS; -} +unsafe impl<'d, T: Instance> crate::can::bx::Instance for BxcanInstance<'d, T> {} foreach_peripheral!( (can, $inst:ident) => { impl sealed::Instance for peripherals::$inst { - const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _; fn regs() -> crate::pac::can::Can { crate::pac::$inst @@ -567,7 +556,7 @@ foreach_peripheral!( foreach_peripheral!( (can, CAN) => { - unsafe impl<'d> bxcan::FilterOwner for BxcanInstance<'d, peripherals::CAN> { + unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN> { const NUM_FILTER_BANKS: u8 = 14; } }; @@ -582,19 +571,19 @@ foreach_peripheral!( ))] { // Most L4 devices and some F7 devices use the name "CAN1" // even if there is no "CAN2" peripheral. - unsafe impl<'d> bxcan::FilterOwner for BxcanInstance<'d, peripherals::CAN1> { + unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN1> { const NUM_FILTER_BANKS: u8 = 14; } } else { - unsafe impl<'d> bxcan::FilterOwner for BxcanInstance<'d, peripherals::CAN1> { + unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN1> { const NUM_FILTER_BANKS: u8 = 28; } - unsafe impl<'d> bxcan::MasterInstance for BxcanInstance<'d, peripherals::CAN1> {} + unsafe impl<'d> crate::can::bx::MasterInstance for BxcanInstance<'d, peripherals::CAN1> {} } } }; (can, CAN3) => { - unsafe impl<'d> bxcan::FilterOwner for BxcanInstance<'d, peripherals::CAN3> { + unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN3> { const NUM_FILTER_BANKS: u8 = 14; } }; @@ -607,12 +596,12 @@ trait Index { fn index(&self) -> usize; } -impl Index for bxcan::Mailbox { +impl Index for crate::can::bx::Mailbox { fn index(&self) -> usize { match self { - bxcan::Mailbox::Mailbox0 => 0, - bxcan::Mailbox::Mailbox1 => 1, - bxcan::Mailbox::Mailbox2 => 2, + crate::can::bx::Mailbox::Mailbox0 => 0, + crate::can::bx::Mailbox::Mailbox1 => 1, + crate::can::bx::Mailbox::Mailbox2 => 2, } } } diff --git a/examples/stm32f1/src/bin/can.rs b/examples/stm32f1/src/bin/can.rs index c1c4f8359..00d61096f 100644 --- a/examples/stm32f1/src/bin/can.rs +++ b/examples/stm32f1/src/bin/can.rs @@ -3,9 +3,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::can::bxcan::filter::Mask32; -use embassy_stm32::can::bxcan::{Fifo, Frame, Id, StandardId}; -use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; +use embassy_stm32::can::{ + filter, Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, + TxInterruptHandler, +}; use embassy_stm32::peripherals::CAN; use embassy_stm32::{bind_interrupts, Config}; use {defmt_rtt as _, panic_probe as _}; @@ -31,7 +32,7 @@ async fn main(_spawner: Spawner) { can.as_mut() .modify_filters() - .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + .enable_bank(0, Fifo::Fifo0, filter::Mask32::accept_all()); can.as_mut() .modify_config() @@ -45,16 +46,16 @@ async fn main(_spawner: Spawner) { let mut i: u8 = 0; loop { - let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); + let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i, 0, 1, 2, 3, 4, 5, 6]); can.write(&tx_frame).await; match can.read().await { Ok(env) => match env.frame.id() { Id::Extended(id) => { - defmt::println!("Extended Frame id={:x}", id.as_raw()); + defmt::println!("Extended Frame id={:x} {:02x}", id.as_raw(), env.frame.data().unwrap()); } Id::Standard(id) => { - defmt::println!("Standard Frame id={:x}", id.as_raw()); + defmt::println!("Standard Frame id={:x} {:02x}", id.as_raw(), env.frame.data().unwrap()); } }, Err(err) => { diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index d074b4265..b20af8cf1 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -4,9 +4,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; -use embassy_stm32::can::bxcan::filter::Mask32; -use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; -use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; +use embassy_stm32::can::filter::Mask32; +use embassy_stm32::can::{ + Can, Fifo, Frame, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, TxInterruptHandler, +}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; use embassy_time::Instant; diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index bcfdb67a8..c3e14bbf4 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -1,16 +1,18 @@ #![no_std] #![no_main] +use core::num::{NonZeroU16, NonZeroU8}; + use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; -use embassy_stm32::can::bxcan::filter::Mask32; -use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; +use embassy_stm32::can::filter::Mask32; use embassy_stm32::can::{ - Can, CanTx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, + Can, CanTx, Fifo, Frame, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, + TxInterruptHandler, }; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN3; +use embassy_stm32::{bind_interrupts, can}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -22,7 +24,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -pub async fn send_can_message(tx: &'static mut CanTx<'static, 'static, CAN3>) { +pub async fn send_can_message(tx: &'static mut CanTx<'static, CAN3>) { loop { let frame = Frame::new_data(unwrap!(StandardId::new(0 as _)), [0]); tx.write(&frame).await; @@ -51,13 +53,18 @@ async fn main(spawner: Spawner) { can.as_mut() .modify_config() - .set_bit_timing(0x001c0001) // http://www.bittiming.can-wiki.info/ + .set_bit_timing(can::util::NominalBitTiming { + prescaler: NonZeroU16::new(2).unwrap(), + seg1: NonZeroU8::new(13).unwrap(), + seg2: NonZeroU8::new(2).unwrap(), + sync_jump_width: NonZeroU8::new(1).unwrap(), + }) // http://www.bittiming.can-wiki.info/ .set_loopback(true) .enable(); let (tx, mut rx) = can.split(); - static CAN_TX: StaticCell> = StaticCell::new(); + static CAN_TX: StaticCell> = StaticCell::new(); let tx = CAN_TX.init(tx); spawner.spawn(send_can_message(tx)).unwrap(); diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index f4effa244..e869e8fb9 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -9,8 +9,8 @@ use common::*; use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; -use embassy_stm32::can::bxcan::filter::Mask32; -use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; +use embassy_stm32::can::bx::filter::Mask32; +use embassy_stm32::can::bx::{Fifo, Frame, StandardId}; use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1;