Skip to content

Commit a377cf4

Browse files
committed
Support for CAN peripherals with the bxcan crate
1 parent b3e2d75 commit a377cf4

File tree

7 files changed

+615
-3
lines changed

7 files changed

+615
-3
lines changed

Cargo.toml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ nb = "0.1.2"
2121
cortex-m-rt = "0.6.8"
2222
stm32f1 = "0.11.0"
2323
embedded-dma = "0.1.2"
24+
bxcan = { git = "https://github.com/stm32-rs/bxcan" }
2425

2526
[dependencies.void]
2627
default-features = false
@@ -45,7 +46,7 @@ panic-semihosting = "0.5.2"
4546
panic-itm = "0.4.1"
4647
cortex-m-rtfm = "0.5"
4748
cortex-m-semihosting = "0.3.3"
48-
heapless = "0.4.3"
49+
heapless = "0.5.6"
4950
m = "0.1.1"
5051
mfrc522 = "0.2.0"
5152
serde_derive = "1.0.90"
@@ -78,7 +79,7 @@ doc = []
7879
rt = ["stm32f1/rt"]
7980
stm32f100 = ["stm32f1/stm32f100", "device-selected"]
8081
stm32f101 = ["stm32f1/stm32f101", "device-selected"]
81-
stm32f103 = ["stm32f1/stm32f103", "device-selected"]
82+
stm32f103 = ["stm32f1/stm32f103", "device-selected", "has-can"]
8283
stm32f105 = ["stm32f1/stm32f107", "device-selected", "connectivity"]
8384
stm32f107 = ["stm32f1/stm32f107", "device-selected", "connectivity"]
8485

@@ -89,7 +90,9 @@ high = ["medium"]
8990
# Devices with 768 Kb ROM or more
9091
xl = ["high"]
9192
# Connectivity line devices (`stm32f105xx` and `stm32f107xx`)
92-
connectivity = ["medium"]
93+
connectivity = ["medium", "has-can"]
94+
# Devices with CAN interface
95+
has-can = []
9396

9497
[profile.dev]
9598
incremental = false
@@ -131,3 +134,15 @@ required-features = ["rt", "medium"]
131134
[[example]]
132135
name = "exti"
133136
required-features = ["rt"]
137+
138+
[[example]]
139+
name = "can-echo"
140+
required-features = ["has-can"]
141+
142+
[[example]]
143+
name = "can-loopback"
144+
required-features = ["has-can"]
145+
146+
[[example]]
147+
name = "can-rtfm"
148+
required-features = ["has-can", "rt"]

examples/can-echo.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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 stm32f1xx_hal::{can::Can, pac, prelude::*};
13+
14+
#[entry]
15+
fn main() -> ! {
16+
let dp = pac::Peripherals::take().unwrap();
17+
18+
let mut flash = dp.FLASH.constrain();
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(&mut flash.acr);
25+
26+
let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
27+
28+
let mut can1 = {
29+
#[cfg(not(feature = "connectivity"))]
30+
let can = Can::new(dp.CAN1, &mut rcc.apb1, dp.USB);
31+
#[cfg(feature = "connectivity")]
32+
let can = Can::new(dp.CAN1, &mut rcc.apb1);
33+
34+
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
35+
let rx = gpioa.pa11.into_floating_input(&mut gpioa.crh);
36+
let tx = gpioa.pa12.into_alternate_push_pull(&mut gpioa.crh);
37+
can.assign_pins((tx, rx), &mut afio.mapr);
38+
39+
bxcan::Can::new(can)
40+
};
41+
can1.configure(|config| {
42+
// APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5%
43+
// Value was calculated with http://www.bittiming.can-wiki.info/
44+
config.set_bit_timing(0x001c_0003);
45+
});
46+
47+
// Configure filters so that can frames can be received.
48+
let mut filters = can1.modify_filters();
49+
filters.enable_bank(0, Mask32::accept_all());
50+
51+
#[cfg(feature = "connectivity")]
52+
let can2 = {
53+
let can = Can::new(dp.CAN2, &mut rcc.apb1);
54+
55+
let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
56+
let rx = gpiob.pb5.into_floating_input(&mut gpiob.crl);
57+
let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
58+
can.assign_pins((tx, rx), &mut afio.mapr);
59+
60+
let mut can2 = bxcan::Can::new(can);
61+
can2.configure(|config| {
62+
// APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5%
63+
// Value was calculated with http://www.bittiming.can-wiki.info/
64+
config.set_bit_timing(0x001c_0003);
65+
});
66+
67+
// A total of 28 filters are shared between the two CAN instances.
68+
// Split them equally between CAN1 and CAN2.
69+
filters.set_split(14);
70+
let mut slave_filters = filters.slave_filters();
71+
slave_filters.enable_bank(14, Mask32::accept_all());
72+
can2
73+
};
74+
75+
// Drop filters to leave filter configuraiton mode.
76+
drop(filters);
77+
78+
// Select the interface.
79+
let mut can = can1;
80+
//let mut can = can2;
81+
82+
// Split the peripheral into transmitter and receiver parts.
83+
block!(can.enable()).unwrap();
84+
85+
// Echo back received packages in sequence.
86+
// See the `can-rtfm` example for an echo implementation that adheres to
87+
// correct frame ordering based on the transfer id.
88+
loop {
89+
if let Ok(frame) = block!(can.receive()) {
90+
block!(can.transmit(&frame)).unwrap();
91+
}
92+
}
93+
}

