Skip to content

Support for CAN peripherals with the bxcan crate #293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added

- LSB/MSB bit format selection for `SPI`
- Support for CAN peripherals with the `bxcan` crate

### Fixed
- Fix > 2 byte i2c reads
Expand Down
21 changes: 18 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ nb = "0.1.2"
cortex-m-rt = "0.6.8"
stm32f1 = "0.11.0"
embedded-dma = "0.1.2"
bxcan = "0.4.0"

[dependencies.void]
default-features = false
Expand All @@ -45,7 +46,7 @@ panic-semihosting = "0.5.2"
panic-itm = "0.4.1"
cortex-m-rtic = "0.5"
cortex-m-semihosting = "0.3.3"
heapless = "0.4.3"
heapless = "0.5.6"
m = "0.1.1"
mfrc522 = "0.2.0"
serde_derive = "1.0.90"
Expand Down Expand Up @@ -78,7 +79,7 @@ doc = []
rt = ["stm32f1/rt"]
stm32f100 = ["stm32f1/stm32f100", "device-selected"]
stm32f101 = ["stm32f1/stm32f101", "device-selected"]
stm32f103 = ["stm32f1/stm32f103", "device-selected"]
stm32f103 = ["stm32f1/stm32f103", "device-selected", "has-can"]
stm32f105 = ["stm32f1/stm32f107", "device-selected", "connectivity"]
stm32f107 = ["stm32f1/stm32f107", "device-selected", "connectivity"]

Expand All @@ -89,7 +90,9 @@ high = ["medium"]
# Devices with 768 Kb ROM or more
xl = ["high"]
# Connectivity line devices (`stm32f105xx` and `stm32f107xx`)
connectivity = ["medium"]
connectivity = ["medium", "has-can"]
# Devices with CAN interface
has-can = []

[profile.dev]
incremental = false
Expand Down Expand Up @@ -131,3 +134,15 @@ required-features = ["rt", "medium"]
[[example]]
name = "exti"
required-features = ["rt"]

[[example]]
name = "can-echo"
required-features = ["has-can"]

[[example]]
name = "can-loopback"
required-features = ["has-can"]

[[example]]
name = "can-rtic"
required-features = ["has-can", "rt"]
90 changes: 90 additions & 0 deletions examples/can-echo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! Simple CAN example.
//! Requires a transceiver connected to PA11, PA12 (CAN1) or PB5 PB6 (CAN2).

#![no_main]
#![no_std]

use panic_halt as _;

use bxcan::filter::Mask32;
use cortex_m_rt::entry;
use nb::block;
use stm32f1xx_hal::{can::Can, pac, prelude::*};

#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();

let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();

// To meet CAN clock accuracy requirements an external crystal or ceramic
// resonator must be used. The blue pill has a 8MHz external crystal.
// Other boards might have a crystal with another frequency or none at all.
rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr);

let mut afio = dp.AFIO.constrain(&mut rcc.apb2);

let mut can1 = {
#[cfg(not(feature = "connectivity"))]
let can = Can::new(dp.CAN1, &mut rcc.apb1, dp.USB);
#[cfg(feature = "connectivity")]
let can = Can::new(dp.CAN1, &mut rcc.apb1);

let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
let rx = gpioa.pa11.into_floating_input(&mut gpioa.crh);
let tx = gpioa.pa12.into_alternate_push_pull(&mut gpioa.crh);
can.assign_pins((tx, rx), &mut afio.mapr);

bxcan::Can::new(can)
};

// APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5%
// Value was calculated with http://www.bittiming.can-wiki.info/
can1.modify_config().set_bit_timing(0x001c_0003);

// Configure filters so that can frames can be received.
let mut filters = can1.modify_filters();
filters.enable_bank(0, Mask32::accept_all());

#[cfg(feature = "connectivity")]
let _can2 = {
let can = Can::new(dp.CAN2, &mut rcc.apb1);

let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
let rx = gpiob.pb5.into_floating_input(&mut gpiob.crl);
let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl);
can.assign_pins((tx, rx), &mut afio.mapr);

let mut can2 = bxcan::Can::new(can);

// APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5%
// Value was calculated with http://www.bittiming.can-wiki.info/
can2.modify_config().set_bit_timing(0x001c_0003);

