Skip to content

Commit a7cd23e

Browse files
author
Cor Peters
committed
Initial FDCAN support
1 parent 41389da commit a7cd23e

21 files changed

+5059
-0
lines changed

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ cortex-m = "0.7.1"
1616
nb = "0.1.1"
1717
stm32g4 = "0.13.0"
1818
paste = "1.0"
19+
bitflags = "1.2"
20+
vcell = "0.1"
21+
static_assertions = "1.1"
1922

2023
[dependencies.cast]
2124
version = "0.2.7"
@@ -40,6 +43,10 @@ version = "1.0.2"
4043
default-features = false
4144
version = "1.1"
4245

46+
[dependencies.defmt]
47+
version = "0.2.3"
48+
optional = true
49+
4350
[dev-dependencies]
4451
cortex-m-rt = "0.6.10"
4552
cortex-m-rtfm = "0.5.1"
@@ -70,6 +77,7 @@ stm32g4a1 = ["stm32g4/stm32g4a1"]
7077
log-itm = ["cortex-m-log/itm"]
7178
log-rtt = []
7279
log-semihost = ["cortex-m-log/semihosting"]
80+
unstable-defmt = ["defmt"]
7381

7482
[profile.dev]
7583
codegen-units = 1

examples/can-echo.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
use crate::hal::{
5+
fdcan::{
6+
config::NominalBitTiming,
7+
filter::{StandardFilter, StandardFilterSlot},
8+
frame::{FrameFormat, TxFrameHeader},
9+
id::StandardId,
10+
FdCan,
11+
},
12+
gpio::GpioExt,
13+
nb::block,
14+
rcc::{Config, RccExt},
15+
stm32::Peripherals,
16+
};
17+
use stm32g4xx_hal as hal;
18+
19+
use cortex_m_rt::entry;
20+
21+
use log::info;
22+
23+
#[macro_use]
24+
mod utils;
25+
26+
#[entry]
27+
fn main() -> ! {
28+
utils::logger::init();
29+
30+
info!("start");
31+
32+
// APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5%
33+
// Value was calculated with http://www.bittiming.can-wiki.info/
34+
// TODO: use the can_bit_timings crate
35+
let btr = NominalBitTiming {
36+
prescaler: 4,
37+
seg1: 13,
38+
seg2: 2,
39+
..Default::default()
40+
};
41+
42+
let dp = Peripherals::take().unwrap();
43+
let _cp = cortex_m::Peripherals::take().expect("cannot take core peripherals");
44+
let rcc = dp.RCC.constrain();
45+
let mut rcc = rcc.freeze(Config::hsi());
46+
47+
let mut can1 = {
48+
let gpioa = dp.GPIOA.split(&mut rcc);
49+
let rx = gpioa.pa11.into_alternate();
50+
let tx = gpioa.pa12.into_alternate();
51+
52+
let can = crate::hal::can::FdCan::new(dp.FDCAN1, (tx, rx), &mut rcc);
53+
let mut can = FdCan::new(can).into_config_mode();
54+
55+
can.set_nominal_bit_timing(btr);
56+
can.set_standard_filter(
57+
StandardFilterSlot::_0,
58+
StandardFilter::accept_all_into_fifo0(),
59+
);
60+
can.into_normal()
61+
};
62+
63+
let mut can2 = {
64+
let gpiob = dp.GPIOB.split(&mut rcc);
65+
let rx = gpiob.pb5.into_alternate();
66+
let tx = gpiob.pb6.into_alternate();
67+
68+
let can = crate::hal::can::FdCan::new(dp.FDCAN2, (tx, rx), &mut rcc);
69+
let mut can = FdCan::new(can).into_config_mode();
70+
71+
can.set_nominal_bit_timing(btr);
72+
can.set_standard_filter(
73+
StandardFilterSlot::_0,
74+
StandardFilter::accept_all_into_fifo0(),
75+
);
76+
77+
can.into_normal()
78+
};
79+
80+
let mut buffer = [0_u32; 2];
81+
let header = TxFrameHeader {
82+
len: buffer.len() as u8 * 4,
83+
id: StandardId::new(0x1).unwrap().into(),
84+
frame_format: FrameFormat::Standard,
85+
bit_rate_switching: false,
86+
marker: None,
87+
};
88+
block!(can1.transmit(header, &mut |b| b.clone_from_slice(&buffer))).unwrap();
89+
90+
loop {
91+
if let Ok(rxheader) = block!(can2.receive0(&mut |h, b| {
92+
buffer.clone_from_slice(b);
93+
h
94+
})) {
95+
block!(
96+
can2.transmit(rxheader.unwrap().to_tx_header(None), &mut |b| b
97+
.clone_from_slice(&buffer))
98+
)
99+
.unwrap();
100+
}
101+
if let Ok(rxheader) = block!(can1.receive0(&mut |h, b| {
102+
buffer.clone_from_slice(b);
103+
h
104+
})) {
105+
block!(
106+
can1.transmit(rxheader.unwrap().to_tx_header(None), &mut |b| b
107+
.clone_from_slice(&buffer))
108+
)
109+
.unwrap();
110+
}
111+
}
112+
}

