diff options
| author | xoviat <[email protected]> | 2023-05-30 21:14:25 -0500 |
|---|---|---|
| committer | xoviat <[email protected]> | 2023-05-30 21:14:25 -0500 |
| commit | 16bfbd4e99dbc765c93610557a7857a9013ff756 (patch) | |
| tree | 3103c09168aa1c672daa47b97eb86e663333e057 | |
| parent | f8d35806dc773a6310a60115fbf90f48fe409207 (diff) | |
stm32/can: add hw test and cleanup
| -rw-r--r-- | embassy-stm32/src/can/bxcan.rs | 78 | ||||
| -rw-r--r-- | tests/stm32/Cargo.toml | 8 | ||||
| -rw-r--r-- | tests/stm32/src/bin/can.rs | 78 |
3 files changed, 121 insertions, 43 deletions
diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 8ae1dcb94..08ba783ff 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs | |||
| @@ -39,6 +39,7 @@ pub struct Rx0InterruptHandler<T: Instance> { | |||
| 39 | 39 | ||
| 40 | impl<T: Instance> interrupt::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> { | 40 | impl<T: Instance> interrupt::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> { |
| 41 | unsafe fn on_interrupt() { | 41 | unsafe fn on_interrupt() { |
| 42 | // info!("rx0 irq"); | ||
| 42 | Can::<T>::receive_fifo(RxFifo::Fifo0); | 43 | Can::<T>::receive_fifo(RxFifo::Fifo0); |
| 43 | } | 44 | } |
| 44 | } | 45 | } |
| @@ -49,6 +50,7 @@ pub struct Rx1InterruptHandler<T: Instance> { | |||
| 49 | 50 | ||
| 50 | impl<T: Instance> interrupt::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> { | 51 | impl<T: Instance> interrupt::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> { |
| 51 | unsafe fn on_interrupt() { | 52 | unsafe fn on_interrupt() { |
| 53 | // info!("rx1 irq"); | ||
| 52 | Can::<T>::receive_fifo(RxFifo::Fifo1); | 54 | Can::<T>::receive_fifo(RxFifo::Fifo1); |
| 53 | } | 55 | } |
| 54 | } | 56 | } |
| @@ -59,6 +61,7 @@ pub struct SceInterruptHandler<T: Instance> { | |||
| 59 | 61 | ||
| 60 | impl<T: Instance> interrupt::Handler<T::SCEInterrupt> for SceInterruptHandler<T> { | 62 | impl<T: Instance> interrupt::Handler<T::SCEInterrupt> for SceInterruptHandler<T> { |
| 61 | unsafe fn on_interrupt() { | 63 | unsafe fn on_interrupt() { |
| 64 | // info!("sce irq"); | ||
| 62 | let msr = T::regs().msr(); | 65 | let msr = T::regs().msr(); |
| 63 | let msr_val = msr.read(); | 66 | let msr_val = msr.read(); |
| 64 | 67 | ||
| @@ -87,36 +90,10 @@ pub enum BusError { | |||
| 87 | BusWarning, | 90 | BusWarning, |
| 88 | } | 91 | } |
| 89 | 92 | ||
| 90 | pub enum FrameOrError { | ||
| 91 | Frame(Frame), | ||
| 92 | Error(BusError), | ||
| 93 | } | ||
| 94 | |||
| 95 | impl<'d, T: Instance> Can<'d, T> { | 93 | impl<'d, T: Instance> Can<'d, T> { |
| 96 | /// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus. | ||
| 97 | pub fn new( | ||
| 98 | peri: impl Peripheral<P = T> + 'd, | ||
| 99 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | ||
| 100 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | ||
| 101 | ) -> Self { | ||
| 102 | into_ref!(peri, rx, tx); | ||
| 103 | |||
| 104 | unsafe { | ||
| 105 | rx.set_as_af(rx.af_num(), AFType::Input); | ||
| 106 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); | ||
| 107 | } | ||
| 108 | |||
| 109 | T::enable(); | ||
| 110 | T::reset(); | ||
| 111 | |||
| 112 | Self { | ||
| 113 | can: bxcan::Can::builder(BxcanInstance(peri)).enable(), | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. | 94 | /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. |
| 118 | /// You must call [Can::enable_non_blocking] to use the peripheral. | 95 | /// You must call [Can::enable_non_blocking] to use the peripheral. |
| 119 | pub fn new_disabled( | 96 | pub fn new( |
| 120 | peri: impl Peripheral<P = T> + 'd, | 97 | peri: impl Peripheral<P = T> + 'd, |
| 121 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 98 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, |
| 122 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 99 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, |
| @@ -137,6 +114,25 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 137 | T::reset(); | 114 | T::reset(); |
| 138 | 115 | ||
| 139 | unsafe { | 116 | unsafe { |
| 117 | use crate::pac::can::vals::{Errie, Fmpie, Tmeie}; | ||
| 118 | |||
| 119 | T::regs().ier().write(|w| { | ||
| 120 | // TODO: fix metapac | ||
| 121 | |||
| 122 | w.set_errie(Errie(1)); | ||
| 123 | w.set_fmpie(0, Fmpie(1)); | ||
| 124 | w.set_fmpie(1, Fmpie(1)); | ||
| 125 | w.set_tmeie(Tmeie(1)); | ||
| 126 | }); | ||
| 127 | |||
| 128 | T::regs().mcr().write(|w| { | ||
| 129 | // Enable timestamps on rx messages | ||
| 130 | |||
| 131 | w.set_ttcm(true); | ||
| 132 | }) | ||
| 133 | } | ||
| 134 | |||
| 135 | unsafe { | ||
| 140 | T::TXInterrupt::steal().unpend(); | 136 | T::TXInterrupt::steal().unpend(); |
| 141 | T::TXInterrupt::steal().enable(); | 137 | T::TXInterrupt::steal().enable(); |
| 142 | 138 | ||
| @@ -159,12 +155,8 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 159 | self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); | 155 | self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); |
| 160 | } | 156 | } |
| 161 | 157 | ||
| 162 | pub async fn transmit(&mut self, frame: &Frame) { | 158 | /// Queues the message to be sent but exerts backpressure |
| 163 | let tx_status = self.queue_transmit(frame).await; | 159 | pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { |
| 164 | self.wait_transission(tx_status.mailbox()).await; | ||
| 165 | } | ||
| 166 | |||
| 167 | async fn queue_transmit(&mut self, frame: &Frame) -> bxcan::TransmitStatus { | ||
| 168 | poll_fn(|cx| { | 160 | poll_fn(|cx| { |
| 169 | if let Ok(status) = self.can.transmit(frame) { | 161 | if let Ok(status) = self.can.transmit(frame) { |
| 170 | return Poll::Ready(status); | 162 | return Poll::Ready(status); |
| @@ -175,7 +167,7 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 175 | .await | 167 | .await |
| 176 | } | 168 | } |
| 177 | 169 | ||
| 178 | async fn wait_transission(&self, mb: bxcan::Mailbox) { | 170 | pub async fn flush(&self, mb: bxcan::Mailbox) { |
| 179 | poll_fn(|cx| unsafe { | 171 | poll_fn(|cx| unsafe { |
| 180 | if T::regs().tsr().read().tme(mb.index()) { | 172 | if T::regs().tsr().read().tme(mb.index()) { |
| 181 | return Poll::Ready(()); | 173 | return Poll::Ready(()); |
| @@ -186,12 +178,13 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 186 | .await; | 178 | .await; |
| 187 | } | 179 | } |
| 188 | 180 | ||
| 189 | pub async fn receive_frame_or_error(&mut self) -> FrameOrError { | 181 | /// Returns a tuple of the time the message was received and the message frame |
| 182 | pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { | ||
| 190 | poll_fn(|cx| { | 183 | poll_fn(|cx| { |
| 191 | if let Poll::Ready(frame) = T::state().rx_queue.recv().poll_unpin(cx) { | 184 | if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { |
| 192 | return Poll::Ready(FrameOrError::Frame(frame)); | 185 | return Poll::Ready(Ok((time, frame))); |
| 193 | } else if let Some(err) = self.curr_error() { | 186 | } else if let Some(err) = self.curr_error() { |
| 194 | return Poll::Ready(FrameOrError::Error(err)); | 187 | return Poll::Ready(Err(err)); |
| 195 | } | 188 | } |
| 196 | T::state().err_waker.register(cx.waker()); | 189 | T::state().err_waker.register(cx.waker()); |
| 197 | Poll::Pending | 190 | Poll::Pending |
| @@ -240,6 +233,7 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 240 | data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); | 233 | data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); |
| 241 | data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); | 234 | data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); |
| 242 | 235 | ||
| 236 | let time = fifo.rdtr().read().time(); | ||
| 243 | let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); | 237 | let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); |
| 244 | 238 | ||
| 245 | rfr.modify(|v| v.set_rfom(true)); | 239 | rfr.modify(|v| v.set_rfom(true)); |
| @@ -247,11 +241,11 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 247 | /* | 241 | /* |
| 248 | NOTE: consensus was reached that if rx_queue is full, packets should be dropped | 242 | NOTE: consensus was reached that if rx_queue is full, packets should be dropped |
| 249 | */ | 243 | */ |
| 250 | let _ = state.rx_queue.try_send(frame); | 244 | let _ = state.rx_queue.try_send((time, frame)); |
| 251 | } | 245 | } |
| 252 | } | 246 | } |
| 253 | 247 | ||
| 254 | pub fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option<u32> { | 248 | pub const fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option<u32> { |
| 255 | const BS1_MAX: u8 = 16; | 249 | const BS1_MAX: u8 = 16; |
| 256 | const BS2_MAX: u8 = 8; | 250 | const BS2_MAX: u8 = 8; |
| 257 | const MAX_SAMPLE_POINT_PERMILL: u16 = 900; | 251 | const MAX_SAMPLE_POINT_PERMILL: u16 = 900; |
| @@ -316,7 +310,7 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 316 | // - With rounding to zero | 310 | // - With rounding to zero |
| 317 | let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first | 311 | let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first |
| 318 | let mut bs2 = bs1_bs2_sum - bs1; | 312 | let mut bs2 = bs1_bs2_sum - bs1; |
| 319 | assert!(bs1_bs2_sum > bs1); | 313 | core::assert!(bs1_bs2_sum > bs1); |
| 320 | 314 | ||
| 321 | let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; | 315 | let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; |
| 322 | if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { | 316 | if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { |
| @@ -379,7 +373,7 @@ pub(crate) mod sealed { | |||
| 379 | pub struct State { | 373 | pub struct State { |
| 380 | pub tx_waker: AtomicWaker, | 374 | pub tx_waker: AtomicWaker, |
| 381 | pub err_waker: AtomicWaker, | 375 | pub err_waker: AtomicWaker, |
| 382 | pub rx_queue: Channel<CriticalSectionRawMutex, bxcan::Frame, 32>, | 376 | pub rx_queue: Channel<CriticalSectionRawMutex, (u16, bxcan::Frame), 32>, |
| 383 | } | 377 | } |
| 384 | 378 | ||
| 385 | impl State { | 379 | impl State { |
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bd8d90abe..70d3eb138 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml | |||
| @@ -7,7 +7,7 @@ autobins = false | |||
| 7 | 7 | ||
| 8 | [features] | 8 | [features] |
| 9 | stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill | 9 | stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill |
| 10 | stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono", "not-gpdma"] # Nucleo | 10 | stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono", "can", "not-gpdma"] # Nucleo |
| 11 | stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo | 11 | stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo |
| 12 | stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo | 12 | stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo |
| 13 | stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo | 13 | stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo |
| @@ -19,6 +19,7 @@ stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board | |||
| 19 | sdmmc = [] | 19 | sdmmc = [] |
| 20 | chrono = ["embassy-stm32/chrono", "dep:chrono"] | 20 | chrono = ["embassy-stm32/chrono", "dep:chrono"] |
| 21 | ble = [] | 21 | ble = [] |
| 22 | can = [] | ||
| 22 | not-gpdma = [] | 23 | not-gpdma = [] |
| 23 | 24 | ||
| 24 | [dependencies] | 25 | [dependencies] |
| @@ -50,6 +51,11 @@ path = "src/bin/ble.rs" | |||
| 50 | required-features = [ "ble",] | 51 | required-features = [ "ble",] |
| 51 | 52 | ||
| 52 | [[bin]] | 53 | [[bin]] |
| 54 | name = "can" | ||
| 55 | path = "src/bin/can.rs" | ||
| 56 | required-features = [ "can",] | ||
| 57 | |||
| 58 | [[bin]] | ||
| 53 | name = "gpio" | 59 | name = "gpio" |
| 54 | path = "src/bin/gpio.rs" | 60 | path = "src/bin/gpio.rs" |
| 55 | required-features = [] | 61 | required-features = [] |
diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs new file mode 100644 index 000000000..f39f83e82 --- /dev/null +++ b/tests/stm32/src/bin/can.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | // required-features: can | ||
| 6 | |||
| 7 | #[path = "../example_common.rs"] | ||
| 8 | mod example_common; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_stm32::bind_interrupts; | ||
| 11 | use embassy_stm32::can::bxcan::filter::Mask32; | ||
| 12 | use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; | ||
| 13 | use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; | ||
| 14 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 15 | use embassy_stm32::peripherals::CAN1; | ||
| 16 | use example_common::*; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | bind_interrupts!(struct Irqs { | ||
| 20 | CAN1_RX0 => Rx0InterruptHandler<CAN1>; | ||
| 21 | CAN1_RX1 => Rx1InterruptHandler<CAN1>; | ||
| 22 | CAN1_SCE => SceInterruptHandler<CAN1>; | ||
| 23 | CAN1_TX => TxInterruptHandler<CAN1>; | ||
| 24 | }); | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(_spawner: Spawner) { | ||
| 28 | let mut p = embassy_stm32::init(config()); | ||
| 29 | info!("Hello World!"); | ||
| 30 | |||
| 31 | // HW is connected as follows: | ||
| 32 | // PB13 -> PD0 | ||
| 33 | // PB12 -> PD1 | ||
| 34 | |||
| 35 | // The next two lines are a workaround for testing without transceiver. | ||
| 36 | // To synchronise to the bus the RX input needs to see a high level. | ||
| 37 | // Use `mem::forget()` to release the borrow on the pin but keep the | ||
| 38 | // pull-up resistor enabled. | ||
| 39 | let rx_pin = Input::new(&mut p.PD0, Pull::Up); | ||
| 40 | core::mem::forget(rx_pin); | ||
| 41 | |||
| 42 | let mut can = Can::new(p.CAN1, p.PD0, p.PD1, Irqs); | ||
| 43 | |||
| 44 | info!("Configuring can..."); | ||
| 45 | |||
| 46 | can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); | ||
| 47 | |||
| 48 | can.set_bitrate(1_000_000); | ||
| 49 | can.modify_config() | ||
| 50 | .set_loopback(true) // Receive own frames | ||
| 51 | .set_silent(true) | ||
| 52 | // .set_bit_timing(0x001c0003) | ||
| 53 | .enable(); | ||
| 54 | |||
| 55 | info!("Can configured"); | ||
| 56 | |||
| 57 | let mut i: u8 = 0; | ||
| 58 | loop { | ||
| 59 | let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); | ||
| 60 | |||
| 61 | info!("Transmitting frame..."); | ||
| 62 | can.write(&tx_frame).await; | ||
| 63 | |||
| 64 | info!("Receiving frame..."); | ||
| 65 | let (time, rx_frame) = can.read().await.unwrap(); | ||
| 66 | |||
| 67 | info!("loopback time {}", time); | ||
| 68 | info!("loopback frame {=u8}", rx_frame.data().unwrap()[0]); | ||
| 69 | |||
| 70 | i += 1; | ||
| 71 | if i > 10 { | ||
| 72 | break; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | info!("Test OK"); | ||
| 77 | cortex_m::asm::bkpt(); | ||
| 78 | } | ||
