Merge pull request #2650 from cschuhen/feature/bxcan_pac

Use stm32-metapac for BXCAN.
This commit is contained in:
Dario Nieuwenhuis 2024-03-12 19:05:22 +00:00 committed by GitHub
commit 9101b9eb01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1863 additions and 73 deletions

View file

@ -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" ]

View file

@ -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<Id>) -> 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<Id>) -> 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<Mask32> 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<I: FilterOwner> 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<BankConfig>) -> &mut Self {
self.banks_imm().enable(index, fifo, config.into());
self
}
}
impl<I: MasterInstance> 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<I: FilterOwner> 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<I: Instance> 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<BankConfig>) -> &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);
}
}

View file

@ -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<Id>, data: impl Into<Data>) -> 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<Id>, 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<core::cmp::Ordering> {
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<Self> {
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);

View file

@ -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<Self> {
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<Self> {
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<StandardId> for Id {
#[inline]
fn from(id: StandardId) -> Self {
Id::Standard(id)
}
}
impl From<ExtendedId> for Id {
#[inline]
fn from(id: ExtendedId) -> Self {
Id::Extended(id)
}
}

View file

@ -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 frames 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<Ordering> {
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<I>,
}
impl<I: Instance> CanConfig<'_, I> {
/// Configures the bit timings.
///
/// You can use <http://www.bittiming.can-wiki.info/> 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<I: Instance> 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<I: Instance> {
can: Can<I>,
}
impl<I: Instance> CanBuilder<I> {
/// Configures the bit timings.
///
/// You can use <http://www.bittiming.can-wiki.info/> 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<I> {
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<I> {
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<I: Instance> {
instance: I,
canregs: crate::pac::can::Can,
}
impl<I> Can<I>
where
I: Instance,
{
/// Creates a [`CanBuilder`] for constructing a CAN interface.
pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder<I> {
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<TransmitStatus, Infallible> {
// Safety: We have a `&mut self` and have unique access to the peripheral.
unsafe { Tx::<I>::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::<I>::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::<I>::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<Frame, OverrunError> {
// Safety: We have a `&mut self` and have unique access to the peripheral.
let mut rx0 = unsafe { Rx0::<I>::conjure(self.canregs) };
let mut rx1 = unsafe { Rx1::<I>::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<I> {
// 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<I> {
// 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<I>, Rx0<I>, Rx1<I>) {
// 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<I>, Rx0<I>, Rx1<I>) {
// 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<I: FilterOwner> Can<I> {
/// 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<I> {
_can: PhantomData<I>,
canregs: crate::pac::can::Can,
}
impl<I> Tx<I>
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<TransmitStatus, Infallible> {
// 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<Frame> {
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<I> {
_can: PhantomData<I>,
canregs: crate::pac::can::Can,
}
impl<I> Rx0<I>
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<Frame, OverrunError> {
receive_fifo(self.canregs, 0)
}
}
/// Interface to receiver FIFO 1.
pub struct Rx1<I> {
_can: PhantomData<I>,
canregs: crate::pac::can::Can,
}
impl<I> Rx1<I>
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<Frame, OverrunError> {
receive_fifo(self.canregs, 1)
}
}
fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result<Frame, OverrunError> {
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<Frame>,
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
}
}

View file

@ -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<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup
/// CAN driver
pub struct Can<'d, T: Instance> {
can: bxcan::Can<BxcanInstance<'d, T>>,
can: crate::can::bx::Can<BxcanInstance<'d, T>>,
}
/// 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<bxcan::TransmitStatus, TryWriteError> {
pub fn try_write(&mut self, frame: &Frame) -> Result<crate::can::bx::TransmitStatus, TryWriteError> {
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::<T>::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<bxcan::Can<BxcanInstance<'d, T>>> for Can<'d, T> {
impl<'d, T: Instance> AsMut<crate::can::bx::Can<BxcanInstance<'d, T>>> for Can<'d, T> {
/// Get mutable access to the lower-level driver from the `bxcan` crate.
fn as_mut(&mut self) -> &mut bxcan::Can<BxcanInstance<'d, T>> {
fn as_mut(&mut self) -> &mut crate::can::bx::Can<BxcanInstance<'d, T>> {
&mut self.can
}
}
/// CAN driver, transmit half.
pub struct CanTx<'c, 'd, T: Instance> {
tx: &'c mut bxcan::Tx<BxcanInstance<'d, T>>,
pub struct CanTx<'d, T: Instance> {
tx: crate::can::bx::Tx<BxcanInstance<'d, T>>,
}
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<bxcan::TransmitStatus, TryWriteError> {
pub fn try_write(&mut self, frame: &Frame) -> Result<crate::can::bx::TransmitStatus, TryWriteError> {
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<BxcanInstance<'d, T>>,
rx1: &'c mut bxcan::Rx1<BxcanInstance<'d, T>>,
pub struct CanRx<'d, T: Instance> {
rx0: crate::can::bx::Rx0<BxcanInstance<'d, T>>,
rx1: crate::can::bx::Rx1<BxcanInstance<'d, T>>,
}
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<BxcanInstance<'d, T>>;
type Target = crate::can::bx::Can<BxcanInstance<'d, T>>;
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,
}
}
}

View file

@ -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) => {

View file

@ -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;

View file

@ -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<CanTx<'static, 'static, CAN3>> = StaticCell::new();
static CAN_TX: StaticCell<CanTx<'static, CAN3>> = StaticCell::new();
let tx = CAN_TX.init(tx);
spawner.spawn(send_can_message(tx)).unwrap();

View file

@ -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;