Skip to content

Commit e293dc3

Browse files
authored
Merge pull request #108 from santiagoalvarez/master
CAN support
2 parents a7a34a9 + 832b1c2 commit e293dc3

File tree

7 files changed

+330
-3
lines changed

7 files changed

+330
-3
lines changed

Cargo.toml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ micromath = "1.0.0"
2525
synopsys-usb-otg = { version = "0.2.3", features = ["cortex-m"], optional = true }
2626
stm32-fmc = { version = "0.2.0", features = ["sdram"], optional = true }
2727
rand_core = "0.5.1"
28+
bxcan = "0.4"
2829

2930
[dependencies.bare-metal]
3031
version = "0.2.4"
@@ -62,7 +63,7 @@ stm32f730 = ["stm32f7/stm32f730", "device-selected", "usb_hs_phy"]
6263
stm32f732 = ["stm32f7/stm32f7x2", "device-selected"]
6364
stm32f733 = ["stm32f7/stm32f7x3", "device-selected", "usb_hs_phy"]
6465
stm32f745 = ["stm32f7/stm32f745", "device-selected", "fmc"]
65-
stm32f746 = ["stm32f7/stm32f7x6", "device-selected", "ltdc", "fmc"]
66+
stm32f746 = ["stm32f7/stm32f7x6", "device-selected", "ltdc", "fmc", "has-can"]
6667
stm32f756 = ["stm32f7/stm32f7x6", "device-selected", "ltdc", "fmc"]
6768
stm32f765 = ["stm32f7/stm32f765", "device-selected", "fmc"]
6869
stm32f767 = ["stm32f7/stm32f7x7", "device-selected", "ltdc", "fmc"]
@@ -74,6 +75,8 @@ stm32f779 = ["stm32f7/stm32f7x9", "device-selected", "ltdc", "fmc"]
7475
usb_fs = ["synopsys-usb-otg", "synopsys-usb-otg/fs"]
7576
usb_hs = ["synopsys-usb-otg", "synopsys-usb-otg/hs"]
7677

78+
has-can = []
79+
7780
[profile.dev]
7881
incremental = false
7982
codegen-units = 1
@@ -132,4 +135,12 @@ name = "rng"
132135

133136
[[example]]
134137
name = "stm32f7disco-qspi-flash"
135-
required-features = ["stm32f746", "rt"]
138+
required-features = ["stm32f746", "rt"]
139+
140+
[[example]]
141+
name = "can-echo"
142+
required-features = ["has-can"]
143+
144+
[[example]]
145+
name = "can-loopback"
146+
required-features = ["has-can"]

examples/can-echo.rs

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