src/can.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
//! # Controller Area Network (CAN) Interface
2+
//!
3+
4+
use crate::fdcan;
5+
use crate::fdcan::message_ram;
6+
use crate::rcc::Rcc;
7+
use crate::stm32::{self, FDCAN1, FDCAN2, FDCAN3};
8+
9+
mod sealed {
10+
pub trait Sealed {}
11+
}
12+
13+
/// A pair of (TX, RX) pins configured for CAN communication
14+
pub trait Pins: sealed::Sealed {
15+
/// The CAN peripheral that uses these pins
16+
type Instance;
17+
}
18+
19+
/// Implements sealed::Sealed and Pins for a (TX, RX) pair of pins associated with a CAN peripheral
20+
/// The alternate function number can be specified after each pin name. If not specified, both
21+
/// default to AF9.
22+
macro_rules! pins {
23+
($($PER:ident => ($tx:ident<$txaf:ident>, $rx:ident<$rxaf:ident>),)+) => {
24+
$(
25+
impl crate::can::sealed::Sealed for ($tx<crate::gpio::Alternate<$txaf>>, $rx<crate::gpio::Alternate<$rxaf>>) {}
26+
impl crate::can::Pins for ($tx<crate::gpio::Alternate<$txaf>>, $rx<crate::gpio::Alternate<$rxaf>>) {
27+
type Instance = $PER;
28+
}
29+
)+
30+
};
31+
($($PER:ident => ($tx:ident, $rx:ident),)+) => {
32+
pins! { $($PER => ($tx<crate::gpio::AF9>, $rx<crate::gpio::AF9>),)+ }
33+
}
34+
}
35+
36+
//TODO: verify correct pins
37+
mod common_pins {
38+
use crate::gpio::{
39+
gpioa::{PA11, PA12},
40+
gpiob::{PB12, PB13, PB5, PB6, PB8, PB9},
41+
gpiod::{PD0, PD1},
42+
AF7,
43+
};
44+
use crate::stm32::{FDCAN1, FDCAN2};
45+
// All STM32F4 models with CAN support these pins
46+
pins! {
47+
FDCAN1 => (PA12<AF7>, PA11<AF7>),
48+
FDCAN1 => (PD1<AF7>, PD0<AF7>),
49+
FDCAN1 => (PB9<AF7>, PB8<AF7>),
50+
FDCAN2 => (PB13<AF7>, PB12<AF7>),
51+
FDCAN2 => (PB6<AF7>, PB5<AF7>),
52+
}
53+
}
54+
/*
55+
//TODO: add other types
56+
//TODO: verify correct pins
57+
#[cfg(any(feature = "stm32g474"))]
58+
mod pb9_pb8_af8 {
59+
use crate::gpio::{
60+
gpiob::{PB8, PB9},
61+
AF8,
62+
};
63+
use crate::stm32::FDCAN1;
64+
pins! { FDCAN1 => (PB9<AF8>, PB8<AF8>), }
65+
}
66+
*/
67+
/*
68+
//TODO: add other types
69+
//TODO: verify correct pins
70+
#[cfg(any(feature = "stm32g474"))]
71+
mod pb9_pb8_af9 {
72+
use crate::gpio::{
73+
gpiob::{PB8, PB9},
74+
AF9,
75+
};
76+
use crate::stm32::FDCAN1;
77+
pins! { FDCAN1 => (PB9<AF9>, PB8<AF9>), }
78+
}
79+
80+
//TODO: add other types
81+
//TODO: verify correct pins
82+
#[cfg(any(feature = "stm32g474"))]
83+
mod pg1_pg0 {
84+
use crate::gpio::{
85+
gpiog::{PG0, PG1},
86+
AF9,
87+
};
88+
use crate::stm32::FDCAN1;
89+
pins! { FDCAN1 => (PG1<AF9>, PG0<AF9>), }
90+
}
91+
92+
//TODO: add other types
93+
//TODO: verify correct pins
94+
#[cfg(any(feature = "stm32g474"))]
95+
mod pg12_pg11 {
96+
use crate::gpio::{
97+
gpiog::{PG11, PG12},
98+
AF9,
99+
};
100+
use crate::stm32::CAN2;
101+
pins! { CAN2 => (PG12<AF9>, PG11<AF9>), }
102+
}
103+
104+
//TODO: add other types
105+
//TODO: verify correct pins
106+
#[cfg(any(feature = "stm32g474"))]
107+
mod ph13_pi9 {
108+
use crate::gpio::{gpioh::PH13, gpioi::PI9, AF9};
109+
use crate::stm32::CAN1;
110+
pins! { CAN1 => (PH13<AF9>, PI9<AF9>), }
111+
}
112+
*/
113+
/// Enable/disable peripheral
114+
pub trait Enable: sealed::Sealed {
115+
/// Enables this peripheral by setting the associated enable bit in an RCC enable register
116+
fn enable(rcc: &Rcc);
117+
}
118+
119+
/// Implements sealed::Sealed and Enable for a CAN peripheral (e.g. CAN1)
120+
///
121+
/// $peren is the index in RCC_APB1ENR of the enable bit for the CAN peripheral, and the
122+
/// index in RCC_APB1RSTR of the reset bit for the CAN peripheral.
123+
impl crate::can::sealed::Sealed for crate::stm32::FDCAN1 {}
124+
impl crate::can::Enable for crate::stm32::FDCAN1 {
125+
#[inline(always)]
126+
fn enable(rcc: &Rcc) {
127+
// Enable peripheral
128+
rcc.rb.apb1enr1.modify(|_, w| w.fdcanen().set_bit());
129+
}
130+
}
131+
impl crate::can::sealed::Sealed for crate::stm32::FDCAN2 {}
132+
impl crate::can::Enable for crate::stm32::FDCAN2 {
133+
#[inline(always)]
134+
fn enable(rcc: &Rcc) {
135+
// Enable peripheral
136+
rcc.rb.apb1enr1.modify(|_, w| w.fdcanen().set_bit());
137+
}
138+
}
139+
/*
140+
/// Pins and definitions for models with a third CAN peripheral
141+
#[cfg(any(feature = "stm32f413", feature = "stm32f423"))]
142+
mod can3 {
143+
use super::Can;
144+
use crate::gpio::{
145+
gpioa::{PA15, PA8},
146+
gpiob::{PB3, PB4},
147+
AF11,
148+
};
149+
use crate::stm32::CAN3;
150+
pins! {
151+
CAN3 => (PA15<AF11>, PA8<AF11>),
152+
CAN3 => (PB4<AF11>, PB3<AF11>),
153+
}
154+
bus! { CAN3 => (27), }
155+
156+
unsafe impl bxcan::Instance for Can<CAN3> {
157+
const REGISTERS: *mut bxcan::RegisterBlock = CAN3::ptr() as *mut _;
158+
}
159+
160+
unsafe impl bxcan::FilterOwner for Can<CAN3> {
161+
const NUM_FILTER_BANKS: u8 = 14;
162+
}
163+
}
164+
*/
165+
/// Interface to the CAN peripheral.
166+
pub struct FdCan<Instance> {
167+
_peripheral: Instance,
168+
}
169+
170+
impl<Instance> FdCan<Instance>
171+
where
172+
Instance: Enable,
173+
{
174+
/// Creates a CAN interface.
175+
pub fn new<P>(can: Instance, _pins: P, rcc: &Rcc) -> FdCan<Instance>
176+
where
177+
P: Pins<Instance = Instance>,
178+
{
179+
Instance::enable(rcc);
180+
FdCan { _peripheral: can }
181+
}
182+
183+
pub fn new_unchecked(can: Instance, rcc: &Rcc) -> FdCan<Instance> {
184+
Instance::enable(rcc);
185+
FdCan { _peripheral: can }
186+
}
187+
}
188+
189+
unsafe impl fdcan::Instance for FdCan<FDCAN1> {
190+
const REGISTERS: *mut stm32::fdcan::RegisterBlock = FDCAN1::ptr() as *mut _;
191+
}
192+
193+
unsafe impl fdcan::Instance for FdCan<FDCAN2> {
194+
const REGISTERS: *mut stm32::fdcan::RegisterBlock = FDCAN2::ptr() as *mut _;
195+
}
196+
197+
unsafe impl fdcan::Instance for FdCan<FDCAN3> {
198+
const REGISTERS: *mut stm32::fdcan::RegisterBlock = FDCAN3::ptr() as *mut _;
199+
}
200+
201+
unsafe impl message_ram::MsgRamExt for FdCan<FDCAN1> {
202+
const MSG_RAM: *mut message_ram::RegisterBlock = (0x4000_ac00 as *mut _);
203+
}
204+
205+
unsafe impl message_ram::MsgRamExt for FdCan<FDCAN2> {
206+
const MSG_RAM: *mut message_ram::RegisterBlock = (0x4000_af54 as *mut _);
207+
}
208+
209+
unsafe impl message_ram::MsgRamExt for FdCan<FDCAN3> {
210+
const MSG_RAM: *mut message_ram::RegisterBlock = (0x4000_b2a4 as *mut _);
211+
}

0 commit comments

Comments
 (0)