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"
cfg-if = "1"
embedded-io = "0.6"
embedded-can = "0.4"
embedded-hal-async = "1"
embedded-io-async = "0.6"

View File

@ -31,26 +31,66 @@ async fn main(_spawner: Spawner) {
va416xx_embassy::init(dp.tim15, dp.tim14, &clocks);
defmt::info!("creating CAN peripheral driver");
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");
let mut can = Can::new(dp.can0, clk_config);
can.set_loopback(true);
can.set_bufflock(true);
can.modify_control(|mut val| {
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.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();
// Transmit channel.
let mut tx = CanTx::new(channels.take(0).unwrap(), None);
// Base channel which has dedicated mask.
let mut rx = CanRx::new(channels.take(14).unwrap());
let send_frame = CanFrame::Normal(CanFrameNormal::new(
can::Id::Standard(can::StandardId::new(0x1).unwrap()),
&[1, 2, 3, 4],
));
let mut rx_dedicated = CanRx::new(channels.take(1).unwrap());
// Base channel which has dedicated mask.
let mut rx_base = CanRx::new(channels.take(14).unwrap());
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");
tx.transmit_frame(send_frame).unwrap();
let _frame = nb::block!(rx.receive(true)).expect("invalid CAN rx state");
defmt::info!("received CAN frame with data");
let frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state");
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 {}
}

View File

@ -14,6 +14,7 @@ categories = ["embedded", "no-std", "hardware-support"]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
va416xx = { version = "0.4", features = ["critical-section"], default-features = false }
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"] }
libm = "0.2"

View File

@ -12,25 +12,31 @@ pub struct 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 mut data_array = [0; 8];
data_array[..size].copy_from_slice(data);
Self {
data_array[0..size].copy_from_slice(data);
Ok(Self {
id,
size,
data: data_array,
}
})
}
#[inline]
pub fn id(&self) -> embedded_can::Id {
self.id
}
#[inline]
pub fn data(&self) -> &[u8] {
&self.data[..self.size]
&self.data[0..self.dlc()]
}
#[inline]
pub fn dlc(&self) -> usize {
self.size
}
@ -76,8 +82,11 @@ impl From<CanFrameRtr> for CanFrame {
impl embedded_can::Frame for CanFrame {
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();
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> {

View File

@ -73,15 +73,19 @@ impl CanChannelLowLevel {
}
}
pub fn reset(&mut self) {
self.msg_buf.reset();
}
#[inline]
pub fn read_state(&self) -> Result<BufferState, u8> {
self.msg_buf.read_stat_ctrl().status()
self.msg_buf.read_stat_ctrl().state()
}
#[inline]
pub fn write_state(&mut self, buffer_state: BufferState) {
self.msg_buf.modify_stat_ctrl(|mut val| {
val.set_status(buffer_state);
val.set_state(buffer_state);
val
});
}
@ -95,7 +99,7 @@ impl CanChannelLowLevel {
if let Some(tx_priority) = tx_priority {
val.set_priority(tx_priority);
}
val.set_status(BufferState::TxNotActive);
val.set_state(BufferState::TxNotActive);
val
});
Ok(())
@ -135,7 +139,7 @@ impl CanChannelLowLevel {
BufStatusAndControl::builder()
.with_dlc(u4::new(0))
.with_priority(u4::new(0))
.with_status(BufferState::RxReady)
.with_state(BufferState::RxReady)
.build(),
);
}
@ -144,6 +148,10 @@ impl CanChannelLowLevel {
let is_remote = frame.is_remote_frame();
self.write_id(frame.id(), is_remote);
let dlc = frame.dlc();
self.msg_buf.modify_stat_ctrl(|mut ctrl| {
ctrl.set_dlc(u4::new(dlc as u8));
ctrl
});
if !is_remote {
self.msg_buf
.write_data0(TwoBytesData::new_with_raw_value(0));
@ -155,35 +163,35 @@ impl CanChannelLowLevel {
.write_data3(TwoBytesData::new_with_raw_value(0));
for idx in 0..dlc {
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
}),
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
}),
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
}),
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
}),
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
}),
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
}),
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
}),
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
}),
@ -191,11 +199,7 @@ impl CanChannelLowLevel {
}
}
}
self.msg_buf.modify_stat_ctrl(|mut ctrl| {
ctrl.set_dlc(u4::new(dlc as u8));
ctrl.set_status(BufferState::TxOnce);
ctrl
});
self.write_state(BufferState::TxOnce);
}
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 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(),
0 => data[i] = data0.data_upper_byte().as_u8(),
1 => data[i] = data0.data_lower_byte().as_u8(),
2 => data[i] = data1.data_upper_byte().as_u8(),
3 => data[i] = data1.data_lower_byte().as_u8(),
4 => data[i] = data2.data_upper_byte().as_u8(),
5 => data[i] = data2.data_lower_byte().as_u8(),
6 => data[i] = data3.data_upper_byte().as_u8(),
7 => data[i] = data3.data_lower_byte().as_u8(),
_ => unreachable!(),
});
};
@ -309,7 +313,7 @@ impl CanChannelLowLevel {
} else {
let dlc = self.msg_buf.read_stat_ctrl().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 {
return Err(ClockConfigError::BitrateIsZero);
}
let mut prescaler = roundf(
clocks.apb1().raw() as f32
/ (bitrate.raw() as f32 * (1.0 + tseg1.as_u32() as f32 + tseg2.as_u32() as f32)),
) as u32;
// defmt::info!("calc prescaler: {}", prescaler);
let nominal_bit_time = 1 + tseg1 as u32 + tseg2 as u32;
let prescaler =
roundf(clocks.apb1().raw() as f32 / (bitrate.raw() as f32 * nominal_bit_time as f32))
as u32;
if !(PRESCALER_MIN as u32..=PRESCALER_MAX as u32).contains(&prescaler) {
return Err(ClockConfigError::CanNotFindPrescaler);
}
let actual_bitrate = (clocks.apb1().raw() as f32)
/ (prescaler * (1 + tseg1.as_u32() + tseg2.as_u32())) as f32;
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);
let actual_bitrate = (clocks.apb1().raw() as f32) / (prescaler * nominal_bit_time) as f32;
let bitrate_deviation = calculate_bitrate_deviation(actual_bitrate, bitrate);
if bitrate_deviation > MAX_BITRATE_DEVIATION {
return Err(ClockConfigError::BitrateErrorTooLarge);
}
@ -392,6 +388,16 @@ impl Can {
&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]
pub fn id(&self) -> CanId {
self.id
@ -403,11 +409,11 @@ impl Can {
}
#[inline]
pub fn set_loopback(&mut self, enable: bool) {
self.regs.modify_control(|mut val| {
val.set_loopback(enable);
val
});
pub fn modify_control<F>(&mut self, f: F)
where
F: FnOnce(Control) -> Control,
{
self.regs.modify_control(f);
}
#[inline]
@ -436,10 +442,29 @@ pub enum TxState {
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)]
#[error("invalid tx state {0:?}")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidTxStateError(pub TxState);
pub struct InvalidTxStateError(pub InvalidTxState);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -474,20 +499,25 @@ impl CanTx {
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() {
self.mode = TxState::TransmittingDataFrame;
} else {
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);
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)));
return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
}
let status = self.ll.read_state();
if status.is_err() {
@ -503,7 +533,7 @@ impl CanTx {
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)));
return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
}
let status = self.ll.read_state();
if status.is_err() {
@ -570,7 +600,7 @@ impl CanRx {
return Err(nb::Error::WouldBlock);
}
let status = status.unwrap();
if status == BufferState::RxReady || status == BufferState::RxOverrun {
if status == BufferState::RxFull || status == BufferState::RxOverrun {
self.mode = RxState::Idle;
if reconfigure_for_reception {
self.ll.write_state(BufferState::RxReady);

View File

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