continue CAN support

This commit is contained in:
Robin Müller 2025-04-08 11:08:25 +02:00
parent e8e7ea9b1c
commit a6c9a6fcdc
Signed by: muellerr
GPG Key ID: A649FB78196E3849
9 changed files with 430 additions and 38 deletions

View File

@ -25,6 +25,7 @@ typenum = "1"
bitflags = "2"
bitbybit = "1.3"
arbitrary-int = "1.3"
derive-mmio = "0.4"
fugit = "0.3"
delegate = ">=0.12, <=0.13"
heapless = "0.8"

View File

@ -1 +0,0 @@

View File

@ -0,0 +1,67 @@
use arbitrary_int::{u2, u3, u4, u7};
use crate::{clock::Clocks, enable_peripheral_clock, PeripheralSelect};
pub mod regs;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CanId {
Can0 = 0,
Can1 = 1,
}
impl CanId {}
pub struct Can {
id: CanId,
}
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;
}
pub struct ClockConfig {
prescaler: u7,
tseg1: u4,
tseg2: u3,
sjw: u2,
}
impl ClockConfig {
/// New clock configuration from the raw configuration values.
pub fn new(prescaler: u7, tseg1: u4, tseg2: u3, sjw: u2) -> Self {
Self {
prescaler,
tseg1,
tseg2,
sjw,
}
}
}
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() }
};
for i in 0..15 {
regs.msg_buf_block_mut(i).reset();
}
Self { id }
}
}

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

@ -0,0 +1,298 @@
//! Custom register definitions for the CAN register block to circumvent PAC API / SVD
//! shortcomings.
use arbitrary_int::{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 {
RxNotActive = 0b0000,
RxReady = 0b0001,
RxBusy0 = 0b0010,
RxFull = 0b0100,
RxBusy1 = 0b0101,
RxOverrun = 0b0110,
RxBusy2 = 0b0111,
TxNotActive = 0b1000,
TxRtr = 0b1010,
TxOnce = 0b1100,
TxBusy0 = 0b1101,
TxOnceRtr = 0b1110,
TxBusy2 = 0b1111,
}
/// Status control register for individual message buffers.
#[bitbybit::bitfield(u32)]
pub struct BufStatusAndControl {
/// Data length code.
#[bits(12..=15, rw)]
dlc: u4,
#[bits(4..=7, rw)]
priority: u4,
#[bits(0..=3, rw)]
status: Option<BufferStatus>,
}
#[derive(Debug)]
pub struct Data16Bit(arbitrary_int::UInt<u32, 16>);
impl Data16Bit {
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();
}
}
#[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,
}
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));
}
}
#[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)]
#[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,
}
#[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,
#[mmio(inner)]
cmb14: CanMsgBuf,
#[mmio(inner)]
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,
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 < 16, "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() },
15 => unsafe { self.steal_hcmb() },
_ => unreachable!(),
}
}
}

View File

@ -396,16 +396,45 @@ impl Clocks {
}
/// Returns the frequency of the APB0 which is equal to the system clock.
///
/// This clock is the reference clock for the following peripherals:
///
/// - Ethernet
/// - SpaceWire
/// - IRQ Router
/// - DMA
/// - Clock Generator
pub const fn apb0(&self) -> Hertz {
self.sysclk()
}
/// Returns system clock divied by 2.
///
/// This clock is the reference clock for the following peripherals:
///
/// - Timer[15:0]
/// - UART2
/// - SPI
/// - I2C
/// - CAN
/// - GPIO
/// - IOCONFIG
/// - System Config
pub const fn apb1(&self) -> Hertz {
self.apb1
}
/// Returns system clock divied by 4.
///
/// This clock is the reference clock for the following peripherals:
///
/// - Timer[23:16]
/// - TRNG
/// - UART[1:0]
/// - DAC
/// - ADC
/// - Watchdog
/// - Utility
pub const fn apb2(&self) -> Hertz {
self.apb2
}

