finished first impl

This commit is contained in:
Robin Müller 2025-04-28 18:51:37 +02:00
parent d9d6e11642
commit 997e0502ab
Signed by: muellerr
GPG Key ID: A649FB78196E3849
6 changed files with 914 additions and 228 deletions

View File

@ -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 }

View 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(())
}
}

View File

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

View File

@ -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(())
reconfigure_for_reception: bool,
) -> nb::Result<CanFrame, InvalidRxStateError> {
if self.mode != RxState::Receiving {
return Err(nb::Error::Other(InvalidRxStateError(self.mode)));
}
/// 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)
let status = self.ll.read_status();
if status.is_err() {
return Err(nb::Error::WouldBlock);
}
} 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()
}
}
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 = 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)]

View File

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