async example works as well

This commit is contained in:
2025-05-13 16:33:50 +02:00
parent d661c940fe
commit dbc527f759
6 changed files with 248 additions and 31 deletions

View File

@ -7,7 +7,7 @@ use crate::can::regs::BufferState;
use super::{
regs::{DiagnosticRegister, InterruptClear, MmioCan, StatusPending},
CanChannelLowLevel, CanFrame, CanId,
CanChannelLowLevel, CanFrame, CanId, InvalidBufferIndexError,
};
#[derive(Debug)]
@ -24,7 +24,8 @@ static TX_STATES: [AtomicU8; 15] = [const { AtomicU8::new(0) }; 15];
static TX_WAKERS: [embassy_sync::waitqueue::AtomicWaker; 15] =
[const { embassy_sync::waitqueue::AtomicWaker::new() }; 15];
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TxEventId {
/// Buffer state went from [BufferState::TxOnce] to [BufferState::TxNotActive].
TxDataFrame,
@ -249,20 +250,46 @@ impl CanTxFuture {
let free_channel_id = channel_is_free.iter().position(|&x| x).unwrap();
let mut channel =
unsafe { CanChannelLowLevel::steal_unchecked(CanId::Can0, free_channel_id) };
TX_STATES[free_channel_id].store(TxChannelState::TxDataFrame as u8, Ordering::Relaxed);
channel.write_state(BufferState::TxNotActive);
channel.transmit_frame_unchecked(frame);
channel.clear_interrupt();
channel.enable_interrupt(true);
channel.enable_error_interrupt(true);
channel.transmit_frame_unchecked(frame);
TX_STATES[free_channel_id].store(TxChannelState::TxDataFrame as u8, Ordering::Relaxed);
Ok(CanTxFuture(free_channel_id))
}
}
pub struct CanTxAsync(pub super::Can);
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ChannelConfigError {
#[error("channel is busy")]
Busy,
#[error("invalid offset: {0}")]
Offset(#[from] InvalidBufferIndexError),
}
pub struct CanTxAsync;
impl CanTxAsync {
pub fn new(can: super::Can) -> Self {
CanTxAsync(can)
pub fn new(can: &mut super::Can) -> Self {
can.clear_interrupts();
can.enable_nvic_interrupt();
CanTxAsync
}
pub fn configure_channel(&mut self, channel_idx: usize) -> Result<(), ChannelConfigError> {
if channel_idx >= TX_STATES.len() {
return Err(ChannelConfigError::Offset(InvalidBufferIndexError(
channel_idx,
)));
}
let state = TX_STATES[channel_idx].load(Ordering::Relaxed);
if state != TxChannelState::Idle as u8 && state != TxChannelState::Unconfigured as u8 {
return Err(ChannelConfigError::Busy);
}
TX_STATES[channel_idx].store(TxChannelState::Idle as u8, Ordering::Relaxed);
Ok(())
}
/// Start a transmission and returns the future which can be polled to completion.

View File

@ -4,7 +4,7 @@ pub use embedded_can::{ExtendedId, Id, StandardId};
#[error("invalid data size error {0}")]
pub struct InvalidDataSizeError(usize);
#[derive(Debug)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct CanFrameNormal {
id: embedded_can::Id,
size: usize,
@ -42,7 +42,7 @@ impl CanFrameNormal {
}
}
#[derive(Debug)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct CanFrameRtr {
id: embedded_can::Id,
dlc: usize,
@ -62,7 +62,7 @@ impl CanFrameRtr {
}
}
#[derive(Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CanFrame {
Normal(CanFrameNormal),
Rtr(CanFrameRtr),

View File

@ -2,7 +2,9 @@ use arbitrary_int::{u11, u15, u3, u4, Number};
use embedded_can::Frame;
use super::{
regs::{BaseId, BufStatusAndControl, BufferState, ExtendedId, MmioCanMsgBuf, TwoBytesData},
regs::{
self, BaseId, BufStatusAndControl, BufferState, ExtendedId, MmioCanMsgBuf, TwoBytesData,
},
CanFrame, CanFrameNormal, CanFrameRtr, CanId, InvalidBufferIndexError,
};
@ -188,6 +190,14 @@ impl CanChannelLowLevel {
self.write_state(BufferState::TxOnce);
}
#[inline]
pub fn clear_interrupt(&mut self) {
let mut regs = unsafe { self.id.steal_regs() };
let mut clear = regs::InterruptClear::new_with_raw_value(0);
clear.set_buffer(self.idx, true);
regs.write_iclr(clear);
}
pub fn enable_error_interrupt(&mut self, enable_translation: bool) {
let mut regs = unsafe { self.id.steal_regs() };
if enable_translation {

View File

@ -7,6 +7,7 @@ use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number};
use embedded_can::Frame;
use ll::CanChannelLowLevel;
use regs::{BaseId, BufferState, Control, MmioCan, TimingConfig};
use vorago_shared_periphs::enable_nvic_interrupt;
use crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect};
use libm::roundf;
@ -44,12 +45,21 @@ impl CanId {
/// # Safety
///
/// See safety of the [regs::Can::new_mmio_fixed_0].
#[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,
}
}
}
/// Sample point between 0 and 1.0 for the given time segments.
@ -142,18 +152,22 @@ impl ClockConfig {
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)
}
@ -231,6 +245,7 @@ pub fn calculate_all_viable_clock_configs(
Ok(configs)
}
#[inline]
pub const fn calculate_nominal_bit_time(
apb1_clock: Hertz,
target_bitrate: Hertz,
@ -239,34 +254,41 @@ pub const fn calculate_nominal_bit_time(
apb1_clock.raw() / (target_bitrate.raw() * prescaler as u32)
}
#[inline]
pub const fn calculate_actual_bitrate(apb1_clock: Hertz, prescaler: u8, nom_bit_time: u32) -> f32 {
apb1_clock.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.raw() as f32).abs() / target_bitrate.raw() as f32
}
pub trait CanMarker {
const ID: CanId;
const IRQ: va416xx::Interrupt;
const PERIPH_SEL: PeripheralSelect;
}
impl CanMarker for va416xx::Can0 {
const ID: CanId = CanId::Can0;
const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN0;
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
}
impl CanMarker 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);
@ -369,6 +391,7 @@ impl Can {
/// 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.
#[inline]
pub fn set_base_mask_for_exact_id_match(&mut self) {
self.regs
.write_bmskx(regs::ExtendedId::new_with_raw_value(0));
@ -377,6 +400,7 @@ impl Can {
/// 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.
#[inline]
pub fn set_base_mask_for_all_match(&mut self) {
self.regs
.write_bmskx(regs::ExtendedId::new_with_raw_value(0xffff));
@ -388,6 +412,22 @@ impl Can {
&mut self.regs
}
#[inline]
pub fn clear_interrupts(&mut self) {
self.regs
.write_iclr(regs::InterruptClear::new_with_raw_value(0xFFFF_FFFF));
}
/// This function only enable the CAN interrupt vector in the NVIC.
///
/// The interrupts for the individual channels or errors still need to be enabled
/// separately.
#[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()
@ -486,6 +526,7 @@ pub struct CanTx {
impl CanTx {
pub fn new(mut ll: CanChannelLowLevel, tx_priority: Option<u4>) -> Self {
ll.reset();
ll.configure_for_transmission(tx_priority);
Self {
ll,
@ -493,6 +534,11 @@ impl CanTx {
}
}
#[inline]
pub fn into_rx_channel(self) -> CanRx {
CanRx::new(self.ll)
}
/// Start transmitting a frame.
///
/// The frame transmission can be polled/awaited to completion using the [Self::transfer_done]
@ -543,6 +589,14 @@ impl CanTx {
/// Poll whether an active remote frame transmission is done.
///
/// On success, returns the channel re-configured to a [CanRx] channel. This is because the
/// default behaviour of the hardware will be to re-configure the channel state to
/// [BufferState::RxReady] once the remote frame has been transmitted so that the response
/// frame can be awaited.
///
/// If the channel should instead be re-configured for transmission again,
/// [Self::remote_transfer_done_with_tx_reconfig] can be used.
///
/// Returns a [state error][InvalidTxStateError] if no transmission is active.
pub fn remote_transfer_done(&mut self) -> nb::Result<CanRx, InvalidTxStateError> {
if self.mode != TxState::TransmittingRemoteFrame {
@ -563,6 +617,29 @@ impl CanTx {
Err(nb::Error::WouldBlock)
}
/// Poll whether an active remote frame transmission is done.
///
/// This function will re-configure the buffer back for transmission once the
/// transmission has completed.
///
/// Returns a [state error][InvalidTxStateError] if no transmission is active.
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;
@ -575,12 +652,24 @@ pub struct CanRx {
}
impl CanRx {
pub fn new(ll: CanChannelLowLevel) -> Self {
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,

View File

@ -202,6 +202,7 @@ pub struct InterruptPending {
#[derive(Debug)]
#[repr(usize)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CanInterruptId {
None = 0b00000,
Error = 0b10000,
@ -226,14 +227,13 @@ impl StatusPending {
return Some(CanInterruptId::None);
}
//let raw_value = ((self.irq() as u8) << 4) | self.ist().as_u8();
if self.irq() && self.ist().value() == 0 {
return Some(CanInterruptId::Error);
}
if !self.irq() {
return None;
}
Some(CanInterruptId::Buffer(self.ist().as_usize()))
Some(CanInterruptId::Buffer(self.ist().as_usize() - 1))
}
}