examples/can-loopback.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//! Showcases advanced CAN filter capabilities.
2+
//! Does not require additional transceiver hardware.
3+
4+
#![no_main]
5+
#![no_std]
6+
7+
use bxcan::{
8+
filter::{ListEntry16, ListEntry32, Mask16},
9+
ExtendedId, Frame, StandardId,
10+
};
11+
use panic_halt as _;
12+
13+
use cortex_m_rt::entry;
14+
use nb::block;
15+
use stm32f7xx_hal::{
16+
can::Can,
17+
pac,
18+
prelude::*,
19+
rcc::{HSEClock, HSEClockMode},
20+
};
21+
22+
#[entry]
23+
fn main() -> ! {
24+
let dp = pac::Peripherals::take().unwrap();
25+
26+
let mut rcc = dp.RCC.constrain();
27+
28+
// To meet CAN clock accuracy requirements, an external crystal or ceramic
29+
// resonator must be used.
30+
let _clocks = rcc
31+
.cfgr
32+
.hse(HSEClock::new(25.mhz(), HSEClockMode::Bypass))
33+
.sysclk(216.mhz())
34+
.hclk(216.mhz())
35+
.freeze();
36+
37+
let gpioa = dp.GPIOA.split();
38+
39+
let rx = gpioa.pa11.into_alternate_af9();
40+
let tx = gpioa.pa12.into_alternate_af9();
41+
42+
let can = Can::new(dp.CAN1, &mut rcc.apb1, (tx, rx));
43+
44+
let mut can = bxcan::Can::new(can);
45+
46+
// Use loopback mode: No pins need to be assigned to peripheral.
47+
can.configure(|config| {
48+
// APB1 (PCLK1): 130MHz, Bit rate: 512kBit/s, Sample Point 87.5%
49+
// Value was calculated with http://www.bittiming.can-wiki.info/
50+
config.set_bit_timing(0x001e_000b);
51+
config.set_loopback(true);
52+
config.set_silent(true);
53+
});
54+
55+
let mut filters = can.modify_filters();
56+
assert!(filters.num_banks() > 3);
57+
58+
// The order of the added filters is important: it must match configuration
59+
// of the `split_filters_advanced()` method.
60+
61+
// 2x 11bit id + mask filter bank: Matches 0, 1, 2
62+
// TODO: Make this accept also ID 2
63+
filters.enable_bank(
64+
0,
65+
[
66+
// accepts 0 and 1
67+
Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(1).unwrap()),
68+
// accepts 0 and 2
69+
Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(2).unwrap()),
70+
],
71+
);
72+
73+
// 2x 29bit id filter bank: Matches 4, 5
74+
filters.enable_bank(
75+
1,
76+
[
77+
ListEntry32::data_frames_with_id(ExtendedId::new(4).unwrap()),
78+
ListEntry32::data_frames_with_id(ExtendedId::new(5).unwrap()),
79+
],
80+
);
81+
82+
// 4x 11bit id filter bank: Matches 8, 9, 10, 11
83+
filters.enable_bank(
84+
2,
85+
[
86+
ListEntry16::data_frames_with_id(StandardId::new(8).unwrap()),
87+
ListEntry16::data_frames_with_id(StandardId::new(9).unwrap()),
88+
ListEntry16::data_frames_with_id(StandardId::new(10).unwrap()),
89+
ListEntry16::data_frames_with_id(StandardId::new(11).unwrap()),
90+
],
91+
);
92+
93+
// Enable filters.
94+
drop(filters);
95+
96+
// Sync to the bus and start normal operation.
97+
block!(can.enable()).ok();
98+
99+
// Some messages shall pass the filters.
100+
for &id in &[0, 1, 2, 8, 9, 10, 11] {
101+
let frame_tx = Frame::new_data(StandardId::new(id).unwrap(), [id as u8]);
102+
block!(can.transmit(&frame_tx)).unwrap();
103+
let frame_rx = block!(can.receive()).unwrap();
104+
assert_eq!(frame_tx, frame_rx);
105+
}
106+
for &id in &[4, 5] {
107+
let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]);
108+
block!(can.transmit(&frame_tx)).unwrap();
109+
let frame_rx = block!(can.receive()).unwrap();
110+
assert_eq!(frame_tx, frame_rx);
111+
}
112+
113+
// Some messages shall not be received.
114+
for &id in &[3, 6, 7, 12] {
115+
let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]);
116+
block!(can.transmit(&frame_tx)).unwrap();
117+
while !can.is_transmitter_idle() {}
118+
119+
assert!(can.receive().is_err());
120+
}
121+
122+
loop {}
123+
}

examples/rng.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ fn main() -> ! {
1717
let val = rng.get_rand().unwrap();
1818
hprintln!("random value {}", val).unwrap();
1919
loop {
20-
core::sync::atomic::spin_loop_hint();
20+
core::hint::spin_loop();
2121
}
2222
}

