finished first impl
This commit is contained in:
parent
d9d6e11642
commit
997e0502ab
@ -13,7 +13,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
va416xx = { version = "0.4", features = ["critical-section"], default-features = false }
|
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"] }
|
vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor4x"] }
|
||||||
|
|
||||||
libm = "0.2"
|
libm = "0.2"
|
||||||
@ -25,6 +25,7 @@ bitbybit = "1.3"
|
|||||||
arbitrary-int = "1.3"
|
arbitrary-int = "1.3"
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
embedded-can = "0.4"
|
embedded-can = "0.4"
|
||||||
|
embassy-sync = "0.6"
|
||||||
thiserror = { version = "2", default-features = false }
|
thiserror = { version = "2", default-features = false }
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
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}")]
|
#[error("invalid data size error {0}")]
|
||||||
pub struct InvalidDataSizeError(usize);
|
pub struct InvalidDataSizeError(usize);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CanFrameNormal {
|
pub struct CanFrameNormal {
|
||||||
id: embedded_can::Id,
|
id: embedded_can::Id,
|
||||||
size: usize,
|
size: usize,
|
||||||
@ -33,6 +34,7 @@ impl CanFrameNormal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CanFrameRtr {
|
pub struct CanFrameRtr {
|
||||||
id: embedded_can::Id,
|
id: embedded_can::Id,
|
||||||
dlc: usize,
|
dlc: usize,
|
||||||
@ -52,6 +54,7 @@ impl CanFrameRtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum CanFrame {
|
pub enum CanFrame {
|
||||||
Normal(CanFrameNormal),
|
Normal(CanFrameNormal),
|
||||||
Rtr(CanFrameRtr),
|
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.
|
//! CAN driver.
|
||||||
//!
|
//!
|
||||||
//! The VA416xx CAN module is based on the CP3UB26 module.
|
//! 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 arbitrary_int::{u11, u15, u2, u3, u4, u7, Number};
|
||||||
use embedded_can::Frame;
|
use embedded_can::Frame;
|
||||||
use regs::{
|
use ll::CanChannelLowLevel;
|
||||||
BaseId, BufStatusAndControl, Control, DataDirection, ExtendedId, MmioCan, TimingConfig,
|
use regs::{BaseId, BufferState, Control, ExtendedId, 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;
|
||||||
|
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
pub mod regs;
|
|
||||||
pub use frame::*;
|
pub use frame::*;
|
||||||
|
|
||||||
|
pub mod asynch;
|
||||||
|
pub mod ll;
|
||||||
|
pub mod regs;
|
||||||
|
|
||||||
pub const PRESCALER_MIN: u8 = 2;
|
pub const PRESCALER_MIN: u8 = 2;
|
||||||
pub const PRESCALER_MAX: u8 = 128;
|
pub const PRESCALER_MAX: u8 = 128;
|
||||||
/// 1 is the minimum value, but not recommended by Vorago.
|
/// 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 MIN_SAMPLE_POINT: f32 = 0.5;
|
||||||
pub const MAX_BITRATE_DEVIATION: f32 = 0.005;
|
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)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum CanId {
|
pub enum CanId {
|
||||||
Can0 = 0,
|
Can0 = 0,
|
||||||
@ -38,7 +44,7 @@ impl CanId {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// See safety of the [regs::Can::new_mmio_fixed_0].
|
/// 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 {
|
match self {
|
||||||
CanId::Can0 => unsafe { regs::Can::new_mmio_fixed_0() },
|
CanId::Can0 => unsafe { regs::Can::new_mmio_fixed_0() },
|
||||||
CanId::Can1 => unsafe { regs::Can::new_mmio_fixed_1() },
|
CanId::Can1 => unsafe { regs::Can::new_mmio_fixed_1() },
|
||||||
@ -122,17 +128,17 @@ pub fn calculate_all_viable_clock_configs(
|
|||||||
Ok(configs)
|
Ok(configs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance {
|
pub trait CanMarker {
|
||||||
const ID: CanId;
|
const ID: CanId;
|
||||||
const PERIPH_SEL: PeripheralSelect;
|
const PERIPH_SEL: PeripheralSelect;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for va416xx::Can0 {
|
impl CanMarker for va416xx::Can0 {
|
||||||
const ID: CanId = CanId::Can0;
|
const ID: CanId = CanId::Can0;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for va416xx::Can1 {
|
impl CanMarker for va416xx::Can1 {
|
||||||
const ID: CanId = CanId::Can1;
|
const ID: CanId = CanId::Can1;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
|
||||||
}
|
}
|
||||||
@ -280,7 +286,7 @@ pub struct Can {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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);
|
enable_peripheral_clock(CanI::PERIPH_SEL);
|
||||||
let id = CanI::ID;
|
let id = CanI::ID;
|
||||||
let mut regs = if id == CanId::Can0 {
|
let mut regs = if id == CanId::Can0 {
|
||||||
@ -291,7 +297,7 @@ impl Can {
|
|||||||
// Disable the CAN bus before configuring it.
|
// Disable the CAN bus before configuring it.
|
||||||
regs.write_control(Control::new_with_raw_value(0));
|
regs.write_control(Control::new_with_raw_value(0));
|
||||||
for i in 0..15 {
|
for i in 0..15 {
|
||||||
regs.msg_buf_block_mut(i).reset();
|
regs.cmbs(i).unwrap().reset();
|
||||||
}
|
}
|
||||||
regs.write_timing(
|
regs.write_timing(
|
||||||
TimingConfig::builder()
|
TimingConfig::builder()
|
||||||
@ -312,6 +318,13 @@ impl Can {
|
|||||||
self.regs.write_gmskb(BaseId::new_with_raw_value(0));
|
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.
|
/// 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
|
/// This is useful for when transmitting remote frames with the RTR bit set. The hardware
|
||||||
@ -383,51 +396,123 @@ impl Can {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum ChannelState {
|
#[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,
|
Idle,
|
||||||
Receiving,
|
Receiving,
|
||||||
Transmitting,
|
|
||||||
AwaitingRtrReply,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CanChannel {
|
#[derive(Debug, thiserror::Error)]
|
||||||
can_id: CanId,
|
#[error("invalid rx state {0:?}")]
|
||||||
idx: usize,
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
regs: regs::MmioCanMsgBuf<'static>,
|
pub struct InvalidRxStateError(pub RxState);
|
||||||
mode: ChannelState,
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CanTx {
|
||||||
|
ll: CanChannelLowLevel,
|
||||||
|
mode: TxState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Debug for CanChannel {
|
impl CanTx {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
pub fn new(ll: CanChannelLowLevel) -> Self {
|
||||||
f.debug_struct("CanChannel")
|
Self {
|
||||||
.field("can_id", &self.can_id)
|
ll,
|
||||||
.field("idx", &self.idx)
|
mode: TxState::Idle,
|
||||||
.field("mode", &self.mode)
|
}
|
||||||
.finish()
|
}
|
||||||
|
|
||||||
|
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(
|
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> {
|
||||||
let mut id1_reg = standard_id.as_raw() << 5;
|
self.ll
|
||||||
if set_rtr {
|
.configure_for_reception_with_standard_id(standard_id, set_rtr)?;
|
||||||
id1_reg |= 1 << 4;
|
self.mode = RxState::Receiving;
|
||||||
}
|
|
||||||
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(),
|
|
||||||
);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,142 +521,81 @@ impl CanChannel {
|
|||||||
extended_id: embedded_can::ExtendedId,
|
extended_id: embedded_can::ExtendedId,
|
||||||
set_rtr: bool,
|
set_rtr: bool,
|
||||||
) -> Result<(), InvalidBufferIndexError> {
|
) -> Result<(), InvalidBufferIndexError> {
|
||||||
let mut regs = unsafe { self.can_id.steal_regs() };
|
self.ll
|
||||||
let mut cmb_block = regs.msg_buf_block_mut(self.idx);
|
.configure_for_reception_with_extended_id(extended_id, set_rtr)?;
|
||||||
let id_raw = extended_id.as_raw();
|
self.mode = RxState::Receiving;
|
||||||
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;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_for_transmission(
|
pub fn receive(
|
||||||
&mut self,
|
&mut self,
|
||||||
tx_priority: u4,
|
reconfigure_for_reception: bool,
|
||||||
) -> Result<(), InvalidBufferIndexError> {
|
) -> nb::Result<CanFrame, InvalidRxStateError> {
|
||||||
let mut regs = unsafe { self.can_id.steal_regs() };
|
if self.mode != RxState::Receiving {
|
||||||
let mut cmb_block = regs.msg_buf_block_mut(self.idx);
|
return Err(nb::Error::Other(InvalidRxStateError(self.mode)));
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
let status = self.ll.read_status();
|
||||||
|
if status.is_err() {
|
||||||
pub fn transmit_frame_unchecked(&mut self, frame: CanFrame) {
|
return Err(nb::Error::WouldBlock);
|
||||||
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 = 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 {
|
pub struct CanChannels {
|
||||||
can: Can,
|
id: CanId,
|
||||||
channels: [CanChannel; 15],
|
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)]
|
#[cfg(test)]
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
//! Custom register definitions for the CAN register block to circumvent PAC API / SVD
|
//! Custom register definitions for the CAN register block to circumvent PAC API / SVD
|
||||||
//! shortcomings.
|
//! 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_0_BASE: usize = 0x4001_4000;
|
||||||
pub const CAN_1_BASE: usize = 0x4001_4400;
|
pub const CAN_1_BASE: usize = 0x4001_4400;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[bitbybit::bitenum(u4)]
|
#[bitbybit::bitenum(u4)]
|
||||||
pub enum BufferState {
|
pub enum BufferState {
|
||||||
/// Passive channel.
|
/// Passive channel.
|
||||||
@ -197,6 +197,42 @@ pub struct InterruptPending {
|
|||||||
buffer: [bool; 15],
|
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)]
|
#[bitbybit::bitfield(u32)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ErrorCounter {
|
pub struct ErrorCounter {
|
||||||
@ -235,40 +271,89 @@ pub struct BaseId {
|
|||||||
mask_17_15: u3,
|
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)]
|
#[derive(derive_mmio::Mmio)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Can {
|
pub struct Can {
|
||||||
#[mmio(inner)]
|
#[mmio(inner)]
|
||||||
cmb0: CanMsgBuf,
|
cmbs: [CanMsgBuf; 15],
|
||||||
#[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,
|
|
||||||
/// Hidden CAN message buffer. Only allowed to be used internally by the peripheral.
|
/// Hidden CAN message buffer. Only allowed to be used internally by the peripheral.
|
||||||
#[mmio(inner)]
|
#[mmio(inner)]
|
||||||
_hcmb: CanMsgBuf,
|
_hcmb: CanMsgBuf,
|
||||||
@ -290,11 +375,11 @@ pub struct Can {
|
|||||||
/// Interrupt Code Enable Register.
|
/// Interrupt Code Enable Register.
|
||||||
icen: InterruptEnable,
|
icen: InterruptEnable,
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
status_pending: u32,
|
status_pending: StatusPending,
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
error_counter: ErrorCounter,
|
error_counter: ErrorCounter,
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
diag: u32,
|
diag: DiagnosticRegister,
|
||||||
#[mmio(PureRead)]
|
#[mmio(PureRead)]
|
||||||
timer: u32,
|
timer: u32,
|
||||||
}
|
}
|
||||||
@ -322,28 +407,3 @@ impl Can {
|
|||||||
Self::new_mmio_at(CAN_1_BASE)
|
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