finished first impl
This commit is contained in:
parent
d9d6e11642
commit
997e0502ab
@ -13,7 +13,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
va416xx = { version = "0.4", features = ["critical-section"], default-features = false }
|
||||
derive-mmio = "0.4"
|
||||
derive-mmio = { version = "0.4", git = "https://github.com/us-irs/derive-mmio.git", branch = "more-constness" }
|
||||
vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor4x"] }
|
||||
|
||||
libm = "0.2"
|
||||
@ -25,6 +25,7 @@ bitbybit = "1.3"
|
||||
arbitrary-int = "1.3"
|
||||
fugit = "0.3"
|
||||
embedded-can = "0.4"
|
||||
embassy-sync = "0.6"
|
||||
thiserror = { version = "2", default-features = false }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
|
289
va416xx-hal/src/can/asynch.rs
Normal file
289
va416xx-hal/src/can/asynch.rs
Normal file
@ -0,0 +1,289 @@
|
||||
use core::{
|
||||
future::Future,
|
||||
sync::atomic::{AtomicU8, Ordering},
|
||||
usize,
|
||||
};
|
||||
|
||||
use crate::can::regs::BufferState;
|
||||
|
||||
use super::{
|
||||
regs::{DiagnosticRegister, InterruptClear, MmioCan, StatusPending},
|
||||
CanChannelLowLevel, CanFrame, CanId,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TxChannelState {
|
||||
Unconfigured = 0,
|
||||
Idle = 1,
|
||||
TxDataFrame = 2,
|
||||
TxRtrTransmission = 3,
|
||||
TxRtrReception = 4,
|
||||
Finished = 5,
|
||||
}
|
||||
|
||||
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)]
|
||||
pub enum TxEventId {
|
||||
/// Buffer state went from [BufferState::TxOnce] to [BufferState::TxNotActive].
|
||||
TxDataFrame,
|
||||
/// Buffer state went from [BufferState::TxOnce] to [BufferState::TxNotActive] for a remote
|
||||
/// frame (RTR bit set). Channel might be in reception mode [BufferState::RxReady] now.
|
||||
TxRemoteFrame,
|
||||
/// A response to a remote frame was performed successfully, and the buffer state went from
|
||||
/// [BufferState::TxOnceRtr] to [BufferState::TxRtr].
|
||||
RtrResponse,
|
||||
/// A remote frame was received and the transmission of a response frame was scheduled. The
|
||||
/// buffer state went from [BufferState::TxRtr] to [BufferState::TxOnceRtr].
|
||||
TransmitScheduling,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InterruptResult {
|
||||
NoInterrupt,
|
||||
ReceivedFrame {
|
||||
channel_index: usize,
|
||||
frame: CanFrame,
|
||||
},
|
||||
TransmissionEvent {
|
||||
channel_index: usize,
|
||||
id: TxEventId,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum InterruptError {
|
||||
UnexpectedError,
|
||||
InvalidInterruptId(StatusPending),
|
||||
InvalidStatus(u8),
|
||||
UnexpectedState(BufferState),
|
||||
CanError(DiagnosticRegister),
|
||||
}
|
||||
|
||||
/// This interrupt handler allow asynchronous transmission and reception of CAN frames.
|
||||
///
|
||||
/// This handler will re-configure a channel to [BufferState::RxReady] after successfull reception
|
||||
/// of a frame without disabling the interrupts, assuming that the user wants to immediately
|
||||
/// receive the next frame on the channel.
|
||||
/// The user should re-configure the buffer state to [BufferState::RxNotActive] if the reception
|
||||
/// should be disabled.
|
||||
///
|
||||
/// The handler will re-configure a channel to [BufferState::TxNotActive] instead of
|
||||
/// [BufferState::RxReady] if the completed frame transmission was a remote frame and after
|
||||
/// successfully having received a response to that remote frame. The assumption is that this
|
||||
/// channel is used to request more frames. If the argument `reconfigure_tx_rtr_to_tx` is set to
|
||||
/// true, the channel will automatically be configured back to [BufferState::TxNotActive] with
|
||||
/// interrupts for the respective channel disabled after transmission of a remote frame.
|
||||
///
|
||||
/// The handler will not disable the interrupts realted to the TX RTR and TX RTR ONCE auto-response
|
||||
/// functionality of the CAN peripheral. It will report the event type to the caller via the
|
||||
/// [TxEventId] enumeration.
|
||||
pub fn on_interrupt_can(
|
||||
id: CanId,
|
||||
reconfigure_tx_rtr_to_tx: bool,
|
||||
) -> Result<InterruptResult, InterruptError> {
|
||||
let mut regs = unsafe { id.steal_regs() };
|
||||
// Check if any interrupts are enabled.
|
||||
let ie = regs.read_ien();
|
||||
if ie.raw_value() == 0 {
|
||||
return Ok(InterruptResult::NoInterrupt);
|
||||
}
|
||||
let pending_id = regs.read_status_pending();
|
||||
if pending_id.interrupt_id().is_none() {
|
||||
regs.write_iclr(InterruptClear::new_with_raw_value(0xFFFF_FFFF));
|
||||
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::Buffer(idx) => {
|
||||
let mut channel = unsafe { CanChannelLowLevel::steal_unchecked(id, idx) };
|
||||
let status = channel.read_status();
|
||||
if status.is_err() {
|
||||
let mut clr = InterruptClear::new_with_raw_value(0);
|
||||
clr.set_buffer(idx as usize, true);
|
||||
regs.write_iclr(clr);
|
||||
regs.modify_ien(|mut val| {
|
||||
val.set_buffer(idx as usize, 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);
|
||||
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();
|
||||
return Ok(InterruptResult::TransmissionEvent {
|
||||
channel_index: idx,
|
||||
id: TxEventId::TxDataFrame,
|
||||
});
|
||||
}
|
||||
}
|
||||
if buf_state == BufferState::RxReady {
|
||||
let tx_state = TX_STATES[idx as usize].load(Ordering::Relaxed);
|
||||
if tx_state == TxChannelState::TxRtrTransmission as u8 {
|
||||
if reconfigure_tx_rtr_to_tx {
|
||||
channel.write_status(BufferState::TxNotActive);
|
||||
clear_and_disable_interrupt(&mut regs, idx);
|
||||
// Transmission complete.
|
||||
TX_STATES[idx as usize]
|
||||
.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]
|
||||
.store(TxChannelState::TxRtrReception as u8, Ordering::Relaxed);
|
||||
}
|
||||
TX_WAKERS[idx as usize].wake();
|
||||
return Ok(InterruptResult::TransmissionEvent {
|
||||
channel_index: idx,
|
||||
id: TxEventId::TxRemoteFrame,
|
||||
});
|
||||
}
|
||||
}
|
||||
if buf_state == BufferState::RxOverrun || buf_state == BufferState::RxFull {
|
||||
let tx_state = TX_STATES[idx as usize].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);
|
||||
} else {
|
||||
// Assume continous reception of frames.
|
||||
channel.write_status(BufferState::RxReady);
|
||||
}
|
||||
return Ok(InterruptResult::ReceivedFrame {
|
||||
channel_index: idx,
|
||||
frame,
|
||||
});
|
||||
}
|
||||
if buf_state == BufferState::TxRtr {
|
||||
// Do not disable interrupt and assume continuous transmission.
|
||||
clear_interrupt(&mut regs, idx);
|
||||
return Ok(InterruptResult::TransmissionEvent {
|
||||
channel_index: idx,
|
||||
id: TxEventId::RtrResponse,
|
||||
});
|
||||
}
|
||||
if buf_state == BufferState::TxOnceRtr {
|
||||
// Do not disable interrupt and assume continuous transmission.
|
||||
clear_interrupt(&mut regs, idx);
|
||||
return Ok(InterruptResult::TransmissionEvent {
|
||||
channel_index: idx,
|
||||
id: TxEventId::TransmitScheduling,
|
||||
});
|
||||
}
|
||||
|
||||
Err(InterruptError::UnexpectedState(buf_state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn clear_interrupt(regs: &mut MmioCan<'static>, idx: usize) {
|
||||
let mut clr = InterruptClear::new_with_raw_value(0);
|
||||
clr.set_buffer(idx, true);
|
||||
regs.write_iclr(clr);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("all channels are unconfigured, none available for TX")]
|
||||
pub struct AllTxChannelsUnconfiguredError;
|
||||
|
||||
pub struct CanTxFuture(usize);
|
||||
|
||||
impl Future for CanTxFuture {
|
||||
type Output = ();
|
||||
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
TX_WAKERS[self.0].register(cx.waker());
|
||||
if TX_STATES[self.0].load(Ordering::Relaxed) == TxChannelState::Finished as u8 {
|
||||
TX_STATES[self.0].store(TxChannelState::Idle as u8, Ordering::Relaxed);
|
||||
return core::task::Poll::Ready(());
|
||||
}
|
||||
core::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
impl CanTxFuture {
|
||||
pub fn new(frame: CanFrame) -> nb::Result<Self, AllTxChannelsUnconfiguredError> {
|
||||
let mut channel_is_free = [false; 15];
|
||||
let mut all_channels_unused = true;
|
||||
for (idx, state) in TX_STATES.iter().enumerate() {
|
||||
let state = state.load(Ordering::Relaxed);
|
||||
if state == TxChannelState::Idle as u8 {
|
||||
channel_is_free[idx] = true;
|
||||
}
|
||||
if state != TxChannelState::Unconfigured as u8 {
|
||||
all_channels_unused = false;
|
||||
}
|
||||
}
|
||||
if channel_is_free.iter().all(|&x| !x) {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
if all_channels_unused {
|
||||
return Err(nb::Error::Other(AllTxChannelsUnconfiguredError));
|
||||
}
|
||||
let free_channel_id = channel_is_free.iter().position(|&x| x).unwrap();
|
||||
let mut channel =
|
||||
unsafe { CanChannelLowLevel::steal_unchecked(CanId::Can0, free_channel_id) };
|
||||
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);
|
||||
|
||||
impl CanTxAsync {
|
||||
pub fn new(can: super::Can) -> Self {
|
||||
CanTxAsync(can)
|
||||
}
|
||||
|
||||
/// Start a transmission and returns the future which can be polled to completion.
|
||||
pub fn start_transmit(
|
||||
&mut self,
|
||||
frame: CanFrame,
|
||||
) -> nb::Result<CanTxFuture, AllTxChannelsUnconfiguredError> {
|
||||
CanTxFuture::new(frame)
|
||||
}
|
||||
|
||||
/// Calls [Self::start_transmit] and awaits the returned future to completion immediately.
|
||||
pub async fn transmit(
|
||||
&mut self,
|
||||
frame: CanFrame,
|
||||
) -> nb::Result<(), AllTxChannelsUnconfiguredError> {
|
||||
self.start_transmit(frame)?.await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
#[error("invalid data size error {0}")]
|
||||
pub struct InvalidDataSizeError(usize);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CanFrameNormal {
|
||||
id: embedded_can::Id,
|
||||
size: usize,
|
||||
@ -33,6 +34,7 @@ impl CanFrameNormal {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CanFrameRtr {
|
||||
id: embedded_can::Id,
|
||||
dlc: usize,
|
||||
@ -52,6 +54,7 @@ impl CanFrameRtr {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CanFrame {
|
||||
Normal(CanFrameNormal),
|
||||
Rtr(CanFrameRtr),
|
||||
|
309
va416xx-hal/src/can/ll.rs
Normal file
309
va416xx-hal/src/can/ll.rs
Normal file
@ -0,0 +1,309 @@
|
||||
use arbitrary_int::{u11, u15, u3, u4, Number};
|
||||
use embedded_can::Frame;
|
||||
|
||||
use super::{
|
||||
regs::{BaseId, BufStatusAndControl, BufferState, ExtendedId, MmioCanMsgBuf, TwoBytesData},
|
||||
CanFrame, CanFrameNormal, CanFrameRtr, CanId, InvalidBufferIndexError,
|
||||
};
|
||||
|
||||
pub struct CanChannelLowLevel {
|
||||
id: CanId,
|
||||
/// Message buffer index.
|
||||
idx: usize,
|
||||
msg_buf: MmioCanMsgBuf<'static>,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for CanChannelLowLevel {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("CanChannel")
|
||||
.field("can_id", &self.id)
|
||||
.field("idx", &self.idx)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl CanChannelLowLevel {
|
||||
#[inline]
|
||||
pub unsafe fn steal(can: CanId, idx: usize) -> Result<Self, InvalidBufferIndexError> {
|
||||
if idx > 14 {
|
||||
return Err(InvalidBufferIndexError(idx));
|
||||
}
|
||||
let msg_buf = unsafe { can.steal_regs().steal_cmbs_unchecked(idx) };
|
||||
Ok(Self {
|
||||
id: can,
|
||||
idx,
|
||||
msg_buf,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const unsafe fn steal_unchecked(can: CanId, idx: usize) -> Self {
|
||||
if idx > 14 {
|
||||
panic!("invalid buffer index for CAN low level channel");
|
||||
}
|
||||
let msg_buf = unsafe { can.steal_regs().steal_cmbs_unchecked(idx) };
|
||||
Self {
|
||||
id: can,
|
||||
idx,
|
||||
msg_buf,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Allows to create an aribtrary amoutn of driver handles to the same message block, which
|
||||
/// might lead to data races on invalid usage.
|
||||
#[inline]
|
||||
pub const unsafe fn clone(&self) -> Self {
|
||||
Self {
|
||||
id: self.id,
|
||||
idx: self.idx,
|
||||
msg_buf: unsafe { self.msg_buf.clone() },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_status(&self) -> Result<BufferState, u8> {
|
||||
self.msg_buf.read_stat_ctrl().status()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_status(&mut self, buffer_state: BufferState) {
|
||||
self.msg_buf.modify_stat_ctrl(|mut val| {
|
||||
val.set_status(buffer_state);
|
||||
val
|
||||
});
|
||||
}
|
||||
|
||||
pub fn configure_for_transmission(
|
||||
&mut self,
|
||||
tx_priority: Option<u4>,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
self.msg_buf.modify_stat_ctrl(|mut val| {
|
||||
val.set_dlc(u4::new(0));
|
||||
if let Some(tx_priority) = tx_priority {
|
||||
val.set_priority(tx_priority);
|
||||
}
|
||||
val.set_status(BufferState::TxNotActive);
|
||||
val
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn configure_for_reception_with_standard_id(
|
||||
&mut self,
|
||||
standard_id: embedded_can::StandardId,
|
||||
set_rtr: bool,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
let mut id1_reg = standard_id.as_raw() << 5;
|
||||
if set_rtr {
|
||||
id1_reg |= 1 << 4;
|
||||
}
|
||||
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(
|
||||
&mut self,
|
||||
extended_id: embedded_can::ExtendedId,
|
||||
set_rtr: bool,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
let id_raw = extended_id.as_raw();
|
||||
let id1_reg = (((id_raw >> 18) & 0x7FF) << 4) as u16 | ((id_raw >> 15) & 0b111) as u16;
|
||||
self.msg_buf
|
||||
.write_id1(BaseId::new_with_raw_value(id1_reg as u32));
|
||||
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));
|
||||
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 transmit_frame_unchecked(&mut self, frame: CanFrame) {
|
||||
let is_remote = frame.is_remote_frame();
|
||||
self.write_id(frame.id(), is_remote);
|
||||
let dlc = frame.dlc();
|
||||
if !is_remote {
|
||||
self.msg_buf
|
||||
.write_data0(TwoBytesData::new_with_raw_value(0));
|
||||
self.msg_buf
|
||||
.write_data1(TwoBytesData::new_with_raw_value(0));
|
||||
self.msg_buf
|
||||
.write_data2(TwoBytesData::new_with_raw_value(0));
|
||||
self.msg_buf
|
||||
.write_data3(TwoBytesData::new_with_raw_value(0));
|
||||
for idx in 0..dlc {
|
||||
match idx {
|
||||
0 => self.msg_buf.modify_data3(|mut val| {
|
||||
val.set_data_upper_byte(frame.data()[idx]);
|
||||
val
|
||||
}),
|
||||
1 => self.msg_buf.modify_data3(|mut val| {
|
||||
val.set_data_lower_byte(frame.data()[idx]);
|
||||
val
|
||||
}),
|
||||
2 => self.msg_buf.modify_data2(|mut val| {
|
||||
val.set_data_upper_byte(frame.data()[idx]);
|
||||
val
|
||||
}),
|
||||
3 => self.msg_buf.modify_data2(|mut val| {
|
||||
val.set_data_lower_byte(frame.data()[idx]);
|
||||
val
|
||||
}),
|
||||
4 => self.msg_buf.modify_data1(|mut val| {
|
||||
val.set_data_upper_byte(frame.data()[idx]);
|
||||
val
|
||||
}),
|
||||
5 => self.msg_buf.modify_data1(|mut val| {
|
||||
val.set_data_lower_byte(frame.data()[idx]);
|
||||
val
|
||||
}),
|
||||
6 => self.msg_buf.modify_data0(|mut val| {
|
||||
val.set_data_upper_byte(frame.data()[idx]);
|
||||
val
|
||||
}),
|
||||
7 => self.msg_buf.modify_data0(|mut val| {
|
||||
val.set_data_lower_byte(frame.data()[idx]);
|
||||
val
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
self.msg_buf.modify_stat_ctrl(|mut ctrl| {
|
||||
ctrl.set_dlc(u4::new(dlc as u8));
|
||||
ctrl.set_status(BufferState::TxOnce);
|
||||
ctrl
|
||||
});
|
||||
}
|
||||
|
||||
pub fn enable_error_interrupt(&mut self, enable_translation: bool) {
|
||||
let mut regs = unsafe { self.id.steal_regs() };
|
||||
if enable_translation {
|
||||
regs.modify_icen(|mut val| {
|
||||
val.set_error(true);
|
||||
val
|
||||
});
|
||||
}
|
||||
regs.modify_ien(|mut val| {
|
||||
val.set_error(true);
|
||||
val
|
||||
});
|
||||
}
|
||||
|
||||
pub fn enable_interrupt(&mut self, enable_translation: bool) {
|
||||
let mut regs = unsafe { self.id.steal_regs() };
|
||||
if enable_translation {
|
||||
regs.modify_icen(|mut val| {
|
||||
val.set_buffer(self.idx, true);
|
||||
val
|
||||
});
|
||||
}
|
||||
regs.modify_ien(|mut val| {
|
||||
val.set_buffer(self.idx, true);
|
||||
val
|
||||
});
|
||||
}
|
||||
|
||||
fn write_id(&mut self, id: embedded_can::Id, is_remote: bool) {
|
||||
match id {
|
||||
embedded_can::Id::Standard(standard_id) => {
|
||||
self.msg_buf.write_id1(
|
||||
BaseId::builder()
|
||||
.with_mask_28_18(u11::new(standard_id.as_raw()))
|
||||
.with_rtr_or_srr(is_remote)
|
||||
.with_ide(false)
|
||||
.with_mask_17_15(u3::new(0))
|
||||
.build(),
|
||||
);
|
||||
self.msg_buf.write_id0(ExtendedId::new_with_raw_value(0));
|
||||
}
|
||||
embedded_can::Id::Extended(extended_id) => {
|
||||
let id_raw = extended_id.as_raw();
|
||||
self.msg_buf.write_id1(
|
||||
BaseId::builder()
|
||||
.with_mask_28_18(u11::new(((id_raw >> 18) & 0x7FF) as u16))
|
||||
.with_rtr_or_srr(true)
|
||||
.with_ide(true)
|
||||
.with_mask_17_15(u3::new(((id_raw >> 15) & 0b111) as u8))
|
||||
.build(),
|
||||
);
|
||||
self.msg_buf.write_id0(
|
||||
ExtendedId::builder()
|
||||
.with_mask_14_0(u15::new((id_raw & 0x7FFF) as u16))
|
||||
.with_xrtr(is_remote)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a received CAN frame from the message buffer.
|
||||
///
|
||||
/// This function does not check whether the pre-requisites for reading a CAN frame were
|
||||
/// met and assumes this was already checked by the user.
|
||||
pub fn read_frame_unchecked(&self) -> CanFrame {
|
||||
let id0 = self.msg_buf.read_id0();
|
||||
let id1 = self.msg_buf.read_id1();
|
||||
let data0 = self.msg_buf.read_data0();
|
||||
let data1 = self.msg_buf.read_data1();
|
||||
let data2 = self.msg_buf.read_data2();
|
||||
let data3 = self.msg_buf.read_data3();
|
||||
let mut data: [u8; 8] = [0; 8];
|
||||
let mut read_data = |dlc: u4| {
|
||||
(0..dlc.as_usize()).for_each(|i| match i {
|
||||
0 => data[i] = data3.data_upper_byte().as_u8(),
|
||||
1 => data[i] = data3.data_lower_byte().as_u8(),
|
||||
2 => data[i] = data2.data_upper_byte().as_u8(),
|
||||
3 => data[i] = data2.data_lower_byte().as_u8(),
|
||||
4 => data[i] = data1.data_upper_byte().as_u8(),
|
||||
5 => data[i] = data1.data_lower_byte().as_u8(),
|
||||
6 => data[i] = data0.data_upper_byte().as_u8(),
|
||||
7 => data[i] = data0.data_lower_byte().as_u8(),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
};
|
||||
let (id, rtr) = if !id1.ide() {
|
||||
let id = embedded_can::Id::Standard(
|
||||
embedded_can::StandardId::new(id1.mask_28_18().as_u16()).unwrap(),
|
||||
);
|
||||
if id1.rtr_or_srr() {
|
||||
(id, true)
|
||||
} else {
|
||||
(id, false)
|
||||
}
|
||||
} else {
|
||||
let id_raw = (id1.mask_28_18().as_u32() << 18)
|
||||
| (id1.mask_17_15().as_u32() << 15)
|
||||
| id0.mask_14_0().as_u32();
|
||||
let id = embedded_can::Id::Extended(embedded_can::ExtendedId::new(id_raw).unwrap());
|
||||
if id0.xrtr() {
|
||||
(id, true)
|
||||
} else {
|
||||
(id, false)
|
||||
}
|
||||
};
|
||||
if rtr {
|
||||
CanFrameRtr::new(id, self.msg_buf.read_stat_ctrl().dlc().as_usize()).into()
|
||||
} else {
|
||||
let dlc = self.msg_buf.read_stat_ctrl().dlc();
|
||||
read_data(dlc);
|
||||
CanFrameNormal::new(id, &data[0..dlc.as_usize()]).into()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +1,23 @@
|
||||
//! CAN driver.
|
||||
//!
|
||||
//! The VA416xx CAN module is based on the CP3UB26 module.
|
||||
use core::sync::atomic::AtomicBool;
|
||||
|
||||
use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number};
|
||||
use embedded_can::Frame;
|
||||
use regs::{
|
||||
BaseId, BufStatusAndControl, Control, DataDirection, ExtendedId, MmioCan, TimingConfig,
|
||||
};
|
||||
use ll::CanChannelLowLevel;
|
||||
use regs::{BaseId, BufferState, Control, ExtendedId, MmioCan, TimingConfig};
|
||||
|
||||
use crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect};
|
||||
use libm::roundf;
|
||||
|
||||
pub mod frame;
|
||||
pub mod regs;
|
||||
pub use frame::*;
|
||||
|
||||
pub mod asynch;
|
||||
pub mod ll;
|
||||
pub mod regs;
|
||||
|
||||
pub const PRESCALER_MIN: u8 = 2;
|
||||
pub const PRESCALER_MAX: u8 = 128;
|
||||
/// 1 is the minimum value, but not recommended by Vorago.
|
||||
@ -26,6 +30,8 @@ pub const SJW_MAX: u8 = 4;
|
||||
pub const MIN_SAMPLE_POINT: f32 = 0.5;
|
||||
pub const MAX_BITRATE_DEVIATION: f32 = 0.005;
|
||||
|
||||
static CHANNELS_TAKEN: [AtomicBool; 2] = [AtomicBool::new(false), AtomicBool::new(false)];
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum CanId {
|
||||
Can0 = 0,
|
||||
@ -38,7 +44,7 @@ impl CanId {
|
||||
/// # Safety
|
||||
///
|
||||
/// See safety of the [regs::Can::new_mmio_fixed_0].
|
||||
pub unsafe fn steal_regs(&self) -> regs::MmioCan<'static> {
|
||||
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() },
|
||||
@ -122,17 +128,17 @@ pub fn calculate_all_viable_clock_configs(
|
||||
Ok(configs)
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
pub trait CanMarker {
|
||||
const ID: CanId;
|
||||
const PERIPH_SEL: PeripheralSelect;
|
||||
}
|
||||
|
||||
impl Instance for va416xx::Can0 {
|
||||
impl CanMarker for va416xx::Can0 {
|
||||
const ID: CanId = CanId::Can0;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
|
||||
}
|
||||
|
||||
impl Instance for va416xx::Can1 {
|
||||
impl CanMarker for va416xx::Can1 {
|
||||
const ID: CanId = CanId::Can1;
|
||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
|
||||
}
|
||||
@ -280,7 +286,7 @@ pub struct Can {
|
||||
}
|
||||
|
||||
impl Can {
|
||||
pub fn new<CanI: Instance>(_can: CanI, clk_config: ClockConfig) -> Self {
|
||||
pub fn new<CanI: CanMarker>(_can: CanI, clk_config: ClockConfig) -> Self {
|
||||
enable_peripheral_clock(CanI::PERIPH_SEL);
|
||||
let id = CanI::ID;
|
||||
let mut regs = if id == CanId::Can0 {
|
||||
@ -291,7 +297,7 @@ impl Can {
|
||||
// Disable the CAN bus before configuring it.
|
||||
regs.write_control(Control::new_with_raw_value(0));
|
||||
for i in 0..15 {
|
||||
regs.msg_buf_block_mut(i).reset();
|
||||
regs.cmbs(i).unwrap().reset();
|
||||
}
|
||||
regs.write_timing(
|
||||
TimingConfig::builder()
|
||||
@ -312,6 +318,13 @@ impl Can {
|
||||
self.regs.write_gmskb(BaseId::new_with_raw_value(0));
|
||||
}
|
||||
|
||||
pub fn take_channels(&self) -> Option<CanChannels> {
|
||||
if CHANNELS_TAKEN[self.id() as usize].swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
return None;
|
||||
}
|
||||
Some(CanChannels::new(self.id))
|
||||
}
|
||||
|
||||
/// Similar to [Self::set_global_mask_for_exact_id_match] but masks the XRTR and RTR/SRR bits.
|
||||
///
|
||||
/// This is useful for when transmitting remote frames with the RTR bit set. The hardware
|
||||
@ -383,51 +396,123 @@ impl Can {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ChannelState {
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TxState {
|
||||
Idle,
|
||||
TransmissionIdle,
|
||||
TransmittingDataFrame,
|
||||
TransmittingRemoteFrame,
|
||||
AwaitingRemoteFrameReply,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid tx state {0:?}")]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct InvalidTxStateError(pub TxState);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum RxState {
|
||||
Idle,
|
||||
Receiving,
|
||||
Transmitting,
|
||||
AwaitingRtrReply,
|
||||
}
|
||||
|
||||
pub struct CanChannel {
|
||||
can_id: CanId,
|
||||
idx: usize,
|
||||
regs: regs::MmioCanMsgBuf<'static>,
|
||||
mode: ChannelState,
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("invalid rx state {0:?}")]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct InvalidRxStateError(pub RxState);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CanTx {
|
||||
ll: CanChannelLowLevel,
|
||||
mode: TxState,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for CanChannel {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("CanChannel")
|
||||
.field("can_id", &self.can_id)
|
||||
.field("idx", &self.idx)
|
||||
.field("mode", &self.mode)
|
||||
.finish()
|
||||
impl CanTx {
|
||||
pub fn new(ll: CanChannelLowLevel) -> Self {
|
||||
Self {
|
||||
ll,
|
||||
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> {
|
||||
if self.mode == TxState::AwaitingRemoteFrameReply {
|
||||
self.configure_for_transmission(None).unwrap();
|
||||
self.mode = TxState::TransmissionIdle;
|
||||
}
|
||||
if self.mode != TxState::TransmissionIdle {
|
||||
return Err(InvalidTxStateError(self.mode));
|
||||
}
|
||||
if !frame.is_remote_frame() {
|
||||
self.mode = TxState::TransmittingDataFrame;
|
||||
} else {
|
||||
self.mode = TxState::TransmittingRemoteFrame;
|
||||
}
|
||||
self.ll.transmit_frame_unchecked(frame);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn data_frame_transfer_done(&mut self) -> nb::Result<(), InvalidTxStateError> {
|
||||
if self.mode != TxState::TransmittingDataFrame {
|
||||
return Err(nb::Error::Other(InvalidTxStateError(self.mode)));
|
||||
}
|
||||
let status = self.ll.read_status();
|
||||
if status.is_err() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
let status = status.unwrap();
|
||||
if status == BufferState::TxNotActive {
|
||||
self.mode = TxState::TransmissionIdle;
|
||||
return Ok(());
|
||||
}
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
|
||||
pub fn remote_frame_transfer_done(&mut self) -> nb::Result<CanRx, InvalidTxStateError> {
|
||||
if self.mode != TxState::TransmittingRemoteFrame {
|
||||
return Err(nb::Error::Other(InvalidTxStateError(self.mode)));
|
||||
}
|
||||
let status = self.ll.read_status();
|
||||
if status.is_err() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
let status = status.unwrap();
|
||||
if status == BufferState::RxReady {
|
||||
self.mode = TxState::AwaitingRemoteFrameReply;
|
||||
return Ok(CanRx {
|
||||
ll: unsafe { self.ll.clone() },
|
||||
mode: RxState::Receiving,
|
||||
});
|
||||
}
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanChannel {
|
||||
pub struct CanRx {
|
||||
ll: CanChannelLowLevel,
|
||||
mode: RxState,
|
||||
}
|
||||
|
||||
impl CanRx {
|
||||
pub fn configure_for_reception_with_standard_id(
|
||||
&mut self,
|
||||
standard_id: embedded_can::StandardId,
|
||||
set_rtr: bool,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
let mut id1_reg = standard_id.as_raw() << 5;
|
||||
if set_rtr {
|
||||
id1_reg |= 1 << 4;
|
||||
}
|
||||
self.regs
|
||||
.write_id1(BaseId::new_with_raw_value(id1_reg as u32));
|
||||
|
||||
self.regs.write_stat_ctrl(
|
||||
BufStatusAndControl::builder()
|
||||
.with_dlc(u4::new(0))
|
||||
.with_priority(u4::new(0))
|
||||
.with_status(regs::BufferState::RxReady)
|
||||
.build(),
|
||||
);
|
||||
self.ll
|
||||
.configure_for_reception_with_standard_id(standard_id, set_rtr)?;
|
||||
self.mode = RxState::Receiving;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -436,142 +521,81 @@ impl CanChannel {
|
||||
extended_id: embedded_can::ExtendedId,
|
||||
set_rtr: bool,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
let mut regs = unsafe { self.can_id.steal_regs() };
|
||||
let mut cmb_block = regs.msg_buf_block_mut(self.idx);
|
||||
let id_raw = extended_id.as_raw();
|
||||
let id1_reg = (((id_raw >> 18) & 0x7FF) << 4) as u16 | ((id_raw >> 15) & 0b111) as u16;
|
||||
cmb_block.write_id1(BaseId::new_with_raw_value(id1_reg as u32));
|
||||
let id0_reg = ((id_raw & 0x7FFF) << 1) as u16 | set_rtr as u16;
|
||||
cmb_block.write_id0(ExtendedId::new_with_raw_value(id0_reg as u32));
|
||||
cmb_block.write_stat_ctrl(
|
||||
BufStatusAndControl::builder()
|
||||
.with_dlc(u4::new(0))
|
||||
.with_priority(u4::new(0))
|
||||
.with_status(regs::BufferState::RxReady)
|
||||
.build(),
|
||||
);
|
||||
self.mode = ChannelState::Receiving;
|
||||
self.ll
|
||||
.configure_for_reception_with_extended_id(extended_id, set_rtr)?;
|
||||
self.mode = RxState::Receiving;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn configure_for_transmission(
|
||||
pub fn receive(
|
||||
&mut self,
|
||||
tx_priority: u4,
|
||||
) -> Result<(), InvalidBufferIndexError> {
|
||||
let mut regs = unsafe { self.can_id.steal_regs() };
|
||||
let mut cmb_block = regs.msg_buf_block_mut(self.idx);
|
||||
cmb_block.write_stat_ctrl(
|
||||
BufStatusAndControl::builder()
|
||||
.with_dlc(u4::new(0))
|
||||
.with_priority(tx_priority)
|
||||
.with_status(regs::BufferState::TxNotActive)
|
||||
.build(),
|
||||
);
|
||||
self.mode = ChannelState::Receiving;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a received CAN frame from the message buffer.
|
||||
///
|
||||
/// This function does not check whether the pre-requisites for reading a CAN frame were
|
||||
/// met and assumes this was already checked by the user.
|
||||
pub fn read_frame_unchecked(&self) -> CanFrame {
|
||||
let id0 = self.regs.read_id0();
|
||||
let id1 = self.regs.read_id1();
|
||||
let data0 = self.regs.read_data0();
|
||||
let data1 = self.regs.read_data1();
|
||||
let data2 = self.regs.read_data2();
|
||||
let data3 = self.regs.read_data3();
|
||||
let mut data: [u8; 8] = [0; 8];
|
||||
let mut read_data = |dlc: u4| {
|
||||
(0..dlc.as_usize()).for_each(|i| match i {
|
||||
0 => data[i] = data3.data_upper_byte().as_u8(),
|
||||
1 => data[i] = data3.data_lower_byte().as_u8(),
|
||||
2 => data[i] = data2.data_upper_byte().as_u8(),
|
||||
3 => data[i] = data2.data_lower_byte().as_u8(),
|
||||
4 => data[i] = data1.data_upper_byte().as_u8(),
|
||||
5 => data[i] = data1.data_lower_byte().as_u8(),
|
||||
6 => data[i] = data0.data_upper_byte().as_u8(),
|
||||
7 => data[i] = data0.data_lower_byte().as_u8(),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
};
|
||||
let (id, rtr) = if !id1.ide() {
|
||||
let id = embedded_can::Id::Standard(
|
||||
embedded_can::StandardId::new(id1.mask_28_18().as_u16()).unwrap(),
|
||||
);
|
||||
if id1.rtr_or_srr() {
|
||||
(id, true)
|
||||
} else {
|
||||
(id, false)
|
||||
}
|
||||
} else {
|
||||
let id_raw = (id1.mask_28_18().as_u32() << 18)
|
||||
| (id1.mask_17_15().as_u32() << 15)
|
||||
| id0.mask_14_0().as_u32();
|
||||
let id = embedded_can::Id::Extended(embedded_can::ExtendedId::new(id_raw).unwrap());
|
||||
if id0.xrtr() {
|
||||
(id, true)
|
||||
} else {
|
||||
(id, false)
|
||||
}
|
||||
};
|
||||
if rtr {
|
||||
CanFrameRtr::new(id, self.regs.read_stat_ctrl().dlc().as_usize()).into()
|
||||
} else {
|
||||
let dlc = self.regs.read_stat_ctrl().dlc();
|
||||
read_data(dlc);
|
||||
CanFrameNormal::new(id, &data[0..dlc.as_usize()]).into()
|
||||
reconfigure_for_reception: bool,
|
||||
) -> nb::Result<CanFrame, InvalidRxStateError> {
|
||||
if self.mode != RxState::Receiving {
|
||||
return Err(nb::Error::Other(InvalidRxStateError(self.mode)));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transmit_frame_unchecked(&mut self, frame: CanFrame) {
|
||||
let is_remote = frame.is_remote_frame();
|
||||
self.write_id(frame.id(), is_remote);
|
||||
let dlc = frame.dlc();
|
||||
self.regs.modify_stat_ctrl(|mut ctrl| {
|
||||
ctrl.set_status(regs::BufferState::TxOnce);
|
||||
ctrl
|
||||
});
|
||||
}
|
||||
|
||||
fn write_id(&mut self, id: embedded_can::Id, is_remote: bool) {
|
||||
match id {
|
||||
embedded_can::Id::Standard(standard_id) => {
|
||||
self.regs.write_id1(
|
||||
BaseId::builder()
|
||||
.with_mask_28_18(u11::new(standard_id.as_raw()))
|
||||
.with_rtr_or_srr(is_remote)
|
||||
.with_ide(false)
|
||||
.with_mask_17_15(u3::new(0))
|
||||
.build(),
|
||||
);
|
||||
self.regs.write_id0(ExtendedId::new_with_raw_value(0));
|
||||
}
|
||||
embedded_can::Id::Extended(extended_id) => {
|
||||
let id_raw = extended_id.as_raw();
|
||||
self.regs.write_id1(
|
||||
BaseId::builder()
|
||||
.with_mask_28_18(u11::new(((id_raw >> 18) & 0x7FF) as u16))
|
||||
.with_rtr_or_srr(true)
|
||||
.with_ide(true)
|
||||
.with_mask_17_15(u3::new(((id_raw >> 15) & 0b111) as u8))
|
||||
.build(),
|
||||
);
|
||||
self.regs.write_id0(
|
||||
ExtendedId::builder()
|
||||
.with_mask_14_0(u15::new((id_raw & 0x7FFF) as u16))
|
||||
.with_xrtr(is_remote)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
let status = self.ll.read_status();
|
||||
if status.is_err() {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
let status = status.unwrap();
|
||||
if status == BufferState::RxReady || status == BufferState::RxOverrun {
|
||||
self.mode = RxState::Idle;
|
||||
if reconfigure_for_reception {
|
||||
self.ll.write_status(BufferState::RxReady);
|
||||
}
|
||||
return Ok(self.ll.read_frame_unchecked());
|
||||
}
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CanWorker {
|
||||
can: Can,
|
||||
channels: [CanChannel; 15],
|
||||
pub struct CanChannels {
|
||||
id: CanId,
|
||||
channels: [Option<CanChannelLowLevel>; 15],
|
||||
}
|
||||
|
||||
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)),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn can_id(&self) -> CanId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn take(&mut self, idx: usize) -> Option<CanChannelLowLevel> {
|
||||
if idx > 14 {
|
||||
return None;
|
||||
}
|
||||
self.channels[idx].take()
|
||||
}
|
||||
|
||||
pub fn give(&mut self, idx: usize, channel: CanChannelLowLevel) {
|
||||
if idx > 14 {
|
||||
panic!("invalid buffer index for CAN channel");
|
||||
}
|
||||
self.channels[idx] = Some(channel);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,12 +1,12 @@
|
||||
//! Custom register definitions for the CAN register block to circumvent PAC API / SVD
|
||||
//! shortcomings.
|
||||
|
||||
use arbitrary_int::{u11, u15, u2, u3, u4, u7};
|
||||
use arbitrary_int::{u11, u15, u2, u3, u4, u6, u7, Number};
|
||||
|
||||
pub const CAN_0_BASE: usize = 0x4001_4000;
|
||||
pub const CAN_1_BASE: usize = 0x4001_4400;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitbybit::bitenum(u4)]
|
||||
pub enum BufferState {
|
||||
/// Passive channel.
|
||||
@ -197,6 +197,42 @@ pub struct InterruptPending {
|
||||
buffer: [bool; 15],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(usize)]
|
||||
pub enum CanInterruptId {
|
||||
None = 0b00000,
|
||||
Error = 0b10000,
|
||||
Buffer(usize),
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct StatusPending {
|
||||
#[bits(5..=7, r)]
|
||||
ns: u3,
|
||||
#[bit(4, r)]
|
||||
irq: bool,
|
||||
#[bits(0..=3, r)]
|
||||
ist: u4,
|
||||
}
|
||||
|
||||
impl StatusPending {
|
||||
pub fn interrupt_id(&self) -> Option<CanInterruptId> {
|
||||
if !self.irq() && self.ist().value() == 0 {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorCounter {
|
||||
@ -235,40 +271,89 @@ pub struct BaseId {
|
||||
mask_17_15: u3,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[bitbybit::bitenum(u4, exhaustive = true)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ErrorFieldId {
|
||||
Error = 0b0000,
|
||||
ErrorDel = 0b0001,
|
||||
ErrorEcho = 0b0010,
|
||||
BusIdle = 0b0011,
|
||||
Ack = 0b0100,
|
||||
Eof = 0b0101,
|
||||
Intermission = 0b0110,
|
||||
SuspendTransmission = 0b0111,
|
||||
Sof = 0b1000,
|
||||
Arbitration = 0b1001,
|
||||
Ide = 0b1010,
|
||||
ExtendedArbitration = 0b1011,
|
||||
R1R0 = 0b1100,
|
||||
Dlc = 0b1101,
|
||||
Data = 0b1110,
|
||||
Crc = 0b1111,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32)]
|
||||
pub struct DiagnosticRegister {
|
||||
/// Shows the output value on the CAN TX pin at the time of the error.
|
||||
#[bit(14, r)]
|
||||
drive: bool,
|
||||
/// Shows the bus value on the CAN RX pin as sampled by the CAN module at the time of the
|
||||
/// error.
|
||||
#[bit(13, r)]
|
||||
mon: bool,
|
||||
/// Indicated whether the CRC is invalid. This bit should only be checked if the EFID field
|
||||
/// is [ErrorFieldId::Ack].
|
||||
#[bit(12, r)]
|
||||
crc: bool,
|
||||
/// Indicated whether the bit stuffing rule was violated at the time the error occured.
|
||||
#[bit(11, r)]
|
||||
stuff: bool,
|
||||
/// Indicated whether the CAN module was an active transmitter at the time the error occured.
|
||||
#[bit(10, r)]
|
||||
txe: bool,
|
||||
#[bits(4..=9, r)]
|
||||
ebid: u6,
|
||||
#[bits(0..=3, r)]
|
||||
efid: ErrorFieldId,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for DiagnosticRegister {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("DiagnosticRegister")
|
||||
.field("efid", &self.efid())
|
||||
.field("ebid", &self.ebid())
|
||||
.field("txe", &self.txe())
|
||||
.field("stuff", &self.stuff())
|
||||
.field("crc", &self.crc())
|
||||
.field("mon", &self.mon())
|
||||
.field("drive", &self.drive())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for DiagnosticRegister {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"DiagnosticRegister {{ efid: {}, ebid: {}, txe: {}, stuff: {}, crc: {}, mon: {}, drive: {} }}",
|
||||
self.efid(),
|
||||
self.ebid(),
|
||||
self.txe(),
|
||||
self.stuff(),
|
||||
self.crc(),
|
||||
self.mon(),
|
||||
self.drive()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
pub struct Can {
|
||||
#[mmio(inner)]
|
||||
cmb0: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb1: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb2: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb3: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb4: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb5: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb6: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb7: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb8: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb9: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb10: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb11: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb12: CanMsgBuf,
|
||||
#[mmio(inner)]
|
||||
cmb13: CanMsgBuf,
|
||||
// This CAN message buffer has different mask registers.
|
||||
#[mmio(inner)]
|
||||
cmb14: CanMsgBuf,
|
||||
cmbs: [CanMsgBuf; 15],
|
||||
/// Hidden CAN message buffer. Only allowed to be used internally by the peripheral.
|
||||
#[mmio(inner)]
|
||||
_hcmb: CanMsgBuf,
|
||||
@ -290,11 +375,11 @@ pub struct Can {
|
||||
/// Interrupt Code Enable Register.
|
||||
icen: InterruptEnable,
|
||||
#[mmio(PureRead)]
|
||||
status_pending: u32,
|
||||
status_pending: StatusPending,
|
||||
#[mmio(PureRead)]
|
||||
error_counter: ErrorCounter,
|
||||
#[mmio(PureRead)]
|
||||
diag: u32,
|
||||
diag: DiagnosticRegister,
|
||||
#[mmio(PureRead)]
|
||||
timer: u32,
|
||||
}
|
||||
@ -322,28 +407,3 @@ impl Can {
|
||||
Self::new_mmio_at(CAN_1_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl MmioCan<'_> {
|
||||
// TODO: It would be nice if derive-mmio could generate this for us..
|
||||
pub fn msg_buf_block_mut(&mut self, idx: usize) -> MmioCanMsgBuf<'static> {
|
||||
assert!(idx < 15, "invalid index for CAN message buffer");
|
||||
match idx {
|
||||
0 => unsafe { self.steal_cmb0() },
|
||||
1 => unsafe { self.steal_cmb1() },
|
||||
2 => unsafe { self.steal_cmb2() },
|
||||
3 => unsafe { self.steal_cmb3() },
|
||||
4 => unsafe { self.steal_cmb4() },
|
||||
5 => unsafe { self.steal_cmb5() },
|
||||
6 => unsafe { self.steal_cmb6() },
|
||||
7 => unsafe { self.steal_cmb7() },
|
||||
8 => unsafe { self.steal_cmb8() },
|
||||
9 => unsafe { self.steal_cmb9() },
|
||||
10 => unsafe { self.steal_cmb10() },
|
||||
11 => unsafe { self.steal_cmb11() },
|
||||
12 => unsafe { self.steal_cmb12() },
|
||||
13 => unsafe { self.steal_cmb13() },
|
||||
14 => unsafe { self.steal_cmb14() },
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user