first CAN test works

This commit is contained in:
Robin Müller 2025-05-13 12:30:20 +02:00
parent d4163c5021
commit 172e319720
Signed by: muellerr
GPG Key ID: A649FB78196E3849
7 changed files with 157 additions and 68 deletions

View File

@ -7,6 +7,7 @@ edition = "2021"
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
cfg-if = "1" cfg-if = "1"
embedded-io = "0.6" embedded-io = "0.6"
embedded-can = "0.4"
embedded-hal-async = "1" embedded-hal-async = "1"
embedded-io-async = "0.6" embedded-io-async = "0.6"

View File

@ -31,26 +31,66 @@ async fn main(_spawner: Spawner) {
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks); va416xx_embassy::init(dp.tim15, dp.tim14, &clocks);
defmt::info!("creating CAN peripheral driver"); defmt::info!("creating CAN peripheral driver");
defmt::info!("clocks: {}", clocks); defmt::info!("clocks: {}", clocks);
let clk_config = ClockConfig::from_bitrate_and_segments(&clocks, 250.kHz(), 4, 14, 5) let clk_config = ClockConfig::from_bitrate_and_segments(&clocks, 250.kHz(), 14, 5, 4)
.expect("CAN clock config error"); .expect("CAN clock config error");
let mut can = Can::new(dp.can0, clk_config); let mut can = Can::new(dp.can0, clk_config);
can.set_loopback(true); can.modify_control(|mut val| {
can.set_bufflock(true); val.set_loopback(true);
val.set_ignore_ack(true);
val.set_internal(true);
val.set_bufflock(true);
val.set_diag_enable(true);
val
});
can.set_base_mask_for_all_match(); can.set_base_mask_for_all_match();
can.enable(); can.enable();
let err_counter = can.read_error_counters();
defmt::info!(
"error count tx {}, error count rx {}",
err_counter.transmit(),
err_counter.receive()
);
let mut channels = can.take_channels().unwrap(); let mut channels = can.take_channels().unwrap();
// Transmit channel. // Transmit channel.
let mut tx = CanTx::new(channels.take(0).unwrap(), None); let mut tx = CanTx::new(channels.take(0).unwrap(), None);
// Base channel which has dedicated mask. // Base channel which has dedicated mask.
let mut rx = CanRx::new(channels.take(14).unwrap()); let mut rx_dedicated = CanRx::new(channels.take(1).unwrap());
let send_frame = CanFrame::Normal(CanFrameNormal::new( // Base channel which has dedicated mask.
can::Id::Standard(can::StandardId::new(0x1).unwrap()), let mut rx_base = CanRx::new(channels.take(14).unwrap());
&[1, 2, 3, 4], let standard_id = can::StandardId::new(0x42).unwrap();
)); let send_frame = CanFrame::Normal(
CanFrameNormal::new(can::Id::Standard(standard_id), &[1, 2, 3, 4]).unwrap(),
);
rx_dedicated
.configure_for_reception_with_standard_id(standard_id, false)
.unwrap();
rx_base.configure_for_reception();
defmt::info!("sending CAN frame"); defmt::info!("sending CAN frame");
tx.transmit_frame(send_frame).unwrap(); tx.transmit_frame(send_frame).unwrap();
let _frame = nb::block!(rx.receive(true)).expect("invalid CAN rx state"); let frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state");
defmt::info!("received CAN frame with data"); let err_counter = can.read_error_counters();
defmt::info!(
"error count tx {}, error count rx {}",
err_counter.transmit(),
err_counter.receive()
);
let diag = can.read_error_diagnostics();
defmt::info!("EFID: {}, EBID: {}", diag.efid(), diag.ebid());
match frame {
CanFrame::Normal(can_frame_normal) => match can_frame_normal.id() {
can::Id::Standard(standard_id) => {
defmt::info!(
"received CAN frame with ID {} and data {}",
standard_id.as_raw(),
can_frame_normal.data()
);
}
can::Id::Extended(_) => (),
},
_ => {
defmt::error!("received unexpected CAN remote frame");
}
}
loop {} loop {}
} }

View File

@ -14,6 +14,7 @@ categories = ["embedded", "no-std", "hardware-support"]
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 = { version = "0.4", git = "https://github.com/knurling-rs/derive-mmio.git" } derive-mmio = { version = "0.4", git = "https://github.com/knurling-rs/derive-mmio.git" }
static_assertions = "1.1"
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"

View File