src/can.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! # Controller Area Network (CAN) Interface
2+
//!
3+
//! ## Alternate function remapping
4+
//!
5+
//! TX: Alternate Push-Pull Output
6+
//! RX: Alternate AF9 Alternate
7+
//!
8+
//! ### CAN1
9+
//!
10+
//! | Function | NoRemap | Remap |
11+
//! |----------|---------|-------|
12+
//! | TX | PA12 | PB9 |
13+
//! | RX | PA11 | PB8 |
14+
//!
15+
//! ### CAN2
16+
//!
17+
//! | Function | NoRemap | Remap |
18+
//! |----------|---------|-------|
19+
//! | TX | PB6 | PB13 |
20+
//! | RX | PB5 | PB12 |
21+
22+
use crate::gpio::gpiob::{PB12, PB13, PB5, PB6, PB8, PB9};
23+
use crate::gpio::{
24+
gpioa::{PA11, PA12},
25+
Alternate, AF9,
26+
};
27+
use crate::pac::CAN1;
28+
use crate::pac::CAN2;
29+
use crate::rcc::APB1;
30+
31+
mod sealed {
32+
pub trait Sealed {}
33+
}
34+
35+
pub trait Pins: sealed::Sealed {
36+
type Instance;
37+
}
38+
39+
impl sealed::Sealed for (PA12<Alternate<AF9>>, PA11<Alternate<AF9>>) {}
40+
impl Pins for (PA12<Alternate<AF9>>, PA11<Alternate<AF9>>) {
41+
type Instance = CAN1;
42+
}
43+
44+
impl sealed::Sealed for (PB9<Alternate<AF9>>, PB8<Alternate<AF9>>) {}
45+
impl Pins for (PB9<Alternate<AF9>>, PB8<Alternate<AF9>>) {
46+
type Instance = CAN1;
47+
}
48+
49+
impl sealed::Sealed for (PB6<Alternate<AF9>>, PB5<Alternate<AF9>>) {}
50+
impl Pins for (PB6<Alternate<AF9>>, PB5<Alternate<AF9>>) {
51+
type Instance = CAN2;
52+
}
53+
54+
impl sealed::Sealed for (PB13<Alternate<AF9>>, PB12<Alternate<AF9>>) {}
55+
impl Pins for (PB13<Alternate<AF9>>, PB12<Alternate<AF9>>) {
56+
type Instance = CAN2;
57+
}
58+
59+
/// Interface to the CAN peripheral.
60+
pub struct Can<Instance> {
61+
_peripheral: Instance,
62+
}
63+
64+
impl<Instance> Can<Instance>
65+
where
66+
Instance: crate::rcc::Enable<Bus = APB1>,
67+
{
68+
/// Creates a CAN interaface.
69+
pub fn new<P>(can: Instance, apb: &mut APB1, _pins: P) -> Can<Instance>
70+
where
71+
P: Pins<Instance = Instance>,
72+
{
73+
Instance::enable(apb);
74+
Can { _peripheral: can }
75+
}
76+
}
77+
78+
unsafe impl bxcan::Instance for Can<CAN1> {
79+
const REGISTERS: *mut bxcan::RegisterBlock = CAN1::ptr() as *mut _;
80+
}
81+
82+
unsafe impl bxcan::Instance for Can<CAN2> {
83+
const REGISTERS: *mut bxcan::RegisterBlock = CAN2::ptr() as *mut _;
84+
}
85+
86+
unsafe impl bxcan::FilterOwner for Can<CAN1> {
87+
const NUM_FILTER_BANKS: u8 = 28;
88+
}
89+
90+
unsafe impl bxcan::MasterInstance for Can<CAN1> {}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ pub use stm32f7::stm32f7x9 as pac;
7171
#[cfg(feature = "rt")]
7272
pub use crate::pac::interrupt;
7373

74+
#[cfg(all(feature = "device-selected", feature = "has-can"))]
75+
pub mod can;
76+
7477
#[cfg(feature = "device-selected")]
7578
pub mod delay;
7679

src/rcc.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,12 @@ bus! {
996996
DMA2D => (AHB1, dma2den, dma2drst),
997997
}
998998

999+
#[cfg(feature = "has-can")]
1000+
bus! {
1001+
CAN1 => (APB1, can1en, can1rst),
1002+
CAN2 => (APB1, can2en, can2rst),
1003+
}
1004+
9991005
#[cfg(test)]
10001006
mod tests {
10011007
use super::{FreqRequest, CFGR};

0 commit comments

Comments
 (0)