Skip to content

CAN support with bxcan crate #352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions embassy-stm32/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ bare-metal = "1.0.0"
atomic-polyfill = "0.1.3"
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] }
vcell = { version = "0.1.3", optional = true }
bxcan = { version = "0.5.1" }

cfg-if = "1.0.0"

Expand Down
144 changes: 144 additions & 0 deletions embassy-stm32/src/can/bxcan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};

use embassy::util::Unborrow;
use embassy_hal_common::unborrow;

use crate::gpio::Pin;
use crate::{peripherals, rcc::RccPeripheral};

pub use bxcan::*;

pub struct Can<'d, T: Instance + bxcan::Instance> {
phantom: PhantomData<&'d mut T>,
can: bxcan::Can<T>,
}

impl<'d, T: Instance + bxcan::Instance> Can<'d, T> {
pub fn new(
peri: impl Unborrow<Target = T> + 'd,
rx: impl Unborrow<Target = impl RxPin<T>> + 'd,
tx: impl Unborrow<Target = impl TxPin<T>> + 'd,
) -> Self {
unborrow!(peri, rx, tx);

unsafe {
rx.set_as_af(rx.af_num());
tx.set_as_af(tx.af_num());
}

T::enable();
T::reset();

Self {
phantom: PhantomData,
can: bxcan::Can::new(peri),
}
}
}

impl<'d, T: Instance + bxcan::Instance> Drop for Can<'d, T> {
fn drop(&mut self) {
// Cannot call `free()` because it moves the instance.
// Manually reset the peripheral.
unsafe {
T::regs().mcr().write(|w| w.set_reset(true));
}
T::disable();
}
}

impl<'d, T: Instance + bxcan::Instance> Deref for Can<'d, T> {
type Target = bxcan::Can<T>;

fn deref(&self) -> &Self::Target {
&self.can
}
}

impl<'d, T: Instance + bxcan::Instance> DerefMut for Can<'d, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.can
}
}

pub(crate) mod sealed {
use super::*;

pub trait Instance {
fn regs() -> &'static crate::pac::can::Can;
}

pub trait RxPin<T: Instance>: Pin {
fn af_num(&self) -> u8;
}

pub trait TxPin<T: Instance>: Pin {
fn af_num(&self) -> u8;
}
}

pub trait Instance: sealed::Instance + RccPeripheral {}
pub trait RxPin<T: Instance>: sealed::RxPin<T> {}
pub trait TxPin<T: Instance>: sealed::TxPin<T> {}

crate::pac::peripherals!(
(can, $inst:ident) => {
impl sealed::Instance for peripherals::$inst {
fn regs() -> &'static crate::pac::can::Can {
&crate::pac::$inst
}
}

impl Instance for peripherals::$inst {}

unsafe impl bxcan::Instance for peripherals::$inst {
const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.0 as *mut _;
}
};
);

crate::pac::peripherals!(
(can, CAN) => {
unsafe impl bxcan::FilterOwner for peripherals::$inst {
const NUM_FILTER_BANKS: u8 = 14;
}
};
// Only correct when CAN2 also exists… Fix on yaml level?
// There are only 14 filter banks when CAN2 is not available.
(can, CAN1) => {
unsafe impl bxcan::FilterOwner for peripherals::CAN1 {
const NUM_FILTER_BANKS: u8 = 28;
}
};
(can, CAN2) => {
// CAN2 is always a slave instance where CAN1 is the master instance
unsafe impl bxcan::MasterInstance for peripherals::CAN1 {}
};
(can, CAN3) => {
unsafe impl bxcan::FilterOwner for peripherals::$inst {
const NUM_FILTER_BANKS: u8 = 14;
}
};
);

macro_rules! impl_pin {
($inst:ident, $pin:ident, $signal:ident, $af:expr) => {
impl $signal<peripherals::$inst> for peripherals::$pin {}

impl sealed::$signal<peripherals::$inst> for peripherals::$pin {
fn af_num(&self) -> u8 {
$af
}
}
};
}

crate::pac::peripheral_pins!(
($inst:ident, can, CAN, $pin:ident, TX, $af:expr) => {
impl_pin!($inst, $pin, TxPin, $af);
};
($inst:ident, can, CAN, $pin:ident, RX, $af:expr) => {
impl_pin!($inst, $pin, RxPin, $af);
};
);
5 changes: 5 additions & 0 deletions embassy-stm32/src/can/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#![macro_use]

#[cfg_attr(can_bxcan, path = "bxcan.rs")]
mod _version;
pub use _version::*;
2 changes: 2 additions & 0 deletions embassy-stm32/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ mod time_driver;

#[cfg(adc)]
pub mod adc;
#[cfg(can)]
pub mod can;
#[cfg(dac)]
pub mod dac;
#[cfg(dbgmcu)]
Expand Down
1 change: 1 addition & 0 deletions examples/stm32f4/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ panic-probe = { version = "0.2.0", features= ["print-defmt"] }
futures = { version = "0.3.8", default-features = false, features = ["async-await"] }
rtt-target = { version = "0.3", features = ["cortex-m"] }
heapless = { version = "0.7.1", default-features = false }
nb = { version = "1.0" }
52 changes: 52 additions & 0 deletions examples/stm32f4/src/bin/can.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#![no_std]
#![no_main]
#![feature(trait_alias)]
#![feature(type_alias_impl_trait)]
#![allow(incomplete_features)]

#[path = "../example_common.rs"]
mod example_common;

use cortex_m_rt::entry;
use embassy_stm32::can::filter::Mask32;
use embassy_stm32::can::{Can, Frame, StandardId};
use embassy_stm32::dbgmcu::Dbgmcu;
use embassy_stm32::gpio::{Input, Pull};
use example_common::*;

#[entry]
fn main() -> ! {
info!("Hello World!");

unsafe {
Dbgmcu::enable_all();
}

let mut p = embassy_stm32::init(Default::default());

// The next two lines are a workaround for testing without transceiver.
// To synchronise to the bus the RX input needs to see a high level.
// Use `mem::forget()` to release the borrow on the pin but keep the
// pull-up resistor enabled.
let rx_pin = Input::new(&mut p.PA11, Pull::Up);
core::mem::forget(rx_pin);

let mut can = Can::new(p.CAN1, p.PA11, p.PA12);

can.modify_config()
.set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/
.set_loopback(true) // Receive own frames
.set_silent(true);
can.modify_filters().enable_bank(0, Mask32::accept_all());
unwrap!(nb::block!(can.enable()));

let mut i: u8 = 0;
loop {
let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]);
unwrap!(nb::block!(can.transmit(&tx_frame)));
while !can.is_transmitter_idle() {}
let rx_frame = unwrap!(nb::block!(can.receive()));
info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]);
i += 1;
}
}