@ -12,25 +12,31 @@ pub struct CanFrameNormal {
} }
impl CanFrameNormal { impl CanFrameNormal {
pub fn new(id: embedded_can::Id, data: &[u8]) -> Self { pub fn new(id: embedded_can::Id, data: &[u8]) -> Result<Self, InvalidDataSizeError> {
if data.len() > 8 {
return Err(InvalidDataSizeError(data.len()));
}
let size = data.len(); let size = data.len();
let mut data_array = [0; 8]; let mut data_array = [0; 8];
data_array[..size].copy_from_slice(data); data_array[0..size].copy_from_slice(data);
Self { Ok(Self {
id, id,
size, size,
data: data_array, data: data_array,
} })
} }
#[inline]
pub fn id(&self) -> embedded_can::Id { pub fn id(&self) -> embedded_can::Id {
self.id self.id
} }
#[inline]
pub fn data(&self) -> &[u8] { pub fn data(&self) -> &[u8] {
&self.data[..self.size] &self.data[0..self.dlc()]
} }
#[inline]
pub fn dlc(&self) -> usize { pub fn dlc(&self) -> usize {
self.size self.size
} }
@ -76,8 +82,11 @@ impl From<CanFrameRtr> for CanFrame {
impl embedded_can::Frame for CanFrame { impl embedded_can::Frame for CanFrame {
fn new(id: impl Into<embedded_can::Id>, data: &[u8]) -> Option<Self> { fn new(id: impl Into<embedded_can::Id>, data: &[u8]) -> Option<Self> {
if data.len() > 8 {
return None;
}
let id: embedded_can::Id = id.into(); let id: embedded_can::Id = id.into();
Some(Self::Normal(CanFrameNormal::new(id, data))) Some(Self::Normal(CanFrameNormal::new(id, data).unwrap()))
} }
fn new_remote(id: impl Into<embedded_can::Id>, dlc: usize) -> Option<Self> { fn new_remote(id: impl Into<embedded_can::Id>, dlc: usize) -> Option<Self> {

View File

@ -73,15 +73,19 @@ impl CanChannelLowLevel {
} }
} }
pub fn reset(&mut self) {
self.msg_buf.reset();
}
#[inline] #[inline]
pub fn read_state(&self) -> Result<BufferState, u8> { pub fn read_state(&self) -> Result<BufferState, u8> {
self.msg_buf.read_stat_ctrl().status() self.msg_buf.read_stat_ctrl().state()
} }
#[inline] #[inline]
pub fn write_state(&mut self, buffer_state: BufferState) { pub fn write_state(&mut self, buffer_state: BufferState) {
self.msg_buf.modify_stat_ctrl(|mut val| { self.msg_buf.modify_stat_ctrl(|mut val| {
val.set_status(buffer_state); val.set_state(buffer_state);
val val
}); });
} }
@ -95,7 +99,7 @@ impl CanChannelLowLevel {
if let Some(tx_priority) = tx_priority { if let Some(tx_priority) = tx_priority {
val.set_priority(tx_priority); val.set_priority(tx_priority);
} }
val.set_status(BufferState::TxNotActive); val.set_state(BufferState::TxNotActive);
val val
}); });
Ok(()) Ok(())
@ -135,7 +139,7 @@ impl CanChannelLowLevel {
BufStatusAndControl::builder() BufStatusAndControl::builder()
.with_dlc(u4::new(0)) .with_dlc(u4::new(0))
.with_priority(u4::new(0)) .with_priority(u4::new(0))
.with_status(BufferState::RxReady) .with_state(BufferState::RxReady)
.build(), .build(),
); );
} }
@ -144,6 +148,10 @@ impl CanChannelLowLevel {
let is_remote = frame.is_remote_frame(); let is_remote = frame.is_remote_frame();
self.write_id(frame.id(), is_remote); self.write_id(frame.id(), is_remote);
let dlc = frame.dlc(); let dlc = frame.dlc();
self.msg_buf.modify_stat_ctrl(|mut ctrl| {
ctrl.set_dlc(u4::new(dlc as u8));
ctrl
});
if !is_remote { if !is_remote {
self.msg_buf self.msg_buf
.write_data0(TwoBytesData::new_with_raw_value(0)); .write_data0(TwoBytesData::new_with_raw_value(0));
@ -155,35 +163,35 @@ impl CanChannelLowLevel {
.write_data3(TwoBytesData::new_with_raw_value(0)); .write_data3(TwoBytesData::new_with_raw_value(0));
for idx in 0..dlc { for idx in 0..dlc {
match idx { match idx {
0 => self.msg_buf.modify_data3(|mut val| { 0 => self.msg_buf.modify_data0(|mut val| {
val.set_data_upper_byte(frame.data()[idx]); val.set_data_upper_byte(frame.data()[idx]);
val val
}), }),
1 => self.msg_buf.modify_data3(|mut val| { 1 => self.msg_buf.modify_data0(|mut val| {
val.set_data_lower_byte(frame.data()[idx]); val.set_data_lower_byte(frame.data()[idx]);
val val
}), }),
2 => self.msg_buf.modify_data2(|mut val| { 2 => self.msg_buf.modify_data1(|mut val| {
val.set_data_upper_byte(frame.data()[idx]); val.set_data_upper_byte(frame.data()[idx]);
val val
}), }),
3 => self.msg_buf.modify_data2(|mut val| { 3 => self.msg_buf.modify_data1(|mut val| {
val.set_data_lower_byte(frame.data()[idx]); val.set_data_lower_byte(frame.data()[idx]);
val val
}), }),
4 => self.msg_buf.modify_data1(|mut val| { 4 => self.msg_buf.modify_data2(|mut val| {
val.set_data_upper_byte(frame.data()[idx]); val.set_data_upper_byte(frame.data()[idx]);
val val
}), }),
5 => self.msg_buf.modify_data1(|mut val| { 5 => self.msg_buf.modify_data2(|mut val| {
val.set_data_lower_byte(frame.data()[idx]); val.set_data_lower_byte(frame.data()[idx]);
val val
}), }),
6 => self.msg_buf.modify_data0(|mut val| { 6 => self.msg_buf.modify_data3(|mut val| {
val.set_data_upper_byte(frame.data()[idx]); val.set_data_upper_byte(frame.data()[idx]);
val val
}), }),
7 => self.msg_buf.modify_data0(|mut val| { 7 => self.msg_buf.modify_data3(|mut val| {
val.set_data_lower_byte(frame.data()[idx]); val.set_data_lower_byte(frame.data()[idx]);
val val
}), }),
@ -191,11 +199,7 @@ impl CanChannelLowLevel {
} }
} }
} }
self.msg_buf.modify_stat_ctrl(|mut ctrl| { self.write_state(BufferState::TxOnce);
ctrl.set_dlc(u4::new(dlc as u8));
ctrl.set_status(BufferState::TxOnce);
ctrl
});
} }
pub fn enable_error_interrupt(&mut self, enable_translation: bool) { pub fn enable_error_interrupt(&mut self, enable_translation: bool) {
@ -273,14 +277,14 @@ impl CanChannelLowLevel {
let mut data: [u8; 8] = [0; 8]; let mut data: [u8; 8] = [0; 8];
let mut read_data = |dlc: u4| { let mut read_data = |dlc: u4| {
(0..dlc.as_usize()).for_each(|i| match i { (0..dlc.as_usize()).for_each(|i| match i {
0 => data[i] = data3.data_upper_byte().as_u8(), 0 => data[i] = data0.data_upper_byte().as_u8(),
1 => data[i] = data3.data_lower_byte().as_u8(), 1 => data[i] = data0.data_lower_byte().as_u8(),
2 => data[i] = data2.data_upper_byte().as_u8(), 2 => data[i] = data1.data_upper_byte().as_u8(),
3 => data[i] = data2.data_lower_byte().as_u8(), 3 => data[i] = data1.data_lower_byte().as_u8(),
4 => data[i] = data1.data_upper_byte().as_u8(), 4 => data[i] = data2.data_upper_byte().as_u8(),
5 => data[i] = data1.data_lower_byte().as_u8(), 5 => data[i] = data2.data_lower_byte().as_u8(),
6 => data[i] = data0.data_upper_byte().as_u8(), 6 => data[i] = data3.data_upper_byte().as_u8(),
7 => data[i] = data0.data_lower_byte().as_u8(), 7 => data[i] = data3.data_lower_byte().as_u8(),
_ => unreachable!(), _ => unreachable!(),
}); });
}; };
@ -309,7 +313,7 @@ impl CanChannelLowLevel {
} else { } else {
let dlc = self.msg_buf.read_stat_ctrl().dlc(); let dlc = self.msg_buf.read_stat_ctrl().dlc();
read_data(dlc); read_data(dlc);
CanFrameNormal::new(id, &data[0..dlc.as_usize()]).into() CanFrameNormal::new(id, &data[0..dlc.as_usize()]).unwrap().into()
} }
} }
} }