// A total of 28 filters are shared between the two CAN instances.
// Split them equally between CAN1 and CAN2.
let mut slave_filters = filters.set_split(14).slave_filters();
slave_filters.enable_bank(14, Mask32::accept_all());
can2
};

// Drop filters to leave filter configuraiton mode.
drop(filters);

// Select the interface.
let mut can = can1;
//let mut can = _can2;

// Split the peripheral into transmitter and receiver parts.
block!(can.enable()).unwrap();

// Echo back received packages in sequence.
// See the `can-rtfm` example for an echo implementation that adheres to
// correct frame ordering based on the transfer id.
loop {
if let Ok(frame) = block!(can.receive()) {
block!(can.transmit(&frame)).unwrap();
}
}
}
117 changes: 117 additions & 0 deletions examples/can-loopback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//! Showcases advanced CAN filter capabilities.
//! Does not require additional transceiver hardware.

#![no_main]
#![no_std]

use bxcan::{
filter::{ListEntry16, ListEntry32, Mask16},
ExtendedId, Frame, StandardId,
};
use panic_halt as _;

use cortex_m_rt::entry;
use embedded_hal::digital::v2::OutputPin;
use nb::block;
use stm32f1xx_hal::{can::Can, pac, prelude::*};

#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();

let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();

// To meet CAN clock accuracy requirements, an external crystal or ceramic
// resonator must be used.
rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr);

#[cfg(not(feature = "connectivity"))]
let can = Can::new(dp.CAN1, &mut rcc.apb1, dp.USB);

#[cfg(feature = "connectivity")]
let can = Can::new(dp.CAN1, &mut rcc.apb1);

let mut can = bxcan::Can::new(can);

// Use loopback mode: No pins need to be assigned to peripheral.
// APB1 (PCLK1): 8MHz, Bit rate: 500Bit/s, Sample Point 87.5%
// Value was calculated with http://www.bittiming.can-wiki.info/
can.modify_config()
.set_bit_timing(0x001c_0000)
.set_loopback(true)
.set_silent(true);

let mut filters = can.modify_filters();
assert!(filters.num_banks() > 3);

// The order of the added filters is important: it must match configuration
// of the `split_filters_advanced()` method.

// 2x 11bit id + mask filter bank: Matches 0, 1, 2
// TODO: Make this accept also ID 2
filters.enable_bank(
0,
[
// accepts 0 and 1
Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(1).unwrap()),
// accepts 0 and 2
Mask16::frames_with_std_id(StandardId::new(0).unwrap(), StandardId::new(2).unwrap()),
],
);

// 2x 29bit id filter bank: Matches 4, 5
filters.enable_bank(
1,
[
ListEntry32::data_frames_with_id(ExtendedId::new(4).unwrap()),
ListEntry32::data_frames_with_id(ExtendedId::new(5).unwrap()),
],
);

// 4x 11bit id filter bank: Matches 8, 9, 10, 11
filters.enable_bank(
2,
[
ListEntry16::data_frames_with_id(StandardId::new(8).unwrap()),
ListEntry16::data_frames_with_id(StandardId::new(9).unwrap()),
ListEntry16::data_frames_with_id(StandardId::new(10).unwrap()),
ListEntry16::data_frames_with_id(StandardId::new(11).unwrap()),
],
);

// Enable filters.
drop(filters);

// Sync to the bus and start normal operation.
block!(can.enable()).ok();

// Some messages shall pass the filters.
for &id in &[0, 1, 2, 8, 9, 10, 11] {
let frame_tx = Frame::new_data(StandardId::new(id).unwrap(), [id as u8]);
block!(can.transmit(&frame_tx)).unwrap();
let frame_rx = block!(can.receive()).unwrap();
assert_eq!(frame_tx, frame_rx);
}
for &id in &[4, 5] {
let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]);
block!(can.transmit(&frame_tx)).unwrap();
let frame_rx = block!(can.receive()).unwrap();
assert_eq!(frame_tx, frame_rx);
}

// Some messages shall not be received.
for &id in &[3, 6, 7, 12] {
let frame_tx = Frame::new_data(ExtendedId::new(id).unwrap(), [id as u8]);
block!(can.transmit(&frame_tx)).unwrap();
while !can.is_transmitter_idle() {}

assert!(can.receive().is_err());
}

let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
let mut led = gpiob.pb9.into_push_pull_output(&mut gpiob.crh);
led.set_high().unwrap();

loop {}
}
Loading