add CAN example app

This commit is contained in:
Robin Müller 2025-04-29 11:24:48 +02:00
parent 997e0502ab
commit 629ba4f8f2
Signed by: muellerr
GPG Key ID: A649FB78196E3849
8 changed files with 155 additions and 91 deletions

View File

@ -18,6 +18,7 @@ static_cell = "2"
critical-section = "1" critical-section = "1"
ringbuf = { version = "0.4", default-features = false } ringbuf = { version = "0.4", default-features = false }
nb = "1"
embassy-sync = "0.6" embassy-sync = "0.6"
embassy-time = "0.4" embassy-time = "0.4"
embassy-executor = { version = "0.7", features = [ embassy-executor = { version = "0.7", features = [

View File

@ -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();
}

View File

@ -53,6 +53,8 @@ pub enum InterruptResult {
}, },
} }
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptError { pub enum InterruptError {
UnexpectedError, UnexpectedError,
InvalidInterruptId(StatusPending), InvalidInterruptId(StatusPending),
@ -95,35 +97,30 @@ pub fn on_interrupt_can(
return Err(InterruptError::InvalidInterruptId(pending_id)); return Err(InterruptError::InvalidInterruptId(pending_id));
} }
match pending_id.interrupt_id().unwrap() { match pending_id.interrupt_id().unwrap() {
super::regs::CanInterruptId::None => { super::regs::CanInterruptId::None => Ok(InterruptResult::NoInterrupt),
return Ok(InterruptResult::NoInterrupt); super::regs::CanInterruptId::Error => Err(InterruptError::CanError(regs.read_diag())),
}
super::regs::CanInterruptId::Error => {
return Err(InterruptError::CanError(regs.read_diag()));
}
super::regs::CanInterruptId::Buffer(idx) => { super::regs::CanInterruptId::Buffer(idx) => {
let mut channel = unsafe { CanChannelLowLevel::steal_unchecked(id, idx) }; let mut channel = unsafe { CanChannelLowLevel::steal_unchecked(id, idx) };
let status = channel.read_status(); let status = channel.read_state();
if status.is_err() { if status.is_err() {
let mut clr = InterruptClear::new_with_raw_value(0); 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.write_iclr(clr);
regs.modify_ien(|mut val| { regs.modify_ien(|mut val| {
val.set_buffer(idx as usize, false); val.set_buffer(idx, false);
val val
}); });
return Err(InterruptError::InvalidStatus(status.unwrap_err())); return Err(InterruptError::InvalidStatus(status.unwrap_err()));
} }
let buf_state = status.unwrap(); let buf_state = status.unwrap();
if buf_state == BufferState::TxNotActive { 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); clear_and_disable_interrupt(&mut regs, idx);
// Handle reading frames, updating states etc. // Handle reading frames, updating states etc.
if tx_state == TxChannelState::TxDataFrame as u8 { if tx_state == TxChannelState::TxDataFrame as u8 {
// Transmission complete. // Transmission complete.
TX_STATES[idx as usize] TX_STATES[idx].store(TxChannelState::Finished as u8, Ordering::Relaxed);
.store(TxChannelState::Finished as u8, Ordering::Relaxed); TX_WAKERS[idx].wake();
TX_WAKERS[idx as usize].wake();
return Ok(InterruptResult::TransmissionEvent { return Ok(InterruptResult::TransmissionEvent {
channel_index: idx, channel_index: idx,
id: TxEventId::TxDataFrame, id: TxEventId::TxDataFrame,
@ -131,22 +128,21 @@ pub fn on_interrupt_can(
} }
} }
if buf_state == BufferState::RxReady { 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 tx_state == TxChannelState::TxRtrTransmission as u8 {
if reconfigure_tx_rtr_to_tx { if reconfigure_tx_rtr_to_tx {
channel.write_status(BufferState::TxNotActive); channel.write_state(BufferState::TxNotActive);
clear_and_disable_interrupt(&mut regs, idx); clear_and_disable_interrupt(&mut regs, idx);
// Transmission complete. // Transmission complete.
TX_STATES[idx as usize] TX_STATES[idx].store(TxChannelState::Idle as u8, Ordering::Relaxed);
.store(TxChannelState::Idle as u8, Ordering::Relaxed);
} else { } else {
// Do not disable interrupt, channel is now used to receive the frame. // Do not disable interrupt, channel is now used to receive the frame.
clear_interrupt(&mut regs, idx); clear_interrupt(&mut regs, idx);
// Transmission complete. // Transmission complete.
TX_STATES[idx as usize] TX_STATES[idx]
.store(TxChannelState::TxRtrReception as u8, Ordering::Relaxed); .store(TxChannelState::TxRtrReception as u8, Ordering::Relaxed);
} }
TX_WAKERS[idx as usize].wake(); TX_WAKERS[idx].wake();
return Ok(InterruptResult::TransmissionEvent { return Ok(InterruptResult::TransmissionEvent {
channel_index: idx, channel_index: idx,
id: TxEventId::TxRemoteFrame, id: TxEventId::TxRemoteFrame,
@ -154,18 +150,18 @@ pub fn on_interrupt_can(
} }
} }
if buf_state == BufferState::RxOverrun || buf_state == BufferState::RxFull { 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. // Do not disable interrupt and assume continuous reception.
clear_interrupt(&mut regs, idx); clear_interrupt(&mut regs, idx);
let frame = channel.read_frame_unchecked(); let frame = channel.read_frame_unchecked();
if tx_state == TxChannelState::TxRtrReception as u8 { if tx_state == TxChannelState::TxRtrReception as u8 {
// Reception of response complete. We can release the channel for TX (or RX) // Reception of response complete. We can release the channel for TX (or RX)
// usage again. // usage again.
TX_STATES[idx as usize].store(TxChannelState::Idle as u8, Ordering::Relaxed); TX_STATES[idx].store(TxChannelState::Idle as u8, Ordering::Relaxed);
channel.write_status(BufferState::TxNotActive); channel.write_state(BufferState::TxNotActive);
} else { } else {
// Assume continous reception of frames. // Assume continous reception of frames.
channel.write_status(BufferState::RxReady); channel.write_state(BufferState::RxReady);
} }
return Ok(InterruptResult::ReceivedFrame { return Ok(InterruptResult::ReceivedFrame {
channel_index: idx, 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) { fn clear_and_disable_interrupt(regs: &mut MmioCan<'static>, idx: usize) {
clear_interrupt(regs, idx); clear_interrupt(regs, idx);
regs.modify_ien(|mut val| { regs.modify_ien(|mut val| {
val.set_buffer(idx as usize, false); val.set_buffer(idx, false);
val val
}); });
} }

View File

@ -1,3 +1,5 @@
pub use embedded_can::{ExtendedId, Id, StandardId};
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[error("invalid data size error {0}")] #[error("invalid data size error {0}")]
pub struct InvalidDataSizeError(usize); pub struct InvalidDataSizeError(usize);

View File

@ -63,12 +63,12 @@ impl CanChannelLowLevel {
} }
#[inline] #[inline]
pub fn read_status(&self) -> Result<BufferState, u8> { pub fn read_state(&self) -> Result<BufferState, u8> {
self.msg_buf.read_stat_ctrl().status() self.msg_buf.read_stat_ctrl().status()
} }
#[inline] #[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| { self.msg_buf.modify_stat_ctrl(|mut val| {
val.set_status(buffer_state); val.set_status(buffer_state);
val val
@ -90,7 +90,7 @@ impl CanChannelLowLevel {
Ok(()) Ok(())
} }
pub fn configure_for_reception_with_standard_id( pub fn set_standard_id(
&mut self, &mut self,
standard_id: embedded_can::StandardId, standard_id: embedded_can::StandardId,
set_rtr: bool, set_rtr: bool,
@ -101,18 +101,10 @@ impl CanChannelLowLevel {
} }
self.msg_buf self.msg_buf
.write_id1(BaseId::new_with_raw_value(id1_reg as u32)); .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(()) Ok(())
} }
pub fn configure_for_reception_with_extended_id( pub fn set_extended_id(
&mut self, &mut self,
extended_id: embedded_can::ExtendedId, extended_id: embedded_can::ExtendedId,
set_rtr: bool, set_rtr: bool,
@ -124,6 +116,10 @@ impl CanChannelLowLevel {
let id0_reg = ((id_raw & 0x7FFF) << 1) as u16 | set_rtr as u16; let id0_reg = ((id_raw & 0x7FFF) << 1) as u16 | set_rtr as u16;
self.msg_buf self.msg_buf
.write_id0(ExtendedId::new_with_raw_value(id0_reg as u32)); .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( self.msg_buf.write_stat_ctrl(
BufStatusAndControl::builder() BufStatusAndControl::builder()
.with_dlc(u4::new(0)) .with_dlc(u4::new(0))
@ -131,7 +127,6 @@ impl CanChannelLowLevel {
.with_status(BufferState::RxReady) .with_status(BufferState::RxReady)
.build(), .build(),
); );
Ok(())
} }
pub fn transmit_frame_unchecked(&mut self, frame: CanFrame) { pub fn transmit_frame_unchecked(&mut self, frame: CanFrame) {

View File

@ -6,7 +6,7 @@ use core::sync::atomic::AtomicBool;
use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number}; use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number};
use embedded_can::Frame; use embedded_can::Frame;
use ll::CanChannelLowLevel; 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 crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect};
use libm::roundf; use libm::roundf;
@ -230,7 +230,7 @@ impl ClockConfig {
} }
/// Calculate the clock configuration for the given input clock, the target bitrate and for a /// 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 /// This function basically calculates the necessary prescaler to achieve the given timing
/// parameters. It also performs sanity and validity checks for the calculated prescaler: /// 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 /// with the ID in the receive message buffers. This is the default reset configuration for
/// the global mask as well. /// the global mask as well.
pub fn set_global_mask_for_exact_id_match(&mut self) { 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)); self.regs.write_gmskb(BaseId::new_with_raw_value(0));
} }
@ -333,7 +333,7 @@ impl Can {
/// on that buffer. /// on that buffer.
pub fn set_global_mask_for_exact_id_match_with_rtr_masked(&mut self) { pub fn set_global_mask_for_exact_id_match_with_rtr_masked(&mut self) {
self.regs.write_gmskx( self.regs.write_gmskx(
ExtendedId::builder() regs::ExtendedId::builder()
.with_mask_14_0(u15::new(0)) .with_mask_14_0(u15::new(0))
.with_xrtr(true) .with_xrtr(true)
.build(), .build(),
@ -352,7 +352,7 @@ impl Can {
/// exact match with the ID in the receive message buffers. This is the default reset /// exact match with the ID in the receive message buffers. This is the default reset
/// configuration for the global mask as well. /// configuration for the global mask as well.
pub fn set_base_mask_for_exact_id_match(&mut self) { 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)); self.regs.write_bmskb(BaseId::new_with_raw_value(0));
} }
@ -360,7 +360,7 @@ impl Can {
/// buffers are accepted by the base buffer 14. /// buffers are accepted by the base buffer 14.
pub fn set_base_mask_for_all_match(&mut self) { pub fn set_base_mask_for_all_match(&mut self) {
self.regs 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)); self.regs.write_bmskb(BaseId::new_with_raw_value(0xffff));
} }
@ -380,9 +380,17 @@ impl Can {
} }
#[inline] #[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| { self.regs.modify_control(|mut ctrl| {
ctrl.set_bufflock(true); ctrl.set_bufflock(enable);
ctrl ctrl
}); });
} }
@ -400,7 +408,6 @@ impl Can {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TxState { pub enum TxState {
Idle, Idle,
TransmissionIdle,
TransmittingDataFrame, TransmittingDataFrame,
TransmittingRemoteFrame, TransmittingRemoteFrame,
AwaitingRemoteFrameReply, AwaitingRemoteFrameReply,
@ -430,28 +437,20 @@ pub struct CanTx {
} }
impl CanTx { impl CanTx {
pub fn new(ll: CanChannelLowLevel) -> Self { pub fn new(mut ll: CanChannelLowLevel, tx_priority: Option<u4>) -> Self {
ll.configure_for_transmission(tx_priority).unwrap();
Self { Self {
ll, ll,
mode: TxState::Idle, mode: TxState::Idle,
} }
} }
pub fn configure_for_transmission(
&mut self,
tx_priority: Option<u4>,
) -> 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> { pub fn transmit_frame(&mut self, frame: CanFrame) -> Result<(), InvalidTxStateError> {
if self.mode == TxState::AwaitingRemoteFrameReply { if self.mode == TxState::AwaitingRemoteFrameReply {
self.configure_for_transmission(None).unwrap(); self.ll.configure_for_transmission(None).unwrap();
self.mode = TxState::TransmissionIdle; self.mode = TxState::Idle;
} }
if self.mode != TxState::TransmissionIdle { if self.mode != TxState::Idle {
return Err(InvalidTxStateError(self.mode)); return Err(InvalidTxStateError(self.mode));
} }
if !frame.is_remote_frame() { if !frame.is_remote_frame() {
@ -467,13 +466,13 @@ impl CanTx {
if self.mode != TxState::TransmittingDataFrame { if self.mode != TxState::TransmittingDataFrame {
return Err(nb::Error::Other(InvalidTxStateError(self.mode))); return Err(nb::Error::Other(InvalidTxStateError(self.mode)));
} }
let status = self.ll.read_status(); let status = self.ll.read_state();
if status.is_err() { if status.is_err() {
return Err(nb::Error::WouldBlock); return Err(nb::Error::WouldBlock);
} }
let status = status.unwrap(); let status = status.unwrap();
if status == BufferState::TxNotActive { if status == BufferState::TxNotActive {
self.mode = TxState::TransmissionIdle; self.mode = TxState::Idle;
return Ok(()); return Ok(());
} }
Err(nb::Error::WouldBlock) Err(nb::Error::WouldBlock)
@ -483,7 +482,7 @@ impl CanTx {
if self.mode != TxState::TransmittingRemoteFrame { if self.mode != TxState::TransmittingRemoteFrame {
return Err(nb::Error::Other(InvalidTxStateError(self.mode))); return Err(nb::Error::Other(InvalidTxStateError(self.mode)));
} }
let status = self.ll.read_status(); let status = self.ll.read_state();
if status.is_err() { if status.is_err() {
return Err(nb::Error::WouldBlock); return Err(nb::Error::WouldBlock);
} }
@ -505,14 +504,19 @@ pub struct CanRx {
} }
impl CanRx { impl CanRx {
pub fn new(ll: CanChannelLowLevel) -> Self {
Self {
ll,
mode: RxState::Idle,
}
}
pub fn configure_for_reception_with_standard_id( pub fn configure_for_reception_with_standard_id(
&mut self, &mut self,
standard_id: embedded_can::StandardId, standard_id: embedded_can::StandardId,
set_rtr: bool, set_rtr: bool,
) -> Result<(), InvalidBufferIndexError> { ) -> Result<(), InvalidBufferIndexError> {
self.ll self.ll.set_standard_id(standard_id, set_rtr)?;
.configure_for_reception_with_standard_id(standard_id, set_rtr)?; self.configure_for_reception();
self.mode = RxState::Receiving;
Ok(()) Ok(())
} }
@ -521,12 +525,16 @@ impl CanRx {
extended_id: embedded_can::ExtendedId, extended_id: embedded_can::ExtendedId,
set_rtr: bool, set_rtr: bool,
) -> Result<(), InvalidBufferIndexError> { ) -> Result<(), InvalidBufferIndexError> {
self.ll self.ll.set_extended_id(extended_id, set_rtr)?;
.configure_for_reception_with_extended_id(extended_id, set_rtr)?; self.configure_for_reception();
self.mode = RxState::Receiving;
Ok(()) Ok(())
} }
pub fn configure_for_reception(&mut self) {
self.ll.configure_for_reception();
self.mode = RxState::Receiving;
}
pub fn receive( pub fn receive(
&mut self, &mut self,
reconfigure_for_reception: bool, reconfigure_for_reception: bool,
@ -534,7 +542,7 @@ impl CanRx {
if self.mode != RxState::Receiving { if self.mode != RxState::Receiving {
return Err(nb::Error::Other(InvalidRxStateError(self.mode))); return Err(nb::Error::Other(InvalidRxStateError(self.mode)));
} }
let status = self.ll.read_status(); let status = self.ll.read_state();
if status.is_err() { if status.is_err() {
return Err(nb::Error::WouldBlock); return Err(nb::Error::WouldBlock);
} }
@ -542,7 +550,7 @@ impl CanRx {
if status == BufferState::RxReady || status == BufferState::RxOverrun { if status == BufferState::RxReady || status == BufferState::RxOverrun {
self.mode = RxState::Idle; self.mode = RxState::Idle;
if reconfigure_for_reception { if reconfigure_for_reception {
self.ll.write_status(BufferState::RxReady); self.ll.write_state(BufferState::RxReady);
} }
return Ok(self.ll.read_frame_unchecked()); return Ok(self.ll.read_frame_unchecked());
} }
@ -557,25 +565,28 @@ pub struct CanChannels {
impl CanChannels { impl CanChannels {
const fn new(id: CanId) -> Self { const fn new(id: CanId) -> Self {
Self { // Safety: Private function, ownership rules enforced by public API.
id, unsafe {
channels: [ Self {
Some(CanChannelLowLevel::steal_unchecked(id, 0)), id,
Some(CanChannelLowLevel::steal_unchecked(id, 1)), channels: [
Some(CanChannelLowLevel::steal_unchecked(id, 2)), Some(CanChannelLowLevel::steal_unchecked(id, 0)),
Some(CanChannelLowLevel::steal_unchecked(id, 3)), Some(CanChannelLowLevel::steal_unchecked(id, 1)),
Some(CanChannelLowLevel::steal_unchecked(id, 4)), Some(CanChannelLowLevel::steal_unchecked(id, 2)),
Some(CanChannelLowLevel::steal_unchecked(id, 5)), Some(CanChannelLowLevel::steal_unchecked(id, 3)),
Some(CanChannelLowLevel::steal_unchecked(id, 6)), Some(CanChannelLowLevel::steal_unchecked(id, 4)),
Some(CanChannelLowLevel::steal_unchecked(id, 7)), Some(CanChannelLowLevel::steal_unchecked(id, 5)),
Some(CanChannelLowLevel::steal_unchecked(id, 8)), Some(CanChannelLowLevel::steal_unchecked(id, 6)),
Some(CanChannelLowLevel::steal_unchecked(id, 9)), Some(CanChannelLowLevel::steal_unchecked(id, 7)),
Some(CanChannelLowLevel::steal_unchecked(id, 10)), Some(CanChannelLowLevel::steal_unchecked(id, 8)),
Some(CanChannelLowLevel::steal_unchecked(id, 11)), Some(CanChannelLowLevel::steal_unchecked(id, 9)),
Some(CanChannelLowLevel::steal_unchecked(id, 12)), Some(CanChannelLowLevel::steal_unchecked(id, 10)),
Some(CanChannelLowLevel::steal_unchecked(id, 13)), Some(CanChannelLowLevel::steal_unchecked(id, 11)),
Some(CanChannelLowLevel::steal_unchecked(id, 14)), Some(CanChannelLowLevel::steal_unchecked(id, 12)),
], Some(CanChannelLowLevel::steal_unchecked(id, 13)),
Some(CanChannelLowLevel::steal_unchecked(id, 14)),
],
}
} }
} }

View File

@ -8,6 +8,7 @@ pub const CAN_1_BASE: usize = 0x4001_4400;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[bitbybit::bitenum(u4)] #[bitbybit::bitenum(u4)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BufferState { pub enum BufferState {
/// Passive channel. /// Passive channel.
RxNotActive = 0b0000, RxNotActive = 0b0000,
@ -207,6 +208,7 @@ pub enum CanInterruptId {
#[bitbybit::bitfield(u32)] #[bitbybit::bitfield(u32)]
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StatusPending { pub struct StatusPending {
#[bits(5..=7, r)] #[bits(5..=7, r)]
ns: u3, ns: u3,

View File

@ -33,7 +33,7 @@ use gpio::Port;
pub use va416xx as device; pub use va416xx as device;
pub use va416xx as pac; pub use va416xx as pac;
pub mod prelude; pub mod can;
pub mod clock; pub mod clock;
pub mod dma; pub mod dma;
pub mod edac; pub mod edac;
@ -41,12 +41,12 @@ pub mod gpio;
pub mod i2c; pub mod i2c;
pub mod irq_router; pub mod irq_router;
pub mod pins; pub mod pins;
pub mod prelude;
pub mod pwm; pub mod pwm;
pub mod spi; pub mod spi;
pub mod time; pub mod time;
pub mod timer; pub mod timer;
pub mod uart; pub mod uart;
pub mod can;
pub mod wdt; pub mod wdt;
#[cfg(feature = "va41630")] #[cfg(feature = "va41630")]