Skip to content

Commit 27abefa

Browse files
committed
CAN: add support with 'bxcan'.
1 parent 364869d commit 27abefa

File tree

6 files changed

+261
-3
lines changed

6 files changed

+261
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
4040
- Add `MonoTimer` and `Instant` structs for basic time measurement.
4141
- Added support for I2S and SAI clocks
4242
- Added support for the Real Time Clock (RTC)
43+
- Added support for canbus with the bxcan crate.
4344

4445
### Fixed
4546

Cargo.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ features = ["stm32f429", "rt", "usb_fs"]
2626
targets = ["thumbv7em-none-eabihf"]
2727

2828
[dependencies]
29+
bxcan = { version = ">=0.3.0", optional = true }
2930
cortex-m = ">=0.5.8,<0.7"
3031
cortex-m-rt = "0.6.10"
3132
nb = "0.1.2"
@@ -65,12 +66,13 @@ micromath = "1.0.0"
6566

6667
[features]
6768
device-selected = []
69+
has-can = ["bxcan"]
6870
rt = ["stm32f4/rt"]
6971
# Note: stm32f4 has only one feature for some very similar device families,
7072
# so it's intended for e.g. stm32f405/415 to both enable stm32f4/stm32f405.
7173
stm32f401 = ["stm32f4/stm32f401", "device-selected"]
72-
stm32f405 = ["stm32f4/stm32f405", "device-selected"]
73-
stm32f407 = ["stm32f4/stm32f407", "device-selected"]
74+
stm32f405 = ["stm32f4/stm32f405", "device-selected", "has-can"]
75+
stm32f407 = ["stm32f4/stm32f407", "device-selected", "has-can"]
7476
stm32f410 = ["stm32f4/stm32f410", "device-selected"]
7577
stm32f411 = ["stm32f4/stm32f411", "device-selected"]
7678
stm32f412 = ["stm32f4/stm32f412", "device-selected"]
@@ -82,7 +84,7 @@ stm32f427 = ["stm32f4/stm32f427", "device-selected"]
8284
stm32f429 = ["stm32f4/stm32f429", "device-selected"]
8385
stm32f437 = ["stm32f4/stm32f427", "device-selected"]
8486
stm32f439 = ["stm32f4/stm32f429", "device-selected"]
85-
stm32f446 = ["stm32f4/stm32f446", "device-selected"]
87+
stm32f446 = ["stm32f4/stm32f446", "device-selected", "has-can"]
8688
stm32f469 = ["stm32f4/stm32f469", "device-selected"]
8789
stm32f479 = ["stm32f4/stm32f469", "device-selected"]
8890

@@ -139,3 +141,7 @@ required-features = ["rt", "stm32f407"]
139141
[[example]]
140142
name = "qei"
141143
required-features = ["rt", "stm32f411"]
144+
145+
[[example]]
146+
name = "can-send"
147+
required-features = ["has-can"]