View File

@ -3,7 +3,9 @@
//! ## Examples
//!
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/peb1-accelerometer.rs)
use crate::{clock::Clocks, pac, time::Hertz, typelevel::Sealed, PeripheralSelect, SyscfgExt as _};
use crate::{
clock::Clocks, enable_peripheral_clock, pac, time::Hertz, typelevel::Sealed, PeripheralSelect,
};
use core::{marker::PhantomData, ops::Deref};
use embedded_hal::i2c::{self, Operation, SevenBitAddress, TenBitAddress};
@ -318,13 +320,12 @@ impl<I2C> I2cBase<I2C> {
impl<I2c: Instance> I2cBase<I2c> {
pub fn new(
i2c: I2c,
syscfg: &mut pac::Sysconfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
ms_cfg: Option<&MasterConfig>,
sl_cfg: Option<&SlaveConfig>,
) -> Result<Self, ClockTooSlowForFastI2cError> {
syscfg.enable_peripheral_clock(I2c::PERIPH_SEL);
enable_peripheral_clock(I2c::PERIPH_SEL);
let mut i2c_base = I2cBase {
i2c,
@ -476,13 +477,12 @@ pub struct I2cMaster<I2c, Addr = SevenBitAddress> {
impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
pub fn new(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: MasterConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cMaster {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?,
i2c_base: I2cBase::new(i2c, clocks, speed_mode, Some(&cfg), None)?,
addr: PhantomData,
}
.enable_master())
@ -737,13 +737,12 @@ pub struct I2cSlave<I2c, Addr = SevenBitAddress> {
impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
fn new_generic(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cSlave {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&cfg))?,
i2c_base: I2cBase::new(i2c, clocks, speed_mode, None, Some(&cfg))?,
addr: PhantomData,
}
.enable_slave())
@ -884,7 +883,6 @@ impl<I2c: Instance> I2cSlave<I2c, SevenBitAddress> {
/// Create a new I2C slave for seven bit addresses
pub fn new(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
@ -892,18 +890,17 @@ impl<I2c: Instance> I2cSlave<I2c, SevenBitAddress> {
if let I2cAddress::TenBit(_) = cfg.addr {
return Err(InitError::WrongAddrMode);
}
Ok(Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)?)
Ok(Self::new_generic(i2c, cfg, clocks, speed_mode)?)
}
}
impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
pub fn new_ten_bit_addr(
i2c: I2c,
sys_cfg: &mut pac::Sysconfig,
cfg: SlaveConfig,
clocks: &Clocks,
speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2cError> {
Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)
Self::new_generic(i2c, cfg, clocks, speed_mode)
}
}

View File

@ -97,39 +97,43 @@ pub enum PeripheralSelect {
pub type PeripheralClock = PeripheralSelect;
#[inline(always)]
pub fn enable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) {
syscfg
pub fn enable_peripheral_clock(clock: PeripheralSelect) {
// Safety: Only bit of peripheral is modified.
unsafe { pac::Sysconfig::steal() }
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
}
#[inline(always)]
pub fn disable_peripheral_clock(syscfg: &mut pac::Sysconfig, clock: PeripheralSelect) {
syscfg
pub fn disable_peripheral_clock(clock: PeripheralSelect) {
// Safety: Only bit of peripheral is modified.
unsafe { pac::Sysconfig::steal() }
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
}
#[inline(always)]
pub fn assert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
syscfg
pub fn assert_periph_reset(periph: PeripheralSelect) {
// Safety: Only reset bit of peripheral is modified.
unsafe { pac::Sysconfig::steal() }
.peripheral_reset()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << periph as u8)) });
}
#[inline(always)]
pub fn deassert_periph_reset(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
syscfg
pub fn deassert_periph_reset(periph: PeripheralSelect) {
// Safety: Only rest bit of peripheral is modified.
unsafe { pac::Sysconfig::steal() }
.peripheral_reset()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << periph as u8)) });
}
#[inline(always)]
fn assert_periph_reset_for_two_cycles(syscfg: &mut pac::Sysconfig, periph: PeripheralSelect) {
assert_periph_reset(syscfg, periph);
fn assert_periph_reset_for_two_cycles(periph: PeripheralSelect) {
assert_periph_reset(periph);
cortex_m::asm::nop();
cortex_m::asm::nop();
deassert_periph_reset(syscfg, periph);
deassert_periph_reset(periph);
}
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
@ -190,27 +194,27 @@ pub trait SyscfgExt {
impl SyscfgExt for pac::Sysconfig {
#[inline(always)]
fn enable_peripheral_clock(&mut self, clock: PeripheralClock) {
enable_peripheral_clock(self, clock)
enable_peripheral_clock(clock)
}
#[inline(always)]
fn disable_peripheral_clock(&mut self, clock: PeripheralClock) {
disable_peripheral_clock(self, clock)
disable_peripheral_clock(clock)
}
#[inline(always)]
fn assert_periph_reset(&mut self, clock: PeripheralSelect) {
assert_periph_reset(self, clock)
assert_periph_reset(clock)
}
#[inline(always)]
fn deassert_periph_reset(&mut self, clock: PeripheralSelect) {
deassert_periph_reset(self, clock)
deassert_periph_reset(clock)
}
#[inline(always)]
fn assert_periph_reset_for_two_cycles(&mut self, periph: PeripheralSelect) {
assert_periph_reset_for_two_cycles(self, periph)
assert_periph_reset_for_two_cycles(periph)
}
}

View File

@ -21,7 +21,7 @@ use crate::{
pac,
time::Hertz,
typelevel::{NoneT, Sealed},
PeripheralSelect, SyscfgExt as _,
PeripheralSelect,
};
#[cfg(not(feature = "va41628"))]
@ -1041,15 +1041,14 @@ where
/// to be done once.
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
pub fn new(
syscfg: &mut pac::Sysconfig,
clocks: &crate::clock::Clocks,
spi: SpiI,
pins: (Sck, Miso, Mosi),
spi_cfg: SpiConfig,
) -> Self {
crate::enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
crate::enable_peripheral_clock(SpiI::PERIPH_SEL);
// This is done in the C HAL.
syscfg.assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL);
crate::assert_periph_reset_for_two_cycles(SpiI::PERIPH_SEL);
let SpiConfig {
clk,
init_mode,

View File

@ -18,7 +18,7 @@ use fugit::RateExtU32;
use crate::clock::Clocks;
use crate::gpio::PF13;
use crate::time::Hertz;
use crate::{disable_nvic_interrupt, enable_nvic_interrupt, PeripheralSelect, SyscfgExt as _};
use crate::{disable_nvic_interrupt, enable_nvic_interrupt, PeripheralSelect};
use crate::{
gpio::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2,
@ -630,15 +630,14 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
Uart<UartInstance, (TxPinInst, RxPinInst)>
{
pub fn new(
syscfg: &mut va416xx::Sysconfig,
uart: UartInstance,
pins: (TxPinInst, RxPinInst),
config: impl Into<Config>,
clocks: &Clocks,
) -> Self {
crate::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
crate::enable_peripheral_clock(UartInstance::PERIPH_SEL);
// This is done in the C HAL.
syscfg.assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL);
crate::assert_periph_reset_for_two_cycles(UartInstance::PERIPH_SEL);
Uart {
inner: UartBase {
uart,
@ -651,13 +650,12 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
}
pub fn new_with_clock_freq(
syscfg: &mut va416xx::Sysconfig,
uart: UartInstance,
pins: (TxPinInst, RxPinInst),
config: impl Into<Config>,
clock: impl Into<Hertz>,
) -> Self {
crate::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
crate::enable_peripheral_clock(UartInstance::PERIPH_SEL);
Uart {
inner: UartBase {
uart,