Merge pull request #2650 from cschuhen/feature/bxcan_pac
Use stm32-metapac for BXCAN.
This commit is contained in:
commit
9101b9eb01
10 changed files with 1863 additions and 73 deletions
|
@ -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" ]
|
||||
|
|
475
embassy-stm32/src/can/bx/filter.rs
Normal file
475
embassy-stm32/src/can/bx/filter.rs
Normal 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);
|
||||
}
|
||||
}
|
248
embassy-stm32/src/can/bx/frame.rs
Normal file
248
embassy-stm32/src/can/bx/frame.rs
Normal 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);
|
113
embassy-stm32/src/can/bx/id.rs
Normal file
113
embassy-stm32/src/can/bx/id.rs
Normal 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)
|
||||
}
|
||||
}
|
956
embassy-stm32/src/can/bx/mod.rs
Normal file
956
embassy-stm32/src/can/bx/mod.rs
Normal 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 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<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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue