diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index 388d392..ff4f5a3 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -21,6 +21,7 @@ embedded-hal-async = "1" embedded-hal = "1" embedded-io = "0.6" embedded-io-async = "0.6" +embedded-can = "0.4" num_enum = { version = "0.7", default-features = false } typenum = "1" bitflags = "2" diff --git a/va416xx-hal/src/can/frame.rs b/va416xx-hal/src/can/frame.rs new file mode 100644 index 0000000..033f9fc --- /dev/null +++ b/va416xx-hal/src/can/frame.rs @@ -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 for CanFrame { + fn from(value: CanFrameNormal) -> Self { + Self::Normal(value) + } +} + +impl From for CanFrame { + fn from(value: CanFrameRtr) -> Self { + Self::Rtr(value) + } +} + +impl embedded_can::Frame for CanFrame { + fn new(id: impl Into, data: &[u8]) -> Option { + let id: embedded_can::Id = id.into(); + Some(Self::Normal(CanFrameNormal::new(id, data))) + } + + fn new_remote(id: impl Into, dlc: usize) -> Option { + 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(_) => &[], + } + } +} diff --git a/va416xx-hal/src/can/mod.rs b/va416xx-hal/src/can/mod.rs index 56e31c8..c321e0d 100644 --- a/va416xx-hal/src/can/mod.rs +++ b/va416xx-hal/src/can/mod.rs @@ -1,9 +1,18 @@ -use arbitrary_int::{u2, u3, u4, u7, Number}; +//! 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; @@ -23,10 +32,18 @@ pub enum CanId { Can1 = 1, } -impl CanId {} - -pub struct Can { - id: CanId, +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. @@ -128,6 +145,10 @@ pub struct ClockConfig { 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); @@ -253,6 +274,11 @@ impl ClockConfig { } } +pub struct Can { + regs: regs::MmioCan<'static>, + id: CanId, +} + impl Can { pub fn new(_can: CanI, clk_config: ClockConfig) -> Self { enable_peripheral_clock(CanI::PERIPH_SEL); @@ -262,15 +288,290 @@ impl Can { } 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(); } - Self { id } + 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)] diff --git a/va416xx-hal/src/can/regs.rs b/va416xx-hal/src/can/regs.rs index 7fe6060..8aad508 100644 --- a/va416xx-hal/src/can/regs.rs +++ b/va416xx-hal/src/can/regs.rs @@ -1,31 +1,43 @@ //! Custom register definitions for the CAN register block to circumvent PAC API / SVD //! shortcomings. -use arbitrary_int::{u2, u3, u4, u7}; +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 BufferStatus { +pub enum BufferState { + /// Passive channel. RxNotActive = 0b0000, - RxReady = 0b0001, - RxBusy0 = 0b0010, + /// 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)] +#[bitbybit::bitfield(u32, default = 0x0)] +#[derive(Debug)] pub struct BufStatusAndControl { /// Data length code. #[bits(12..=15, rw)] @@ -33,13 +45,13 @@ pub struct BufStatusAndControl { #[bits(4..=7, rw)] priority: u4, #[bits(0..=3, rw)] - status: Option, + status: Option, } #[derive(Debug)] -pub struct Data16Bit(arbitrary_int::UInt); +pub struct Timestamp(arbitrary_int::UInt); -impl Data16Bit { +impl Timestamp { pub fn new(value: u16) -> Self { Self(value.into()) } @@ -52,29 +64,38 @@ impl Data16Bit { } } +#[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: Data16Bit, - data3: Data16Bit, - data2: Data16Bit, - data1: Data16Bit, - data0: Data16Bit, - id0: Data16Bit, - id1: Data16Bit, + 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(Data16Bit::new(0)); - self.write_data3(Data16Bit::new(0)); - self.write_data2(Data16Bit::new(0)); - self.write_data1(Data16Bit::new(0)); - self.write_data0(Data16Bit::new(0)); - self.write_id1(Data16Bit::new(0)); - self.write_id0(Data16Bit::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_id1(BaseId::new_with_raw_value(0)); + self.write_id0(ExtendedId::new_with_raw_value(0)); } } @@ -136,7 +157,7 @@ pub struct Control { enable: bool, } -#[bitbybit::bitfield(u32)] +#[bitbybit::bitfield(u32, default = 0x0)] #[derive(Debug)] pub struct TimingConfig { #[bits(0..=2, rw)] @@ -185,6 +206,35 @@ pub struct ErrorCounter { 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 { @@ -216,20 +266,22 @@ pub struct Can { 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, + _hcmb: CanMsgBuf, control: Control, timing: TimingConfig, - /// Global mask extension. - gmskx: u32, - /// Global mask base. - gmskb: u32, - /// Basic mask extension. - bmskx: u32, - /// Basic mask base. - bmskb: u32, + /// 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, @@ -274,7 +326,7 @@ impl Can { 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 < 16, "invalid index for CAN message buffer"); + assert!(idx < 15, "invalid index for CAN message buffer"); match idx { 0 => unsafe { self.steal_cmb0() }, 1 => unsafe { self.steal_cmb1() }, @@ -291,7 +343,6 @@ impl MmioCan<'_> { 12 => unsafe { self.steal_cmb12() }, 13 => unsafe { self.steal_cmb13() }, 14 => unsafe { self.steal_cmb14() }, - 15 => unsafe { self.steal_hcmb() }, _ => unreachable!(), } }