examples/can-send.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! Simple CAN example.
2+
//! Requires a transceiver connected to PB8, 9 (CAN1) or PB5 PB6 (CAN2).
3+
4+
#![no_main]
5+
#![no_std]
6+
7+
use panic_halt as _;
8+
9+
use bxcan::filter::Mask32;
10+
use bxcan::{Frame, StandardId};
11+
use cortex_m_rt::entry;
12+
use nb::block;
13+
use stm32f4xx_hal::{can::Can, pac, prelude::*};
14+
15+
#[entry]
16+
fn main() -> ! {
17+
let dp = pac::Peripherals::take().unwrap();
18+
19+
let mut rcc = dp.RCC.constrain();
20+
21+
// To meet CAN clock accuracy requirements an external crystal or ceramic
22+
// resonator must be used. The blue pill has a 8MHz external crystal.
23+
// Other boards might have a crystal with another frequency or none at all.
24+
rcc.cfgr.use_hse(8.mhz()).freeze();
25+
26+
let gpiob = dp.GPIOB.split();
27+
let mut can1 = {
28+
let rx = gpiob.pb8.into_alternate_af9();
29+
let tx = gpiob.pb9.into_alternate_af9();
30+
31+
let can = Can::new(dp.CAN1, (tx, rx), &mut rcc.apb1);
32+
33+
bxcan::Can::new(can)
34+
};
35+
can1.configure(|config| {
36+
// APB1 (PCLK1): 8MHz, Bit rate: 500kBit/s, Sample Point 87.5%
37+
// Value was calculated with http://www.bittiming.can-wiki.info/
38+
config.set_bit_timing(0x001c_0000);
39+
});
40+
41+
// Configure filters so that can frames can be received.
42+
let mut filters = can1.modify_filters();
43+
filters.enable_bank(0, Mask32::accept_all());
44+
45+
let can2 = {
46+
let tx = gpiob.pb13.into_alternate_af9();
47+
let rx = gpiob.pb12.into_alternate_af9();
48+
49+
let can = Can::new(dp.CAN2, (tx, rx), &mut rcc.apb1);
50+
51+
let mut can2 = bxcan::Can::new(can);
52+
can2.configure(|config| {
53+
// APB1 (PCLK1): 8MHz, Bit rate: 500kBit/s, Sample Point 87.5%
54+
// Value was calculated with http://www.bittiming.can-wiki.info/
55+
config.set_bit_timing(0x001c_0000);
56+
});
57+
58+
// A total of 28 filters are shared between the two CAN instances.
59+
// Split them equally between CAN1 and CAN2.
60+
filters.set_split(14);
61+
let mut slave_filters = filters.slave_filters();
62+
slave_filters.enable_bank(14, Mask32::accept_all());
63+
can2
64+
};
65+
66+
// Drop filters to leave filter configuraiton mode.
67+
drop(filters);
68+
69+
// Select the interface.
70+
let mut can = can1;
71+
//let mut can = can2;
72+
73+
// Split the peripheral into transmitter and receiver parts.
74+
block!(can.enable()).unwrap();
75+
76+
// Echo back received packages in sequence.
77+
// See the `can-rtfm` example for an echo implementation that adheres to
78+
// correct frame ordering based on the transfer id.
79+
let mut test: [u8; 8] = [0; 8];
80+
let mut count: u8 = 0;
81+
let id: u16 = 0x500;
82+
loop {
83+
test[0] = count;
84+
test[1] = 1;
85+
test[2] = 2;
86+
test[3] = 3;
87+
test[4] = 4;
88+
test[5] = 5;
89+
test[6] = 6;
90+
test[7] = 7;
91+
let test_frame = Frame::new_data(StandardId::new(id).unwrap(), test);
92+
block!(can.transmit(&test_frame)).unwrap();
93+
if count < 255 {
94+
count += 1;
95+
} else {
96+
count = 0;
97+
}
98+
}
99+
}

