use core::sync::atomic::AtomicBool;
use arbitrary_int::{prelude::*, u2, u3, u4, u7, u11, u15};
use embedded_can::Frame;
use ll::CanChannelLowLevel;
use regs::{BaseId, BufferState, Control, MmioCan, TimingConfig};
use vorago_shared_hal::enable_nvic_interrupt;
use crate::{PeripheralSelect, clock::Clocks, enable_peripheral_clock, time::Hertz};
use libm::roundf;
pub mod frame;
pub use frame::*;
pub mod asynch;
pub mod ll;
pub mod regs;
pub const PRESCALER_MIN: u8 = 2;
pub const PRESCALER_MAX: u8 = 128;
pub const TSEG1_MIN: u8 = 1;
pub const TSEG1_MAX: u8 = 16;
pub const TSEG2_MAX: u8 = 8;
pub const SJW_MAX: u8 = 4;
pub const MIN_SAMPLE_POINT: f32 = 0.5;
pub const MAX_BITRATE_DEVIATION: f32 = 0.005;
static CHANNELS_TAKEN: [AtomicBool; 2] = [AtomicBool::new(false), AtomicBool::new(false)];
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CanId {
Can0 = 0,
Can1 = 1,
}
impl CanId {
#[inline]
pub const 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() },
}
}
#[inline]
pub const fn irq_id(&self) -> va416xx::Interrupt {
match self {
CanId::Can0 => va416xx::Interrupt::CAN0,
CanId::Can1 => va416xx::Interrupt::CAN1,
}
}
}
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)
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClockConfig {
prescaler: u8,
tseg1: u8,
tseg2: u8,
sjw: u8,
}
impl ClockConfig {
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,
})
}
pub fn from_bitrate_and_segments(
clocks: &Clocks,
bitrate: Hertz,
tseg1: u8,
tseg2: u8,
sjw: u8,
) -> Result<ClockConfig, ClockConfigError> {
if bitrate.to_raw() == 0 {
return Err(ClockConfigError::BitrateIsZero);
}
let nominal_bit_time = 1 + tseg1 as u32 + tseg2 as u32;
let prescaler = roundf(
clocks.apb1().to_raw() as f32 / (bitrate.to_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().to_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);
}
Self::new(prescaler as u8, tseg1, tseg2, sjw)
}
#[inline]
pub fn sjw_reg_value(&self) -> u2 {
u2::new(self.sjw.value() - 1)
}
#[inline]
pub fn tseg1_reg_value(&self) -> u4 {
u4::new(self.tseg1.value() - 1)
}
#[inline]
pub fn tseg2_reg_value(&self) -> u3 {
u3::new(self.tseg2.value() - 1)
}
#[inline]
pub fn prescaler_reg_value(&self) -> u7 {
u7::new(self.prescaler.value() - 2)
}
}
#[cfg(feature = "alloc")]
pub fn calculate_all_viable_clock_configs(
apb1_clock: Hertz,
bitrate: Hertz,
sample_point: f32,
) -> Result<alloc::vec::Vec<(ClockConfig, f32)>, 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 = calculate_nominal_bit_time(apb1_clock, bitrate, prescaler);
if nom_bit_time < 8 {
break;
}
let actual_bitrate = calculate_actual_bitrate(apb1_clock, prescaler, nom_bit_time);
let bitrate_deviation = calculate_bitrate_deviation(actual_bitrate, bitrate);
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;
}
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;
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,
},
bitrate_deviation,
));
}
Ok(configs)
}
#[inline]
pub const fn calculate_nominal_bit_time(
apb1_clock: Hertz,
target_bitrate: Hertz,
prescaler: u8,
) -> u32 {
apb1_clock.to_raw() / (target_bitrate.to_raw() * prescaler as u32)
}
#[inline]
pub const fn calculate_actual_bitrate(apb1_clock: Hertz, prescaler: u8, nom_bit_time: u32) -> f32 {
apb1_clock.to_raw() as f32 / (prescaler as u32 * nom_bit_time) as f32
}
#[inline]
pub const fn calculate_bitrate_deviation(actual_bitrate: f32, target_bitrate: Hertz) -> f32 {
(actual_bitrate - target_bitrate.to_raw() as f32).abs() / target_bitrate.to_raw() as f32
}
pub trait CanInstance {
const ID: CanId;
const IRQ: va416xx::Interrupt;
const PERIPH_SEL: PeripheralSelect;
}
impl CanInstance for va416xx::Can0 {
const ID: CanId = CanId::Can0;
const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
}
impl CanInstance for va416xx::Can1 {
const ID: CanId = CanId::Can1;
const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN1;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("invalid buffer index {0}")]
pub struct InvalidBufferIndexError(usize);
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[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}")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidSamplePointError {
sample_point: f32,
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
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,
}
pub struct Can {
regs: regs::MmioCan<'static>,
id: CanId,
}
impl Can {
pub fn new<CanI: CanInstance>(_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() }
};
regs.write_control(Control::new_with_raw_value(0));
for i in 0..15 {
regs.cmbs(i).unwrap().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 }
}
pub fn set_global_mask_for_exact_id_match(&mut self) {
self.regs
.write_gmskx(regs::ExtendedId::new_with_raw_value(0));
self.regs.write_gmskb(BaseId::new_with_raw_value(0));
}
pub fn take_channels(&self) -> Option<CanChannels> {
if CHANNELS_TAKEN[self.id() as usize].swap(true, core::sync::atomic::Ordering::SeqCst) {
return None;
}
Some(CanChannels::new(self.id))
}
pub fn set_global_mask_for_exact_id_match_with_rtr_masked(&mut self) {
self.regs.write_gmskx(
regs::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(),
);
}
#[inline]
pub fn set_base_mask_for_exact_id_match(&mut self) {
self.regs
.write_bmskx(regs::ExtendedId::new_with_raw_value(0));
self.regs.write_bmskb(BaseId::new_with_raw_value(0));
}
#[inline]
pub fn set_base_mask_for_all_match(&mut self) {
self.regs
.write_bmskx(regs::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 clear_interrupts(&mut self) {
self.regs
.write_iclr(regs::InterruptClear::new_with_raw_value(0xFFFF_FFFF));
}
#[inline]
pub fn enable_nvic_interrupt(&mut self) {
unsafe {
enable_nvic_interrupt(self.id().irq_id());
}
}
#[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
}
#[inline]
pub fn write_ctrl_reg(&mut self, ctrl: Control) {
self.regs.write_control(ctrl);
}
#[inline]
pub fn modify_control<F>(&mut self, f: F)
where
F: FnOnce(Control) -> Control,
{
self.regs.modify_control(f);
}
#[inline]
pub fn set_bufflock(&mut self, enable: bool) {
self.regs.modify_control(|mut ctrl| {
ctrl.set_bufflock(enable);
ctrl
});
}
#[inline]
pub fn enable(&mut self) {
self.regs.modify_control(|mut ctrl| {
ctrl.set_enable(true);
ctrl
});
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TxState {
Idle,
TransmittingDataFrame,
TransmittingRemoteFrame,
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 InvalidTxState);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RxState {
Idle,
Receiving,
}
#[derive(Debug, thiserror::Error)]
#[error("invalid rx state {0:?}")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidRxStateError(pub RxState);
#[derive(Debug)]
pub struct CanTx {
ll: CanChannelLowLevel,
mode: TxState,
}
impl CanTx {
pub fn new(mut ll: CanChannelLowLevel, tx_priority: Option<u4>) -> Self {
ll.reset();
ll.configure_for_transmission(tx_priority);
Self {
ll,
mode: TxState::Idle,
}
}
#[inline]
pub fn into_rx_channel(self) -> CanRx {
CanRx::new(self.ll)
}
pub fn transmit_frame(&mut self, frame: CanFrame) -> Result<(), InvalidTxStateError> {
if self.mode == TxState::AwaitingRemoteFrameReply {
self.ll.configure_for_transmission(None);
self.mode = TxState::Idle;
}
if self.mode != TxState::Idle {
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()
&& state != BufferState::TxNotActive
{
return Err(InvalidTxStateError(state.into()));
}
self.ll.transmit_frame_unchecked(frame);
Ok(())
}
pub fn transfer_done(&mut self) -> nb::Result<(), InvalidTxStateError> {
if self.mode != TxState::TransmittingDataFrame {
return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
}
let status = self.ll.read_state();
if status.is_err() {
return Err(nb::Error::WouldBlock);
}
let status = status.unwrap();
if status == BufferState::TxNotActive {
self.mode = TxState::Idle;
return Ok(());
}
Err(nb::Error::WouldBlock)
}
pub fn remote_transfer_done(&mut self) -> nb::Result<CanRx, InvalidTxStateError> {
if self.mode != TxState::TransmittingRemoteFrame {
return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
}
let status = self.ll.read_state();
if status.is_err() {
return Err(nb::Error::WouldBlock);
}
let status = status.unwrap();
if status == BufferState::RxReady {
self.mode = TxState::AwaitingRemoteFrameReply;
return Ok(CanRx {
ll: unsafe { self.ll.clone() },
mode: RxState::Receiving,
});
}
Err(nb::Error::WouldBlock)
}
pub fn remote_transfer_done_with_tx_reconfig(&mut self) -> nb::Result<(), InvalidTxStateError> {
if self.mode != TxState::TransmittingRemoteFrame {
return Err(nb::Error::Other(InvalidTxStateError(self.mode.into())));
}
let status = self.ll.read_state();
if status.is_err() {
return Err(nb::Error::WouldBlock);
}
let status = status.unwrap();
if status == BufferState::RxReady {
self.ll.write_state(BufferState::TxNotActive);
self.mode = TxState::Idle;
return Ok(());
}
Err(nb::Error::WouldBlock)
}
pub fn reset(&mut self) {
self.ll.reset();
self.mode = TxState::Idle;
}
}
pub struct CanRx {
ll: CanChannelLowLevel,
mode: RxState,
}
impl CanRx {
pub fn new(mut ll: CanChannelLowLevel) -> Self {
ll.reset();
Self {
ll,
mode: RxState::Idle,
}
}
#[inline]
pub fn into_tx_channel(self, tx_priority: Option<u4>) -> CanTx {
CanTx::new(self.ll, tx_priority)
}
#[inline]
pub fn enable_interrupt(&mut self, enable_translation: bool) {
self.ll.enable_interrupt(enable_translation);
}
pub fn configure_for_reception_with_standard_id(
&mut self,
standard_id: embedded_can::StandardId,
set_rtr: bool,
) {
self.ll.set_standard_id(standard_id, set_rtr);
self.configure_for_reception();
}
pub fn configure_for_reception_with_extended_id(
&mut self,
extended_id: embedded_can::ExtendedId,
set_rtr: bool,
) {
self.ll.set_extended_id(extended_id, set_rtr);
self.configure_for_reception();
}
pub fn configure_for_reception(&mut self) {
self.ll.configure_for_reception();
self.mode = RxState::Receiving;
}
#[inline]
pub fn frame_available(&self) -> bool {
self.ll
.read_state()
.is_ok_and(|state| state == BufferState::RxFull || state == BufferState::RxOverrun)
}
pub fn receive(
&mut self,
reconfigure_for_reception: bool,
) -> nb::Result<CanFrame, InvalidRxStateError> {
if self.mode != RxState::Receiving {
return Err(nb::Error::Other(InvalidRxStateError(self.mode)));
}
let status = self.ll.read_state();
if status.is_err() {
return Err(nb::Error::WouldBlock);
}
let status = status.unwrap();
if status == BufferState::RxFull || status == BufferState::RxOverrun {
self.mode = RxState::Idle;
if reconfigure_for_reception {
self.ll.write_state(BufferState::RxReady);
}
return Ok(self.ll.read_frame_unchecked());
}
Err(nb::Error::WouldBlock)
}
}
pub struct CanChannels {
id: CanId,
channels: [Option<CanChannelLowLevel>; 15],
}
impl CanChannels {
const fn new(id: CanId) -> Self {
unsafe {
Self {
id,
channels: [
Some(CanChannelLowLevel::steal_unchecked(id, 0)),
Some(CanChannelLowLevel::steal_unchecked(id, 1)),
Some(CanChannelLowLevel::steal_unchecked(id, 2)),
Some(CanChannelLowLevel::steal_unchecked(id, 3)),
Some(CanChannelLowLevel::steal_unchecked(id, 4)),
Some(CanChannelLowLevel::steal_unchecked(id, 5)),
Some(CanChannelLowLevel::steal_unchecked(id, 6)),
Some(CanChannelLowLevel::steal_unchecked(id, 7)),
Some(CanChannelLowLevel::steal_unchecked(id, 8)),
Some(CanChannelLowLevel::steal_unchecked(id, 9)),
Some(CanChannelLowLevel::steal_unchecked(id, 10)),
Some(CanChannelLowLevel::steal_unchecked(id, 11)),
Some(CanChannelLowLevel::steal_unchecked(id, 12)),
Some(CanChannelLowLevel::steal_unchecked(id, 13)),
Some(CanChannelLowLevel::steal_unchecked(id, 14)),
],
}
}
}
pub const fn can_id(&self) -> CanId {
self.id
}
pub fn take(&mut self, idx: usize) -> Option<CanChannelLowLevel> {
if idx > 14 {
return None;
}
self.channels[idx].take()
}
pub fn give(&mut self, idx: usize, channel: CanChannelLowLevel) {
if idx > 14 {
panic!("invalid buffer index for CAN channel");
}
self.channels[idx] = Some(channel);
}
}
#[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");
assert_eq!(configs[0].prescaler, 84);
assert_eq!(configs[0].tseg1, 16);
assert_eq!(configs[0].tseg2, 6);
assert_eq!(configs[0].sjw, 4);
let sample_cfg = configs
.iter()
.find(|c| c.prescaler == 100)
.expect("clock config not found");
assert_eq!(sample_cfg.tseg1, 14);
assert_eq!(sample_cfg.tseg2, 5);
}
}