View File

@ -125,20 +125,16 @@ impl ClockConfig {
if bitrate.raw() == 0 { if bitrate.raw() == 0 {
return Err(ClockConfigError::BitrateIsZero); return Err(ClockConfigError::BitrateIsZero);
} }
let mut prescaler = roundf( let nominal_bit_time = 1 + tseg1 as u32 + tseg2 as u32;
clocks.apb1().raw() as f32 let prescaler =
/ (bitrate.raw() as f32 * (1.0 + tseg1.as_u32() as f32 + tseg2.as_u32() as f32)), roundf(clocks.apb1().raw() as f32 / (bitrate.raw() as f32 * nominal_bit_time as f32))
) as u32; as u32;
// defmt::info!("calc prescaler: {}", prescaler);
if !(PRESCALER_MIN as u32..=PRESCALER_MAX as u32).contains(&prescaler) { if !(PRESCALER_MIN as u32..=PRESCALER_MAX as u32).contains(&prescaler) {
return Err(ClockConfigError::CanNotFindPrescaler); return Err(ClockConfigError::CanNotFindPrescaler);
} }
let actual_bitrate = (clocks.apb1().raw() as f32) let actual_bitrate = (clocks.apb1().raw() as f32) / (prescaler * nominal_bit_time) as f32;
/ (prescaler * (1 + tseg1.as_u32() + tseg2.as_u32())) as f32; let bitrate_deviation = calculate_bitrate_deviation(actual_bitrate, bitrate);
let bitrate_deviation =
((actual_bitrate as i32 - bitrate.raw() as i32).abs() as f32) / bitrate.raw() as f32;
//defmt::info!("actual bitrate: {}, target {}", actual_bitrate, bitrate);
if bitrate_deviation > MAX_BITRATE_DEVIATION { if bitrate_deviation > MAX_BITRATE_DEVIATION {
return Err(ClockConfigError::BitrateErrorTooLarge); return Err(ClockConfigError::BitrateErrorTooLarge);
} }
@ -392,6 +388,16 @@ impl Can {
&mut self.regs &mut self.regs
} }
#[inline]
pub fn read_error_counters(&self) -> regs::ErrorCounter {
self.regs.read_error_counter()
}
#[inline]
pub fn read_error_diagnostics(&self) -> regs::DiagnosticRegister {
self.regs.read_diag()
}
#[inline] #[inline]
pub fn id(&self) -> CanId { pub fn id(&self) -> CanId {
self.id self.id
@ -403,11 +409,11 @@ impl Can {
} }
#[inline] #[inline]
pub fn set_loopback(&mut self, enable: bool) { pub fn modify_control<F>(&mut self, f: F)
self.regs.modify_control(|mut val| { where
val.set_loopback(enable); F: FnOnce(Control) -> Control,
val {
}); self.regs.modify_control(f);
} }
#[inline] #[inline]
@ -436,10 +442,29 @@ pub enum TxState {
AwaitingRemoteFrameReply, AwaitingRemoteFrameReply,
} }
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InvalidTxState {
State(TxState),
BufferState(BufferState),
}
impl From<TxState> for InvalidTxState {
fn from(state: TxState) -> Self {
InvalidTxState::State(state)
}
}
impl From<BufferState> for InvalidTxState {
fn from(state: BufferState) -> Self {
InvalidTxState::BufferState(state)
}
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
#[error("invalid tx state {0:?}")] #[error("invalid tx state {0:?}")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidTxStateError(pub TxState); pub struct InvalidTxStateError(pub InvalidTxState);
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -474,20 +499,25 @@ impl CanTx {
self.mode = TxState::Idle; self.mode = TxState::Idle;
} }
if self.mode != TxState::Idle { if self.mode != TxState::Idle {
return Err(InvalidTxStateError(self.mode)); return Err(InvalidTxStateError(self.mode.into()));
} }
if !frame.is_remote_frame() { if !frame.is_remote_frame() {
self.mode = TxState::TransmittingDataFrame; self.mode = TxState::TransmittingDataFrame;
} else { } else {
self.mode = TxState::TransmittingRemoteFrame; self.mode = TxState::TransmittingRemoteFrame;
} }
if let Ok(state) = self.ll.read_state() {
if state != BufferState::TxNotActive {
return Err(InvalidTxStateError(state.into()));
}
}
self.ll.transmit_frame_unchecked(frame); self.ll.transmit_frame_unchecked(frame);
Ok(()) Ok(())
} }
pub fn data_frame_transfer_done(&mut self) -> nb::Result<(), InvalidTxStateError> { pub fn data_frame_transfer_done(&mut self) -> nb::Result<(), InvalidTxStateError> {
if self.mode != TxState::TransmittingDataFrame { if self.mode != TxState::TransmittingDataFrame {
return Err(nb::Error::Other(InvalidTxStateError(self.mode))); return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
} }
let status = self.ll.read_state(); let status = self.ll.read_state();
if status.is_err() { if status.is_err() {
@ -503,7 +533,7 @@ impl CanTx {
pub fn remote_frame_transfer_done(&mut self) -> nb::Result<CanRx, InvalidTxStateError> { pub fn remote_frame_transfer_done(&mut self) -> nb::Result<CanRx, InvalidTxStateError> {
if self.mode != TxState::TransmittingRemoteFrame { if self.mode != TxState::TransmittingRemoteFrame {
return Err(nb::Error::Other(InvalidTxStateError(self.mode))); return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
} }
let status = self.ll.read_state(); let status = self.ll.read_state();
if status.is_err() { if status.is_err() {
@ -570,7 +600,7 @@ impl CanRx {
return Err(nb::Error::WouldBlock); return Err(nb::Error::WouldBlock);
} }
let status = status.unwrap(); let status = status.unwrap();
if status == BufferState::RxReady || status == BufferState::RxOverrun { if status == BufferState::RxFull || status == BufferState::RxOverrun {
self.mode = RxState::Idle; self.mode = RxState::Idle;
if reconfigure_for_reception { if reconfigure_for_reception {
self.ll.write_state(BufferState::RxReady); self.ll.write_state(BufferState::RxReady);

View File

@ -46,7 +46,7 @@ pub struct BufStatusAndControl {
#[bits(4..=7, rw)] #[bits(4..=7, rw)]
priority: u4, priority: u4,
#[bits(0..=3, rw)] #[bits(0..=3, rw)]
status: Option<BufferState>, state: Option<BufferState>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -68,10 +68,10 @@ impl Timestamp {
#[bitbybit::bitfield(u32, default = 0x0)] #[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)] #[derive(Debug)]
pub struct TwoBytesData { pub struct TwoBytesData {
#[bits(0..=7, rw)]
data_lower_byte: u8,
#[bits(8..=15, rw)] #[bits(8..=15, rw)]
data_upper_byte: u8, data_upper_byte: u8,
#[bits(8..=15, rw)]
data_lower_byte: u8,
} }
#[derive(derive_mmio::Mmio)] #[derive(derive_mmio::Mmio)]
@ -87,14 +87,16 @@ pub struct CanMsgBuf {
id1: BaseId, id1: BaseId,
} }
static_assertions::const_assert_eq!(core::mem::size_of::<CanMsgBuf>(), 0x20);
impl MmioCanMsgBuf<'_> { impl MmioCanMsgBuf<'_> {
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.write_stat_ctrl(BufStatusAndControl::new_with_raw_value(0)); self.write_stat_ctrl(BufStatusAndControl::new_with_raw_value(0));
self.write_timestamp(Timestamp::new(0)); self.write_timestamp(Timestamp::new(0));
self.write_data3(TwoBytesData::new_with_raw_value(0));
self.write_data2(TwoBytesData::new_with_raw_value(0));
self.write_data1(TwoBytesData::new_with_raw_value(0));
self.write_data0(TwoBytesData::new_with_raw_value(0)); self.write_data0(TwoBytesData::new_with_raw_value(0));
self.write_data1(TwoBytesData::new_with_raw_value(0));
self.write_data2(TwoBytesData::new_with_raw_value(0));
self.write_data3(TwoBytesData::new_with_raw_value(0));
self.write_id1(BaseId::new_with_raw_value(0)); self.write_id1(BaseId::new_with_raw_value(0));
self.write_id0(ExtendedId::new_with_raw_value(0)); self.write_id0(ExtendedId::new_with_raw_value(0));
} }
@ -387,6 +389,8 @@ pub struct Can {
timer: u32, timer: u32,
} }
static_assertions::const_assert_eq!(core::mem::size_of::<Can>(), 0x238);
impl Can { impl Can {
/// Create a new CAN MMIO instance for peripheral 0. /// Create a new CAN MMIO instance for peripheral 0.
/// ///