manually transfer can support

This commit is contained in:
Robin Müller 2025-04-24 17:10:11 +02:00
parent 47596415d3
commit 80318ebe0c
Signed by: muellerr
GPG Key ID: A649FB78196E3849
5 changed files with 1076 additions and 1 deletions

View File

@ -13,8 +13,10 @@ 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"
vorago-shared-periphs = { git = "https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs.git", features = ["vor4x"] }
libm = "0.2"
nb = "1"
embedded-hal = "1"
num_enum = { version = "0.7", default-features = false }
@ -22,6 +24,7 @@ bitflags = "2"
bitbybit = "1.3"
arbitrary-int = "1.3"
fugit = "0.3"
embedded-can = "0.4"
thiserror = { version = "2", default-features = false }
defmt = { version = "0.3", optional = true }

View File

@ -0,0 +1,117 @@
#[derive(Debug, thiserror::Error)]
#[error("invalid data size error {0}")]
pub struct InvalidDataSizeError(usize);
pub struct CanFrameNormal {
id: embedded_can::Id,
size: usize,
data: [u8; 8],
}
impl CanFrameNormal {
pub fn new(id: embedded_can::Id, data: &[u8]) -> Self {
let size = data.len();
let mut data_array = [0; 8];
data_array[..size].copy_from_slice(data);
Self {
id,
size,
data: data_array,
}
}
pub fn id(&self) -> embedded_can::Id {
self.id
}
pub fn data(&self) -> &[u8] {
&self.data[..self.size]
}
pub fn dlc(&self) -> usize {
self.size
}
}
pub struct CanFrameRtr {
id: embedded_can::Id,
dlc: usize,
}
impl CanFrameRtr {
pub fn new(id: embedded_can::Id, dlc: usize) -> Self {
Self { id, dlc }
}
pub fn id(&self) -> embedded_can::Id {
self.id
}
pub fn dlc(&self) -> usize {
self.dlc
}
}
pub enum CanFrame {
Normal(CanFrameNormal),
Rtr(CanFrameRtr),
}
impl From<CanFrameNormal> for CanFrame {
fn from(value: CanFrameNormal) -> Self {
Self::Normal(value)
}
}
impl From<CanFrameRtr> for CanFrame {
fn from(value: CanFrameRtr) -> Self {
Self::Rtr(value)
}
}
impl embedded_can::Frame for CanFrame {
fn new(id: impl Into<embedded_can::Id>, data: &[u8]) -> Option<Self> {
let id: embedded_can::Id = id.into();
Some(Self::Normal(CanFrameNormal::new(id, data)))
}
fn new_remote(id: impl Into<embedded_can::Id>, dlc: usize) -> Option<Self> {
let id: embedded_can::Id = id.into();
Some(Self::Rtr(CanFrameRtr::new(id, dlc)))
}
fn is_extended(&self) -> bool {
match self.id() {
embedded_can::Id::Extended(_) => true,
embedded_can::Id::Standard(_) => false,
}
}
fn is_remote_frame(&self) -> bool {
match self {
CanFrame::Normal(_) => false,
CanFrame::Rtr(_) => true,
}
}
fn id(&self) -> embedded_can::Id {
match self {
CanFrame::Normal(can_frame_normal) => can_frame_normal.id(),
CanFrame::Rtr(can_frame_rtr) => can_frame_rtr.id(),
}
}
fn dlc(&self) -> usize {
match self {
CanFrame::Normal(can_frame_normal) => can_frame_normal.dlc(),
CanFrame::Rtr(can_frame_rtr) => can_frame_rtr.dlc(),
}
}
fn data(&self) -> &[u8] {
match self {
CanFrame::Normal(can_frame_normal) => can_frame_normal.data(),
CanFrame::Rtr(_) => &[],
}
}
}

606
va416xx-hal/src/can/mod.rs Normal file
View File

