async example works as well
This commit is contained in:
parent
d661c940fe
commit
dbc527f759
@ -1,6 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
// Import panic provider.
|
// Import panic provider.
|
||||||
use panic_probe as _;
|
use panic_probe as _;
|
||||||
// Import logger.
|
// Import logger.
|
||||||
@ -8,7 +9,7 @@ use defmt_rtt as _;
|
|||||||
|
|
||||||
use embassy_example::EXTCLK_FREQ;
|
use embassy_example::EXTCLK_FREQ;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use va416xx_hal::can::asynch::on_interrupt_can;
|
use va416xx_hal::can::asynch::{on_interrupt_can, CanTxAsync};
|
||||||
use va416xx_hal::can::{
|
use va416xx_hal::can::{
|
||||||
Can, CanFrame, CanFrameNormal, CanFrameRtr, CanId, CanRx, CanTx, ClockConfig,
|
Can, CanFrame, CanFrameNormal, CanFrameRtr, CanId, CanRx, CanTx, ClockConfig,
|
||||||
};
|
};
|
||||||
@ -19,6 +20,14 @@ use va416xx_hal::{can, prelude::*};
|
|||||||
|
|
||||||
const STANDARD_ID_0: can::StandardId = can::StandardId::new(0x42).unwrap();
|
const STANDARD_ID_0: can::StandardId = can::StandardId::new(0x42).unwrap();
|
||||||
const STANDARD_ID_1: can::StandardId = can::StandardId::new(0x5).unwrap();
|
const STANDARD_ID_1: can::StandardId = can::StandardId::new(0x5).unwrap();
|
||||||
|
const EXTENDED_ID_0: can::ExtendedId = can::ExtendedId::new(0x10).unwrap();
|
||||||
|
|
||||||
|
// Declare a bounded channel of 3 u32s.
|
||||||
|
static CAN_RX_CHANNEL: embassy_sync::channel::Channel<
|
||||||
|
CriticalSectionRawMutex,
|
||||||
|
(usize, CanFrame),
|
||||||
|
3,
|
||||||
|
> = embassy_sync::channel::Channel::<CriticalSectionRawMutex, (usize, CanFrame), 3>::new();
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) {
|
async fn main(_spawner: Spawner) {
|
||||||
@ -50,12 +59,6 @@ async fn main(_spawner: Spawner) {
|
|||||||
can.set_global_mask_for_exact_id_match_with_rtr_masked();
|
can.set_global_mask_for_exact_id_match_with_rtr_masked();
|
||||||
can.set_base_mask_for_all_match();
|
can.set_base_mask_for_all_match();
|
||||||
can.enable();
|
can.enable();
|
||||||
let err_counter = can.read_error_counters();
|
|
||||||
defmt::info!(
|
|
||||||
"error count tx {}, error count rx {}",
|
|
||||||
err_counter.transmit(),
|
|
||||||
err_counter.receive()
|
|
||||||
);
|
|
||||||
let mut channels = can.take_channels().unwrap();
|
let mut channels = can.take_channels().unwrap();
|
||||||
// Transmit channel.
|
// Transmit channel.
|
||||||
let mut tx = CanTx::new(channels.take(0).unwrap(), None);
|
let mut tx = CanTx::new(channels.take(0).unwrap(), None);
|
||||||
@ -65,8 +68,18 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut rx_base = CanRx::new(channels.take(14).unwrap());
|
let mut rx_base = CanRx::new(channels.take(14).unwrap());
|
||||||
rx_base.configure_for_reception();
|
rx_base.configure_for_reception();
|
||||||
|
|
||||||
|
defmt::info!("Running blocking examples");
|
||||||
|
|
||||||
send_and_receive_on_dedicated_channel(&mut can, &mut tx, &mut rx_dedicated);
|
send_and_receive_on_dedicated_channel(&mut can, &mut tx, &mut rx_dedicated);
|
||||||
send_and_receive_rtr_on_dedicated_channel(&mut can, &mut tx, &mut rx_dedicated);
|
send_and_receive_rtr_on_dedicated_channel(&mut can, &mut tx, &mut rx_dedicated);
|
||||||
|
send_extended_on_base_channel(&mut can, &mut tx, &mut rx_base);
|
||||||
|
|
||||||
|
defmt::info!("Running non-blocking (asycnhronous) examples");
|
||||||
|
|
||||||
|
non_blocking_example(&mut can, &mut rx_dedicated, &mut rx_base).await;
|
||||||
|
|
||||||
|
defmt::info!("Non-blocking (asycnhronous) examples done");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
cortex_m::asm::nop();
|
cortex_m::asm::nop();
|
||||||
}
|
}
|
||||||
@ -74,21 +87,22 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
fn send_and_receive_on_dedicated_channel(can: &mut Can, tx: &mut CanTx, rx_dedicated: &mut CanRx) {
|
fn send_and_receive_on_dedicated_channel(can: &mut Can, tx: &mut CanTx, rx_dedicated: &mut CanRx) {
|
||||||
let send_data = &[1, 2, 3, 4];
|
let send_data = &[1, 2, 3, 4];
|
||||||
|
let sent_frame =
|
||||||
|
CanFrame::Normal(CanFrameNormal::new(can::Id::Standard(STANDARD_ID_0), send_data).unwrap());
|
||||||
defmt::info!(
|
defmt::info!(
|
||||||
"sending CAN frame with ID {:#X} and data {}",
|
"sending CAN frame with ID {:#X} and data {}",
|
||||||
STANDARD_ID_0.as_raw(),
|
STANDARD_ID_0.as_raw(),
|
||||||
send_data
|
send_data
|
||||||
);
|
);
|
||||||
rx_dedicated.configure_for_reception_with_standard_id(STANDARD_ID_0, false);
|
rx_dedicated.configure_for_reception_with_standard_id(STANDARD_ID_0, false);
|
||||||
let send_frame =
|
tx.transmit_frame(sent_frame).unwrap();
|
||||||
CanFrame::Normal(CanFrameNormal::new(can::Id::Standard(STANDARD_ID_0), send_data).unwrap());
|
|
||||||
tx.transmit_frame(send_frame).unwrap();
|
|
||||||
// Await frame transmission completion.
|
// Await frame transmission completion.
|
||||||
nb::block!(tx.transfer_done()).unwrap();
|
nb::block!(tx.transfer_done()).unwrap();
|
||||||
check_and_handle_errors(can);
|
check_and_handle_errors(can);
|
||||||
let frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state");
|
let received_frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state");
|
||||||
check_and_handle_errors(can);
|
check_and_handle_errors(can);
|
||||||
if let CanFrame::Normal(can_frame_normal) = frame {
|
assert_eq!(received_frame, sent_frame);
|
||||||
|
if let CanFrame::Normal(can_frame_normal) = received_frame {
|
||||||
if let can::Id::Standard(standard_id) = can_frame_normal.id() {
|
if let can::Id::Standard(standard_id) = can_frame_normal.id() {
|
||||||
defmt::info!(
|
defmt::info!(
|
||||||
"received CAN frame with ID {:#X} and data {}",
|
"received CAN frame with ID {:#X} and data {}",
|
||||||
@ -113,11 +127,12 @@ fn send_and_receive_rtr_on_dedicated_channel(
|
|||||||
rx_dedicated.configure_for_reception_with_standard_id(STANDARD_ID_1, false);
|
rx_dedicated.configure_for_reception_with_standard_id(STANDARD_ID_1, false);
|
||||||
tx.transmit_frame(rtr_frame).unwrap();
|
tx.transmit_frame(rtr_frame).unwrap();
|
||||||
// Await frame transmission completion.
|
// Await frame transmission completion.
|
||||||
nb::block!(tx.remote_transfer_done()).unwrap();
|
nb::block!(tx.remote_transfer_done_with_tx_reconfig()).unwrap();
|
||||||
check_and_handle_errors(can);
|
check_and_handle_errors(can);
|
||||||
let frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state");
|
let received_frame = nb::block!(rx_dedicated.receive(true)).expect("invalid CAN rx state");
|
||||||
check_and_handle_errors(can);
|
check_and_handle_errors(can);
|
||||||
if let CanFrame::Rtr(can_frame_rtr) = frame {
|
assert_eq!(received_frame, rtr_frame);
|
||||||
|
if let CanFrame::Rtr(can_frame_rtr) = received_frame {
|
||||||
if let can::Id::Standard(standard_id) = can_frame_rtr.id() {
|
if let can::Id::Standard(standard_id) = can_frame_rtr.id() {
|
||||||
defmt::info!("received CAN RTR frame with ID {:#X}", standard_id.as_raw(),);
|
defmt::info!("received CAN RTR frame with ID {:#X}", standard_id.as_raw(),);
|
||||||
} else {
|
} else {
|
||||||
@ -141,8 +156,84 @@ fn check_and_handle_errors(can: &mut Can) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_extended_on_base_channel(can: &mut Can, tx: &mut CanTx, rx: &mut CanRx) {
|
||||||
|
let send_data = &[4, 3, 2, 1];
|
||||||
|
let sent_frame =
|
||||||
|
CanFrame::Normal(CanFrameNormal::new(can::Id::Extended(EXTENDED_ID_0), send_data).unwrap());
|
||||||
|
tx.transmit_frame(sent_frame).unwrap();
|
||||||
|
// Await frame transmission completion.
|
||||||
|
nb::block!(tx.transfer_done()).unwrap();
|
||||||
|
check_and_handle_errors(can);
|
||||||
|
let received_frame = nb::block!(rx.receive(true)).expect("invalid CAN rx state");
|
||||||
|
check_and_handle_errors(can);
|
||||||
|
assert_eq!(sent_frame, received_frame);
|
||||||
|
if let CanFrame::Normal(can_frame_normal) = received_frame {
|
||||||
|
if let can::Id::Extended(extended_id) = can_frame_normal.id() {
|
||||||
|
defmt::info!(
|
||||||
|
"received CAN frame with ID {:#X} and data {}",
|
||||||
|
extended_id.as_raw(),
|
||||||
|
can_frame_normal.data()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("unexpected CAN extended frame ID");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defmt::error!("received unexpected CAN data frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn non_blocking_example(can: &mut Can, rx_dedicated: &mut CanRx, rx_base: &mut CanRx) {
|
||||||
|
let mut tx_async = CanTxAsync::new(can);
|
||||||
|
// Enable interrupts for RX channels.
|
||||||
|
rx_dedicated.enable_interrupt(true);
|
||||||
|
rx_base.enable_interrupt(true);
|
||||||
|
|
||||||
|
// For asynchronous mode, all TX channels needs to be configured explicitely. Configuring more
|
||||||
|
// channels allows multiple active transfers when using the async API.
|
||||||
|
tx_async.configure_channel(0).unwrap();
|
||||||
|
let send_data = &[1, 2, 3, 4];
|
||||||
|
let send_frame =
|
||||||
|
CanFrame::Normal(CanFrameNormal::new(can::Id::Standard(STANDARD_ID_0), send_data).unwrap());
|
||||||
|
let fut = tx_async.start_transmit(send_frame).unwrap();
|
||||||
|
fut.await;
|
||||||
|
let (ch_idx, frame) = CAN_RX_CHANNEL.receive().await;
|
||||||
|
assert_eq!(send_frame, frame);
|
||||||
|
// Received on base channel.
|
||||||
|
assert_eq!(ch_idx, 14);
|
||||||
|
if let CanFrame::Normal(can_frame_normal) = frame {
|
||||||
|
if let can::Id::Standard(standard_id) = can_frame_normal.id() {
|
||||||
|
defmt::info!(
|
||||||
|
"received CAN frame with ID {:#X} and data {}",
|
||||||
|
standard_id.as_raw(),
|
||||||
|
can_frame_normal.data()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("unexpected CAN extended frame ID");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defmt::error!("received unexpected CAN remote frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn CAN0() {
|
fn CAN0() {
|
||||||
on_interrupt_can(CanId::Can0, false).unwrap();
|
match on_interrupt_can(CanId::Can0, false).unwrap() {
|
||||||
|
can::asynch::InterruptResult::NoInterrupt => {
|
||||||
|
defmt::warn!("unexpected interrupt on CAN0");
|
||||||
|
}
|
||||||
|
can::asynch::InterruptResult::ReceivedFrame {
|
||||||
|
channel_index,
|
||||||
|
frame,
|
||||||
|
} => {
|
||||||
|
CAN_RX_CHANNEL.try_send((channel_index, frame)).unwrap();
|
||||||
|
}
|
||||||
|
can::asynch::InterruptResult::TransmissionEvent { channel_index, id } => {
|
||||||
|
defmt::info!(
|
||||||
|
"transmission event on channel {} with event ID {}",
|
||||||
|
channel_index,
|
||||||
|
id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use crate::can::regs::BufferState;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
regs::{DiagnosticRegister, InterruptClear, MmioCan, StatusPending},
|
regs::{DiagnosticRegister, InterruptClear, MmioCan, StatusPending},
|
||||||
CanChannelLowLevel, CanFrame, CanId,
|
CanChannelLowLevel, CanFrame, CanId, InvalidBufferIndexError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -24,7 +24,8 @@ static TX_STATES: [AtomicU8; 15] = [const { AtomicU8::new(0) }; 15];
|
|||||||
static TX_WAKERS: [embassy_sync::waitqueue::AtomicWaker; 15] =
|
static TX_WAKERS: [embassy_sync::waitqueue::AtomicWaker; 15] =
|
||||||
[const { embassy_sync::waitqueue::AtomicWaker::new() }; 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 {
|
pub enum TxEventId {
|
||||||
/// Buffer state went from [BufferState::TxOnce] to [BufferState::TxNotActive].
|
/// Buffer state went from [BufferState::TxOnce] to [BufferState::TxNotActive].
|
||||||
TxDataFrame,
|
TxDataFrame,
|
||||||
@ -249,20 +250,46 @@ impl CanTxFuture {
|
|||||||
let free_channel_id = channel_is_free.iter().position(|&x| x).unwrap();
|
let free_channel_id = channel_is_free.iter().position(|&x| x).unwrap();
|
||||||
let mut channel =
|
let mut channel =
|
||||||
unsafe { CanChannelLowLevel::steal_unchecked(CanId::Can0, free_channel_id) };
|
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_interrupt(true);
|
||||||
channel.enable_error_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))
|
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 {
|
impl CanTxAsync {
|
||||||
pub fn new(can: super::Can) -> Self {
|
pub fn new(can: &mut super::Can) -> Self {
|
||||||
CanTxAsync(can)
|
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.
|
/// Start a transmission and returns the future which can be polled to completion.
|
||||||
|
@ -4,7 +4,7 @@ pub use embedded_can::{ExtendedId, Id, StandardId};
|
|||||||
#[error("invalid data size error {0}")]
|
#[error("invalid data size error {0}")]
|
||||||
pub struct InvalidDataSizeError(usize);
|
pub struct InvalidDataSizeError(usize);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct CanFrameNormal {
|
pub struct CanFrameNormal {
|
||||||
id: embedded_can::Id,
|
id: embedded_can::Id,
|
||||||
size: usize,
|
size: usize,
|
||||||
@ -42,7 +42,7 @@ impl CanFrameNormal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct CanFrameRtr {
|
pub struct CanFrameRtr {
|
||||||
id: embedded_can::Id,
|
id: embedded_can::Id,
|
||||||
dlc: usize,
|
dlc: usize,
|
||||||
@ -62,7 +62,7 @@ impl CanFrameRtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum CanFrame {
|
pub enum CanFrame {
|
||||||
Normal(CanFrameNormal),
|
Normal(CanFrameNormal),
|
||||||
Rtr(CanFrameRtr),
|
Rtr(CanFrameRtr),
|
||||||
|
@ -2,7 +2,9 @@ use arbitrary_int::{u11, u15, u3, u4, Number};
|
|||||||
use embedded_can::Frame;
|
use embedded_can::Frame;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
regs::{BaseId, BufStatusAndControl, BufferState, ExtendedId, MmioCanMsgBuf, TwoBytesData},
|
regs::{
|
||||||
|
self, BaseId, BufStatusAndControl, BufferState, ExtendedId, MmioCanMsgBuf, TwoBytesData,
|
||||||
|
},
|
||||||
CanFrame, CanFrameNormal, CanFrameRtr, CanId, InvalidBufferIndexError,
|
CanFrame, CanFrameNormal, CanFrameRtr, CanId, InvalidBufferIndexError,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -188,6 +190,14 @@ impl CanChannelLowLevel {
|
|||||||
self.write_state(BufferState::TxOnce);
|
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) {
|
pub fn enable_error_interrupt(&mut self, enable_translation: bool) {
|
||||||
let mut regs = unsafe { self.id.steal_regs() };
|
let mut regs = unsafe { self.id.steal_regs() };
|
||||||
if enable_translation {
|
if enable_translation {
|
||||||
|
@ -7,6 +7,7 @@ use arbitrary_int::{u11, u15, u2, u3, u4, u7, Number};
|
|||||||
use embedded_can::Frame;
|
use embedded_can::Frame;
|
||||||
use ll::CanChannelLowLevel;
|
use ll::CanChannelLowLevel;
|
||||||
use regs::{BaseId, BufferState, Control, MmioCan, TimingConfig};
|
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 crate::{clock::Clocks, enable_peripheral_clock, time::Hertz, PeripheralSelect};
|
||||||
use libm::roundf;
|
use libm::roundf;
|
||||||
@ -44,12 +45,21 @@ impl CanId {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// See safety of the [regs::Can::new_mmio_fixed_0].
|
/// See safety of the [regs::Can::new_mmio_fixed_0].
|
||||||
|
#[inline]
|
||||||
pub const unsafe fn steal_regs(&self) -> regs::MmioCan<'static> {
|
pub const unsafe fn steal_regs(&self) -> regs::MmioCan<'static> {
|
||||||
match self {
|
match self {
|
||||||
CanId::Can0 => unsafe { regs::Can::new_mmio_fixed_0() },
|
CanId::Can0 => unsafe { regs::Can::new_mmio_fixed_0() },
|
||||||
CanId::Can1 => unsafe { regs::Can::new_mmio_fixed_1() },
|
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.
|
/// 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)
|
Self::new(prescaler as u8, tseg1, tseg2, sjw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn sjw_reg_value(&self) -> u2 {
|
pub fn sjw_reg_value(&self) -> u2 {
|
||||||
u2::new(self.sjw.value() - 1)
|
u2::new(self.sjw.value() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn tseg1_reg_value(&self) -> u4 {
|
pub fn tseg1_reg_value(&self) -> u4 {
|
||||||
u4::new(self.tseg1.value() - 1)
|
u4::new(self.tseg1.value() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn tseg2_reg_value(&self) -> u3 {
|
pub fn tseg2_reg_value(&self) -> u3 {
|
||||||
u3::new(self.tseg2.value() - 1)
|
u3::new(self.tseg2.value() - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn prescaler_reg_value(&self) -> u7 {
|
pub fn prescaler_reg_value(&self) -> u7 {
|
||||||
u7::new(self.prescaler.value() - 2)
|
u7::new(self.prescaler.value() - 2)
|
||||||
}
|
}
|
||||||
@ -231,6 +245,7 @@ pub fn calculate_all_viable_clock_configs(
|
|||||||
Ok(configs)
|
Ok(configs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub const fn calculate_nominal_bit_time(
|
pub const fn calculate_nominal_bit_time(
|
||||||
apb1_clock: Hertz,
|
apb1_clock: Hertz,
|
||||||
target_bitrate: Hertz,
|
target_bitrate: Hertz,
|
||||||
@ -239,34 +254,41 @@ pub const fn calculate_nominal_bit_time(
|
|||||||
apb1_clock.raw() / (target_bitrate.raw() * prescaler as u32)
|
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 {
|
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
|
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 {
|
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
|
(actual_bitrate - target_bitrate.raw() as f32).abs() / target_bitrate.raw() as f32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CanMarker {
|
pub trait CanMarker {
|
||||||
const ID: CanId;
|
const ID: CanId;
|
||||||
|
const IRQ: va416xx::Interrupt;
|
||||||
const PERIPH_SEL: PeripheralSelect;
|
const PERIPH_SEL: PeripheralSelect;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanMarker for va416xx::Can0 {
|
impl CanMarker for va416xx::Can0 {
|
||||||
const ID: CanId = CanId::Can0;
|
const ID: CanId = CanId::Can0;
|
||||||
|
const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN0;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanMarker for va416xx::Can1 {
|
impl CanMarker for va416xx::Can1 {
|
||||||
const ID: CanId = CanId::Can1;
|
const ID: CanId = CanId::Can1;
|
||||||
|
const IRQ: va416xx::Interrupt = va416xx::Interrupt::CAN1;
|
||||||
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("invalid buffer index {0}")]
|
#[error("invalid buffer index {0}")]
|
||||||
pub struct InvalidBufferIndexError(usize);
|
pub struct InvalidBufferIndexError(usize);
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[error("sjw must be less than or equal to the smaller tseg value")]
|
#[error("sjw must be less than or equal to the smaller tseg value")]
|
||||||
pub struct InvalidSjwError(u8);
|
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
|
/// 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
|
/// exact match with the ID in the receive message buffers. This is the default reset
|
||||||
/// configuration for the global mask as well.
|
/// configuration for the global mask as well.
|
||||||
|
#[inline]
|
||||||
pub fn set_base_mask_for_exact_id_match(&mut self) {
|
pub fn set_base_mask_for_exact_id_match(&mut self) {
|
||||||
self.regs
|
self.regs
|
||||||
.write_bmskx(regs::ExtendedId::new_with_raw_value(0));
|
.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
|
/// 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.
|
/// buffers are accepted by the base buffer 14.
|
||||||
|
#[inline]
|
||||||
pub fn set_base_mask_for_all_match(&mut self) {
|
pub fn set_base_mask_for_all_match(&mut self) {
|
||||||
self.regs
|
self.regs
|
||||||
.write_bmskx(regs::ExtendedId::new_with_raw_value(0xffff));
|
.write_bmskx(regs::ExtendedId::new_with_raw_value(0xffff));
|
||||||
@ -388,6 +412,22 @@ impl Can {
|
|||||||
&mut self.regs
|
&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]
|
#[inline]
|
||||||
pub fn read_error_counters(&self) -> regs::ErrorCounter {
|
pub fn read_error_counters(&self) -> regs::ErrorCounter {
|
||||||
self.regs.read_error_counter()
|
self.regs.read_error_counter()
|
||||||
@ -486,6 +526,7 @@ pub struct CanTx {
|
|||||||
|
|
||||||
impl CanTx {
|
impl CanTx {
|
||||||
pub fn new(mut ll: CanChannelLowLevel, tx_priority: Option<u4>) -> Self {
|
pub fn new(mut ll: CanChannelLowLevel, tx_priority: Option<u4>) -> Self {
|
||||||
|
ll.reset();
|
||||||
ll.configure_for_transmission(tx_priority);
|
ll.configure_for_transmission(tx_priority);
|
||||||
Self {
|
Self {
|
||||||
ll,
|
ll,
|
||||||
@ -493,6 +534,11 @@ impl CanTx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_rx_channel(self) -> CanRx {
|
||||||
|
CanRx::new(self.ll)
|
||||||
|
}
|
||||||
|
|
||||||
/// Start transmitting a frame.
|
/// Start transmitting a frame.
|
||||||
///
|
///
|
||||||
/// The frame transmission can be polled/awaited to completion using the [Self::transfer_done]
|
/// 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.
|
/// 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.
|
/// Returns a [state error][InvalidTxStateError] if no transmission is active.
|
||||||
pub fn remote_transfer_done(&mut self) -> nb::Result<CanRx, InvalidTxStateError> {
|
pub fn remote_transfer_done(&mut self) -> nb::Result<CanRx, InvalidTxStateError> {
|
||||||
if self.mode != TxState::TransmittingRemoteFrame {
|
if self.mode != TxState::TransmittingRemoteFrame {
|
||||||
@ -563,6 +617,29 @@ impl CanTx {
|
|||||||
Err(nb::Error::WouldBlock)
|
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) {
|
pub fn reset(&mut self) {
|
||||||
self.ll.reset();
|
self.ll.reset();
|
||||||
self.mode = TxState::Idle;
|
self.mode = TxState::Idle;
|
||||||
@ -575,12 +652,24 @@ pub struct CanRx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CanRx {
|
impl CanRx {
|
||||||
pub fn new(ll: CanChannelLowLevel) -> Self {
|
pub fn new(mut ll: CanChannelLowLevel) -> Self {
|
||||||
|
ll.reset();
|
||||||
Self {
|
Self {
|
||||||
ll,
|
ll,
|
||||||
mode: RxState::Idle,
|
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(
|
pub fn configure_for_reception_with_standard_id(
|
||||||
&mut self,
|
&mut self,
|
||||||
standard_id: embedded_can::StandardId,
|
standard_id: embedded_can::StandardId,
|
||||||
|
@ -202,6 +202,7 @@ pub struct InterruptPending {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(usize)]
|
#[repr(usize)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum CanInterruptId {
|
pub enum CanInterruptId {
|
||||||
None = 0b00000,
|
None = 0b00000,
|
||||||
Error = 0b10000,
|
Error = 0b10000,
|
||||||
@ -226,14 +227,13 @@ impl StatusPending {
|
|||||||
return Some(CanInterruptId::None);
|
return Some(CanInterruptId::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
//let raw_value = ((self.irq() as u8) << 4) | self.ist().as_u8();
|
|
||||||
if self.irq() && self.ist().value() == 0 {
|
if self.irq() && self.ist().value() == 0 {
|
||||||
return Some(CanInterruptId::Error);
|
return Some(CanInterruptId::Error);
|
||||||
}
|
}
|
||||||
if !self.irq() {
|
if !self.irq() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(CanInterruptId::Buffer(self.ist().as_usize()))
|
Some(CanInterruptId::Buffer(self.ist().as_usize() - 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user