diff --git a/examples/embassy/src/bin/can.rs b/examples/embassy/src/bin/can.rs index 30650b0..7907d70 100644 --- a/examples/embassy/src/bin/can.rs +++ b/examples/embassy/src/bin/can.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; // Import panic provider. use panic_probe as _; // Import logger. @@ -8,7 +9,7 @@ use defmt_rtt as _; use embassy_example::EXTCLK_FREQ; use embassy_executor::Spawner; -use va416xx_hal::can::asynch::on_interrupt_can; +use va416xx_hal::can::asynch::{on_interrupt_can, CanTxAsync}; use va416xx_hal::can::{ Can, CanFrame, CanFrameNormal, CanFrameRtr, CanId, CanRx, CanTx, ClockConfig, }; @@ -19,6 +20,14 @@ use va416xx_hal::{can, prelude::*}; const STANDARD_ID_0: can::StandardId = can::StandardId::new(0x42).unwrap(); const STANDARD_ID_1: can::StandardId = can::StandardId::new(0x5).unwrap(); +const EXTENDED_ID_0: can::ExtendedId = can::ExtendedId::new(0x10).unwrap(); + +// Declare a bounded channel of 3 u32s. +static CAN_RX_CHANNEL: embassy_sync::channel::Channel< + CriticalSectionRawMutex, + (usize, CanFrame), + 3, +> = embassy_sync::channel::Channel::::new(); #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -50,12 +59,6 @@ async fn main(_spawner: Spawner) { can.set_global_mask_for_exact_id_match_with_rtr_masked(); can.set_base_mask_for_all_match(); can.enable(); - let err_counter = can.read_error_counters(); - defmt::info!( - "error count tx {}, error count rx {}", - err_counter.transmit(), - err_counter.receive() - ); let mut channels = can.take_channels().unwrap(); // Transmit channel. let mut tx = CanTx::new(channels.take(0).unwrap(), None); @@ -65,8 +68,18 @@ async fn main(_spawner: Spawner) { let mut rx_base = CanRx::new(channels.take(14).unwrap()); rx_base.configure_for_reception(); + defmt::info!("Running blocking examples"); + send_and_receive_on_dedicated_channel(&mut can, &mut tx, &mut rx_dedicated); send_and_receive_rtr_on_dedicated_channel(&mut can, &mut tx, &mut rx_dedicated); + send_extended_on_base_channel(&mut can, &mut tx, &mut rx_base); + + defmt::info!("Running non-blocking (asycnhronous) examples"); + + non_blocking_example(&mut can, &mut rx_dedicated, &mut rx_base).await; + + defmt::info!("Non-blocking (asycnhronous) examples done"); + loop { cortex_m::asm::nop(); } @@ -74,21 +87,22 @@ async fn main(_spawner: Spawner) { fn send_and_receive_on_dedicated_channel(can: &mut Can, tx: &mut CanTx, rx_dedicated: &mut CanRx) { let send_data = &[1, 2, 3, 4]; + let sent_frame = + CanFrame::Normal(CanFrameNormal::new(can::Id::Standard(STANDARD_ID_0), send_data).unwrap()); defmt::info!( "sending CAN frame with ID {:#X} and data {}", STANDARD_ID_0.as_raw(), send_data ); rx_dedicated.configure_for_reception_with_standard_id(STANDARD_ID_0, false); - let send_frame = - CanFrame::Normal(CanFrameNormal::new(can::Id::Standard(STANDARD_ID_0), send_data).unwrap()); - tx.transmit_frame(send_frame).unwrap(); + tx.transmit_frame(sent_frame).unwrap(); // Await frame transmission completion. nb::block!(tx.transfer_done()).unwrap(); check_and_handle_errors(can); - let frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state"); + let received_frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state"); check_and_handle_errors(can); - if let CanFrame::Normal(can_frame_normal) = frame { + assert_eq!(received_frame, sent_frame); + if let CanFrame::Normal(can_frame_normal) = received_frame { if let can::Id::Standard(standard_id) = can_frame_normal.id() { defmt::info!( "received CAN frame with ID {:#X} and data {}", @@ -113,11 +127,12 @@ fn send_and_receive_rtr_on_dedicated_channel( rx_dedicated.configure_for_reception_with_standard_id(STANDARD_ID_1, false); tx.transmit_frame(rtr_frame).unwrap(); // Await frame transmission completion. - nb::block!(tx.remote_transfer_done()).unwrap(); + nb::block!(tx.remote_transfer_done_with_tx_reconfig()).unwrap(); check_and_handle_errors(can); - let frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state"); + let received_frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state"); check_and_handle_errors(can); - if let CanFrame::Rtr(can_frame_rtr) = frame { + assert_eq!(received_frame, rtr_frame); + if let CanFrame::Rtr(can_frame_rtr) = received_frame { if let can::Id::Standard(standard_id) = can_frame_rtr.id() { defmt::info!("received CAN RTR frame with ID {:#X}", standard_id.as_raw(),); } else { @@ -141,8 +156,84 @@ fn check_and_handle_errors(can: &mut Can) { } } +fn send_extended_on_base_channel(can: &mut Can, tx: &mut CanTx, rx: &mut CanRx) { + let send_data = &[4, 3, 2, 1]; + let sent_frame = + CanFrame::Normal(CanFrameNormal::new(can::Id::Extended(EXTENDED_ID_0), send_data).unwrap()); + tx.transmit_frame(sent_frame).unwrap(); + // Await frame transmission completion. + nb::block!(tx.transfer_done()).unwrap(); + check_and_handle_errors(can); + let received_frame = nb::block!(rx.receive(true)).expect("invalid CAN rx state"); + check_and_handle_errors(can); + assert_eq!(sent_frame, received_frame); + if let CanFrame::Normal(can_frame_normal) = received_frame { + if let can::Id::Extended(extended_id) = can_frame_normal.id() { + defmt::info!( + "received CAN frame with ID {:#X} and data {}", + extended_id.as_raw(), + can_frame_normal.data() + ); + } else { + panic!("unexpected CAN extended frame ID"); + } + } else { + defmt::error!("received unexpected CAN data frame"); + } +} + +async fn non_blocking_example(can: &mut Can, rx_dedicated: &mut CanRx, rx_base: &mut CanRx) { + let mut tx_async = CanTxAsync::new(can); + // Enable interrupts for RX channels. + rx_dedicated.enable_interrupt(true); + rx_base.enable_interrupt(true); + + // For asynchronous mode, all TX channels needs to be configured explicitely. Configuring more + // channels allows multiple active transfers when using the async API. + tx_async.configure_channel(0).unwrap(); + let send_data = &[1, 2, 3, 4]; + let send_frame = + CanFrame::Normal(CanFrameNormal::new(can::Id::Standard(STANDARD_ID_0), send_data).unwrap()); + let fut = tx_async.start_transmit(send_frame).unwrap(); + fut.await; + let (ch_idx, frame) = CAN_RX_CHANNEL.receive().await; + assert_eq!(send_frame, frame); + // Received on base channel. + assert_eq!(ch_idx, 14); + if let CanFrame::Normal(can_frame_normal) = frame { + if let can::Id::Standard(standard_id) = can_frame_normal.id() { + defmt::info!( + "received CAN frame with ID {:#X} and data {}", + standard_id.as_raw(), + can_frame_normal.data() + ); + } else { + panic!("unexpected CAN extended frame ID"); + } + } else { + defmt::error!("received unexpected CAN remote frame"); + } +} + #[interrupt] #[allow(non_snake_case)] fn CAN0() { - on_interrupt_can(CanId::Can0, false).unwrap(); + match on_interrupt_can(CanId::Can0, false).unwrap() { + can::asynch::InterruptResult::NoInterrupt => { + defmt::warn!("unexpected interrupt on CAN0"); + } + can::asynch::InterruptResult::ReceivedFrame { + channel_index, + frame, + } => { + CAN_RX_CHANNEL.try_send((channel_index, frame)).unwrap(); + } + can::asynch::InterruptResult::TransmissionEvent { channel_index, id } => { + defmt::info!( + "transmission event on channel {} with event ID {}", + channel_index, + id + ); + } + } } diff --git a/va416xx-hal/src/can/asynch.rs b/va416xx-hal/src/can/asynch.rs index 8207099..5b22fe2 100644 --- a/va416xx-hal/src/can/asynch.rs +++ b/va416xx-hal/src/can/asynch.rs @@ -7,7 +7,7 @@ use crate::can::regs::BufferState; use super::{ regs::{DiagnosticRegister, InterruptClear, MmioCan, StatusPending}, - CanChannelLowLevel, CanFrame, CanId, + CanChannelLowLevel, CanFrame, CanId, InvalidBufferIndexError, }; #[derive(Debug)] @@ -24,7 +24,8 @@ static TX_STATES: [AtomicU8; 15] = [const { AtomicU8::new(0) }; 15]; static TX_WAKERS: [embassy_sync::waitqueue::AtomicWaker; 15] = [const { embassy_sync::waitqueue::AtomicWaker::new() }; 15]; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TxEventId { /// Buffer state went from [BufferState::TxOnce] to [BufferState::TxNotActive]. TxDataFrame, @@ -249,20 +250,46 @@ impl CanTxFuture { let free_channel_id = channel_is_free.iter().position(|&x| x).unwrap(); let mut channel = unsafe { CanChannelLowLevel::steal_unchecked(CanId::Can0, free_channel_id) }; + TX_STATES[free_channel_id].store(TxChannelState::TxDataFrame as u8, Ordering::Relaxed); + channel.write_state(BufferState::TxNotActive); + channel.transmit_frame_unchecked(frame); + channel.clear_interrupt(); channel.enable_interrupt(true); channel.enable_error_interrupt(true); - channel.transmit_frame_unchecked(frame); - - TX_STATES[free_channel_id].store(TxChannelState::TxDataFrame as u8, Ordering::Relaxed); Ok(CanTxFuture(free_channel_id)) } } -pub struct CanTxAsync(pub super::Can); +#[derive(Debug, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ChannelConfigError { + #[error("channel is busy")] + Busy, + #[error("invalid offset: {0}")] + Offset(#[from] InvalidBufferIndexError), +} + +pub struct CanTxAsync; impl CanTxAsync { - pub fn new(can: super::Can) -> Self { - CanTxAsync(can) + pub fn new(can: &mut super::Can) -> Self { + can.clear_interrupts(); + can.enable_nvic_interrupt(); + CanTxAsync + } + + pub fn configure_channel(&mut self, channel_idx: usize) -> Result<(), ChannelConfigError> { + if channel_idx >= TX_STATES.len() { + return Err(ChannelConfigError::Offset(InvalidBufferIndexError( + channel_idx, + ))); + } + let state = TX_STATES[channel_idx].load(Ordering::Relaxed); + if state != TxChannelState::Idle as u8 && state != TxChannelState::Unconfigured as u8 { + return Err(ChannelConfigError::Busy); + } + TX_STATES[channel_idx].store(TxChannelState::Idle as u8, Ordering::Relaxed); + Ok(()) } /// Start a transmission and returns the future which can be polled to completion. diff --git a/va416xx-hal/src/can/frame.rs b/va416xx-hal/src/can/frame.rs index 838740a..0f2989e 100644 --- a/va416xx-hal/src/can/frame.rs +++ b/va416xx-hal/src/can/frame.rs @@ -4,7 +4,7 @@ pub use embedded_can::{ExtendedId, Id, StandardId}; #[error("invalid data size error {0}")] pub struct InvalidDataSizeError(usize); -#[derive(Debug)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct CanFrameNormal { id: embedded_can::Id, size: usize, @@ -42,7 +42,7 @@ impl CanFrameNormal { } } -#[derive(Debug)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct CanFrameRtr { id: embedded_can::Id, dlc: usize, @@ -62,7 +62,7 @@ impl CanFrameRtr { } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CanFrame { Normal(CanFrameNormal), Rtr(CanFrameRtr), diff --git a/va416xx-hal/src/can/ll.rs b/va416xx-hal/src/can/ll.rs index c67f864..622e66b 100644 --- a/va416xx-hal/src/can/ll.rs +++ b/va416xx-hal/src/can/ll.rs @@ -2,7 +2,9 @@ use arbitrary_int::{u11, u15, u3, u4, Number}; use embedded_can::Frame; use super::{ - regs::{BaseId, BufStatusAndControl, BufferState, ExtendedId, MmioCanMsgBuf, TwoBytesData}, + regs::{ + self, BaseId, BufStatusAndControl, BufferState, ExtendedId, MmioCanMsgBuf, TwoBytesData, + }, CanFrame, CanFrameNormal, CanFrameRtr, CanId, InvalidBufferIndexError, }; @@ -188,6 +190,14 @@ impl CanChannelLowLevel { self.write_state(BufferState::TxOnce); } + #[inline] + pub fn clear_interrupt(&mut self) { + let mut regs = unsafe { self.id.steal_regs() }; + let mut clear = regs::InterruptClear::new_with_raw_value(0); + clear.set_buffer(self.idx, true); + regs.write_iclr(clear); + } + pub fn enable_error_interrupt(&mut self, enable_translation: bool) { let mut regs = unsafe { self.id.steal_regs() }; if enable_translation { diff --git a/va416xx-hal/src/can/mod.rs b/va416xx-hal/src/can/mod.rs index 3d97338..3943a12 100644 --- a/va416xx-hal/src/can/mod.rs +++ b/va416xx-hal/src/can/mod.rs @@ -7,6 +7,7 @@ use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number}; use embedded_can::Frame; use ll::CanChannelLowLevel; use regs::{BaseId, BufferState, Control, MmioCan, TimingConfig}; +use vorago_shared_periphs::enable_nvic_interrupt; use crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect}; use libm::roundf; @@ -44,12 +45,21 @@ impl CanId { /// # Safety /// /// See safety of the [regs::Can::new_mmio_fixed_0]. + #[inline] pub const unsafe fn steal_regs(&self) -> regs::MmioCan<'static> { match self { CanId::Can0 => unsafe { regs::Can::new_mmio_fixed_0() }, CanId::Can1 => unsafe { regs::Can::new_mmio_fixed_1() }, } } + + #[inline] + pub const fn irq_id(&self) -> va416xx::Interrupt { + match self { + CanId::Can0 => va416xx::Interrupt::CAN0, + CanId::Can1 => va416xx::Interrupt::CAN1, + } + } } /// Sample point between 0 and 1.0 for the given time segments. @@ -142,18 +152,22 @@ impl ClockConfig { Self::new(prescaler as u8, tseg1, tseg2, sjw) } + #[inline] pub fn sjw_reg_value(&self) -> u2 { u2::new(self.sjw.value() - 1) } + #[inline] pub fn tseg1_reg_value(&self) -> u4 { u4::new(self.tseg1.value() - 1) } + #[inline] pub fn tseg2_reg_value(&self) -> u3 { u3::new(self.tseg2.value() - 1) } + #[inline] pub fn prescaler_reg_value(&self) -> u7 { u7::new(self.prescaler.value() - 2) } @@ -231,6 +245,7 @@ pub fn calculate_all_viable_clock_configs( Ok(configs) } +#[inline] pub const fn calculate_nominal_bit_time( apb1_clock: Hertz, target_bitrate: Hertz, @@ -239,34 +254,41 @@ pub const fn calculate_nominal_bit_time( apb1_clock.raw() / (target_bitrate.raw() * prescaler as u32) } +#[inline] pub const fn calculate_actual_bitrate(apb1_clock: Hertz, prescaler: u8, nom_bit_time: u32) -> f32 { apb1_clock.raw() as f32 / (prescaler as u32 * nom_bit_time) as f32 } +#[inline] pub const fn calculate_bitrate_deviation(actual_bitrate: f32, target_bitrate: Hertz) -> f32 { (actual_bitrate - target_bitrate.raw() as f32).abs() / target_bitrate.raw() as f32 } pub trait CanMarker { const ID: CanId; + const IRQ: va416xx::Interrupt; const PERIPH_SEL: PeripheralSelect; } impl CanMarker for va416xx::Can0 { const ID: CanId = CanId::Can0; + const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN0; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0; } impl CanMarker for va416xx::Can1 { const ID: CanId = CanId::Can1; + const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN1; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1; } #[derive(Debug, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[error("invalid buffer index {0}")] pub struct InvalidBufferIndexError(usize); #[derive(Debug, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[error("sjw must be less than or equal to the smaller tseg value")] pub struct InvalidSjwError(u8); @@ -369,6 +391,7 @@ impl Can { /// This configures the base mask for buffer 14 so that acceptance is only determined by an /// exact match with the ID in the receive message buffers. This is the default reset /// configuration for the global mask as well. + #[inline] pub fn set_base_mask_for_exact_id_match(&mut self) { self.regs .write_bmskx(regs::ExtendedId::new_with_raw_value(0)); @@ -377,6 +400,7 @@ impl Can { /// This configures the base mask so that all CAN frames which are not handled by any other /// buffers are accepted by the base buffer 14. + #[inline] pub fn set_base_mask_for_all_match(&mut self) { self.regs .write_bmskx(regs::ExtendedId::new_with_raw_value(0xffff)); @@ -388,6 +412,22 @@ impl Can { &mut self.regs } + #[inline] + pub fn clear_interrupts(&mut self) { + self.regs + .write_iclr(regs::InterruptClear::new_with_raw_value(0xFFFF_FFFF)); + } + /// This function only enable the CAN interrupt vector in the NVIC. + /// + /// The interrupts for the individual channels or errors still need to be enabled + /// separately. + #[inline] + pub fn enable_nvic_interrupt(&mut self) { + unsafe { + enable_nvic_interrupt(self.id().irq_id()); + } + } + #[inline] pub fn read_error_counters(&self) -> regs::ErrorCounter { self.regs.read_error_counter() @@ -486,6 +526,7 @@ pub struct CanTx { impl CanTx { pub fn new(mut ll: CanChannelLowLevel, tx_priority: Option) -> Self { + ll.reset(); ll.configure_for_transmission(tx_priority); Self { ll, @@ -493,6 +534,11 @@ impl CanTx { } } + #[inline] + pub fn into_rx_channel(self) -> CanRx { + CanRx::new(self.ll) + } + /// Start transmitting a frame. /// /// The frame transmission can be polled/awaited to completion using the [Self::transfer_done] @@ -543,6 +589,14 @@ impl CanTx { /// Poll whether an active remote frame transmission is done. /// + /// On success, returns the channel re-configured to a [CanRx] channel. This is because the + /// default behaviour of the hardware will be to re-configure the channel state to + /// [BufferState::RxReady] once the remote frame has been transmitted so that the response + /// frame can be awaited. + /// + /// If the channel should instead be re-configured for transmission again, + /// [Self::remote_transfer_done_with_tx_reconfig] can be used. + /// /// Returns a [state error][InvalidTxStateError] if no transmission is active. pub fn remote_transfer_done(&mut self) -> nb::Result { if self.mode != TxState::TransmittingRemoteFrame { @@ -563,6 +617,29 @@ impl CanTx { Err(nb::Error::WouldBlock) } + /// Poll whether an active remote frame transmission is done. + /// + /// This function will re-configure the buffer back for transmission once the + /// transmission has completed. + /// + /// Returns a [state error][InvalidTxStateError] if no transmission is active. + pub fn remote_transfer_done_with_tx_reconfig(&mut self) -> nb::Result<(), InvalidTxStateError> { + if self.mode != TxState::TransmittingRemoteFrame { + return Err(nb::Error::Other(InvalidTxStateError(self.mode.into()))); + } + let status = self.ll.read_state(); + if status.is_err() { + return Err(nb::Error::WouldBlock); + } + let status = status.unwrap(); + if status == BufferState::RxReady { + self.ll.write_state(BufferState::TxNotActive); + self.mode = TxState::Idle; + return Ok(()); + } + Err(nb::Error::WouldBlock) + } + pub fn reset(&mut self) { self.ll.reset(); self.mode = TxState::Idle; @@ -575,12 +652,24 @@ pub struct CanRx { } impl CanRx { - pub fn new(ll: CanChannelLowLevel) -> Self { + pub fn new(mut ll: CanChannelLowLevel) -> Self { + ll.reset(); Self { ll, mode: RxState::Idle, } } + + #[inline] + pub fn into_tx_channel(self, tx_priority: Option) -> CanTx { + CanTx::new(self.ll, tx_priority) + } + + #[inline] + pub fn enable_interrupt(&mut self, enable_translation: bool) { + self.ll.enable_interrupt(enable_translation); + } + pub fn configure_for_reception_with_standard_id( &mut self, standard_id: embedded_can::StandardId, diff --git a/va416xx-hal/src/can/regs.rs b/va416xx-hal/src/can/regs.rs index 5bdc1d9..e3bd3e8 100644 --- a/va416xx-hal/src/can/regs.rs +++ b/va416xx-hal/src/can/regs.rs @@ -202,6 +202,7 @@ pub struct InterruptPending { #[derive(Debug)] #[repr(usize)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum CanInterruptId { None = 0b00000, Error = 0b10000, @@ -226,14 +227,13 @@ impl StatusPending { return Some(CanInterruptId::None); } - //let raw_value = ((self.irq() as u8) << 4) | self.ist().as_u8(); if self.irq() && self.ist().value() == 0 { return Some(CanInterruptId::Error); } if !self.irq() { return None; } - Some(CanInterruptId::Buffer(self.ist().as_usize())) + Some(CanInterruptId::Buffer(self.ist().as_usize() - 1)) } }