diff --git a/examples/embassy/Cargo.toml b/examples/embassy/Cargo.toml index b37f672..5daa165 100644 --- a/examples/embassy/Cargo.toml +++ b/examples/embassy/Cargo.toml @@ -18,6 +18,7 @@ static_cell = "2" critical-section = "1" ringbuf = { version = "0.4", default-features = false } +nb = "1" embassy-sync = "0.6" embassy-time = "0.4" embassy-executor = { version = "0.7", features = [ diff --git a/examples/embassy/src/bin/can.rs b/examples/embassy/src/bin/can.rs new file mode 100644 index 0000000..17fb23d --- /dev/null +++ b/examples/embassy/src/bin/can.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] + +// Import panic provider. +use panic_probe as _; +// Import logger. +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::{Can, CanFrame, CanFrameNormal, CanId, CanRx, CanTx, ClockConfig}; +use va416xx_hal::clock::ClockConfigurator; +use va416xx_hal::pac::{self, interrupt}; +use va416xx_hal::time::Hertz; +use va416xx_hal::{can, prelude::*}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + defmt::println!("-- VA416xx CAN Demo --"); + + let dp = pac::Peripherals::take().unwrap(); + + // Initialize the systick interrupt & obtain the token to prove that we did + // Use the external clock connected to XTAL_N. + let clocks = ClockConfigurator::new(dp.clkgen) + .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) + .freeze() + .unwrap(); + // Safety: Only called once here. + va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); + let clk_config = ClockConfig::from_bitrate_and_segments(&clocks, 250.kHz(), 16, 4, 4).unwrap(); + let mut can = Can::new(dp.can0, clk_config); + can.set_loopback(true); + can.set_bufflock(true); + can.set_base_mask_for_all_match(); + can.enable(); + let mut channels = can.take_channels().unwrap(); + // Transmit channel. + let mut tx = CanTx::new(channels.take(0).unwrap(), None); + // Base channel which has dedicated mask. + let mut rx = CanRx::new(channels.take(14).unwrap()); + let send_frame = CanFrame::Normal(CanFrameNormal::new( + can::Id::Standard(can::StandardId::new(0x1).unwrap()), + &[1, 2, 3, 4], + )); + defmt::info!("sending CAN frame"); + tx.transmit_frame(send_frame).unwrap(); + let _frame = nb::block!(rx.receive(true)).expect("invalid CAN rx state"); + defmt::info!("received CAN frame with data"); +} + +#[interrupt] +#[allow(non_snake_case)] +fn CAN0() { + on_interrupt_can(CanId::Can0, false).unwrap(); +} diff --git a/va416xx-hal/src/can/asynch.rs b/va416xx-hal/src/can/asynch.rs index eca03cf..ee941de 100644 --- a/va416xx-hal/src/can/asynch.rs +++ b/va416xx-hal/src/can/asynch.rs @@ -53,6 +53,8 @@ pub enum InterruptResult { }, } +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum InterruptError { UnexpectedError, InvalidInterruptId(StatusPending), @@ -95,35 +97,30 @@ pub fn on_interrupt_can( return Err(InterruptError::InvalidInterruptId(pending_id)); } match pending_id.interrupt_id().unwrap() { - super::regs::CanInterruptId::None => { - return Ok(InterruptResult::NoInterrupt); - } - super::regs::CanInterruptId::Error => { - return Err(InterruptError::CanError(regs.read_diag())); - } + super::regs::CanInterruptId::None => Ok(InterruptResult::NoInterrupt), + super::regs::CanInterruptId::Error => Err(InterruptError::CanError(regs.read_diag())), super::regs::CanInterruptId::Buffer(idx) => { let mut channel = unsafe { CanChannelLowLevel::steal_unchecked(id, idx) }; - let status = channel.read_status(); + let status = channel.read_state(); if status.is_err() { let mut clr = InterruptClear::new_with_raw_value(0); - clr.set_buffer(idx as usize, true); + clr.set_buffer(idx, true); regs.write_iclr(clr); regs.modify_ien(|mut val| { - val.set_buffer(idx as usize, false); + val.set_buffer(idx, false); val }); return Err(InterruptError::InvalidStatus(status.unwrap_err())); } let buf_state = status.unwrap(); if buf_state == BufferState::TxNotActive { - let tx_state = TX_STATES[idx as usize].load(Ordering::Relaxed); + let tx_state = TX_STATES[idx].load(Ordering::Relaxed); clear_and_disable_interrupt(&mut regs, idx); // Handle reading frames, updating states etc. if tx_state == TxChannelState::TxDataFrame as u8 { // Transmission complete. - TX_STATES[idx as usize] - .store(TxChannelState::Finished as u8, Ordering::Relaxed); - TX_WAKERS[idx as usize].wake(); + TX_STATES[idx].store(TxChannelState::Finished as u8, Ordering::Relaxed); + TX_WAKERS[idx].wake(); return Ok(InterruptResult::TransmissionEvent { channel_index: idx, id: TxEventId::TxDataFrame, @@ -131,22 +128,21 @@ pub fn on_interrupt_can( } } if buf_state == BufferState::RxReady { - let tx_state = TX_STATES[idx as usize].load(Ordering::Relaxed); + let tx_state = TX_STATES[idx].load(Ordering::Relaxed); if tx_state == TxChannelState::TxRtrTransmission as u8 { if reconfigure_tx_rtr_to_tx { - channel.write_status(BufferState::TxNotActive); + channel.write_state(BufferState::TxNotActive); clear_and_disable_interrupt(&mut regs, idx); // Transmission complete. - TX_STATES[idx as usize] - .store(TxChannelState::Idle as u8, Ordering::Relaxed); + TX_STATES[idx].store(TxChannelState::Idle as u8, Ordering::Relaxed); } else { // Do not disable interrupt, channel is now used to receive the frame. clear_interrupt(&mut regs, idx); // Transmission complete. - TX_STATES[idx as usize] + TX_STATES[idx] .store(TxChannelState::TxRtrReception as u8, Ordering::Relaxed); } - TX_WAKERS[idx as usize].wake(); + TX_WAKERS[idx].wake(); return Ok(InterruptResult::TransmissionEvent { channel_index: idx, id: TxEventId::TxRemoteFrame, @@ -154,18 +150,18 @@ pub fn on_interrupt_can( } } if buf_state == BufferState::RxOverrun || buf_state == BufferState::RxFull { - let tx_state = TX_STATES[idx as usize].load(Ordering::Relaxed); + let tx_state = TX_STATES[idx].load(Ordering::Relaxed); // Do not disable interrupt and assume continuous reception. clear_interrupt(&mut regs, idx); let frame = channel.read_frame_unchecked(); if tx_state == TxChannelState::TxRtrReception as u8 { // Reception of response complete. We can release the channel for TX (or RX) // usage again. - TX_STATES[idx as usize].store(TxChannelState::Idle as u8, Ordering::Relaxed); - channel.write_status(BufferState::TxNotActive); + TX_STATES[idx].store(TxChannelState::Idle as u8, Ordering::Relaxed); + channel.write_state(BufferState::TxNotActive); } else { // Assume continous reception of frames. - channel.write_status(BufferState::RxReady); + channel.write_state(BufferState::RxReady); } return Ok(InterruptResult::ReceivedFrame { channel_index: idx, @@ -205,7 +201,7 @@ fn clear_interrupt(regs: &mut MmioCan<'static>, idx: usize) { fn clear_and_disable_interrupt(regs: &mut MmioCan<'static>, idx: usize) { clear_interrupt(regs, idx); regs.modify_ien(|mut val| { - val.set_buffer(idx as usize, false); + val.set_buffer(idx, false); val }); } diff --git a/va416xx-hal/src/can/frame.rs b/va416xx-hal/src/can/frame.rs index 3c3e8bc..f4bef5e 100644 --- a/va416xx-hal/src/can/frame.rs +++ b/va416xx-hal/src/can/frame.rs @@ -1,3 +1,5 @@ +pub use embedded_can::{ExtendedId, Id, StandardId}; + #[derive(Debug, thiserror::Error)] #[error("invalid data size error {0}")] pub struct InvalidDataSizeError(usize); diff --git a/va416xx-hal/src/can/ll.rs b/va416xx-hal/src/can/ll.rs index 322a1d6..277798b 100644 --- a/va416xx-hal/src/can/ll.rs +++ b/va416xx-hal/src/can/ll.rs @@ -63,12 +63,12 @@ impl CanChannelLowLevel { } #[inline] - pub fn read_status(&self) -> Result { + pub fn read_state(&self) -> Result { self.msg_buf.read_stat_ctrl().status() } #[inline] - pub fn write_status(&mut self, buffer_state: BufferState) { + pub fn write_state(&mut self, buffer_state: BufferState) { self.msg_buf.modify_stat_ctrl(|mut val| { val.set_status(buffer_state); val @@ -90,7 +90,7 @@ impl CanChannelLowLevel { Ok(()) } - pub fn configure_for_reception_with_standard_id( + pub fn set_standard_id( &mut self, standard_id: embedded_can::StandardId, set_rtr: bool, @@ -101,18 +101,10 @@ impl CanChannelLowLevel { } self.msg_buf .write_id1(BaseId::new_with_raw_value(id1_reg as u32)); - - self.msg_buf.write_stat_ctrl( - BufStatusAndControl::builder() - .with_dlc(u4::new(0)) - .with_priority(u4::new(0)) - .with_status(BufferState::RxReady) - .build(), - ); Ok(()) } - pub fn configure_for_reception_with_extended_id( + pub fn set_extended_id( &mut self, extended_id: embedded_can::ExtendedId, set_rtr: bool, @@ -124,6 +116,10 @@ impl CanChannelLowLevel { let id0_reg = ((id_raw & 0x7FFF) << 1) as u16 | set_rtr as u16; self.msg_buf .write_id0(ExtendedId::new_with_raw_value(id0_reg as u32)); + Ok(()) + } + + pub fn configure_for_reception(&mut self) { self.msg_buf.write_stat_ctrl( BufStatusAndControl::builder() .with_dlc(u4::new(0)) @@ -131,7 +127,6 @@ impl CanChannelLowLevel { .with_status(BufferState::RxReady) .build(), ); - Ok(()) } pub fn transmit_frame_unchecked(&mut self, frame: CanFrame) { diff --git a/va416xx-hal/src/can/mod.rs b/va416xx-hal/src/can/mod.rs index a0e9cd8..b78da1e 100644 --- a/va416xx-hal/src/can/mod.rs +++ b/va416xx-hal/src/can/mod.rs @@ -6,7 +6,7 @@ use core::sync::atomic::AtomicBool; use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number}; use embedded_can::Frame; use ll::CanChannelLowLevel; -use regs::{BaseId, BufferState, Control, ExtendedId, MmioCan, TimingConfig}; +use regs::{BaseId, BufferState, Control, MmioCan, TimingConfig}; use crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect}; use libm::roundf; @@ -230,7 +230,7 @@ impl ClockConfig { } /// Calculate the clock configuration for the given input clock, the target bitrate and for a - /// set of timing parameters. + /// set of timing parameters. The CAN controller uses the APB1 clock. /// /// This function basically calculates the necessary prescaler to achieve the given timing /// parameters. It also performs sanity and validity checks for the calculated prescaler: @@ -314,7 +314,7 @@ impl Can { /// with the ID in the receive message buffers. This is the default reset configuration for /// the global mask as well. pub fn set_global_mask_for_exact_id_match(&mut self) { - self.regs.write_gmskx(ExtendedId::new_with_raw_value(0)); + self.regs.write_gmskx(regs::ExtendedId::new_with_raw_value(0)); self.regs.write_gmskb(BaseId::new_with_raw_value(0)); } @@ -333,7 +333,7 @@ impl Can { /// on that buffer. pub fn set_global_mask_for_exact_id_match_with_rtr_masked(&mut self) { self.regs.write_gmskx( - ExtendedId::builder() + regs::ExtendedId::builder() .with_mask_14_0(u15::new(0)) .with_xrtr(true) .build(), @@ -352,7 +352,7 @@ impl Can { /// exact match with the ID in the receive message buffers. This is the default reset /// configuration for the global mask as well. pub fn set_base_mask_for_exact_id_match(&mut self) { - self.regs.write_bmskx(ExtendedId::new_with_raw_value(0)); + self.regs.write_bmskx(regs::ExtendedId::new_with_raw_value(0)); self.regs.write_bmskb(BaseId::new_with_raw_value(0)); } @@ -360,7 +360,7 @@ impl Can { /// buffers are accepted by the base buffer 14. pub fn set_base_mask_for_all_match(&mut self) { self.regs - .write_bmskx(ExtendedId::new_with_raw_value(0xffff)); + .write_bmskx(regs::ExtendedId::new_with_raw_value(0xffff)); self.regs.write_bmskb(BaseId::new_with_raw_value(0xffff)); } @@ -380,9 +380,17 @@ impl Can { } #[inline] - pub fn enable_bufflock(&mut self) { + pub fn set_loopback(&mut self, enable: bool) { + self.regs.modify_control(|mut val| { + val.set_loopback(enable); + val + }); + } + + #[inline] + pub fn set_bufflock(&mut self, enable: bool) { self.regs.modify_control(|mut ctrl| { - ctrl.set_bufflock(true); + ctrl.set_bufflock(enable); ctrl }); } @@ -400,7 +408,6 @@ impl Can { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TxState { Idle, - TransmissionIdle, TransmittingDataFrame, TransmittingRemoteFrame, AwaitingRemoteFrameReply, @@ -430,28 +437,20 @@ pub struct CanTx { } impl CanTx { - pub fn new(ll: CanChannelLowLevel) -> Self { + pub fn new(mut ll: CanChannelLowLevel, tx_priority: Option) -> Self { + ll.configure_for_transmission(tx_priority).unwrap(); Self { ll, mode: TxState::Idle, } } - pub fn configure_for_transmission( - &mut self, - tx_priority: Option, - ) -> Result<(), InvalidBufferIndexError> { - self.ll.configure_for_transmission(tx_priority).unwrap(); - self.mode = TxState::TransmissionIdle; - Ok(()) - } - pub fn transmit_frame(&mut self, frame: CanFrame) -> Result<(), InvalidTxStateError> { if self.mode == TxState::AwaitingRemoteFrameReply { - self.configure_for_transmission(None).unwrap(); - self.mode = TxState::TransmissionIdle; + self.ll.configure_for_transmission(None).unwrap(); + self.mode = TxState::Idle; } - if self.mode != TxState::TransmissionIdle { + if self.mode != TxState::Idle { return Err(InvalidTxStateError(self.mode)); } if !frame.is_remote_frame() { @@ -467,13 +466,13 @@ impl CanTx { if self.mode != TxState::TransmittingDataFrame { return Err(nb::Error::Other(InvalidTxStateError(self.mode))); } - let status = self.ll.read_status(); + let status = self.ll.read_state(); if status.is_err() { return Err(nb::Error::WouldBlock); } let status = status.unwrap(); if status == BufferState::TxNotActive { - self.mode = TxState::TransmissionIdle; + self.mode = TxState::Idle; return Ok(()); } Err(nb::Error::WouldBlock) @@ -483,7 +482,7 @@ impl CanTx { if self.mode != TxState::TransmittingRemoteFrame { return Err(nb::Error::Other(InvalidTxStateError(self.mode))); } - let status = self.ll.read_status(); + let status = self.ll.read_state(); if status.is_err() { return Err(nb::Error::WouldBlock); } @@ -505,14 +504,19 @@ pub struct CanRx { } impl CanRx { + pub fn new(ll: CanChannelLowLevel) -> Self { + Self { + ll, + mode: RxState::Idle, + } + } pub fn configure_for_reception_with_standard_id( &mut self, standard_id: embedded_can::StandardId, set_rtr: bool, ) -> Result<(), InvalidBufferIndexError> { - self.ll - .configure_for_reception_with_standard_id(standard_id, set_rtr)?; - self.mode = RxState::Receiving; + self.ll.set_standard_id(standard_id, set_rtr)?; + self.configure_for_reception(); Ok(()) } @@ -521,12 +525,16 @@ impl CanRx { extended_id: embedded_can::ExtendedId, set_rtr: bool, ) -> Result<(), InvalidBufferIndexError> { - self.ll - .configure_for_reception_with_extended_id(extended_id, set_rtr)?; - self.mode = RxState::Receiving; + self.ll.set_extended_id(extended_id, set_rtr)?; + self.configure_for_reception(); Ok(()) } + pub fn configure_for_reception(&mut self) { + self.ll.configure_for_reception(); + self.mode = RxState::Receiving; + } + pub fn receive( &mut self, reconfigure_for_reception: bool, @@ -534,7 +542,7 @@ impl CanRx { if self.mode != RxState::Receiving { return Err(nb::Error::Other(InvalidRxStateError(self.mode))); } - let status = self.ll.read_status(); + let status = self.ll.read_state(); if status.is_err() { return Err(nb::Error::WouldBlock); } @@ -542,7 +550,7 @@ impl CanRx { if status == BufferState::RxReady || status == BufferState::RxOverrun { self.mode = RxState::Idle; if reconfigure_for_reception { - self.ll.write_status(BufferState::RxReady); + self.ll.write_state(BufferState::RxReady); } return Ok(self.ll.read_frame_unchecked()); } @@ -557,25 +565,28 @@ pub struct CanChannels { impl CanChannels { const fn new(id: CanId) -> Self { - Self { - id, - channels: [ - Some(CanChannelLowLevel::steal_unchecked(id, 0)), - Some(CanChannelLowLevel::steal_unchecked(id, 1)), - Some(CanChannelLowLevel::steal_unchecked(id, 2)), - Some(CanChannelLowLevel::steal_unchecked(id, 3)), - Some(CanChannelLowLevel::steal_unchecked(id, 4)), - Some(CanChannelLowLevel::steal_unchecked(id, 5)), - Some(CanChannelLowLevel::steal_unchecked(id, 6)), - Some(CanChannelLowLevel::steal_unchecked(id, 7)), - Some(CanChannelLowLevel::steal_unchecked(id, 8)), - Some(CanChannelLowLevel::steal_unchecked(id, 9)), - Some(CanChannelLowLevel::steal_unchecked(id, 10)), - Some(CanChannelLowLevel::steal_unchecked(id, 11)), - Some(CanChannelLowLevel::steal_unchecked(id, 12)), - Some(CanChannelLowLevel::steal_unchecked(id, 13)), - Some(CanChannelLowLevel::steal_unchecked(id, 14)), - ], + // Safety: Private function, ownership rules enforced by public API. + unsafe { + Self { + id, + channels: [ + Some(CanChannelLowLevel::steal_unchecked(id, 0)), + Some(CanChannelLowLevel::steal_unchecked(id, 1)), + Some(CanChannelLowLevel::steal_unchecked(id, 2)), + Some(CanChannelLowLevel::steal_unchecked(id, 3)), + Some(CanChannelLowLevel::steal_unchecked(id, 4)), + Some(CanChannelLowLevel::steal_unchecked(id, 5)), + Some(CanChannelLowLevel::steal_unchecked(id, 6)), + Some(CanChannelLowLevel::steal_unchecked(id, 7)), + Some(CanChannelLowLevel::steal_unchecked(id, 8)), + Some(CanChannelLowLevel::steal_unchecked(id, 9)), + Some(CanChannelLowLevel::steal_unchecked(id, 10)), + Some(CanChannelLowLevel::steal_unchecked(id, 11)), + Some(CanChannelLowLevel::steal_unchecked(id, 12)), + Some(CanChannelLowLevel::steal_unchecked(id, 13)), + Some(CanChannelLowLevel::steal_unchecked(id, 14)), + ], + } } } diff --git a/va416xx-hal/src/can/regs.rs b/va416xx-hal/src/can/regs.rs index 7f331f0..3879f7c 100644 --- a/va416xx-hal/src/can/regs.rs +++ b/va416xx-hal/src/can/regs.rs @@ -8,6 +8,7 @@ pub const CAN_1_BASE: usize = 0x4001_4400; #[derive(Debug, PartialEq, Eq)] #[bitbybit::bitenum(u4)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BufferState { /// Passive channel. RxNotActive = 0b0000, @@ -207,6 +208,7 @@ pub enum CanInterruptId { #[bitbybit::bitfield(u32)] #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StatusPending { #[bits(5..=7, r)] ns: u3, diff --git a/va416xx-hal/src/lib.rs b/va416xx-hal/src/lib.rs index 1d01fdb..de5faa5 100644 --- a/va416xx-hal/src/lib.rs +++ b/va416xx-hal/src/lib.rs @@ -33,7 +33,7 @@ use gpio::Port; pub use va416xx as device; pub use va416xx as pac; -pub mod prelude; +pub mod can; pub mod clock; pub mod dma; pub mod edac; @@ -41,12 +41,12 @@ pub mod gpio; pub mod i2c; pub mod irq_router; pub mod pins; +pub mod prelude; pub mod pwm; pub mod spi; pub mod time; pub mod timer; pub mod uart; -pub mod can; pub mod wdt; #[cfg(feature = "va41630")]