@ -0,0 +1,606 @@
//! CAN driver.
//!
//! The VA416xx CAN module is based on the CP3UB26 module.
use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number};
use embedded_can::Frame;
use regs::{
BaseId, BufStatusAndControl, Control, DataDirection, 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 const PRESCALER_MIN: u8 = 2;
pub const PRESCALER_MAX: u8 = 128;
/// 1 is the minimum value, but not recommended by Vorago.
pub const TSEG1_MIN: u8 = 1;
pub const TSEG1_MAX: u8 = 16;
pub const TSEG2_MAX: u8 = 8;
/// In addition, SJW may not be larger than TSEG2.
pub const SJW_MAX: u8 = 4;
pub const MIN_SAMPLE_POINT: f32 = 0.5;
pub const MAX_BITRATE_DEVIATION: f32 = 0.005;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CanId {
Can0 = 0,
Can1 = 1,
}
impl CanId {
/// Steal the register block for the CAN ID.
///
/// # Safety
///
/// See safety of the [regs::Can::new_mmio_fixed_0].
pub 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() },
}
}
}
/// Sample point between 0 and 1.0 for the given time segments.
pub const fn calculate_sample_point(tseg1: u8, tseg2: u8) -> f32 {
let tseg1_val = tseg1 as f32;
(tseg1_val + 1.0) / (1.0 + tseg1_val + tseg2 as f32)
}
/// Calculate all viable clock configurations for the given input clock, the target bitrate and
/// for a sample point between 0.5 and 1.0.
///
/// There are various recommendations for the sample point when using the CAN bus. The value
/// depends on different parameters like the bus length and propagation time, as well as
/// the information processing time of the nodes. It should always be at least 50 %.
/// In doubt, select a value like 0.75.
///
/// - The [Python CAN library](https://python-can.readthedocs.io/en/stable/bit_timing.html)
/// assumes a default value of 69 % as the sample point if none is specified.
/// - CiA-301 recommends 87.5 %
/// - For simpler setups like laboratory setups, smaller values should work as well.
///
/// A clock configuration is consideres viable when
///
/// - The sample point deviation is less than 5 %.
/// - The bitrate error is less than +-0.5 %.
///
/// SJW will be set to either TSEG2 or 4, whichever is smaller.
#[cfg(feature = "alloc")]
pub fn calculate_all_viable_clock_configs(
apb1_clock: Hertz,
bitrate: Hertz,
sample_point: f32,
) -> Result<alloc::vec::Vec<ClockConfig>, InvalidSamplePointError> {
if sample_point < 0.5 || sample_point > 1.0 {
return Err(InvalidSamplePointError { sample_point });
}
let mut configs = alloc::vec::Vec::new();
for prescaler in PRESCALER_MIN..PRESCALER_MAX {
let nom_bit_time = apb1_clock / (bitrate * prescaler as u32);
// This is taken from the Python CAN library. NBT should not be too small.
if nom_bit_time < 8 {
break;
}
let actual_bitrate = apb1_clock / (prescaler as u32 * nom_bit_time);
let bitrate_deviation = ((actual_bitrate.raw() as i32 - bitrate.raw() as i32).abs() as f32)
/ bitrate.raw() as f32;
if bitrate_deviation > 0.05 {
continue;
}
let tseg1 = roundf(sample_point * nom_bit_time as f32) as u32 - 1;
if tseg1 > TSEG1_MAX as u32 || tseg1 < TSEG1_MIN as u32 {
continue;
}
// limit tseg1, so tseg2 is at least 1 TQ
let tseg1 = core::cmp::min(tseg1, nom_bit_time - 2) as u8;
let tseg2 = nom_bit_time - tseg1 as u32 - 1;
if tseg2 > TSEG2_MAX as u32 {
continue;
}
let tseg2 = tseg2 as u8;
let sjw = core::cmp::min(tseg2, 4) as u8;
// Use percent to have a higher resolution for the sample point deviation.
let sample_point_actual = roundf(calculate_sample_point(tseg1, tseg2) * 100.0) as u32;
let sample_point = roundf(sample_point * 100.0) as u32;
let deviation = (sample_point_actual as i32 - sample_point as i32).abs();
if deviation > 5 {
continue;
}
configs.push(ClockConfig {
prescaler,
tseg1,
tseg2,
sjw,
});
}
Ok(configs)
}
pub trait Instance {
const ID: CanId;
const PERIPH_SEL: PeripheralSelect;
}
impl Instance for va416xx::Can0 {
const ID: CanId = CanId::Can0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
}
impl Instance for va416xx::Can1 {
const ID: CanId = CanId::Can1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
}
#[derive(Debug, Clone, Copy)]
pub struct ClockConfig {
prescaler: u8,
tseg1: u8,
tseg2: u8,
sjw: u8,
}
#[derive(Debug, thiserror::Error)]
#[error("invalid buffer index {0}")]
pub struct InvalidBufferIndexError(usize);
#[derive(Debug, thiserror::Error)]
#[error("sjw must be less than or equal to the smaller tseg value")]
pub struct InvalidSjwError(u8);
#[derive(Debug, thiserror::Error)]
#[error("invalid sample point {sample_point}")]
pub struct InvalidSamplePointError {
/// Sample point, should be larger than 0.5 (50 %) but was not.
sample_point: f32,
}
#[derive(Debug, thiserror::Error)]
pub enum ClockConfigError {
#[error("invalid sjw: {0}")]
InvalidSjw(#[from] InvalidSjwError),
#[error("TSEG is zero which is not allowed")]
TsegIsZero,
#[error("TSEG1 is larger than 16")]
InvalidTseg1,
#[error("TSEG1 is larger than 8")]
InvalidTseg2,
#[error("invalid sample point: {0}")]
InvalidSamplePoint(#[from] InvalidSamplePointError),
#[error("bitrate is zero")]
BitrateIsZero,
#[error("bitrate error larger than +-0.5 %")]
BitrateErrorTooLarge,
#[error("maximum or minimum allowed prescaler is not sufficient for target bitrate clock")]
CanNotFindPrescaler,
}
impl ClockConfig {
/// New clock configuration from the raw configuration values.
///
/// The values specified here are not the register values, but the actual numerical values
/// relevant for calculations.
///
/// The values have the following requirements:
///
/// - Prescaler must be between 2 and 128.
/// - TSEG1 must be smaller than 16 and should be larger than 1.
/// - TSEG2 must be smaller than 8 and small enough so that the calculated sample point
/// is larger than 0.5 (50 %).
/// - SJW (Synchronization Jump Width) must be smaller than the smaller of the time segment
/// configuration values and smaller than 4.
pub fn new(prescaler: u8, tseg1: u8, tseg2: u8, sjw: u8) -> Result<Self, ClockConfigError> {
if !(PRESCALER_MIN..=PRESCALER_MAX).contains(&prescaler.value()) {
return Err(ClockConfigError::CanNotFindPrescaler);
}
if tseg1 == 0 || tseg2 == 0 {
return Err(ClockConfigError::TsegIsZero);
}
if tseg1 > TSEG1_MAX {
return Err(ClockConfigError::InvalidTseg1);
}
if tseg2 > TSEG2_MAX {
return Err(ClockConfigError::InvalidTseg2);
}
let smaller_tseg = core::cmp::min(tseg1.value(), tseg2.value());
if sjw.value() > smaller_tseg || sjw > SJW_MAX {
return Err(InvalidSjwError(sjw).into());
}
let sample_point = calculate_sample_point(tseg1, tseg2);
if sample_point < MIN_SAMPLE_POINT {
return Err(InvalidSamplePointError { sample_point }.into());
}
Ok(Self {
prescaler,
tseg1,
tseg2,
sjw,
})
}
/// Calculate the clock configuration for the given input clock, the target bitrate and for a
/// set of timing parameters.
///
/// This function basically calculates the necessary prescaler to achieve the given timing
/// parameters. It also performs sanity and validity checks for the calculated prescaler:
/// The bitrate error for the given prescaler needs to be smaller than 0.5 %.
pub fn from_bitrate_and_segments(
clocks: &Clocks,
bitrate: Hertz,
tseg1: u8,
tseg2: u8,
sjw: u8,
) -> Result<ClockConfig, ClockConfigError> {
if bitrate.raw() == 0 {
return Err(ClockConfigError::BitrateIsZero);
}
let 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;
if !(PRESCALER_MIN as u32..=PRESCALER_MAX as u32).contains(&prescaler) {
return Err(ClockConfigError::CanNotFindPrescaler);
}
let actual_bitrate = clocks.apb1() / (prescaler * (1 + tseg1.as_u32() + tseg2.as_u32()));
let bitrate_deviation = ((actual_bitrate.raw() as i32 - bitrate.raw() as i32).abs() as f32)
/ bitrate.raw() as f32;
if bitrate_deviation > MAX_BITRATE_DEVIATION {
return Err(ClockConfigError::BitrateErrorTooLarge);
}
// The subtractions are fine because we made checks to avoid underflows.
Self::new(prescaler as u8, tseg1, tseg2, sjw)
}
pub fn sjw_reg_value(&self) -> u2 {
u2::new(self.sjw.value() - 1)
}
pub fn tseg1_reg_value(&self) -> u4 {
u4::new(self.tseg1.value() - 1)
}
pub fn tseg2_reg_value(&self) -> u3 {
u3::new(self.tseg2.value() - 1)
}
pub fn prescaler_reg_value(&self) -> u7 {
u7::new(self.prescaler.value() - 2)
}
}
pub struct Can {
regs: regs::MmioCan<'static>,
id: CanId,
}
impl Can {
pub fn new<CanI: Instance>(_can: CanI, clk_config: ClockConfig) -> Self {
enable_peripheral_clock(CanI::PERIPH_SEL);
let id = CanI::ID;
let mut regs = if id == CanId::Can0 {
unsafe { regs::Can::new_mmio_fixed_0() }
} else {
unsafe { regs::Can::new_mmio_fixed_1() }
};
// 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.write_timing(
TimingConfig::builder()
.with_tseg2(clk_config.tseg2_reg_value())
.with_tseg1(clk_config.tseg1_reg_value())
.with_sync_jump_width(clk_config.sjw_reg_value())
.with_prescaler(clk_config.prescaler_reg_value())
.build(),
);
Self { regs, id }
}
/// This configures the global mask so that acceptance is only determined by an exact match
/// with the ID in the receive message buffers. This is the default reset configuration for
/// the global mask as well.
pub fn set_global_mask_for_exact_id_match(&mut self) {
self.regs.write_gmskx(ExtendedId::new_with_raw_value(0));
self.regs.write_gmskb(BaseId::new_with_raw_value(0));
}
/// 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
/// will automatically go into the [regs::BufferState::RxReady] state after the transmission,
/// but the XRTR and RTR/SRR bits need to be masked for the response frame to be accepted
/// on that buffer.
pub fn set_global_mask_for_exact_id_match_with_rtr_masked(&mut self) {
self.regs.write_gmskx(
ExtendedId::builder()
.with_mask_14_0(u15::new(0))
.with_xrtr(true)
.build(),
);
self.regs.write_gmskb(
BaseId::builder()
.with_mask_28_18(u11::new(0))
.with_rtr_or_srr(true)
.with_ide(false)
.with_mask_17_15(u3::new(0))
.build(),
);
}
/// This configures the base mask for buffer 14 so that acceptance is only determined by an
/// exact match with the ID in the receive message buffers. This is the default reset
/// configuration for the global mask as well.
pub fn set_base_mask_for_exact_id_match(&mut self) {
self.regs.write_bmskx(ExtendedId::new_with_raw_value(0));
self.regs.write_bmskb(BaseId::new_with_raw_value(0));
}
/// This configures the base mask so that all CAN frames which are not handled by any other
/// buffers are accepted by the base buffer 14.
pub fn set_base_mask_for_all_match(&mut self) {
self.regs
.write_bmskx(ExtendedId::new_with_raw_value(0xffff));
self.regs.write_bmskb(BaseId::new_with_raw_value(0xffff));
}
#[inline]
pub fn regs(&mut self) -> &mut MmioCan<'static> {
&mut self.regs
}
#[inline]
pub fn id(&self) -> CanId {
self.id
}
#[inline]
pub fn write_ctrl_reg(&mut self, ctrl: Control) {
self.regs.write_control(ctrl);
}
#[inline]
pub fn enable_bufflock(&mut self) {
self.regs.modify_control(|mut ctrl| {
ctrl.set_bufflock(true);
ctrl
});
}
#[inline]
pub fn enable(&mut self) {
self.regs.modify_control(|mut ctrl| {
ctrl.set_enable(true);
ctrl
});
}
}
#[derive(Debug)]
pub enum ChannelState {
Idle,
Receiving,
Transmitting,
AwaitingRtrReply,
}
pub struct CanChannel {
can_id: CanId,
idx: usize,
regs: regs::MmioCanMsgBuf<'static>,
mode: ChannelState,
}
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 CanChannel {
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(),
);
Ok(())
}
pub fn configure_for_reception_with_extended_id(
&mut self,
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;
Ok(())
}
pub fn configure_for_transmission(
&mut self,
tx_priority: u4,
) -> Result<(), InvalidBufferIndexError> {
let mut regs = unsafe { self.can_id.steal_regs() };
let mut cmb_block = regs.msg_buf_block_mut(self.idx);
cmb_block.write_stat_ctrl(
BufStatusAndControl::builder()
.with_dlc(u4::new(0))
.with_priority(tx_priority)
.with_status(regs::BufferState::TxNotActive)
.build(),
);
self.mode = ChannelState::Receiving;
Ok(())
}
/// Reads a received CAN frame from the message buffer.
///
/// This function does not check whether the pre-requisites for reading a CAN frame were
/// met and assumes this was already checked by the user.
pub fn read_frame_unchecked(&self) -> CanFrame {
let id0 = self.regs.read_id0();
let id1 = self.regs.read_id1();
let data0 = self.regs.read_data0();
let data1 = self.regs.read_data1();
let data2 = self.regs.read_data2();
let data3 = self.regs.read_data3();
let mut data: [u8; 8] = [0; 8];
let mut read_data = |dlc: u4| {
(0..dlc.as_usize()).for_each(|i| match i {
0 => data[i] = data3.data_upper_byte().as_u8(),
1 => data[i] = data3.data_lower_byte().as_u8(),
2 => data[i] = data2.data_upper_byte().as_u8(),
3 => data[i] = data2.data_lower_byte().as_u8(),
4 => data[i] = data1.data_upper_byte().as_u8(),
5 => data[i] = data1.data_lower_byte().as_u8(),
6 => data[i] = data0.data_upper_byte().as_u8(),
7 => data[i] = data0.data_lower_byte().as_u8(),
_ => unreachable!(),
});
};
let (id, rtr) = if !id1.ide() {
let id = embedded_can::Id::Standard(
embedded_can::StandardId::new(id1.mask_28_18().as_u16()).unwrap(),
);
if id1.rtr_or_srr() {
(id, true)
} else {
(id, false)
}
} else {
let id_raw = (id1.mask_28_18().as_u32() << 18)
| (id1.mask_17_15().as_u32() << 15)
| id0.mask_14_0().as_u32();
let id = embedded_can::Id::Extended(embedded_can::ExtendedId::new(id_raw).unwrap());
if id0.xrtr() {
(id, true)
} else {
(id, false)
}
};
if rtr {
CanFrameRtr::new(id, self.regs.read_stat_ctrl().dlc().as_usize()).into()
} else {
let dlc = self.regs.read_stat_ctrl().dlc();
read_data(dlc);
CanFrameNormal::new(id, &data[0..dlc.as_usize()]).into()
}
}
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(),
);
}
}
}
}
pub struct CanWorker {
can: Can,
channels: [CanChannel; 15],
}
#[cfg(test)]
mod tests {
#[cfg(feature = "alloc")]
use std::println;
#[cfg(feature = "alloc")]
#[test]
pub fn test_clock_calculator_example_1() {
let configs = super::calculate_all_viable_clock_configs(
crate::time::Hertz::from_raw(50_000_000),
crate::time::Hertz::from_raw(25_000),
0.75,
)
.expect("clock calculation failed");
// Bitrate: 25278.05 Hz. Sample point: 0.7391
assert_eq!(configs[0].prescaler, 84);
assert_eq!(configs[0].tseg1, 16);
assert_eq!(configs[0].tseg2, 6);
assert_eq!(configs[0].sjw, 4);
// Vorago sample value.
let sample_cfg = configs
.iter()
.find(|c| c.prescaler == 100)
.expect("clock config not found");
// Slightly different distribution because we use a different sample point, but
// the sum of TSEG1 and TSEG2 is the same as the Vorago example 1.
assert_eq!(sample_cfg.tseg1, 14);
assert_eq!(sample_cfg.tseg2, 5);
}
}

