From 8702fa7905b4a01a1bf58162419d8bb5e4f2fdca Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 2 May 2021 21:30:49 +0200 Subject: [PATCH 1/3] Started bxCAN impl --- Cargo.toml | 1 + src/can.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 3 files changed, 114 insertions(+) create mode 100644 src/can.rs diff --git a/Cargo.toml b/Cargo.toml index 85c24d56..900a108d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ edition = "2018" cortex-m = "0.6.3" nb = "0.1.1" stm32l4 = "0.13.0" +bxcan = "0.5" [dependencies.rand_core] version = "0.6.2" diff --git a/src/can.rs b/src/can.rs new file mode 100644 index 00000000..7cbc943e --- /dev/null +++ b/src/can.rs @@ -0,0 +1,111 @@ +//! # Controller Area Network (CAN) Interface +//! +//! Based on STM32F4xx HAL. + +use crate::pac::CAN1; + +mod sealed { + pub trait Sealed {} +} + +/// A pair of (TX, RX) pins configured for CAN communication +pub trait Pins: sealed::Sealed { + /// The CAN peripheral that uses these pins + type Instance; +} + +/// Implements sealed::Sealed and Pins for a (TX, RX) pair of pins associated with a CAN peripheral +/// The alternate function number can be specified after each pin name. +macro_rules! pins { + ($($PER:ident => ($tx:ident<$txaf:ident>, $rx:ident<$rxaf:ident>),)+) => { + $( + impl crate::can::sealed::Sealed for ($tx>>, $rx>>) {} + impl crate::can::Pins for ($tx>>, $rx>>) { + type Instance = $PER; + } + )+ + } +} + +mod common_pins { + use crate::gpio::{ + gpioa::{PA11, PA12}, + gpiob::{PB8, PB9}, + gpiod::{PD0, PD1}, + Floating, Input, AF9, + }; + use crate::pac::CAN1; + + // All STM32L4 models with CAN support these pins + pins! { + CAN1 => (PA12, PA11), + CAN1 => (PD1, PD0), + CAN1 => (PB9, PB8), + } +} + +#[cfg(feature = "stm32l4x1")] +mod pb13_pb12_af10 { + use crate::gpio::{ + gpiob::{PB12, PB13}, + Floating, Input, AF10, + }; + use crate::pac::CAN1; + pins! { CAN1 => (PB13, PB12), } +} + +/// Enable/disable peripheral +pub trait Enable: sealed::Sealed { + /// Enables this peripheral by setting the associated enable bit in an RCC enable register + fn enable(); +} + +impl crate::can::sealed::Sealed for crate::pac::CAN1 {} + +impl crate::can::Enable for crate::pac::CAN1 { + #[inline(always)] + fn enable() { + unsafe { + // NOTE(unsafe) this reference will only be used for atomic writes with no side effects. + let rcc = &(*crate::pac::RCC::ptr()); + // Enable peripheral clock + rcc.apb1enr1.modify(|_, w| w.can1en().set_bit()); + rcc.apb1rstr1.modify(|_, w| w.can1rst().set_bit()); + rcc.apb1rstr1.modify(|_, w| w.can1rst().clear_bit()); + }; + } +} + +/// Interface to the CAN peripheral. +pub struct Can { + _peripheral: Instance, +} + +impl Can +where + Instance: Enable, +{ + /// Creates a CAN interface. + pub fn new

