Skip to content

Commit 840819f

Browse files
bors[bot]xoviat
andauthored
Merge #246
246: add can support r=thalesfragoso a=xoviat This adds support for can with the bxcan crate. This differs slightly from the stm32f1 pull request and the stm32f7 pull request: the `Can::new` method takes ownership of the pins, which requires the user to configure the pins correctly, and doesn't use the afio functionality, which doesn't appear to be present here. Co-authored-by: xoviat <[email protected]>
2 parents 2dc4277 + 77066a6 commit 840819f

File tree

6 files changed

+250
-2
lines changed

6 files changed

+250
-2
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ jobs:
4646
- uses: actions-rs/cargo@v1
4747
with:
4848
command: check
49-
args: --features=${{ matrix.mcu }},rt,usb_fs,sdio --examples
49+
args: --features=${{ matrix.mcu }},rt,usb_fs,sdio,can --examples

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3939
- Added support for hardware-based CRC32 functionality
4040
- Add `MonoTimer` and `Instant` structs for basic time measurement.
4141
- Added support for I2S and SAI clocks
42+
- Added support for canbus with the bxcan crate.
4243

4344
### Fixed
4445

Cargo.toml

+8-1
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ repository = "https://github.com/stm32-rs/stm32f4xx-hal"
2222
version = "0.8.3"
2323

2424
[package.metadata.docs.rs]
25-
features = ["stm32f429", "rt", "usb_fs"]
25+
features = ["stm32f429", "rt", "usb_fs", "can"]
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"
@@ -88,6 +89,8 @@ stm32f479 = ["stm32f4/stm32f469", "device-selected"]
8889
usb_fs = ["synopsys-usb-otg", "synopsys-usb-otg/fs"]
8990
usb_hs = ["synopsys-usb-otg", "synopsys-usb-otg/hs"]
9091

92+
can = ["bxcan"]
93+
9194
sdio = ["sdio-host"]
9295

9396
[profile.dev]
@@ -138,3 +141,7 @@ required-features = ["rt", "stm32f407"]
138141
[[example]]
139142
name = "qei"
140143
required-features = ["rt", "stm32f411"]
144+
145+
[[example]]
146+
name = "can-send"
147+
required-features = ["can", "stm32f405"]

examples/can-send.rs

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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 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));
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));
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+
83+
test[1] = 1;
84+
test[2] = 2;
85+
test[3] = 3;
86+
test[4] = 4;
87+
test[5] = 5;
88+
test[6] = 6;
89+
test[7] = 7;
90+
loop {
91+
test[0] = count;
92+
let test_frame = Frame::new_data(StandardId::new(id).unwrap(), test);
93+
block!(can.transmit(&test_frame)).unwrap();
94+
if count < 255 {
95+
count += 1;
96+
} else {
97+
count = 0;
98+
}
99+
}
100+
}

src/can.rs

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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::stm32::RCC;
24+
25+
mod sealed {
26+
pub trait Sealed {}
27+
}
28+
29+
pub trait Pins: sealed::Sealed {
30+
type Instance;
31+
}
32+
33+
/*
34+
order: tx, rx similar to serial
35+
*/
36+
macro_rules! pins {
37+
($($PER:ident => ($tx:ident, $rx:ident),)+) => {
38+
$(
39+
impl sealed::Sealed for ($tx<Alternate<AF9>>, $rx<Alternate<AF9>>) {}
40+
impl Pins for ($tx<Alternate<AF9>>, $rx<Alternate<AF9>>) {
41+
type Instance = $PER;
42+
}
43+
)+
44+
}
45+
}
46+
47+
/*
48+
See DS8626 Rev 9 Table 9.
49+
*/
50+
#[cfg(any(feature = "stm32f405", feature = "stm32f407"))]
51+
pins! {
52+
CAN1 => (PA12, PA11),
53+
CAN1 => (PB9, PB8),
54+
CAN1 => (PD1, PD0),
55+
CAN1 => (PH13, PI9),
56+
CAN2 => (PB13, PB12),
57+
CAN2 => (PB6, PB5),
58+
}
59+
60+
/*
61+
See DS10693 Rev 9 Table 11.
62+
*/
63+
#[cfg(feature = "stm32f446")]
64+
pins! {
65+
CAN1 => (PA12, PA11),
66+
CAN1 => (PB9, PB8),
67+
CAN1 => (PD1, PD0),
68+
CAN2 => (PB13, PB12),
69+
CAN2 => (PB6, PB5),
70+
}
71+
72+
/// Enable/disable peripheral
73+
pub trait Enable: sealed::Sealed {
74+
fn enable();
75+
}
76+
77+
macro_rules! bus {
78+
($($PER:ident => ($peren:literal),)+) => {
79+
$(
80+
impl sealed::Sealed for crate::pac::$PER {}
81+
impl Enable for crate::pac::$PER {
82+
#[inline(always)]
83+
fn enable() {
84+
unsafe {
85+
let rcc = &(*RCC::ptr());
86+
bb::set(&rcc.apb1enr, $peren)
87+
};
88+
}
89+
}
90+
)+
91+
}
92+
}
93+
94+
bus! {
95+
CAN1 => (25),
96+
CAN2 => (26),
97+
}
98+
99+
/// Interface to the CAN peripheral.
100+
pub struct Can<Instance> {
101+
_peripheral: Instance,
102+
}
103+
104+
impl<Instance> Can<Instance>
105+
where
106+
Instance: Enable,
107+
{
108+
/// Creates a CAN interaface.
109+
pub fn new<P>(can: Instance, _pins: P) -> Can<Instance>
110+
where
111+
P: Pins<Instance = Instance>,
112+
{
113+
Instance::enable();
114+
Can { _peripheral: can }
115+
}
116+
117+
pub fn new_unchecked(can: Instance) -> Can<Instance> {
118+
Instance::enable();
119+
Can { _peripheral: can }
120+
}
121+
}
122+
123+
unsafe impl bxcan::Instance for Can<CAN1> {
124+
const REGISTERS: *mut bxcan::RegisterBlock = CAN1::ptr() as *mut _;
125+
}
126+
127+
unsafe impl bxcan::Instance for Can<CAN2> {
128+
const REGISTERS: *mut bxcan::RegisterBlock = CAN2::ptr() as *mut _;
129+
}
130+
131+
unsafe impl bxcan::FilterOwner for Can<CAN1> {
132+
const NUM_FILTER_BANKS: u8 = 28;
133+
}
134+
135+
unsafe impl bxcan::MasterInstance for Can<CAN1> {}

src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ pub use crate::stm32::interrupt;
8787
pub mod adc;
8888
#[cfg(feature = "device-selected")]
8989
pub mod bb;
90+
#[cfg(all(
91+
feature = "can",
92+
any(feature = "stm32f405", feature = "stm32f407", feature = "stm32f446")
93+
))]
94+
pub mod can;
9095
#[cfg(feature = "device-selected")]
9196
pub mod crc32;
9297
#[cfg(all(

0 commit comments

Comments
 (0)