349
va416xx-hal/src/can/regs.rs Normal file
View File

@ -0,0 +1,349 @@
//! Custom register definitions for the CAN register block to circumvent PAC API / SVD
//! shortcomings.
use arbitrary_int::{u11, u15, u2, u3, u4, u7};
pub const CAN_0_BASE: usize = 0x4001_4000;
pub const CAN_1_BASE: usize = 0x4001_4400;
#[derive(Debug)]
#[bitbybit::bitenum(u4)]
pub enum BufferState {
/// Passive channel.
RxNotActive = 0b0000,
/// This condition indicated that SW wrote RxNotActive to a buffer when a data copy
/// process is still active.
RxBusy = 0b0001,
RxReady = 0b0010,
/// Indicated that data is being copied for the first time (RxRead -> RxBusy0).
RxBusy0 = 0b0011,
RxFull = 0b0100,
/// Indicated that data is being copied for the second time (RxFull -> RxBusy2).
RxBusy1 = 0b0101,
RxOverrun = 0b0110,
RxBusy2 = 0b0111,
TxNotActive = 0b1000,
/// Automatical response to a remote frame.
TxRtr = 0b1010,
/// Transmit one frame.
TxOnce = 0b1100,
TxBusy0 = 0b1101,
/// Transmit one frame, and changes to TxRtr after that. This can either be written by
/// software, or it will be written by the hardware after an auto response of the
/// [BufferState::TxRtr] state.
TxOnceRtr = 0b1110,
TxBusy2 = 0b1111,
}
/// Status control register for individual message buffers.
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct BufStatusAndControl {
/// Data length code.
#[bits(12..=15, rw)]
dlc: u4,
#[bits(4..=7, rw)]
priority: u4,
#[bits(0..=3, rw)]
status: Option<BufferState>,
}
#[derive(Debug)]
pub struct Timestamp(arbitrary_int::UInt<u32, 16>);
impl Timestamp {
pub fn new(value: u16) -> Self {
Self(value.into())
}
pub fn value(&self) -> u16 {
self.0.value() as u16
}
pub fn write(&mut self, value: u16) {
self.0 = value.into();
}
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct TwoBytesData {
#[bits(8..=15, rw)]
data_upper_byte: u8,
#[bits(8..=15, rw)]
data_lower_byte: u8,
}
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct CanMsgBuf {
stat_ctrl: BufStatusAndControl,
timestamp: Timestamp,
data3: TwoBytesData,
data2: TwoBytesData,
data1: TwoBytesData,
data0: TwoBytesData,
id0: ExtendedId,
id1: BaseId,
}
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_id1(BaseId::new_with_raw_value(0));
self.write_id0(ExtendedId::new_with_raw_value(0));
}
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum PinLogicLevel {
DominantIsZero = 0b0,
DominantIsOne = 0b1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum ErrorInterruptType {
/// EIPND bit is set on every error.
EveryError = 0b0,
/// EIPND bit is only set if error state changes as a result of a receive or transmit
/// error counter increment.
ErrorOnRxTxCounterChange = 0b1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug)]
pub enum DataDirection {
FirstByteAtHighestAddr = 0b0,
LastByteAtHighestAddr = 0b1,
}
#[bitbybit::bitfield(u32)]
pub struct Control {
#[bit(11, rw)]
error_interrupt_type: ErrorInterruptType,
/// Enables special diagnostics features of the CAN like LO, IGNACK, LOOPBACK, INTERNAL.
#[bit(10, rw)]
diag_enable: bool,
/// CANTX and CANRX pins are internally connected to each other.
#[bit(9, rw)]
internal: bool,
/// All messages sent by the CAN controller can also be received by a CAN buffer with a
/// matching buffer ID.
#[bit(8, rw)]
loopback: bool,
/// IGNACK feature. The CAN does not expect to receive an ACK bit.
#[bit(7, rw)]
ignore_ack: bool,
/// LO feature. The CAN is only configured as a receiver.
#[bit(6, rw)]
listen_only: bool,
#[bit(5, rw)]
data_dir: DataDirection,
#[bit(4, rw)]
timestamp_enable: bool,
#[bit(3, rw)]
bufflock: bool,
#[bit(2, rw)]
tx_logic_level: PinLogicLevel,
#[bit(1, rw)]
rx_logic_level: PinLogicLevel,
#[bit(0, rw)]
enable: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct TimingConfig {
#[bits(0..=2, rw)]
tseg2: u3,
#[bits(3..=6, rw)]
tseg1: u4,
#[bits(7..=8, rw)]
sync_jump_width: u2,
#[bits(9..=15, rw)]
prescaler: u7,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptEnable {
#[bit(15, rw)]
error: bool,
#[bit(0, rw)]
buffer: [bool; 15],
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptClear {
#[bit(15, w)]
error: bool,
#[bit(0, w)]
buffer: [bool; 15],
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptPending {
#[bit(15, r)]
error: bool,
#[bit(0, r)]
buffer: [bool; 15],
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct ErrorCounter {
#[bits(0..=7, r)]
transmit: u8,
#[bits(8..=15, r)]
receive: u8,
}
/// This register is unused for standard frames.
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct ExtendedId {
/// Mask for ID bits \[14:0\] of extended frames.
#[bits(1..=15, rw)]
mask_14_0: u15,
/// CAN XRTR bit.
#[bit(0, rw)]
xrtr: bool,
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct BaseId {
/// This will contain ID\[10:0\] for standard frames and bits [28:18] for extended frames.
#[bits(5..=15, rw)]
mask_28_18: u11,
/// This is the RTR bit for standard frames, and the SRR bit for extended frames.
#[bit(4, rw)]
rtr_or_srr: bool,
/// Identifier extension bit.
#[bit(3, rw)]
ide: bool,
/// Mask for ID bits \[17:15\] of extended frames.
#[bits(0..=2, rw)]
mask_17_15: u3,
}
#[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,
/// Hidden CAN message buffer. Only allowed to be used internally by the peripheral.
#[mmio(inner)]
_hcmb: CanMsgBuf,
control: Control,
timing: TimingConfig,
/// Global mask extension used for buffers 0 to 13.
gmskx: ExtendedId,
/// Global mask base used for buffers 0 to 13.
gmskb: BaseId,
/// Basic mask extension used for buffer 14.
bmskx: ExtendedId,
/// Basic mask base used for buffer 14.
bmskb: BaseId,
ien: InterruptEnable,
#[mmio(PureRead)]
ipnd: InterruptPending,
#[mmio(Write)]
iclr: InterruptClear,
/// Interrupt Code Enable Register.
icen: InterruptEnable,
#[mmio(PureRead)]
status_pending: u32,
#[mmio(PureRead)]
error_counter: ErrorCounter,
#[mmio(PureRead)]
diag: u32,
#[mmio(PureRead)]
timer: u32,
}
impl Can {
/// Create a new CAN MMIO instance for peripheral 0.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_0() -> MmioCan<'static> {
Self::new_mmio_at(CAN_0_BASE)
}
/// Create a new CAN MMIO instance for peripheral 1.
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_1() -> MmioCan<'static> {
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!(),
}
}
}

View File

@ -34,7 +34,6 @@ pub use va416xx as device;
pub use va416xx as pac;
pub mod prelude;
pub mod clock;
pub mod dma;
pub mod edac;
@ -47,6 +46,7 @@ pub mod spi;
pub mod time;
pub mod timer;
pub mod uart;
pub mod can;
pub mod wdt;
#[cfg(feature = "va41630")]