Skip to content

Commit

Permalink
Merge pull request #186 from zklapow/can-fix-patch
Browse files Browse the repository at this point in the history
Small fixes for CAN
  • Loading branch information
Sh3Rm4n authored Mar 7, 2021
2 parents 1053b01 + 30ac7f2 commit 9e29ec6
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 30 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Changed

- Added support for more CAN bit rates and modes. ([#186](https://github.com/stm32-rs/stm32f3xx-hal/pull/186)

## [v0.6.1] - 2020-12-10

### Changed
Expand Down
127 changes: 97 additions & 30 deletions src/can.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,72 @@ use crate::stm32;
use nb::{self, Error};

use core::sync::atomic::{AtomicU8, Ordering};
pub use stm32::can::btr::LBKM_A;

const EXID_MASK: u32 = 0b1_1111_1111_1100_0000_0000_0000_0000;
const MAX_EXTENDED_ID: u32 = 0x1FFF_FFFF;

/// Options the CAN bus. This is primarily used to set bus timings, but also controls options like enabling loopback or silent mode for debugging.
/// See http://www.bittiming.can-wiki.info/#bxCAN for generating the timing parameters for different baud rates and clocks.
///
/// Use `CanOpts::default()` to get 250kbps at 32mhz system clock
pub struct CanOpts {
brp: u16,
sjw: u8,
ts1: u8,
ts2: u8,
lbkm: LBKM_A,
}

impl CanOpts {
/// Create new `CanOpts` using the default settings from `CanOpts::default()` to get 250kbps at 32mhz system clock.
pub fn new() -> CanOpts {
CanOpts::default()
}

/// Set the Baud Rate Prescaler. See http://www.bittiming.can-wiki.info/#bxCAN for generating the timing parameters for different baud rates and clocks.
pub fn brp(mut self, brp: u16) -> Self {
self.brp = brp;
self
}

/// Set the Resynchronisation Jump Width. See http://www.bittiming.can-wiki.info/#bxCAN for generating the timing parameters for different baud rates and clocks.
pub fn sjw(mut self, sjw: u8) -> Self {
self.sjw = sjw;
self
}

/// Set Time Segment One. See http://www.bittiming.can-wiki.info/#bxCAN for generating the timing parameters for different baud rates and clocks.
pub fn ts1(mut self, ts1: u8) -> Self {
self.ts1 = ts1;
self
}

/// Set Time Segment Two. See http://www.bittiming.can-wiki.info/#bxCAN for generating the timing parameters for different baud rates and clocks.
pub fn ts2(mut self, ts2: u8) -> Self {
self.ts2 = ts2;
self
}

/// Enable or disable loopback mode on the CAN device. This is useful for debugging.
pub fn lbkm(mut self, lbkm: LBKM_A) -> Self {
self.lbkm = lbkm;
self
}
}

impl Default for CanOpts {
fn default() -> Self {
CanOpts {
brp: 4,
sjw: 0,
ts1: 10,
ts2: 3,
lbkm: LBKM_A::DISABLED,
}
}
}

/// A CAN identifier, which can be either 11 or 27 (extended) bits.
/// u16 and u32 respectively are used here despite the fact that the upper bits are unused.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
Expand Down Expand Up @@ -252,42 +314,28 @@ impl CanFilterData {
}

impl Can {
/// Initialize the CAN Peripheral
pub fn new(
/// Initialize the CAN peripheral using the options specified by `opts`.
pub fn new_with_opts(
can: stm32::CAN,
rx: gpioa::PA11<AF9>,
tx: gpioa::PA12<AF9>,
apb1: &mut APB1,
) -> Self {
opts: CanOpts,
) -> Can {
apb1.enr().modify(|_, w| w.canen().enabled());
can.mcr.modify(|_, w| w.sleep().clear_bit());
can.mcr.modify(|_, w| w.inrq().set_bit());

// Wait for INAK to confirm we have entered initialization mode
while !can.msr.read().inak().bit_is_set() {}

// TODO: actually calculate baud params

// Our baud rate calc here is aiming for roughly 4000uS total bit time or about 250kbps
// Though we actually allow closer to 5500uS total given the sjw setting
// Calculations for timing value from http://www.bittiming.can-wiki.info/#bxCAN

// Baud rate prescaler defines time quanta
// tq = (BRP[9:0]+1) x tPCLK
let brp: u16 = 4;

// Resynchronization jump width: number of quanta segments may be expanded to resync
// tRJW = tq x (SJW[1:0] + 1)
let sjw = 0;

// Time seg 2
// tBS2 = tq x (TS2[2:0] + 1)
let ts2 = 3;

// Time seg 1
// tBS1 = tq x (TS1[3:0] + 1)
let ts1 = 10;

let CanOpts {
brp,
sjw,
ts1,
ts2,
lbkm,
} = opts;
can.btr.modify(|_, w| unsafe {
w.brp()
.bits(brp)
Expand All @@ -297,8 +345,8 @@ impl Can {
.bits(ts1)
.ts2()
.bits(ts2)
//.lbkm()
//.set_bit()
.lbkm()
.variant(lbkm)
});

// Leave initialization mode by clearing INRQ and switch to normal mode
Expand All @@ -312,6 +360,15 @@ impl Can {
_tx: tx,
}
}
/// Initialize the CAN Peripheral using default options from `CanOpts::default()`
pub fn new(
can: stm32::CAN,
rx: gpioa::PA11<AF9>,
tx: gpioa::PA12<AF9>,
apb1: &mut APB1,
) -> Self {
Can::new_with_opts(can, rx, tx, apb1, CanOpts::default())
}

/// Enable CAN event interrupts for `Event`
pub fn listen(&mut self, event: Event) {
Expand Down Expand Up @@ -407,7 +464,7 @@ impl embedded_hal_can::Transmitter for CanTransmitter {

// NOTE(unsafe): full 8bit write is unsafe via the svd2rust api
tx.tdtr
.modify(|_, w| unsafe { w.dlc().bits(data.len() as u8) });
.modify(|_, w| unsafe { w.dlc().bits(frame.dlc as u8) });

tx.tir.modify(|_, w| w.rtr().clear_bit());
} else {
Expand Down Expand Up @@ -554,7 +611,7 @@ impl CanFrame {
///
/// This function will panic if length of `data` is greater than `8`
pub fn new_data(id: CanId, data: &[u8]) -> CanFrame {
crate::assert!((0..8).contains(&data.len()));
crate::assert!((0..=8).contains(&data.len()));

let mut frame = Self {
id,
Expand All @@ -569,7 +626,7 @@ impl CanFrame {
///
/// # Panics
///
/// This function will panic if `dlc` is not inside the vliad range `0..=8`.
/// This function will panic if `dlc` is not inside the valid range `0..=8`.
pub fn new_remote(id: CanId, dlc: usize) -> CanFrame {
assert!((0..=8).contains(&dlc));

Expand All @@ -579,4 +636,14 @@ impl CanFrame {
data: [0; 8],
}
}

/// Length of the frame data
pub fn len(&self) -> usize {
self.dlc
}

/// Is this frame empty. This usually indicates a remote frame.
pub fn is_empty(&self) -> bool {
self.dlc == 0
}
}

0 comments on commit 9e29ec6

Please sign in to comment.