examples/can-loopback.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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 embedded_hal::digital::v2::OutputPin;
15+
use nb::block;
16+
use stm32f1xx_hal::{can::Can, pac, prelude::*};
17+
18+
#[entry]
19+
fn main() -> ! {
20+
let dp = pac::Peripherals::take().unwrap();
21+
22+
let mut flash = dp.FLASH.constrain();
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.
27+
rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr);
28+
29+
#[cfg(not(feature = "connectivity"))]
30+
let can = Can::new(dp.CAN1, &mut rcc.apb1, dp.USB);
31+
32+
#[cfg(feature = "connectivity")]
33+
let can = Can::new(dp.CAN1, &mut rcc.apb1);
34+
35+
let mut can = bxcan::Can::new(can);
36+
37+
// Use loopback mode: No pins need to be assigned to peripheral.
38+
can.configure(|config| {
39+
// APB1 (PCLK1): 8MHz, Bit rate: 500Bit/s, Sample Point 87.5%
40+
// Value was calculated with http://www.bittiming.can-wiki.info/
41+
config.set_bit_timing(0x001c_0000);
42+
config.set_loopback(true);
43+
config.set_silent(true);
44+
});
45+
46+
let mut filters = can.modify_filters();
47+
assert!(filters.num_banks() > 3);
48+
49+
// The order of the added filters is important: it must match configuration
50+
// of the `split_filters_advanced()` method.
51+
52+
// 2x 11bit id + mask filter bank: Matches 0, 1, 2
53+
// TODO: Make this accept also ID 2
54+
filters.enable_bank(
55+
0,
56+
[
57+
Mask16::frames_with_std_id(StandardId::new(0).unwrap()),
58+
Mask16::frames_with_std_id(StandardId::new(1).unwrap()),
59+
],
60+
);
61+
62+
// 2x 29bit id filter bank: Matches 4, 5
63+
filters.enable_bank(
64+
1,
65+
[
66+
ListEntry32::data_frames_with_id(ExtendedId::new(4).unwrap()),
67+
ListEntry32::data_frames_with_id(ExtendedId::new(5).unwrap()),
68+
],
69+
);
70+
71+
// 4x 11bit id filter bank: Matches 8, 9, 10, 11
72+
filters.enable_bank(
73+
2,
74+
[
75+
ListEntry16::data_frames_with_id(StandardId::new(8).unwrap()),
76+
ListEntry16::data_frames_with_id(StandardId::new(9).unwrap()),
77+
ListEntry16::data_frames_with_id(StandardId::new(10).unwrap()),
78+
ListEntry16::data_frames_with_id(StandardId::new(11).unwrap()),
79+
],
80+
);
81+
82+
// Enable filters.
83+
drop(filters);
84+
85+
// Sync to the bus and start normal operation.
86+
block!(can.enable()).ok();
87+
88+
// Some messages shall pass the filters.
89+
for &id in &[0, 1, 8, 9, 10, 11] {
90+
let frame_tx = Frame::new_data(StandardId::new(id).unwrap(), [id as u8]);
91+
block!(can.transmit(&frame_tx)).unwrap();
92+
let frame_rx = block!(can.receive()).unwrap();
93+
assert_eq!(frame_tx, frame_rx);
94+
}
95+
for &id in &[4, 5] {
96+
let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]);
97+
block!(can.transmit(&frame_tx)).unwrap();
98+
let frame_rx = block!(can.receive()).unwrap();
99+
assert_eq!(frame_tx, frame_rx);
100+
}
101+
102+
// Some messages shall not be received.
103+
for &id in &[3, 6, 7, 12] {
104+
let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]);
105+
block!(can.transmit(&frame_tx)).unwrap();
106+
while !can.is_transmitter_idle() {}
107+
108+
assert!(can.receive().is_err());
109+
}
110+
111+
let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
112+
let mut led = gpiob.pb9.into_push_pull_output(&mut gpiob.crh);
113+
led.set_high().unwrap();
114+
115+
loop {}
116+
}

0 commit comments

Comments
 (0)