(can: Instance, _pins: P) -> Can + where + P: Pins, + { + Instance::enable(); + Can { _peripheral: can } + } + + pub fn new_unchecked(can: Instance) -> Can { + Instance::enable(); + Can { _peripheral: can } + } +} + +unsafe impl bxcan::Instance for Can { + const REGISTERS: *mut bxcan::RegisterBlock = CAN1::ptr() as *mut _; +} + +unsafe impl bxcan::FilterOwner for Can { + const NUM_FILTER_BANKS: u8 = 14; +} + +unsafe impl bxcan::MasterInstance for Can {} diff --git a/src/lib.rs b/src/lib.rs index fec12a6f..c478f205 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,8 @@ pub mod traits; feature = "stm32l4x6" ))] pub mod adc; +#[cfg(any(feature = "stm32l4x1", feature = "stm32l4x5",))] +pub mod can; #[cfg(any( feature = "stm32l4x1", feature = "stm32l4x2", From 575b7fc98f17e09843853dd93bcaea95ce4b10af Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 31 May 2021 13:35:26 +0200 Subject: [PATCH 2/3] Added small CAN loopback example --- Cargo.toml | 4 ++ examples/can-loopback.rs | 94 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 examples/can-loopback.rs diff --git a/Cargo.toml b/Cargo.toml index 900a108d..5fef979e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,6 +100,10 @@ lto = true name = "adc" required-features = ["rt", "stm32l4x3"] +[[example]] +name = "can-loopback" +required-features = ["rt", "stm32l4x1"] + [[example]] name = "irq_button" required-features = ["rt"] diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs new file mode 100644 index 00000000..c729b1bc --- /dev/null +++ b/examples/can-loopback.rs @@ -0,0 +1,94 @@ +//! Run the bxCAN peripheral in loopback mode. + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use bxcan::{ + filter::Mask32, + {Frame, StandardId}, +}; +use panic_halt as _; +use rtic::app; +use rtt_target::{rprintln, rtt_init_print}; +use stm32l4xx_hal::{can::Can, prelude::*}; + +#[app(device = stm32l4xx_hal::stm32, peripherals = true)] +const APP: () = { + #[init] + fn init(cx: init::Context) { + rtt_init_print!(); + + let dp = cx.device; + + let mut flash = dp.FLASH.constrain(); + let mut rcc = dp.RCC.constrain(); + let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1); + let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2); + + // Set the clocks to 80 MHz + let _clocks = rcc.cfgr.sysclk(80.mhz()).freeze(&mut flash.acr, &mut pwr); + + rprintln!(" - CAN init"); + + let mut can = { + let rx = gpioa.pa11.into_af9(&mut gpioa.moder, &mut gpioa.afrh); + let tx = gpioa.pa12.into_af9(&mut gpioa.moder, &mut gpioa.afrh); + + let can = Can::new(dp.CAN1, (tx, rx)); + + bxcan::Can::new(can) + }; + can.configure(|config| { + // APB1 (PCLK1): 80 MHz, Bit rate: 100kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + config.set_bit_timing(0x001c_0031); + config.set_loopback(true); + }); + + // Configure filters so that can frames can be received. + let mut filters = can.modify_filters(); + filters.enable_bank(0, Mask32::accept_all()); + + // Drop filters to leave filter configuraiton mode. + drop(filters); + + // Enable and wait for bxCAN sync to bus + while let Err(_) = can.enable() {} + + // Send a frame + let mut test: [u8; 8] = [0; 8]; + let id: u16 = 0x500; + + test[0] = 72; + test[1] = 1; + test[2] = 2; + test[3] = 3; + test[4] = 4; + test[5] = 5; + test[6] = 6; + test[7] = 7; + let test_frame = Frame::new_data(StandardId::new(id).unwrap(), test); + can.transmit(&test_frame).unwrap(); + + // Wait for TX to finish + while !can.is_transmitter_idle() {} + + rprintln!(" - CAN tx complete: {:?}", test_frame); + + // Receive the packet back + let r = can.receive(); + + rprintln!(" - CAN rx {:?}", r); + + assert_eq!(Ok(test_frame), r); + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + continue; + } + } +}; From 21f60cec0a16d0bfb6ce38f4ac921758f55b2917 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 31 May 2021 14:23:00 +0200 Subject: [PATCH 3/3] Now type safe --- examples/can-loopback.rs | 2 +- src/can.rs | 46 ++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index c729b1bc..73c8ed1b 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -36,7 +36,7 @@ const APP: () = { let rx = gpioa.pa11.into_af9(&mut gpioa.moder, &mut gpioa.afrh); let tx = gpioa.pa12.into_af9(&mut gpioa.moder, &mut gpioa.afrh); - let can = Can::new(dp.CAN1, (tx, rx)); + let can = Can::new(&mut rcc.apb1r1, dp.CAN1, (tx, rx)); bxcan::Can::new(can) }; diff --git a/src/can.rs b/src/can.rs index 7cbc943e..59db902d 100644 --- a/src/can.rs +++ b/src/can.rs @@ -3,6 +3,7 @@ //! Based on STM32F4xx HAL. use crate::pac::CAN1; +use crate::rcc::APB1R1; mod sealed { pub trait Sealed {} @@ -57,55 +58,50 @@ mod pb13_pb12_af10 { /// Enable/disable peripheral pub trait Enable: sealed::Sealed { /// Enables this peripheral by setting the associated enable bit in an RCC enable register - fn enable(); + fn enable(apb: &mut APB1R1); } impl crate::can::sealed::Sealed for crate::pac::CAN1 {} impl crate::can::Enable for crate::pac::CAN1 { #[inline(always)] - fn enable() { - unsafe { - // NOTE(unsafe) this reference will only be used for atomic writes with no side effects. - let rcc = &(*crate::pac::RCC::ptr()); - // Enable peripheral clock - rcc.apb1enr1.modify(|_, w| w.can1en().set_bit()); - rcc.apb1rstr1.modify(|_, w| w.can1rst().set_bit()); - rcc.apb1rstr1.modify(|_, w| w.can1rst().clear_bit()); - }; + fn enable(apb: &mut APB1R1) { + // Enable peripheral clock + apb.enr().modify(|_, w| w.can1en().set_bit()); + apb.rstr().modify(|_, w| w.can1rst().set_bit()); + apb.rstr().modify(|_, w| w.can1rst().clear_bit()); } } /// Interface to the CAN peripheral. -pub struct Can { - _peripheral: Instance, +pub struct Can { + can: Instance, + pins: Pins, } -impl Can +impl Can where Instance: Enable, + P: Pins, { /// Creates a CAN interface. - pub fn new

(can: Instance, _pins: P) -> Can - where - P: Pins, - { - Instance::enable(); - Can { _peripheral: can } + pub fn new(apb: &mut APB1R1, can: Instance, pins: P) -> Can { + Instance::enable(apb); + Can { can, pins } } - pub fn new_unchecked(can: Instance) -> Can { - Instance::enable(); - Can { _peripheral: can } + // Split the peripheral back into its components. + pub fn split(self) -> (Instance, P) { + (self.can, self.pins) } } -unsafe impl bxcan::Instance for Can { +unsafe impl bxcan::Instance for Can { const REGISTERS: *mut bxcan::RegisterBlock = CAN1::ptr() as *mut _; } -unsafe impl bxcan::FilterOwner for Can { +unsafe impl bxcan::FilterOwner for Can { const NUM_FILTER_BANKS: u8 = 14; } -unsafe impl bxcan::MasterInstance for Can {} +unsafe impl bxcan::MasterInstance for Can {}