src/can.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//! # Controller Area Network (CAN) Interface
2+
//!
3+
4+
use crate::bb;
5+
#[cfg(any(feature = "stm32f405", feature = "stm32f407"))]
6+
use crate::gpio::{
7+
gpioa::{PA11, PA12},
8+
gpiob::{PB12, PB13, PB5, PB6, PB8, PB9},
9+
gpiod::{PD0, PD1},
10+
gpioh::PH13,
11+
gpioi::PI9,
12+
Alternate, AF9,
13+
};
14+
15+
#[cfg(feature = "stm32f446")]
16+
use crate::gpio::{
17+
gpioa::{PA11, PA12},
18+
gpiob::{PB12, PB13, PB5, PB6, PB8, PB9},
19+
gpiod::{PD0, PD1},
20+
Alternate, AF9,
21+
};
22+
use crate::pac::{CAN1, CAN2};
23+
use crate::rcc::APB1;
24+
25+
mod sealed {
26+
pub trait Sealed {}
27+
28+
/// Bus associated to peripheral
29+
pub trait RccBus {
30+
/// Bus type;
31+
type Bus;
32+
}
33+
}
34+
35+
pub trait Pins: sealed::Sealed {
36+
type Instance;
37+
}
38+
39+
/*
40+
order: tx, rx similar to serial
41+
*/
42+
macro_rules! pins {
43+
($($PER:ident => ($tx:ident, $rx:ident),)+) => {
44+
$(
45+
impl sealed::Sealed for ($tx<Alternate<AF9>>, $rx<Alternate<AF9>>) {}
46+
impl Pins for ($tx<Alternate<AF9>>, $rx<Alternate<AF9>>) {
47+
type Instance = $PER;
48+
}
49+
)+
50+
}
51+
}
52+
53+
/*
54+
See DS8626 Rev 9 Table 9.
55+
*/
56+
#[cfg(any(feature = "stm32f405", feature = "stm32f407"))]
57+
pins! {
58+
CAN1 => (PA12, PA11),
59+
CAN1 => (PB9, PB8),
60+
CAN1 => (PD1, PD0),
61+
CAN1 => (PH13, PI9),
62+
CAN2 => (PB13, PB12),
63+
CAN2 => (PB6, PB5),
64+
}
65+
66+
/*
67+
See DS10693 Rev 9 Table 11.
68+
*/
69+
#[cfg(feature = "stm32f446")]
70+
pins! {
71+
CAN1 => (PA12, PA11),
72+
CAN1 => (PB9, PB8),
73+
CAN1 => (PD1, PD0),
74+
CAN2 => (PB13, PB12),
75+
CAN2 => (PB6, PB5),
76+
}
77+
78+
use sealed::RccBus;
79+
80+
/// Enable/disable peripheral
81+
pub trait Enable: RccBus {
82+
fn enable(apb: &mut Self::Bus);
83+
}
84+
85+
macro_rules! bus {
86+
($($PER:ident => ($apbX:ty, $peren:literal),)+) => {
87+
$(
88+
impl RccBus for crate::pac::$PER {
89+
type Bus = $apbX;
90+
}
91+
impl Enable for crate::pac::$PER {
92+
#[inline(always)]
93+
fn enable(apb: &mut Self::Bus) {
94+
unsafe { bb::set(apb.enr(), $peren) };
95+
}
96+
}
97+
)+
98+
}
99+
}
100+
101+
bus! {
102+
CAN1 => (APB1, 25),
103+
CAN2 => (APB1, 26),
104+
}
105+
106+
/// Interface to the CAN peripheral.
107+
pub struct Can<Instance> {
108+
_peripheral: Instance,
109+
}
110+
111+
impl<Instance> Can<Instance>
112+
where
113+
Instance: Enable<Bus = APB1>,
114+
{
115+
/// Creates a CAN interaface.
116+
pub fn new<P>(can: Instance, _pins: P, apb: &mut APB1) -> Can<Instance>
117+
where
118+
P: Pins<Instance = Instance>,
119+
{
120+
Instance::enable(apb);
121+
Can { _peripheral: can }
122+
}
123+
124+
pub fn new_unchecked<P>(can: Instance, apb: &mut APB1) -> Can<Instance>
125+
where
126+
P: Pins<Instance = Instance>,
127+
{
128+
Instance::enable(apb);
129+
Can { _peripheral: can }
130+
}
131+
}
132+
133+
unsafe impl bxcan::Instance for Can<CAN1> {
134+
const REGISTERS: *mut bxcan::RegisterBlock = CAN1::ptr() as *mut _;
135+
}
136+
137+
unsafe impl bxcan::Instance for Can<CAN2> {
138+
const REGISTERS: *mut bxcan::RegisterBlock = CAN2::ptr() as *mut _;
139+
}
140+
141+
unsafe impl bxcan::FilterOwner for Can<CAN1> {
142+
const NUM_FILTER_BANKS: u8 = 28;
143+
}
144+
145+
unsafe impl bxcan::MasterInstance for Can<CAN1> {}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ pub use crate::stm32::interrupt;
8787
pub mod adc;
8888
#[cfg(feature = "device-selected")]
8989
pub mod bb;
90+
#[cfg(all(feature = "device-selected", feature = "has-can"))]
91+
pub mod can;
9092
#[cfg(feature = "device-selected")]
9193
pub mod crc32;
9294
#[cfg(all(

src/rcc/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ impl APB1 {
253253
// NOTE(unsafe) this proxy grants exclusive access to this register
254254
unsafe { &(*RCC::ptr()).apb1enr }
255255
}
256+
257+
pub(crate) fn rstr(&mut self) -> &rcc::APB1RSTR {
258+
// NOTE(unsafe) this proxy grants exclusive access to this register
259+
unsafe { &(*RCC::ptr()).apb1rstr }
260+
}
256261
}
257262

258263
impl APB1 {

0 commit comments

Comments
 (0)