Skip to content

Commit 0f367e6

Browse files
timokroegerTheZoq2
andauthored
Support for CAN peripherals with the bxcan crate (#293)
* Support for CAN peripherals with the `bxcan` crate * can: Update loopback example with masked filters * can: Rename `rtfm` to `rtic` in loopback example * can: Use v0.3.0 of the `bxcan` crate * can: Fix warnings in examples * can: Update to bxcan v0.4.0 * Add CHANGELOG entry Co-authored-by: TheZoq2 <[email protected]>
1 parent fcf5741 commit 0f367e6

File tree

8 files changed

+611
-3
lines changed

8 files changed

+611
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1111

1212
- Support for OpenDrain pin configuration on SPI CLK and MOSI pins
1313
- LSB/MSB bit format selection for `SPI`
14+
- Support for CAN peripherals with the `bxcan` crate
1415

1516
### Fixed
1617
- Fix > 2 byte i2c reads

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 = "0.4.0"
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-rtic = "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-rtic"
148+
required-features = ["has-can", "rt"]

examples/can-echo.rs

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

examples/can-loopback.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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+
// APB1 (PCLK1): 8MHz, Bit rate: 500Bit/s, Sample Point 87.5%
39+
// Value was calculated with http://www.bittiming.can-wiki.info/
40+
can.modify_config()
41+
.set_bit_timing(0x001c_0000)
42+
.set_loopback(true)
43+
.set_silent(true);
44+
45+
let mut filters = can.modify_filters();
46+
assert!(filters.num_banks() > 3);
47+
48+
// The order of the added filters is important: it must match configuration
49+
// of the `split_filters_advanced()` method.
50+
51+
// 2x 11bit id + mask filter bank: Matches 0, 1, 2
52+
// TODO: Make this accept also ID 2
53+
filters.enable_bank(
54+
0,
55+
[
56+
// accepts 0 and 1
57+
Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(1).unwrap()),
58+
// accepts 0 and 2
59+
Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(2).unwrap()),
60+
],
61+
);
62+
63+
// 2x 29bit id filter bank: Matches 4, 5
64+
filters.enable_bank(
65+
1,
66+
[
67+
ListEntry32::data_frames_with_id(ExtendedId::new(4).unwrap()),
68+
ListEntry32::data_frames_with_id(ExtendedId::new(5).unwrap()),
69+
],
70+
);
71+
72+
// 4x 11bit id filter bank: Matches 8, 9, 10, 11
73+
filters.enable_bank(
74+
2,
75+
[
76+
ListEntry16::data_frames_with_id(StandardId::new(8).unwrap()),
77+
ListEntry16::data_frames_with_id(StandardId::new(9).unwrap()),
78+
ListEntry16::data_frames_with_id(StandardId::new(10).unwrap()),
79+
ListEntry16::data_frames_with_id(StandardId::new(11).unwrap()),
80+
],
81+
);
82+
83+
// Enable filters.
84+
drop(filters);
85+
86+
// Sync to the bus and start normal operation.
87+
block!(can.enable()).ok();
88+
89+
// Some messages shall pass the filters.
90+
for &id in &[0, 1, 2, 8, 9, 10, 11] {
91+
let frame_tx = Frame::new_data(StandardId::new(id).unwrap(), [id as u8]);
92+
block!(can.transmit(&frame_tx)).unwrap();
93+
let frame_rx = block!(can.receive()).unwrap();
94+
assert_eq!(frame_tx, frame_rx);
95+
}
96+
for &id in &[4, 5] {
97+
let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]);
98+
block!(can.transmit(&frame_tx)).unwrap();
99+
let frame_rx = block!(can.receive()).unwrap();
100+
assert_eq!(frame_tx, frame_rx);
101+
}
102+
103+
// Some messages shall not be received.
104+
for &id in &[3, 6, 7, 12] {
105+
let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]);
106+
block!(can.transmit(&frame_tx)).unwrap();
107+
while !can.is_transmitter_idle() {}
108+
109+
assert!(can.receive().is_err());
110+
}
111+
112+
let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
113+
let mut led = gpiob.pb9.into_push_pull_output(&mut gpiob.crh);
114+
led.set_high().unwrap();
115+
116+
loop {}
117+
}

0 commit comments

Comments
 (0)