Merge branch 'main' into stm32l0-reset-rtc
This commit is contained in:
commit
a6a5d9913c
51 changed files with 6654 additions and 1990 deletions
ci.sh
embassy-hal-internal/src
embassy-net/src
embassy-nrf/src
embassy-rp/src
embassy-stm32
Cargo.toml
src
examples
rp/src/bin
stm32g4
stm32h5/src/bin
stm32h7/src/bin
stm32u5/src/bin
tests
nrf52840/src/bin
rp
stm32
3
ci.sh
3
ci.sh
|
@ -239,9 +239,6 @@ cargo batch \
|
|||
$BUILD_EXTRA
|
||||
|
||||
|
||||
# failed, a wire must've come loose, will check asap.
|
||||
rm out/tests/rpi-pico/i2c
|
||||
|
||||
rm out/tests/stm32wb55rg/wpan_mac
|
||||
rm out/tests/stm32wb55rg/wpan_ble
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Atomic reusable ringbuffer.
|
||||
use core::slice;
|
||||
use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
|
||||
use core::{ptr, slice};
|
||||
|
||||
/// Atomic reusable ringbuffer
|
||||
///
|
||||
|
@ -73,6 +73,7 @@ impl RingBuffer {
|
|||
pub unsafe fn deinit(&self) {
|
||||
// Ordering: it's OK to use `Relaxed` because this is not called
|
||||
// concurrently with other methods.
|
||||
self.buf.store(ptr::null_mut(), Ordering::Relaxed);
|
||||
self.len.store(0, Ordering::Relaxed);
|
||||
self.start.store(0, Ordering::Relaxed);
|
||||
self.end.store(0, Ordering::Relaxed);
|
||||
|
@ -82,20 +83,46 @@ impl RingBuffer {
|
|||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only one reader can exist at a time.
|
||||
/// - Only one reader can exist at a time.
|
||||
/// - Ringbuffer must be initialized.
|
||||
pub unsafe fn reader(&self) -> Reader<'_> {
|
||||
Reader(self)
|
||||
}
|
||||
|
||||
/// Try creating a reader, fails if not initialized.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only one reader can exist at a time.
|
||||
pub unsafe fn try_reader(&self) -> Option<Reader<'_>> {
|
||||
if self.buf.load(Ordering::Relaxed).is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(Reader(self))
|
||||
}
|
||||
|
||||
/// Create a writer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only one writer can exist at a time.
|
||||
/// - Only one writer can exist at a time.
|
||||
/// - Ringbuffer must be initialized.
|
||||
pub unsafe fn writer(&self) -> Writer<'_> {
|
||||
Writer(self)
|
||||
}
|
||||
|
||||
/// Try creating a writer, fails if not initialized.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only one writer can exist at a time.
|
||||
pub unsafe fn try_writer(&self) -> Option<Writer<'_>> {
|
||||
if self.buf.load(Ordering::Relaxed).is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(Writer(self))
|
||||
}
|
||||
|
||||
/// Return length of buffer.
|
||||
pub fn len(&self) -> usize {
|
||||
self.len.load(Ordering::Relaxed)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use core::marker::PhantomData;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ops::Deref;
|
||||
|
||||
/// An exclusive reference to a peripheral.
|
||||
///
|
||||
|
@ -86,13 +86,6 @@ impl<'a, T> Deref for PeripheralRef<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for PeripheralRef<'a, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for any type that can be used as a peripheral of type `P`.
|
||||
///
|
||||
/// This is used in driver constructors, to allow passing either owned peripherals (e.g. `TWISPI0`),
|
||||
|
@ -162,7 +155,7 @@ pub trait Peripheral: Sized {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'b, T: DerefMut> Peripheral for T
|
||||
impl<'b, T: Deref> Peripheral for T
|
||||
where
|
||||
T::Target: Peripheral,
|
||||
{
|
||||
|
|
|
@ -491,10 +491,16 @@ impl<'d> TcpIo<'d> {
|
|||
async fn flush(&mut self) -> Result<(), Error> {
|
||||
poll_fn(move |cx| {
|
||||
self.with_mut(|s, _| {
|
||||
let waiting_close = s.state() == tcp::State::Closed && s.remote_endpoint().is_some();
|
||||
let data_pending = s.send_queue() > 0;
|
||||
let fin_pending = matches!(
|
||||
s.state(),
|
||||
tcp::State::FinWait1 | tcp::State::Closing | tcp::State::LastAck
|
||||
);
|
||||
let rst_pending = s.state() == tcp::State::Closed && s.remote_endpoint().is_some();
|
||||
|
||||
// If there are outstanding send operations, register for wake up and wait
|
||||
// smoltcp issues wake-ups when octets are dequeued from the send buffer
|
||||
if s.send_queue() > 0 || waiting_close {
|
||||
if data_pending || fin_pending || rst_pending {
|
||||
s.register_send_waker(cx.waker());
|
||||
Poll::Pending
|
||||
// No outstanding sends, socket is flushed
|
||||
|
|
|
@ -17,29 +17,26 @@ use core::task::Poll;
|
|||
|
||||
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
// Re-export SVD variants to allow user to directly set values
|
||||
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
|
||||
|
||||
use crate::gpio::sealed::Pin;
|
||||
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
|
||||
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::ppi::{
|
||||
self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task,
|
||||
};
|
||||
use crate::timer::{Instance as TimerInstance, Timer};
|
||||
use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance};
|
||||
use crate::uarte::{configure, drop_tx_rx, Config, Instance as UarteInstance};
|
||||
use crate::{interrupt, pac, Peripheral};
|
||||
|
||||
mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub struct State {
|
||||
pub tx_waker: AtomicWaker,
|
||||
pub tx_buf: RingBuffer,
|
||||
pub tx_count: AtomicUsize,
|
||||
|
||||
pub rx_waker: AtomicWaker,
|
||||
pub rx_buf: RingBuffer,
|
||||
pub rx_started: AtomicBool,
|
||||
pub rx_started_count: AtomicU8,
|
||||
|
@ -61,11 +58,9 @@ pub(crate) use sealed::State;
|
|||
impl State {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
tx_waker: AtomicWaker::new(),
|
||||
tx_buf: RingBuffer::new(),
|
||||
tx_count: AtomicUsize::new(0),
|
||||
|
||||
rx_waker: AtomicWaker::new(),
|
||||
rx_buf: RingBuffer::new(),
|
||||
rx_started: AtomicBool::new(false),
|
||||
rx_started_count: AtomicU8::new(0),
|
||||
|
@ -84,128 +79,131 @@ impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for Interrupt
|
|||
unsafe fn on_interrupt() {
|
||||
//trace!("irq: start");
|
||||
let r = U::regs();
|
||||
let ss = U::state();
|
||||
let s = U::buffered_state();
|
||||
|
||||
let buf_len = s.rx_buf.len();
|
||||
let half_len = buf_len / 2;
|
||||
let mut tx = unsafe { s.tx_buf.reader() };
|
||||
let mut rx = unsafe { s.rx_buf.writer() };
|
||||
if let Some(mut rx) = unsafe { s.rx_buf.try_writer() } {
|
||||
let buf_len = s.rx_buf.len();
|
||||
let half_len = buf_len / 2;
|
||||
|
||||
if r.events_error.read().bits() != 0 {
|
||||
r.events_error.reset();
|
||||
let errs = r.errorsrc.read();
|
||||
r.errorsrc.write(|w| unsafe { w.bits(errs.bits()) });
|
||||
if r.events_error.read().bits() != 0 {
|
||||
r.events_error.reset();
|
||||
let errs = r.errorsrc.read();
|
||||
r.errorsrc.write(|w| unsafe { w.bits(errs.bits()) });
|
||||
|
||||
if errs.overrun().bit() {
|
||||
panic!("BufferedUarte overrun");
|
||||
if errs.overrun().bit() {
|
||||
panic!("BufferedUarte overrun");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Received some bytes, wake task.
|
||||
if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().bits() != 0 {
|
||||
r.intenclr.write(|w| w.rxdrdy().clear());
|
||||
r.events_rxdrdy.reset();
|
||||
s.rx_waker.wake();
|
||||
}
|
||||
// Received some bytes, wake task.
|
||||
if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().bits() != 0 {
|
||||
r.intenclr.write(|w| w.rxdrdy().clear());
|
||||
r.events_rxdrdy.reset();
|
||||
ss.rx_waker.wake();
|
||||
}
|
||||
|
||||
if r.events_endrx.read().bits() != 0 {
|
||||
//trace!(" irq_rx: endrx");
|
||||
r.events_endrx.reset();
|
||||
if r.events_endrx.read().bits() != 0 {
|
||||
//trace!(" irq_rx: endrx");
|
||||
r.events_endrx.reset();
|
||||
|
||||
let val = s.rx_ended_count.load(Ordering::Relaxed);
|
||||
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
|
||||
}
|
||||
let val = s.rx_ended_count.load(Ordering::Relaxed);
|
||||
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
if r.events_rxstarted.read().bits() != 0 || !s.rx_started.load(Ordering::Relaxed) {
|
||||
//trace!(" irq_rx: rxstarted");
|
||||
let (ptr, len) = rx.push_buf();
|
||||
if len >= half_len {
|
||||
r.events_rxstarted.reset();
|
||||
if r.events_rxstarted.read().bits() != 0 || !s.rx_started.load(Ordering::Relaxed) {
|
||||
//trace!(" irq_rx: rxstarted");
|
||||
let (ptr, len) = rx.push_buf();
|
||||
if len >= half_len {
|
||||
r.events_rxstarted.reset();
|
||||
|
||||
//trace!(" irq_rx: starting second {:?}", half_len);
|
||||
//trace!(" irq_rx: starting second {:?}", half_len);
|
||||
|
||||
// Set up the DMA read
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
|
||||
// Set up the DMA read
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
|
||||
|
||||
let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
|
||||
let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
|
||||
|
||||
// Enable endrx -> startrx PPI channel.
|
||||
// From this point on, if endrx happens, startrx is automatically fired.
|
||||
ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
|
||||
// Enable endrx -> startrx PPI channel.
|
||||
// From this point on, if endrx happens, startrx is automatically fired.
|
||||
ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
|
||||
|
||||
// It is possible that endrx happened BEFORE enabling the PPI. In this case
|
||||
// the PPI channel doesn't trigger, and we'd hang. We have to detect this
|
||||
// and manually start.
|
||||
// It is possible that endrx happened BEFORE enabling the PPI. In this case
|
||||
// the PPI channel doesn't trigger, and we'd hang. We have to detect this
|
||||
// and manually start.
|
||||
|
||||
// check again in case endrx has happened between the last check and now.
|
||||
if r.events_endrx.read().bits() != 0 {
|
||||
//trace!(" irq_rx: endrx");
|
||||
r.events_endrx.reset();
|
||||
// check again in case endrx has happened between the last check and now.
|
||||
if r.events_endrx.read().bits() != 0 {
|
||||
//trace!(" irq_rx: endrx");
|
||||
r.events_endrx.reset();
|
||||
|
||||
let val = s.rx_ended_count.load(Ordering::Relaxed);
|
||||
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
|
||||
let val = s.rx_ended_count.load(Ordering::Relaxed);
|
||||
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
let rx_ended = s.rx_ended_count.load(Ordering::Relaxed);
|
||||
let rx_started = s.rx_started_count.load(Ordering::Relaxed);
|
||||
|
||||
// If we started the same amount of transfers as ended, the last rxend has
|
||||
// already occured.
|
||||
let rxend_happened = rx_started == rx_ended;
|
||||
|
||||
// Check if the PPI channel is still enabled. The PPI channel disables itself
|
||||
// when it fires, so if it's still enabled it hasn't fired.
|
||||
let ppi_ch_enabled = ppi::regs().chen.read().bits() & (1 << chn) != 0;
|
||||
|
||||
// if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed.
|
||||
// this condition also naturally matches if `!started`, needed to kickstart the DMA.
|
||||
if rxend_happened && ppi_ch_enabled {
|
||||
//trace!("manually starting.");
|
||||
|
||||
// disable the ppi ch, it's of no use anymore.
|
||||
ppi::regs().chenclr.write(|w| unsafe { w.bits(1 << chn) });
|
||||
|
||||
// manually start
|
||||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
rx.push_done(half_len);
|
||||
|
||||
s.rx_started_count.store(rx_started.wrapping_add(1), Ordering::Relaxed);
|
||||
s.rx_started.store(true, Ordering::Relaxed);
|
||||
} else {
|
||||
//trace!(" irq_rx: rxstarted no buf");
|
||||
r.intenclr.write(|w| w.rxstarted().clear());
|
||||
}
|
||||
|
||||
let rx_ended = s.rx_ended_count.load(Ordering::Relaxed);
|
||||
let rx_started = s.rx_started_count.load(Ordering::Relaxed);
|
||||
|
||||
// If we started the same amount of transfers as ended, the last rxend has
|
||||
// already occured.
|
||||
let rxend_happened = rx_started == rx_ended;
|
||||
|
||||
// Check if the PPI channel is still enabled. The PPI channel disables itself
|
||||
// when it fires, so if it's still enabled it hasn't fired.
|
||||
let ppi_ch_enabled = ppi::regs().chen.read().bits() & (1 << chn) != 0;
|
||||
|
||||
// if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed.
|
||||
// this condition also naturally matches if `!started`, needed to kickstart the DMA.
|
||||
if rxend_happened && ppi_ch_enabled {
|
||||
//trace!("manually starting.");
|
||||
|
||||
// disable the ppi ch, it's of no use anymore.
|
||||
ppi::regs().chenclr.write(|w| unsafe { w.bits(1 << chn) });
|
||||
|
||||
// manually start
|
||||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
rx.push_done(half_len);
|
||||
|
||||
s.rx_started_count.store(rx_started.wrapping_add(1), Ordering::Relaxed);
|
||||
s.rx_started.store(true, Ordering::Relaxed);
|
||||
} else {
|
||||
//trace!(" irq_rx: rxstarted no buf");
|
||||
r.intenclr.write(|w| w.rxstarted().clear());
|
||||
}
|
||||
}
|
||||
|
||||
// =============================
|
||||
|
||||
// TX end
|
||||
if r.events_endtx.read().bits() != 0 {
|
||||
r.events_endtx.reset();
|
||||
if let Some(mut tx) = unsafe { s.tx_buf.try_reader() } {
|
||||
// TX end
|
||||
if r.events_endtx.read().bits() != 0 {
|
||||
r.events_endtx.reset();
|
||||
|
||||
let n = s.tx_count.load(Ordering::Relaxed);
|
||||
//trace!(" irq_tx: endtx {:?}", n);
|
||||
tx.pop_done(n);
|
||||
s.tx_waker.wake();
|
||||
s.tx_count.store(0, Ordering::Relaxed);
|
||||
}
|
||||
let n = s.tx_count.load(Ordering::Relaxed);
|
||||
//trace!(" irq_tx: endtx {:?}", n);
|
||||
tx.pop_done(n);
|
||||
ss.tx_waker.wake();
|
||||
s.tx_count.store(0, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// If not TXing, start.
|
||||
if s.tx_count.load(Ordering::Relaxed) == 0 {
|
||||
let (ptr, len) = tx.pop_buf();
|
||||
if len != 0 {
|
||||
//trace!(" irq_tx: starting {:?}", len);
|
||||
s.tx_count.store(len, Ordering::Relaxed);
|
||||
// If not TXing, start.
|
||||
if s.tx_count.load(Ordering::Relaxed) == 0 {
|
||||
let (ptr, len) = tx.pop_buf();
|
||||
if len != 0 {
|
||||
//trace!(" irq_tx: starting {:?}", len);
|
||||
s.tx_count.store(len, Ordering::Relaxed);
|
||||
|
||||
// Set up the DMA write
|
||||
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
// Set up the DMA write
|
||||
r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||
r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
|
||||
// Start UARTE Transmit transaction
|
||||
r.tasks_starttx.write(|w| unsafe { w.bits(1) });
|
||||
// Start UARTE Transmit transaction
|
||||
r.tasks_starttx.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,11 +213,8 @@ impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for Interrupt
|
|||
|
||||
/// Buffered UARTE driver.
|
||||
pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> {
|
||||
_peri: PeripheralRef<'d, U>,
|
||||
timer: Timer<'d, T>,
|
||||
_ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>,
|
||||
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>,
|
||||
_ppi_group: PpiGroup<'d, AnyGroup>,
|
||||
tx: BufferedUarteTx<'d, U>,
|
||||
rx: BufferedUarteRx<'d, U, T>,
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {}
|
||||
|
@ -243,7 +238,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
rx_buffer: &'d mut [u8],
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
into_ref!(rxd, txd, ppi_ch1, ppi_ch2, ppi_group);
|
||||
into_ref!(uarte, timer, rxd, txd, ppi_ch1, ppi_ch2, ppi_group);
|
||||
Self::new_inner(
|
||||
uarte,
|
||||
timer,
|
||||
|
@ -280,7 +275,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
rx_buffer: &'d mut [u8],
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
into_ref!(rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group);
|
||||
into_ref!(uarte, timer, rxd, txd, cts, rts, ppi_ch1, ppi_ch2, ppi_group);
|
||||
Self::new_inner(
|
||||
uarte,
|
||||
timer,
|
||||
|
@ -298,8 +293,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: impl Peripheral<P = U> + 'd,
|
||||
timer: impl Peripheral<P = T> + 'd,
|
||||
peri: PeripheralRef<'d, U>,
|
||||
timer: PeripheralRef<'d, T>,
|
||||
ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>,
|
||||
ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>,
|
||||
ppi_group: PeripheralRef<'d, AnyGroup>,
|
||||
|
@ -311,16 +306,127 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
rx_buffer: &'d mut [u8],
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
into_ref!(peri, timer);
|
||||
configure(U::regs(), config, cts.is_some());
|
||||
|
||||
assert!(rx_buffer.len() % 2 == 0);
|
||||
let tx = BufferedUarteTx::new_innerer(unsafe { peri.clone_unchecked() }, txd, cts, tx_buffer);
|
||||
let rx = BufferedUarteRx::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer);
|
||||
|
||||
U::Interrupt::pend();
|
||||
unsafe { U::Interrupt::enable() };
|
||||
|
||||
U::state().tx_rx_refcount.store(2, Ordering::Relaxed);
|
||||
|
||||
Self { tx, rx }
|
||||
}
|
||||
|
||||
/// Adjust the baud rate to the provided value.
|
||||
pub fn set_baudrate(&mut self, baudrate: Baudrate) {
|
||||
let r = U::regs();
|
||||
r.baudrate.write(|w| w.baudrate().variant(baudrate));
|
||||
}
|
||||
|
||||
let hwfc = cts.is_some();
|
||||
/// Split the UART in reader and writer parts.
|
||||
///
|
||||
/// This allows reading and writing concurrently from independent tasks.
|
||||
pub fn split(self) -> (BufferedUarteRx<'d, U, T>, BufferedUarteTx<'d, U>) {
|
||||
(self.rx, self.tx)
|
||||
}
|
||||
|
||||
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
||||
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
||||
/// Split the UART in reader and writer parts, by reference.
|
||||
///
|
||||
/// The returned halves borrow from `self`, so you can drop them and go back to using
|
||||
/// the "un-split" `self`. This allows temporarily splitting the UART.
|
||||
pub fn split_by_ref(&mut self) -> (&mut BufferedUarteRx<'d, U, T>, &mut BufferedUarteTx<'d, U>) {
|
||||
(&mut self.rx, &mut self.tx)
|
||||
}
|
||||
|
||||
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
|
||||
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
self.rx.read(buf).await
|
||||
}
|
||||
|
||||
/// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
|
||||
pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {
|
||||
self.rx.fill_buf().await
|
||||
}
|
||||
|
||||
/// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
|
||||
pub fn consume(&mut self, amt: usize) {
|
||||
self.rx.consume(amt)
|
||||
}
|
||||
|
||||
/// Write a buffer into this writer, returning how many bytes were written.
|
||||
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
|
||||
self.tx.write(buf).await
|
||||
}
|
||||
|
||||
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
|
||||
pub async fn flush(&mut self) -> Result<(), Error> {
|
||||
self.tx.flush().await
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader part of the buffered UARTE driver.
|
||||
pub struct BufferedUarteTx<'d, U: UarteInstance> {
|
||||
_peri: PeripheralRef<'d, U>,
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> {
|
||||
/// Create a new BufferedUarteTx without hardware flow control.
|
||||
pub fn new(
|
||||
uarte: impl Peripheral<P = U> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,
|
||||
txd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
into_ref!(uarte, txd);
|
||||
Self::new_inner(uarte, txd.map_into(), None, config, tx_buffer)
|
||||
}
|
||||
|
||||
/// Create a new BufferedUarte with hardware flow control (RTS/CTS)
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `rx_buffer.len()` is odd.
|
||||
pub fn new_with_cts(
|
||||
uarte: impl Peripheral<P = U> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,
|
||||
txd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
cts: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
into_ref!(uarte, txd, cts);
|
||||
Self::new_inner(uarte, txd.map_into(), Some(cts.map_into()), config, tx_buffer)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: PeripheralRef<'d, U>,
|
||||
txd: PeripheralRef<'d, AnyPin>,
|
||||
cts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
config: Config,
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
configure(U::regs(), config, cts.is_some());
|
||||
|
||||
let this = Self::new_innerer(peri, txd, cts, tx_buffer);
|
||||
|
||||
U::Interrupt::pend();
|
||||
unsafe { U::Interrupt::enable() };
|
||||
|
||||
U::state().tx_rx_refcount.store(1, Ordering::Relaxed);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
fn new_innerer(
|
||||
peri: PeripheralRef<'d, U>,
|
||||
txd: PeripheralRef<'d, AnyPin>,
|
||||
cts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
let r = U::regs();
|
||||
|
||||
txd.set_high();
|
||||
txd.conf().write(|w| w.dir().output().drive().h0h1());
|
||||
|
@ -331,6 +437,203 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
}
|
||||
r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) });
|
||||
|
||||
// Initialize state
|
||||
let s = U::buffered_state();
|
||||
s.tx_count.store(0, Ordering::Relaxed);
|
||||
let len = tx_buffer.len();
|
||||
unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
|
||||
|
||||
r.events_txstarted.reset();
|
||||
|
||||
// Enable interrupts
|
||||
r.intenset.write(|w| {
|
||||
w.endtx().set();
|
||||
w
|
||||
});
|
||||
|
||||
Self { _peri: peri }
|
||||
}
|
||||
|
||||
/// Write a buffer into this writer, returning how many bytes were written.
|
||||
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
|
||||
poll_fn(move |cx| {
|
||||
//trace!("poll_write: {:?}", buf.len());
|
||||
let ss = U::state();
|
||||
let s = U::buffered_state();
|
||||
let mut tx = unsafe { s.tx_buf.writer() };
|
||||
|
||||
let tx_buf = tx.push_slice();
|
||||
if tx_buf.is_empty() {
|
||||
//trace!("poll_write: pending");
|
||||
ss.tx_waker.register(cx.waker());
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
let n = min(tx_buf.len(), buf.len());
|
||||
tx_buf[..n].copy_from_slice(&buf[..n]);
|
||||
tx.push_done(n);
|
||||
|
||||
//trace!("poll_write: queued {:?}", n);
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
U::Interrupt::pend();
|
||||
|
||||
Poll::Ready(Ok(n))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
|
||||
pub async fn flush(&mut self) -> Result<(), Error> {
|
||||
poll_fn(move |cx| {
|
||||
//trace!("poll_flush");
|
||||
let ss = U::state();
|
||||
let s = U::buffered_state();
|
||||
if !s.tx_buf.is_empty() {
|
||||
//trace!("poll_flush: pending");
|
||||
ss.tx_waker.register(cx.waker());
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U: UarteInstance> Drop for BufferedUarteTx<'a, U> {
|
||||
fn drop(&mut self) {
|
||||
let r = U::regs();
|
||||
|
||||
r.intenclr.write(|w| {
|
||||
w.txdrdy().set_bit();
|
||||
w.txstarted().set_bit();
|
||||
w.txstopped().set_bit();
|
||||
w
|
||||
});
|
||||
r.events_txstopped.reset();
|
||||
r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
|
||||
while r.events_txstopped.read().bits() == 0 {}
|
||||
|
||||
let s = U::buffered_state();
|
||||
unsafe { s.tx_buf.deinit() }
|
||||
|
||||
let s = U::state();
|
||||
drop_tx_rx(r, s);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader part of the buffered UARTE driver.
|
||||
pub struct BufferedUarteRx<'d, U: UarteInstance, T: TimerInstance> {
|
||||
_peri: PeripheralRef<'d, U>,
|
||||
timer: Timer<'d, T>,
|
||||
_ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>,
|
||||
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>,
|
||||
_ppi_group: PpiGroup<'d, AnyGroup>,
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
|
||||
/// Create a new BufferedUarte without hardware flow control.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `rx_buffer.len()` is odd.
|
||||
pub fn new(
|
||||
uarte: impl Peripheral<P = U> + 'd,
|
||||
timer: impl Peripheral<P = T> + 'd,
|
||||
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
|
||||
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd,
|
||||
ppi_group: impl Peripheral<P = impl Group> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,
|
||||
rxd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
into_ref!(uarte, timer, rxd, ppi_ch1, ppi_ch2, ppi_group);
|
||||
Self::new_inner(
|
||||
uarte,
|
||||
timer,
|
||||
ppi_ch1.map_into(),
|
||||
ppi_ch2.map_into(),
|
||||
ppi_group.map_into(),
|
||||
rxd.map_into(),
|
||||
None,
|
||||
config,
|
||||
rx_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new BufferedUarte with hardware flow control (RTS/CTS)
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `rx_buffer.len()` is odd.
|
||||
pub fn new_with_rts(
|
||||
uarte: impl Peripheral<P = U> + 'd,
|
||||
timer: impl Peripheral<P = T> + 'd,
|
||||
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
|
||||
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd,
|
||||
ppi_group: impl Peripheral<P = impl Group> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,
|
||||
rxd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
rts: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
into_ref!(uarte, timer, rxd, rts, ppi_ch1, ppi_ch2, ppi_group);
|
||||
Self::new_inner(
|
||||
uarte,
|
||||
timer,
|
||||
ppi_ch1.map_into(),
|
||||
ppi_ch2.map_into(),
|
||||
ppi_group.map_into(),
|
||||
rxd.map_into(),
|
||||
Some(rts.map_into()),
|
||||
config,
|
||||
rx_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: PeripheralRef<'d, U>,
|
||||
timer: PeripheralRef<'d, T>,
|
||||
ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>,
|
||||
ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>,
|
||||
ppi_group: PeripheralRef<'d, AnyGroup>,
|
||||
rxd: PeripheralRef<'d, AnyPin>,
|
||||
rts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
config: Config,
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
configure(U::regs(), config, rts.is_some());
|
||||
|
||||
let this = Self::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer);
|
||||
|
||||
U::Interrupt::pend();
|
||||
unsafe { U::Interrupt::enable() };
|
||||
|
||||
U::state().tx_rx_refcount.store(1, Ordering::Relaxed);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
fn new_innerer(
|
||||
peri: PeripheralRef<'d, U>,
|
||||
timer: PeripheralRef<'d, T>,
|
||||
ppi_ch1: PeripheralRef<'d, AnyConfigurableChannel>,
|
||||
ppi_ch2: PeripheralRef<'d, AnyConfigurableChannel>,
|
||||
ppi_group: PeripheralRef<'d, AnyGroup>,
|
||||
rxd: PeripheralRef<'d, AnyPin>,
|
||||
rts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> Self {
|
||||
assert!(rx_buffer.len() % 2 == 0);
|
||||
|
||||
let r = U::regs();
|
||||
|
||||
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
||||
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
||||
|
||||
if let Some(pin) = &rts {
|
||||
pin.set_high();
|
||||
pin.conf().write(|w| w.dir().output().drive().h0h1());
|
||||
|
@ -339,35 +642,21 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
|
||||
// Initialize state
|
||||
let s = U::buffered_state();
|
||||
s.tx_count.store(0, Ordering::Relaxed);
|
||||
s.rx_started_count.store(0, Ordering::Relaxed);
|
||||
s.rx_ended_count.store(0, Ordering::Relaxed);
|
||||
s.rx_started.store(false, Ordering::Relaxed);
|
||||
let len = tx_buffer.len();
|
||||
unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
|
||||
let len = rx_buffer.len();
|
||||
unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), len) };
|
||||
|
||||
// Configure
|
||||
r.config.write(|w| {
|
||||
w.hwfc().bit(hwfc);
|
||||
w.parity().variant(config.parity);
|
||||
w
|
||||
});
|
||||
r.baudrate.write(|w| w.baudrate().variant(config.baudrate));
|
||||
|
||||
// clear errors
|
||||
let errors = r.errorsrc.read().bits();
|
||||
r.errorsrc.write(|w| unsafe { w.bits(errors) });
|
||||
|
||||
r.events_rxstarted.reset();
|
||||
r.events_txstarted.reset();
|
||||
r.events_error.reset();
|
||||
r.events_endrx.reset();
|
||||
r.events_endtx.reset();
|
||||
|
||||
// Enable interrupts
|
||||
r.intenclr.write(|w| unsafe { w.bits(!0) });
|
||||
r.intenset.write(|w| {
|
||||
w.endtx().set();
|
||||
w.rxstarted().set();
|
||||
|
@ -376,10 +665,6 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
w
|
||||
});
|
||||
|
||||
// Enable UARTE instance
|
||||
apply_workaround_for_enable_anomaly(r);
|
||||
r.enable.write(|w| w.enable().enabled());
|
||||
|
||||
// Configure byte counter.
|
||||
let timer = Timer::new_counter(timer);
|
||||
timer.cc(1).write(rx_buffer.len() as u32 * 2);
|
||||
|
@ -401,9 +686,6 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
ppi_ch2.disable();
|
||||
ppi_group.add_channel(&ppi_ch2);
|
||||
|
||||
U::Interrupt::pend();
|
||||
unsafe { U::Interrupt::enable() };
|
||||
|
||||
Self {
|
||||
_peri: peri,
|
||||
timer,
|
||||
|
@ -413,80 +695,24 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn pend_irq() {
|
||||
U::Interrupt::pend()
|
||||
}
|
||||
|
||||
/// Adjust the baud rate to the provided value.
|
||||
pub fn set_baudrate(&mut self, baudrate: Baudrate) {
|
||||
let r = U::regs();
|
||||
r.baudrate.write(|w| w.baudrate().variant(baudrate));
|
||||
}
|
||||
|
||||
/// Split the UART in reader and writer parts.
|
||||
///
|
||||
/// This allows reading and writing concurrently from independent tasks.
|
||||
pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) {
|
||||
(BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self })
|
||||
}
|
||||
|
||||
async fn inner_read(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
let data = self.inner_fill_buf().await?;
|
||||
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
|
||||
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
let data = self.fill_buf().await?;
|
||||
let n = data.len().min(buf.len());
|
||||
buf[..n].copy_from_slice(&data[..n]);
|
||||
self.inner_consume(n);
|
||||
self.consume(n);
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, Error> {
|
||||
poll_fn(move |cx| {
|
||||
//trace!("poll_write: {:?}", buf.len());
|
||||
let s = U::buffered_state();
|
||||
let mut tx = unsafe { s.tx_buf.writer() };
|
||||
|
||||
let tx_buf = tx.push_slice();
|
||||
if tx_buf.is_empty() {
|
||||
//trace!("poll_write: pending");
|
||||
s.tx_waker.register(cx.waker());
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
let n = min(tx_buf.len(), buf.len());
|
||||
tx_buf[..n].copy_from_slice(&buf[..n]);
|
||||
tx.push_done(n);
|
||||
|
||||
//trace!("poll_write: queued {:?}", n);
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
Self::pend_irq();
|
||||
|
||||
Poll::Ready(Ok(n))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn inner_flush<'a>(&'a self) -> Result<(), Error> {
|
||||
poll_fn(move |cx| {
|
||||
//trace!("poll_flush");
|
||||
let s = U::buffered_state();
|
||||
if !s.tx_buf.is_empty() {
|
||||
//trace!("poll_flush: pending");
|
||||
s.tx_waker.register(cx.waker());
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> {
|
||||
/// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
|
||||
pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {
|
||||
poll_fn(move |cx| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
//trace!("poll_read");
|
||||
|
||||
let r = U::regs();
|
||||
let s = U::buffered_state();
|
||||
let ss = U::state();
|
||||
|
||||
// Read the RXDRDY counter.
|
||||
T::regs().tasks_capture[0].write(|w| unsafe { w.bits(1) });
|
||||
|
@ -510,7 +736,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
let len = s.rx_buf.len();
|
||||
if start == end {
|
||||
//trace!(" empty");
|
||||
s.rx_waker.register(cx.waker());
|
||||
ss.rx_waker.register(cx.waker());
|
||||
r.intenset.write(|w| w.rxdrdy().set_bit());
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
@ -532,7 +758,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
.await
|
||||
}
|
||||
|
||||
fn inner_consume(&self, amt: usize) {
|
||||
/// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
|
||||
pub fn consume(&mut self, amt: usize) {
|
||||
if amt == 0 {
|
||||
return;
|
||||
}
|
||||
|
@ -542,69 +769,31 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||
rx.pop_done(amt);
|
||||
U::regs().intenset.write(|w| w.rxstarted().set());
|
||||
}
|
||||
|
||||
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
|
||||
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
self.inner_read(buf).await
|
||||
}
|
||||
|
||||
/// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
|
||||
pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {
|
||||
self.inner_fill_buf().await
|
||||
}
|
||||
|
||||
/// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
|
||||
pub fn consume(&mut self, amt: usize) {
|
||||
self.inner_consume(amt)
|
||||
}
|
||||
|
||||
/// Write a buffer into this writer, returning how many bytes were written.
|
||||
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
|
||||
self.inner_write(buf).await
|
||||
}
|
||||
|
||||
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
|
||||
pub async fn flush(&mut self) -> Result<(), Error> {
|
||||
self.inner_flush().await
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader part of the buffered UARTE driver.
|
||||
pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> {
|
||||
inner: &'u BufferedUarte<'d, U, T>,
|
||||
}
|
||||
impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarteRx<'a, U, T> {
|
||||
fn drop(&mut self) {
|
||||
self._ppi_group.disable_all();
|
||||
|
||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteTx<'u, 'd, U, T> {
|
||||
/// Write a buffer into this writer, returning how many bytes were written.
|
||||
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
|
||||
self.inner.inner_write(buf).await
|
||||
}
|
||||
let r = U::regs();
|
||||
|
||||
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
|
||||
pub async fn flush(&mut self) -> Result<(), Error> {
|
||||
self.inner.inner_flush().await
|
||||
}
|
||||
}
|
||||
self.timer.stop();
|
||||
|
||||
/// Writer part of the buffered UARTE driver.
|
||||
pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> {
|
||||
inner: &'u BufferedUarte<'d, U, T>,
|
||||
}
|
||||
r.intenclr.write(|w| {
|
||||
w.rxdrdy().set_bit();
|
||||
w.rxstarted().set_bit();
|
||||
w.rxto().set_bit();
|
||||
w
|
||||
});
|
||||
r.events_rxto.reset();
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
while r.events_rxto.read().bits() == 0 {}
|
||||
|
||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> {
|
||||
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
|
||||
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
self.inner.inner_read(buf).await
|
||||
}
|
||||
let s = U::buffered_state();
|
||||
unsafe { s.rx_buf.deinit() }
|
||||
|
||||
/// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty.
|
||||
pub async fn fill_buf(&mut self) -> Result<&[u8], Error> {
|
||||
self.inner.inner_fill_buf().await
|
||||
}
|
||||
|
||||
/// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`.
|
||||
pub fn consume(&mut self, amt: usize) {
|
||||
self.inner.inner_consume(amt)
|
||||
let s = U::state();
|
||||
drop_tx_rx(r, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -621,95 +810,63 @@ mod _embedded_io {
|
|||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteRx<'u, 'd, U, T> {
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteRx<'d, U, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteTx<'u, 'd, U, T> {
|
||||
impl<'d, U: UarteInstance> embedded_io_async::ErrorType for BufferedUarteTx<'d, U> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarte<'d, U, T> {
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.inner_read(buf).await
|
||||
self.read(buf).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarteRx<'u, 'd, U, T> {
|
||||
impl<'d: 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarteRx<'d, U, T> {
|
||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.inner.inner_read(buf).await
|
||||
self.read(buf).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarte<'d, U, T> {
|
||||
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||
self.inner_fill_buf().await
|
||||
self.fill_buf().await
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.inner_consume(amt)
|
||||
self.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarteRx<'u, 'd, U, T> {
|
||||
impl<'d: 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarteRx<'d, U, T> {
|
||||
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||
self.inner.inner_fill_buf().await
|
||||
self.fill_buf().await
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.inner.inner_consume(amt)
|
||||
self.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarte<'d, U, T> {
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.inner_write(buf).await
|
||||
self.write(buf).await
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.inner_flush().await
|
||||
self.flush().await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarteTx<'u, 'd, U, T> {
|
||||
impl<'d: 'd, U: UarteInstance> embedded_io_async::Write for BufferedUarteTx<'d, U> {
|
||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.inner.inner_write(buf).await
|
||||
self.write(buf).await
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.inner.inner_flush().await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarte<'a, U, T> {
|
||||
fn drop(&mut self) {
|
||||
self._ppi_group.disable_all();
|
||||
|
||||
let r = U::regs();
|
||||
|
||||
self.timer.stop();
|
||||
|
||||
r.inten.reset();
|
||||
r.events_rxto.reset();
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
r.events_txstopped.reset();
|
||||
r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
while r.events_txstopped.read().bits() == 0 {}
|
||||
while r.events_rxto.read().bits() == 0 {}
|
||||
|
||||
r.enable.write(|w| w.enable().disabled());
|
||||
|
||||
gpio::deconfigure_pin(r.psel.rxd.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.txd.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.rts.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.cts.read().bits());
|
||||
|
||||
let s = U::buffered_state();
|
||||
unsafe {
|
||||
s.rx_buf.deinit();
|
||||
s.tx_buf.deinit();
|
||||
self.flush().await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ impl<'d> Output<'d> {
|
|||
}
|
||||
}
|
||||
|
||||
fn convert_drive(drive: OutputDrive) -> DRIVE_A {
|
||||
pub(crate) fn convert_drive(drive: OutputDrive) -> DRIVE_A {
|
||||
match drive {
|
||||
OutputDrive::Standard => DRIVE_A::S0S1,
|
||||
OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1,
|
||||
|
|
|
@ -15,7 +15,7 @@ pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
|
|||
|
||||
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
|
||||
use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut};
|
||||
use crate::{interrupt, pac, Peripheral};
|
||||
|
@ -46,6 +46,15 @@ pub struct Config {
|
|||
/// When doing bidirectional transfers, if the TX buffer is shorter than the RX buffer,
|
||||
/// this byte will be transmitted in the MOSI line for the left-over bytes.
|
||||
pub orc: u8,
|
||||
|
||||
/// Drive strength for the SCK line.
|
||||
pub sck_drive: OutputDrive,
|
||||
|
||||
/// Drive strength for the MOSI line.
|
||||
pub mosi_drive: OutputDrive,
|
||||
|
||||
/// Drive strength for the MISO line.
|
||||
pub miso_drive: OutputDrive,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -55,6 +64,9 @@ impl Default for Config {
|
|||
mode: MODE_0,
|
||||
bit_order: BitOrder::MSB_FIRST,
|
||||
orc: 0x00,
|
||||
sck_drive: OutputDrive::HighDrive,
|
||||
mosi_drive: OutputDrive::HighDrive,
|
||||
miso_drive: OutputDrive::HighDrive,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,13 +171,16 @@ impl<'d, T: Instance> Spim<'d, T> {
|
|||
|
||||
// Configure pins
|
||||
if let Some(sck) = &sck {
|
||||
sck.conf().write(|w| w.dir().output().drive().h0h1());
|
||||
sck.conf()
|
||||
.write(|w| w.dir().output().drive().variant(convert_drive(config.sck_drive)));
|
||||
}
|
||||
if let Some(mosi) = &mosi {
|
||||
mosi.conf().write(|w| w.dir().output().drive().h0h1());
|
||||
mosi.conf()
|
||||
.write(|w| w.dir().output().drive().variant(convert_drive(config.mosi_drive)));
|
||||
}
|
||||
if let Some(miso) = &miso {
|
||||
miso.conf().write(|w| w.input().connect().drive().h0h1());
|
||||
miso.conf()
|
||||
.write(|w| w.input().connect().drive().variant(convert_drive(config.miso_drive)));
|
||||
}
|
||||
|
||||
match config.mode.polarity {
|
||||
|
|
|
@ -115,7 +115,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||
let endrx = r.events_endrx.read().bits();
|
||||
let error = r.events_error.read().bits();
|
||||
if endrx != 0 || error != 0 {
|
||||
s.endrx_waker.wake();
|
||||
s.rx_waker.wake();
|
||||
if endrx != 0 {
|
||||
r.intenclr.write(|w| w.endrx().clear());
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
|||
}
|
||||
}
|
||||
if r.events_endtx.read().bits() != 0 {
|
||||
s.endtx_waker.wake();
|
||||
s.tx_waker.wake();
|
||||
r.intenclr.write(|w| w.endtx().clear());
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
|||
txd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rxd, txd);
|
||||
into_ref!(uarte, rxd, txd);
|
||||
Self::new_inner(uarte, rxd.map_into(), txd.map_into(), None, None, config)
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
|||
rts: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rxd, txd, cts, rts);
|
||||
into_ref!(uarte, rxd, txd, cts, rts);
|
||||
Self::new_inner(
|
||||
uarte,
|
||||
rxd.map_into(),
|
||||
|
@ -185,17 +185,22 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
|||
}
|
||||
|
||||
fn new_inner(
|
||||
uarte: impl Peripheral<P = T> + 'd,
|
||||
uarte: PeripheralRef<'d, T>,
|
||||
rxd: PeripheralRef<'d, AnyPin>,
|
||||
txd: PeripheralRef<'d, AnyPin>,
|
||||
cts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
rts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(uarte);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
let hardware_flow_control = match (rts.is_some(), cts.is_some()) {
|
||||
(false, false) => false,
|
||||
(true, true) => true,
|
||||
_ => panic!("RTS and CTS pins must be either both set or none set."),
|
||||
};
|
||||
configure(r, config, hardware_flow_control);
|
||||
|
||||
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
||||
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
||||
|
||||
|
@ -217,13 +222,6 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
|||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
let hardware_flow_control = match (rts.is_some(), cts.is_some()) {
|
||||
(false, false) => false,
|
||||
(true, true) => true,
|
||||
_ => panic!("RTS and CTS pins must be either both set or none set."),
|
||||
};
|
||||
configure(r, config, hardware_flow_control);
|
||||
|
||||
let s = T::state();
|
||||
s.tx_rx_refcount.store(2, Ordering::Relaxed);
|
||||
|
||||
|
@ -242,6 +240,14 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
|||
(self.tx, self.rx)
|
||||
}
|
||||
|
||||
/// Split the UART in reader and writer parts, by reference.
|
||||
///
|
||||
/// The returned halves borrow from `self`, so you can drop them and go back to using
|
||||
/// the "un-split" `self`. This allows temporarily splitting the UART.
|
||||
pub fn split_by_ref(&mut self) -> (&mut UarteTx<'d, T>, &mut UarteRx<'d, T>) {
|
||||
(&mut self.tx, &mut self.rx)
|
||||
}
|
||||
|
||||
/// Split the Uarte into the transmitter and receiver with idle support parts.
|
||||
///
|
||||
/// This is useful to concurrently transmit and receive from independent tasks.
|
||||
|
@ -291,7 +297,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn configure(r: &RegisterBlock, config: Config, hardware_flow_control: bool) {
|
||||
pub(crate) fn configure(r: &RegisterBlock, config: Config, hardware_flow_control: bool) {
|
||||
r.config.write(|w| {
|
||||
w.hwfc().bit(hardware_flow_control);
|
||||
w.parity().variant(config.parity);
|
||||
|
@ -307,6 +313,12 @@ fn configure(r: &RegisterBlock, config: Config, hardware_flow_control: bool) {
|
|||
r.events_rxstarted.reset();
|
||||
r.events_txstarted.reset();
|
||||
|
||||
// reset all pins
|
||||
r.psel.txd.write(|w| w.connect().disconnected());
|
||||
r.psel.rxd.write(|w| w.connect().disconnected());
|
||||
r.psel.cts.write(|w| w.connect().disconnected());
|
||||
r.psel.rts.write(|w| w.connect().disconnected());
|
||||
|
||||
// Enable
|
||||
apply_workaround_for_enable_anomaly(r);
|
||||
r.enable.write(|w| w.enable().enabled());
|
||||
|
@ -320,7 +332,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
|
|||
txd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(txd);
|
||||
into_ref!(uarte, txd);
|
||||
Self::new_inner(uarte, txd.map_into(), None, config)
|
||||
}
|
||||
|
||||
|
@ -332,20 +344,20 @@ impl<'d, T: Instance> UarteTx<'d, T> {
|
|||
cts: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(txd, cts);
|
||||
into_ref!(uarte, txd, cts);
|
||||
Self::new_inner(uarte, txd.map_into(), Some(cts.map_into()), config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
uarte: impl Peripheral<P = T> + 'd,
|
||||
uarte: PeripheralRef<'d, T>,
|
||||
txd: PeripheralRef<'d, AnyPin>,
|
||||
cts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(uarte);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
configure(r, config, cts.is_some());
|
||||
|
||||
txd.set_high();
|
||||
txd.conf().write(|w| w.dir().output().drive().s0s1());
|
||||
r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) });
|
||||
|
@ -355,12 +367,6 @@ impl<'d, T: Instance> UarteTx<'d, T> {
|
|||
}
|
||||
r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) });
|
||||
|
||||
r.psel.rxd.write(|w| w.connect().disconnected());
|
||||
r.psel.rts.write(|w| w.connect().disconnected());
|
||||
|
||||
let hardware_flow_control = cts.is_some();
|
||||
configure(r, config, hardware_flow_control);
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
|
@ -425,7 +431,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
|
|||
r.tasks_starttx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
poll_fn(|cx| {
|
||||
s.endtx_waker.register(cx.waker());
|
||||
s.tx_waker.register(cx.waker());
|
||||
if r.events_endtx.read().bits() != 0 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
@ -516,7 +522,7 @@ impl<'d, T: Instance> UarteRx<'d, T> {
|
|||
rxd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rxd);
|
||||
into_ref!(uarte, rxd);
|
||||
Self::new_inner(uarte, rxd.map_into(), None, config)
|
||||
}
|
||||
|
||||
|
@ -528,7 +534,7 @@ impl<'d, T: Instance> UarteRx<'d, T> {
|
|||
rts: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rxd, rts);
|
||||
into_ref!(uarte, rxd, rts);
|
||||
Self::new_inner(uarte, rxd.map_into(), Some(rts.map_into()), config)
|
||||
}
|
||||
|
||||
|
@ -541,15 +547,15 @@ impl<'d, T: Instance> UarteRx<'d, T> {
|
|||
}
|
||||
|
||||
fn new_inner(
|
||||
uarte: impl Peripheral<P = T> + 'd,
|
||||
uarte: PeripheralRef<'d, T>,
|
||||
rxd: PeripheralRef<'d, AnyPin>,
|
||||
rts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(uarte);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
configure(r, config, rts.is_some());
|
||||
|
||||
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
||||
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
||||
|
||||
|
@ -559,15 +565,9 @@ impl<'d, T: Instance> UarteRx<'d, T> {
|
|||
}
|
||||
r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) });
|
||||
|
||||
r.psel.txd.write(|w| w.connect().disconnected());
|
||||
r.psel.cts.write(|w| w.connect().disconnected());
|
||||
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
let hardware_flow_control = rts.is_some();
|
||||
configure(r, config, hardware_flow_control);
|
||||
|
||||
let s = T::state();
|
||||
s.tx_rx_refcount.store(1, Ordering::Relaxed);
|
||||
|
||||
|
@ -672,7 +672,7 @@ impl<'d, T: Instance> UarteRx<'d, T> {
|
|||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
let result = poll_fn(|cx| {
|
||||
s.endrx_waker.register(cx.waker());
|
||||
s.rx_waker.register(cx.waker());
|
||||
|
||||
if let Err(e) = self.check_and_clear_errors() {
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
|
@ -819,7 +819,7 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
|
|||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
let result = poll_fn(|cx| {
|
||||
s.endrx_waker.register(cx.waker());
|
||||
s.rx_waker.register(cx.waker());
|
||||
|
||||
if let Err(e) = self.rx.check_and_clear_errors() {
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
|
@ -962,15 +962,15 @@ pub(crate) mod sealed {
|
|||
use super::*;
|
||||
|
||||
pub struct State {
|
||||
pub endrx_waker: AtomicWaker,
|
||||
pub endtx_waker: AtomicWaker,
|
||||
pub rx_waker: AtomicWaker,
|
||||
pub tx_waker: AtomicWaker,
|
||||
pub tx_rx_refcount: AtomicU8,
|
||||
}
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
endrx_waker: AtomicWaker::new(),
|
||||
endtx_waker: AtomicWaker::new(),
|
||||
rx_waker: AtomicWaker::new(),
|
||||
tx_waker: AtomicWaker::new(),
|
||||
tx_rx_refcount: AtomicU8::new(0),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,18 @@ pub enum Error {
|
|||
AddressReserved(u16),
|
||||
}
|
||||
|
||||
/// I2C Config error
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ConfigError {
|
||||
/// Max i2c speed is 1MHz
|
||||
FrequencyTooHigh,
|
||||
/// The sys clock is too slow to support given frequency
|
||||
ClockTooSlow,
|
||||
/// The sys clock is too fast to support given frequency
|
||||
ClockTooFast,
|
||||
}
|
||||
|
||||
/// I2C config.
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -365,37 +377,32 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
|
|||
) -> Self {
|
||||
into_ref!(_peri);
|
||||
|
||||
assert!(config.frequency <= 1_000_000);
|
||||
assert!(config.frequency > 0);
|
||||
|
||||
let p = T::regs();
|
||||
|
||||
let reset = T::reset();
|
||||
crate::reset::reset(reset);
|
||||
crate::reset::unreset_wait(reset);
|
||||
|
||||
p.ic_enable().write(|w| w.set_enable(false));
|
||||
|
||||
// Select controller mode & speed
|
||||
p.ic_con().modify(|w| {
|
||||
// Always use "fast" mode (<= 400 kHz, works fine for standard
|
||||
// mode too)
|
||||
w.set_speed(i2c::vals::Speed::FAST);
|
||||
w.set_master_mode(true);
|
||||
w.set_ic_slave_disable(true);
|
||||
w.set_ic_restart_en(true);
|
||||
w.set_tx_empty_ctrl(true);
|
||||
});
|
||||
|
||||
// Set FIFO watermarks to 1 to make things simpler. This is encoded
|
||||
// by a register value of 0.
|
||||
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
// Configure SCL & SDA pins
|
||||
set_up_i2c_pin(&scl);
|
||||
set_up_i2c_pin(&sda);
|
||||
|
||||
let mut me = Self { phantom: PhantomData };
|
||||
|
||||
if let Err(e) = me.set_config_inner(&config) {
|
||||
panic!("Error configuring i2c: {:?}", e);
|
||||
}
|
||||
|
||||
me
|
||||
}
|
||||
|
||||
fn set_config_inner(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||
if config.frequency > 1_000_000 {
|
||||
return Err(ConfigError::FrequencyTooHigh);
|
||||
}
|
||||
|
||||
let p = T::regs();
|
||||
|
||||
p.ic_enable().write(|w| w.set_enable(false));
|
||||
|
||||
// Configure baudrate
|
||||
|
||||
// There are some subtleties to I2C timing which we are completely
|
||||
|
@ -408,10 +415,12 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
|
|||
let hcnt = period - lcnt; // and 2/5 (40%) of the period high
|
||||
|
||||
// Check for out-of-range divisors:
|
||||
assert!(hcnt <= 0xffff);
|
||||
assert!(lcnt <= 0xffff);
|
||||
assert!(hcnt >= 8);
|
||||
assert!(lcnt >= 8);
|
||||
if hcnt > 0xffff || lcnt > 0xffff {
|
||||
return Err(ConfigError::ClockTooFast);
|
||||
}
|
||||
if hcnt < 8 || lcnt < 8 {
|
||||
return Err(ConfigError::ClockTooSlow);
|
||||
}
|
||||
|
||||
// Per I2C-bus specification a device in standard or fast mode must
|
||||
// internally provide a hold time of at least 300ns for the SDA
|
||||
|
@ -424,14 +433,19 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
|
|||
((clk_base * 3) / 10_000_000) + 1
|
||||
} else {
|
||||
// fast mode plus requires a clk_base > 32MHz
|
||||
assert!(clk_base >= 32_000_000);
|
||||
if clk_base <= 32_000_000 {
|
||||
return Err(ConfigError::ClockTooSlow);
|
||||
}
|
||||
|
||||
// sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s /
|
||||
// 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't
|
||||
// fit in uint. Add 1 to avoid division truncation.
|
||||
((clk_base * 3) / 25_000_000) + 1
|
||||
};
|
||||
assert!(sda_tx_hold_count <= lcnt - 2);
|
||||
|
||||
if sda_tx_hold_count > lcnt - 2 {
|
||||
return Err(ConfigError::ClockTooSlow);
|
||||
}
|
||||
|
||||
p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16));
|
||||
p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16));
|
||||
|
@ -440,10 +454,9 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
|
|||
p.ic_sda_hold()
|
||||
.modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16));
|
||||
|
||||
// Enable I2C block
|
||||
p.ic_enable().write(|w| w.set_enable(true));
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup(addr: u16) -> Result<(), Error> {
|
||||
|
@ -757,6 +770,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embassy_embedded_hal::SetConfig for I2c<'d, T, M> {
|
||||
type Config = Config;
|
||||
type ConfigError = ConfigError;
|
||||
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||
self.set_config_inner(config)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if address is reserved.
|
||||
pub fn i2c_reserved_addr(addr: u16) -> bool {
|
||||
((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0
|
||||
|
|
|
@ -83,6 +83,7 @@ impl Default for Config {
|
|||
pub struct I2cSlave<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
pending_byte: Option<u8>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
|
@ -99,6 +100,25 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
assert!(!i2c_reserved_addr(config.addr));
|
||||
assert!(config.addr != 0);
|
||||
|
||||
// Configure SCL & SDA pins
|
||||
set_up_i2c_pin(&scl);
|
||||
set_up_i2c_pin(&sda);
|
||||
|
||||
let mut ret = Self {
|
||||
phantom: PhantomData,
|
||||
pending_byte: None,
|
||||
config,
|
||||
};
|
||||
|
||||
ret.reset();
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus.
|
||||
/// You can recover the bus by calling this function, but doing so will almost certainly cause
|
||||
/// an i/o error in the master.
|
||||
pub fn reset(&mut self) {
|
||||
let p = T::regs();
|
||||
|
||||
let reset = T::reset();
|
||||
|
@ -107,7 +127,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
|
||||
p.ic_enable().write(|w| w.set_enable(false));
|
||||
|
||||
p.ic_sar().write(|w| w.set_ic_sar(config.addr));
|
||||
p.ic_sar().write(|w| w.set_ic_sar(self.config.addr));
|
||||
p.ic_con().modify(|w| {
|
||||
w.set_master_mode(false);
|
||||
w.set_ic_slave_disable(false);
|
||||
|
@ -121,10 +141,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
// Generate stop interrupts for general calls
|
||||
// This also causes stop interrupts for other devices on the bus but those will not be
|
||||
// propagated up to the application.
|
||||
w.set_stop_det_ifaddressed(!config.general_call);
|
||||
w.set_stop_det_ifaddressed(!self.config.general_call);
|
||||
});
|
||||
p.ic_ack_general_call()
|
||||
.write(|w| w.set_ack_gen_call(config.general_call));
|
||||
.write(|w| w.set_ack_gen_call(self.config.general_call));
|
||||
|
||||
// Set FIFO watermarks to 1 to make things simpler. This is encoded
|
||||
// by a register value of 0. Rx watermark should never change, but Tx watermark will be
|
||||
|
@ -132,10 +152,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
// Configure SCL & SDA pins
|
||||
set_up_i2c_pin(&scl);
|
||||
set_up_i2c_pin(&sda);
|
||||
|
||||
// Clear interrupts
|
||||
p.ic_clr_intr().read();
|
||||
|
||||
|
@ -146,11 +162,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
p.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
pending_byte: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls `f` to check if we are ready or not.
|
||||
|
@ -178,15 +189,13 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {
|
||||
let p = T::regs();
|
||||
|
||||
for b in &mut buffer[*offset..] {
|
||||
if let Some(pending) = self.pending_byte.take() {
|
||||
*b = pending;
|
||||
*offset += 1;
|
||||
continue;
|
||||
}
|
||||
if let Some(pending) = self.pending_byte.take() {
|
||||
buffer[*offset] = pending;
|
||||
*offset += 1;
|
||||
}
|
||||
|
||||
let status = p.ic_status().read();
|
||||
if !status.rfne() {
|
||||
for b in &mut buffer[*offset..] {
|
||||
if !p.ic_status().read().rfne() {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -207,14 +216,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_to_fifo(&mut self, buffer: &[u8]) {
|
||||
let p = T::regs();
|
||||
for byte in buffer {
|
||||
p.ic_data_cmd().write(|w| w.set_dat(*byte));
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait asynchronously for commands from an I2C master.
|
||||
/// `buffer` is provided in case master does a 'write', 'write read', or 'general call' and is unused for 'read'.
|
||||
pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
|
||||
|
@ -227,8 +228,9 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
self.wait_on(
|
||||
|me| {
|
||||
let stat = p.ic_raw_intr_stat().read();
|
||||
trace!("ls:{:013b} len:{}", stat.0, len);
|
||||
|
||||
if p.ic_rxflr().read().rxflr() > 0 {
|
||||
if p.ic_rxflr().read().rxflr() > 0 || me.pending_byte.is_some() {
|
||||
me.drain_fifo(buffer, &mut len);
|
||||
// we're recieving data, set rx fifo watermark to 12 bytes (3/4 full) to reduce interrupt noise
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(11));
|
||||
|
@ -241,6 +243,10 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
return Poll::Ready(Err(Error::PartialWrite(buffer.len())));
|
||||
}
|
||||
}
|
||||
trace!("len:{}, pend:{:?}", len, me.pending_byte);
|
||||
if me.pending_byte.is_some() {
|
||||
warn!("pending")
|
||||
}
|
||||
|
||||
if stat.restart_det() && stat.rd_req() {
|
||||
p.ic_clr_restart_det().read();
|
||||
|
@ -257,12 +263,17 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
p.ic_clr_restart_det().read();
|
||||
p.ic_clr_gen_call().read();
|
||||
Poll::Ready(Ok(Command::Read))
|
||||
} else if stat.stop_det() {
|
||||
// clear stuck stop bit
|
||||
// This can happen if the SDA/SCL pullups are enabled after calling this func
|
||||
p.ic_clr_stop_det().read();
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
},
|
||||
|_me| {
|
||||
p.ic_intr_mask().modify(|w| {
|
||||
p.ic_intr_mask().write(|w| {
|
||||
w.set_m_stop_det(true);
|
||||
w.set_m_restart_det(true);
|
||||
w.set_m_gen_call(true);
|
||||
|
@ -286,27 +297,30 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
|
||||
self.wait_on(
|
||||
|me| {
|
||||
if let Err(abort_reason) = me.read_and_clear_abort_reason() {
|
||||
if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
|
||||
p.ic_clr_intr().read();
|
||||
return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes)));
|
||||
} else {
|
||||
return Poll::Ready(Err(abort_reason));
|
||||
let stat = p.ic_raw_intr_stat().read();
|
||||
trace!("rs:{:013b}", stat.0);
|
||||
|
||||
if stat.tx_abrt() {
|
||||
if let Err(abort_reason) = me.read_and_clear_abort_reason() {
|
||||
if let Error::Abort(AbortReason::TxNotEmpty(bytes)) = abort_reason {
|
||||
p.ic_clr_intr().read();
|
||||
return Poll::Ready(Ok(ReadStatus::LeftoverBytes(bytes)));
|
||||
} else {
|
||||
return Poll::Ready(Err(abort_reason));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(chunk) = chunks.next() {
|
||||
me.write_to_fifo(chunk);
|
||||
|
||||
p.ic_clr_rd_req().read();
|
||||
for byte in chunk {
|
||||
p.ic_clr_rd_req().read();
|
||||
p.ic_data_cmd().write(|w| w.set_dat(*byte));
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
} else {
|
||||
let stat = p.ic_raw_intr_stat().read();
|
||||
|
||||
if stat.rx_done() && stat.stop_det() {
|
||||
if stat.rx_done() {
|
||||
p.ic_clr_rx_done().read();
|
||||
p.ic_clr_stop_det().read();
|
||||
Poll::Ready(Ok(ReadStatus::Done))
|
||||
} else if stat.rd_req() && stat.tx_empty() {
|
||||
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
|
||||
|
@ -316,8 +330,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
}
|
||||
},
|
||||
|_me| {
|
||||
p.ic_intr_mask().modify(|w| {
|
||||
w.set_m_stop_det(true);
|
||||
p.ic_intr_mask().write(|w| {
|
||||
w.set_m_rx_done(true);
|
||||
w.set_m_tx_empty(true);
|
||||
w.set_m_tx_abrt(true);
|
||||
|
@ -329,9 +342,14 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
|
||||
/// Respond to reads with the fill byte until the controller stops asking
|
||||
pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> {
|
||||
// Send fill bytes a full fifo at a time, to reduce interrupt noise.
|
||||
// This does mean we'll almost certainly abort the write, but since these are fill bytes,
|
||||
// we don't care.
|
||||
let buff = [fill; FIFO_SIZE as usize];
|
||||
loop {
|
||||
match self.respond_to_read(&[fill]).await {
|
||||
match self.respond_to_read(&buff).await {
|
||||
Ok(ReadStatus::NeedMoreBytes) => (),
|
||||
Ok(ReadStatus::LeftoverBytes(_)) => break Ok(()),
|
||||
Ok(_) => break Ok(()),
|
||||
Err(e) => break Err(e),
|
||||
}
|
||||
|
@ -353,10 +371,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
|||
#[inline(always)]
|
||||
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
|
||||
let p = T::regs();
|
||||
let mut abort_reason = p.ic_tx_abrt_source().read();
|
||||
|
||||
// Mask off master_dis
|
||||
abort_reason.set_abrt_master_dis(false);
|
||||
let abort_reason = p.ic_tx_abrt_source().read();
|
||||
|
||||
if abort_reason.0 != 0 {
|
||||
// Note clearing the abort flag also clears the reason, and this
|
||||
|
|
|
@ -55,10 +55,12 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un
|
|||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
embedded-hal-async = { version = "1.0" }
|
||||
embedded-hal-nb = { version = "1.0" }
|
||||
embedded-can = "0.4"
|
||||
|
||||
embedded-storage = "0.3.1"
|
||||
embedded-storage-async = { version = "0.4.1" }
|
||||
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
cortex-m-rt = ">=0.6.15,<0.8"
|
||||
|
@ -80,7 +82,10 @@ chrono = { version = "^0.4", default-features = false, optional = true}
|
|||
bit_field = "0.10.2"
|
||||
document-features = "0.2.7"
|
||||
|
||||
fdcan = { version = "0.2.0", optional = true }
|
||||
static_assertions = { version = "1.1" }
|
||||
volatile-register = { version = "0.2.1" }
|
||||
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
critical-section = { version = "1.1", features = ["std"] }
|
||||
|
@ -695,373 +700,373 @@ stm32f779ai = [ "stm32-metapac/stm32f779ai" ]
|
|||
stm32f779bi = [ "stm32-metapac/stm32f779bi" ]
|
||||
stm32f779ii = [ "stm32-metapac/stm32f779ii" ]
|
||||
stm32f779ni = [ "stm32-metapac/stm32f779ni" ]
|
||||
stm32g030c6 = [ "stm32-metapac/stm32g030c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g030c8 = [ "stm32-metapac/stm32g030c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g030f6 = [ "stm32-metapac/stm32g030f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g030j6 = [ "stm32-metapac/stm32g030j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g030k6 = [ "stm32-metapac/stm32g030k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g030k8 = [ "stm32-metapac/stm32g030k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031c4 = [ "stm32-metapac/stm32g031c4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031c6 = [ "stm32-metapac/stm32g031c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031c8 = [ "stm32-metapac/stm32g031c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031f4 = [ "stm32-metapac/stm32g031f4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031f6 = [ "stm32-metapac/stm32g031f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031f8 = [ "stm32-metapac/stm32g031f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031g4 = [ "stm32-metapac/stm32g031g4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031g6 = [ "stm32-metapac/stm32g031g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031g8 = [ "stm32-metapac/stm32g031g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031j4 = [ "stm32-metapac/stm32g031j4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031j6 = [ "stm32-metapac/stm32g031j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031k4 = [ "stm32-metapac/stm32g031k4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031k6 = [ "stm32-metapac/stm32g031k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031k8 = [ "stm32-metapac/stm32g031k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g031y8 = [ "stm32-metapac/stm32g031y8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041c6 = [ "stm32-metapac/stm32g041c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041c8 = [ "stm32-metapac/stm32g041c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041f6 = [ "stm32-metapac/stm32g041f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041f8 = [ "stm32-metapac/stm32g041f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041g6 = [ "stm32-metapac/stm32g041g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041g8 = [ "stm32-metapac/stm32g041g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041j6 = [ "stm32-metapac/stm32g041j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041k6 = [ "stm32-metapac/stm32g041k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041k8 = [ "stm32-metapac/stm32g041k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g041y8 = [ "stm32-metapac/stm32g041y8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g050c6 = [ "stm32-metapac/stm32g050c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g050c8 = [ "stm32-metapac/stm32g050c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g050f6 = [ "stm32-metapac/stm32g050f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g050k6 = [ "stm32-metapac/stm32g050k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g050k8 = [ "stm32-metapac/stm32g050k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g051c6 = [ "stm32-metapac/stm32g051c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g051c8 = [ "stm32-metapac/stm32g051c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g051f6 = [ "stm32-metapac/stm32g051f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g051f8 = [ "stm32-metapac/stm32g051f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g051g6 = [ "stm32-metapac/stm32g051g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g051g8 = [ "stm32-metapac/stm32g051g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g051k6 = [ "stm32-metapac/stm32g051k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g051k8 = [ "stm32-metapac/stm32g051k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g061c6 = [ "stm32-metapac/stm32g061c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g061c8 = [ "stm32-metapac/stm32g061c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g061f6 = [ "stm32-metapac/stm32g061f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g061f8 = [ "stm32-metapac/stm32g061f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g061g6 = [ "stm32-metapac/stm32g061g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g061g8 = [ "stm32-metapac/stm32g061g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g061k6 = [ "stm32-metapac/stm32g061k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g061k8 = [ "stm32-metapac/stm32g061k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g070cb = [ "stm32-metapac/stm32g070cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g070kb = [ "stm32-metapac/stm32g070kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g070rb = [ "stm32-metapac/stm32g070rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071c6 = [ "stm32-metapac/stm32g071c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071c8 = [ "stm32-metapac/stm32g071c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071cb = [ "stm32-metapac/stm32g071cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071eb = [ "stm32-metapac/stm32g071eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071g6 = [ "stm32-metapac/stm32g071g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071g8 = [ "stm32-metapac/stm32g071g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071gb = [ "stm32-metapac/stm32g071gb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071k6 = [ "stm32-metapac/stm32g071k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071k8 = [ "stm32-metapac/stm32g071k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071kb = [ "stm32-metapac/stm32g071kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071r6 = [ "stm32-metapac/stm32g071r6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071r8 = [ "stm32-metapac/stm32g071r8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g071rb = [ "stm32-metapac/stm32g071rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g081cb = [ "stm32-metapac/stm32g081cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g081eb = [ "stm32-metapac/stm32g081eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g081gb = [ "stm32-metapac/stm32g081gb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g081kb = [ "stm32-metapac/stm32g081kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g081rb = [ "stm32-metapac/stm32g081rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b0re = [ "stm32-metapac/stm32g0b0re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1me = [ "stm32-metapac/stm32g0b1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1re = [ "stm32-metapac/stm32g0b1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1me = [ "stm32-metapac/stm32g0c1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1re = [ "stm32-metapac/stm32g0c1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431c6 = [ "stm32-metapac/stm32g431c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431c8 = [ "stm32-metapac/stm32g431c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431cb = [ "stm32-metapac/stm32g431cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431k6 = [ "stm32-metapac/stm32g431k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431k8 = [ "stm32-metapac/stm32g431k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431kb = [ "stm32-metapac/stm32g431kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431m6 = [ "stm32-metapac/stm32g431m6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431m8 = [ "stm32-metapac/stm32g431m8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431mb = [ "stm32-metapac/stm32g431mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431r6 = [ "stm32-metapac/stm32g431r6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431r8 = [ "stm32-metapac/stm32g431r8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431rb = [ "stm32-metapac/stm32g431rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431v6 = [ "stm32-metapac/stm32g431v6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431v8 = [ "stm32-metapac/stm32g431v8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g431vb = [ "stm32-metapac/stm32g431vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g441cb = [ "stm32-metapac/stm32g441cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g441kb = [ "stm32-metapac/stm32g441kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g441mb = [ "stm32-metapac/stm32g441mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g441rb = [ "stm32-metapac/stm32g441rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g441vb = [ "stm32-metapac/stm32g441vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471cc = [ "stm32-metapac/stm32g471cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471ce = [ "stm32-metapac/stm32g471ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471mc = [ "stm32-metapac/stm32g471mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471me = [ "stm32-metapac/stm32g471me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471qc = [ "stm32-metapac/stm32g471qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471qe = [ "stm32-metapac/stm32g471qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471rc = [ "stm32-metapac/stm32g471rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471re = [ "stm32-metapac/stm32g471re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471vc = [ "stm32-metapac/stm32g471vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g471ve = [ "stm32-metapac/stm32g471ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473cb = [ "stm32-metapac/stm32g473cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473cc = [ "stm32-metapac/stm32g473cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473ce = [ "stm32-metapac/stm32g473ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473mb = [ "stm32-metapac/stm32g473mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473mc = [ "stm32-metapac/stm32g473mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473me = [ "stm32-metapac/stm32g473me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473pb = [ "stm32-metapac/stm32g473pb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473pc = [ "stm32-metapac/stm32g473pc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473pe = [ "stm32-metapac/stm32g473pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473qb = [ "stm32-metapac/stm32g473qb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473qc = [ "stm32-metapac/stm32g473qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473qe = [ "stm32-metapac/stm32g473qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473rb = [ "stm32-metapac/stm32g473rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473rc = [ "stm32-metapac/stm32g473rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473re = [ "stm32-metapac/stm32g473re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473vb = [ "stm32-metapac/stm32g473vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473vc = [ "stm32-metapac/stm32g473vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g473ve = [ "stm32-metapac/stm32g473ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474cb = [ "stm32-metapac/stm32g474cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474cc = [ "stm32-metapac/stm32g474cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474ce = [ "stm32-metapac/stm32g474ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474mb = [ "stm32-metapac/stm32g474mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474mc = [ "stm32-metapac/stm32g474mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474me = [ "stm32-metapac/stm32g474me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474pb = [ "stm32-metapac/stm32g474pb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474pc = [ "stm32-metapac/stm32g474pc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474pe = [ "stm32-metapac/stm32g474pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474qb = [ "stm32-metapac/stm32g474qb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474qc = [ "stm32-metapac/stm32g474qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474qe = [ "stm32-metapac/stm32g474qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474rb = [ "stm32-metapac/stm32g474rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474rc = [ "stm32-metapac/stm32g474rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474re = [ "stm32-metapac/stm32g474re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474vb = [ "stm32-metapac/stm32g474vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474vc = [ "stm32-metapac/stm32g474vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g474ve = [ "stm32-metapac/stm32g474ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g483ce = [ "stm32-metapac/stm32g483ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g483me = [ "stm32-metapac/stm32g483me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g483pe = [ "stm32-metapac/stm32g483pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g483qe = [ "stm32-metapac/stm32g483qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g483re = [ "stm32-metapac/stm32g483re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g483ve = [ "stm32-metapac/stm32g483ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g484ce = [ "stm32-metapac/stm32g484ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g484me = [ "stm32-metapac/stm32g484me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g484pe = [ "stm32-metapac/stm32g484pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g484qe = [ "stm32-metapac/stm32g484qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g484re = [ "stm32-metapac/stm32g484re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g484ve = [ "stm32-metapac/stm32g484ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491cc = [ "stm32-metapac/stm32g491cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491ce = [ "stm32-metapac/stm32g491ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491kc = [ "stm32-metapac/stm32g491kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491ke = [ "stm32-metapac/stm32g491ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491mc = [ "stm32-metapac/stm32g491mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491me = [ "stm32-metapac/stm32g491me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491rc = [ "stm32-metapac/stm32g491rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491re = [ "stm32-metapac/stm32g491re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491vc = [ "stm32-metapac/stm32g491vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g491ve = [ "stm32-metapac/stm32g491ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g4a1me = [ "stm32-metapac/stm32g4a1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g4a1re = [ "stm32-metapac/stm32g4a1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h503cb = [ "stm32-metapac/stm32h503cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h503eb = [ "stm32-metapac/stm32h503eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h503kb = [ "stm32-metapac/stm32h503kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h503rb = [ "stm32-metapac/stm32h503rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562ag = [ "stm32-metapac/stm32h562ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562ai = [ "stm32-metapac/stm32h562ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562ig = [ "stm32-metapac/stm32h562ig", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562ii = [ "stm32-metapac/stm32h562ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562rg = [ "stm32-metapac/stm32h562rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562ri = [ "stm32-metapac/stm32h562ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562vg = [ "stm32-metapac/stm32h562vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562vi = [ "stm32-metapac/stm32h562vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562zg = [ "stm32-metapac/stm32h562zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h562zi = [ "stm32-metapac/stm32h562zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563ag = [ "stm32-metapac/stm32h563ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563ai = [ "stm32-metapac/stm32h563ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563ig = [ "stm32-metapac/stm32h563ig", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563ii = [ "stm32-metapac/stm32h563ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563mi = [ "stm32-metapac/stm32h563mi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563rg = [ "stm32-metapac/stm32h563rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563ri = [ "stm32-metapac/stm32h563ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563vg = [ "stm32-metapac/stm32h563vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563vi = [ "stm32-metapac/stm32h563vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563zg = [ "stm32-metapac/stm32h563zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h563zi = [ "stm32-metapac/stm32h563zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h573ai = [ "stm32-metapac/stm32h573ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h573ii = [ "stm32-metapac/stm32h573ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h573mi = [ "stm32-metapac/stm32h573mi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h573ri = [ "stm32-metapac/stm32h573ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h573vi = [ "stm32-metapac/stm32h573vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h573zi = [ "stm32-metapac/stm32h573zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32h723ve = [ "stm32-metapac/stm32h723ve", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h723vg = [ "stm32-metapac/stm32h723vg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h723ze = [ "stm32-metapac/stm32h723ze", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h723zg = [ "stm32-metapac/stm32h723zg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725ae = [ "stm32-metapac/stm32h725ae", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725ag = [ "stm32-metapac/stm32h725ag", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725ie = [ "stm32-metapac/stm32h725ie", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725ig = [ "stm32-metapac/stm32h725ig", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725re = [ "stm32-metapac/stm32h725re", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725rg = [ "stm32-metapac/stm32h725rg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725ve = [ "stm32-metapac/stm32h725ve", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725vg = [ "stm32-metapac/stm32h725vg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725ze = [ "stm32-metapac/stm32h725ze", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h725zg = [ "stm32-metapac/stm32h725zg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h730ab = [ "stm32-metapac/stm32h730ab", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h730ib = [ "stm32-metapac/stm32h730ib", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h730vb = [ "stm32-metapac/stm32h730vb", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h730zb = [ "stm32-metapac/stm32h730zb", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h733vg = [ "stm32-metapac/stm32h733vg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h733zg = [ "stm32-metapac/stm32h733zg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h735ag = [ "stm32-metapac/stm32h735ag", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h735ig = [ "stm32-metapac/stm32h735ig", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h735rg = [ "stm32-metapac/stm32h735rg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h735vg = [ "stm32-metapac/stm32h735vg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h735zg = [ "stm32-metapac/stm32h735zg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742ag = [ "stm32-metapac/stm32h742ag", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742ai = [ "stm32-metapac/stm32h742ai", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742bg = [ "stm32-metapac/stm32h742bg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742bi = [ "stm32-metapac/stm32h742bi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742ig = [ "stm32-metapac/stm32h742ig", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742ii = [ "stm32-metapac/stm32h742ii", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742vg = [ "stm32-metapac/stm32h742vg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742vi = [ "stm32-metapac/stm32h742vi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742xg = [ "stm32-metapac/stm32h742xg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742xi = [ "stm32-metapac/stm32h742xi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742zg = [ "stm32-metapac/stm32h742zg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h742zi = [ "stm32-metapac/stm32h742zi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743ag = [ "stm32-metapac/stm32h743ag", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743ai = [ "stm32-metapac/stm32h743ai", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743bg = [ "stm32-metapac/stm32h743bg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743bi = [ "stm32-metapac/stm32h743bi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743ig = [ "stm32-metapac/stm32h743ig", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743ii = [ "stm32-metapac/stm32h743ii", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743vg = [ "stm32-metapac/stm32h743vg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743vi = [ "stm32-metapac/stm32h743vi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743xg = [ "stm32-metapac/stm32h743xg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743xi = [ "stm32-metapac/stm32h743xi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743zg = [ "stm32-metapac/stm32h743zg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h743zi = [ "stm32-metapac/stm32h743zi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h750ib = [ "stm32-metapac/stm32h750ib", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h750vb = [ "stm32-metapac/stm32h750vb", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h750xb = [ "stm32-metapac/stm32h750xb", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h750zb = [ "stm32-metapac/stm32h750zb", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h753ai = [ "stm32-metapac/stm32h753ai", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h753bi = [ "stm32-metapac/stm32h753bi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h753ii = [ "stm32-metapac/stm32h753ii", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h753vi = [ "stm32-metapac/stm32h753vi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h753xi = [ "stm32-metapac/stm32h753xi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h753zi = [ "stm32-metapac/stm32h753zi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3li = [ "stm32-metapac/stm32h7a3li", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b3li = [ "stm32-metapac/stm32h7b3li", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi", "dep:fdcan", "fdcan/fdcan_h7" ]
|
||||
stm32g030c6 = [ "stm32-metapac/stm32g030c6" ]
|
||||
stm32g030c8 = [ "stm32-metapac/stm32g030c8" ]
|
||||
stm32g030f6 = [ "stm32-metapac/stm32g030f6" ]
|
||||
stm32g030j6 = [ "stm32-metapac/stm32g030j6" ]
|
||||
stm32g030k6 = [ "stm32-metapac/stm32g030k6" ]
|
||||
stm32g030k8 = [ "stm32-metapac/stm32g030k8" ]
|
||||
stm32g031c4 = [ "stm32-metapac/stm32g031c4" ]
|
||||
stm32g031c6 = [ "stm32-metapac/stm32g031c6" ]
|
||||
stm32g031c8 = [ "stm32-metapac/stm32g031c8" ]
|
||||
stm32g031f4 = [ "stm32-metapac/stm32g031f4" ]
|
||||
stm32g031f6 = [ "stm32-metapac/stm32g031f6" ]
|
||||
stm32g031f8 = [ "stm32-metapac/stm32g031f8" ]
|
||||
stm32g031g4 = [ "stm32-metapac/stm32g031g4" ]
|
||||
stm32g031g6 = [ "stm32-metapac/stm32g031g6" ]
|
||||
stm32g031g8 = [ "stm32-metapac/stm32g031g8" ]
|
||||
stm32g031j4 = [ "stm32-metapac/stm32g031j4" ]
|
||||
stm32g031j6 = [ "stm32-metapac/stm32g031j6" ]
|
||||
stm32g031k4 = [ "stm32-metapac/stm32g031k4" ]
|
||||
stm32g031k6 = [ "stm32-metapac/stm32g031k6" ]
|
||||
stm32g031k8 = [ "stm32-metapac/stm32g031k8" ]
|
||||
stm32g031y8 = [ "stm32-metapac/stm32g031y8" ]
|
||||
stm32g041c6 = [ "stm32-metapac/stm32g041c6" ]
|
||||
stm32g041c8 = [ "stm32-metapac/stm32g041c8" ]
|
||||
stm32g041f6 = [ "stm32-metapac/stm32g041f6" ]
|
||||
stm32g041f8 = [ "stm32-metapac/stm32g041f8" ]
|
||||
stm32g041g6 = [ "stm32-metapac/stm32g041g6" ]
|
||||
stm32g041g8 = [ "stm32-metapac/stm32g041g8" ]
|
||||
stm32g041j6 = [ "stm32-metapac/stm32g041j6" ]
|
||||
stm32g041k6 = [ "stm32-metapac/stm32g041k6" ]
|
||||
stm32g041k8 = [ "stm32-metapac/stm32g041k8" ]
|
||||
stm32g041y8 = [ "stm32-metapac/stm32g041y8" ]
|
||||
stm32g050c6 = [ "stm32-metapac/stm32g050c6" ]
|
||||
stm32g050c8 = [ "stm32-metapac/stm32g050c8" ]
|
||||
stm32g050f6 = [ "stm32-metapac/stm32g050f6" ]
|
||||
stm32g050k6 = [ "stm32-metapac/stm32g050k6" ]
|
||||
stm32g050k8 = [ "stm32-metapac/stm32g050k8" ]
|
||||
stm32g051c6 = [ "stm32-metapac/stm32g051c6" ]
|
||||
stm32g051c8 = [ "stm32-metapac/stm32g051c8" ]
|
||||
stm32g051f6 = [ "stm32-metapac/stm32g051f6" ]
|
||||
stm32g051f8 = [ "stm32-metapac/stm32g051f8" ]
|
||||
stm32g051g6 = [ "stm32-metapac/stm32g051g6" ]
|
||||
stm32g051g8 = [ "stm32-metapac/stm32g051g8" ]
|
||||
stm32g051k6 = [ "stm32-metapac/stm32g051k6" ]
|
||||
stm32g051k8 = [ "stm32-metapac/stm32g051k8" ]
|
||||
stm32g061c6 = [ "stm32-metapac/stm32g061c6" ]
|
||||
stm32g061c8 = [ "stm32-metapac/stm32g061c8" ]
|
||||
stm32g061f6 = [ "stm32-metapac/stm32g061f6" ]
|
||||
stm32g061f8 = [ "stm32-metapac/stm32g061f8" ]
|
||||
stm32g061g6 = [ "stm32-metapac/stm32g061g6" ]
|
||||
stm32g061g8 = [ "stm32-metapac/stm32g061g8" ]
|
||||
stm32g061k6 = [ "stm32-metapac/stm32g061k6" ]
|
||||
stm32g061k8 = [ "stm32-metapac/stm32g061k8" ]
|
||||
stm32g070cb = [ "stm32-metapac/stm32g070cb" ]
|
||||
stm32g070kb = [ "stm32-metapac/stm32g070kb" ]
|
||||
stm32g070rb = [ "stm32-metapac/stm32g070rb" ]
|
||||
stm32g071c6 = [ "stm32-metapac/stm32g071c6" ]
|
||||
stm32g071c8 = [ "stm32-metapac/stm32g071c8" ]
|
||||
stm32g071cb = [ "stm32-metapac/stm32g071cb" ]
|
||||
stm32g071eb = [ "stm32-metapac/stm32g071eb" ]
|
||||
stm32g071g6 = [ "stm32-metapac/stm32g071g6" ]
|
||||
stm32g071g8 = [ "stm32-metapac/stm32g071g8" ]
|
||||
stm32g071gb = [ "stm32-metapac/stm32g071gb" ]
|
||||
stm32g071k6 = [ "stm32-metapac/stm32g071k6" ]
|
||||
stm32g071k8 = [ "stm32-metapac/stm32g071k8" ]
|
||||
stm32g071kb = [ "stm32-metapac/stm32g071kb" ]
|
||||
stm32g071r6 = [ "stm32-metapac/stm32g071r6" ]
|
||||
stm32g071r8 = [ "stm32-metapac/stm32g071r8" ]
|
||||
stm32g071rb = [ "stm32-metapac/stm32g071rb" ]
|
||||
stm32g081cb = [ "stm32-metapac/stm32g081cb" ]
|
||||
stm32g081eb = [ "stm32-metapac/stm32g081eb" ]
|
||||
stm32g081gb = [ "stm32-metapac/stm32g081gb" ]
|
||||
stm32g081kb = [ "stm32-metapac/stm32g081kb" ]
|
||||
stm32g081rb = [ "stm32-metapac/stm32g081rb" ]
|
||||
stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce" ]
|
||||
stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke" ]
|
||||
stm32g0b0re = [ "stm32-metapac/stm32g0b0re" ]
|
||||
stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve" ]
|
||||
stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb" ]
|
||||
stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc" ]
|
||||
stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce" ]
|
||||
stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb" ]
|
||||
stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc" ]
|
||||
stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke" ]
|
||||
stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb" ]
|
||||
stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc" ]
|
||||
stm32g0b1me = [ "stm32-metapac/stm32g0b1me" ]
|
||||
stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne" ]
|
||||
stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb" ]
|
||||
stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc" ]
|
||||
stm32g0b1re = [ "stm32-metapac/stm32g0b1re" ]
|
||||
stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb" ]
|
||||
stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc" ]
|
||||
stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve" ]
|
||||
stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc" ]
|
||||
stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce" ]
|
||||
stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc" ]
|
||||
stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke" ]
|
||||
stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc" ]
|
||||
stm32g0c1me = [ "stm32-metapac/stm32g0c1me" ]
|
||||
stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne" ]
|
||||
stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc" ]
|
||||
stm32g0c1re = [ "stm32-metapac/stm32g0c1re" ]
|
||||
stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc" ]
|
||||
stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve" ]
|
||||
stm32g431c6 = [ "stm32-metapac/stm32g431c6" ]
|
||||
stm32g431c8 = [ "stm32-metapac/stm32g431c8" ]
|
||||
stm32g431cb = [ "stm32-metapac/stm32g431cb" ]
|
||||
stm32g431k6 = [ "stm32-metapac/stm32g431k6" ]
|
||||
stm32g431k8 = [ "stm32-metapac/stm32g431k8" ]
|
||||
stm32g431kb = [ "stm32-metapac/stm32g431kb" ]
|
||||
stm32g431m6 = [ "stm32-metapac/stm32g431m6" ]
|
||||
stm32g431m8 = [ "stm32-metapac/stm32g431m8" ]
|
||||
stm32g431mb = [ "stm32-metapac/stm32g431mb" ]
|
||||
stm32g431r6 = [ "stm32-metapac/stm32g431r6" ]
|
||||
stm32g431r8 = [ "stm32-metapac/stm32g431r8" ]
|
||||
stm32g431rb = [ "stm32-metapac/stm32g431rb" ]
|
||||
stm32g431v6 = [ "stm32-metapac/stm32g431v6" ]
|
||||
stm32g431v8 = [ "stm32-metapac/stm32g431v8" ]
|
||||
stm32g431vb = [ "stm32-metapac/stm32g431vb" ]
|
||||
stm32g441cb = [ "stm32-metapac/stm32g441cb" ]
|
||||
stm32g441kb = [ "stm32-metapac/stm32g441kb" ]
|
||||
stm32g441mb = [ "stm32-metapac/stm32g441mb" ]
|
||||
stm32g441rb = [ "stm32-metapac/stm32g441rb" ]
|
||||
stm32g441vb = [ "stm32-metapac/stm32g441vb" ]
|
||||
stm32g471cc = [ "stm32-metapac/stm32g471cc" ]
|
||||
stm32g471ce = [ "stm32-metapac/stm32g471ce" ]
|
||||
stm32g471mc = [ "stm32-metapac/stm32g471mc" ]
|
||||
stm32g471me = [ "stm32-metapac/stm32g471me" ]
|
||||
stm32g471qc = [ "stm32-metapac/stm32g471qc" ]
|
||||
stm32g471qe = [ "stm32-metapac/stm32g471qe" ]
|
||||
stm32g471rc = [ "stm32-metapac/stm32g471rc" ]
|
||||
stm32g471re = [ "stm32-metapac/stm32g471re" ]
|
||||
stm32g471vc = [ "stm32-metapac/stm32g471vc" ]
|
||||
stm32g471ve = [ "stm32-metapac/stm32g471ve" ]
|
||||
stm32g473cb = [ "stm32-metapac/stm32g473cb" ]
|
||||
stm32g473cc = [ "stm32-metapac/stm32g473cc" ]
|
||||
stm32g473ce = [ "stm32-metapac/stm32g473ce" ]
|
||||
stm32g473mb = [ "stm32-metapac/stm32g473mb" ]
|
||||
stm32g473mc = [ "stm32-metapac/stm32g473mc" ]
|
||||
stm32g473me = [ "stm32-metapac/stm32g473me" ]
|
||||
stm32g473pb = [ "stm32-metapac/stm32g473pb" ]
|
||||
stm32g473pc = [ "stm32-metapac/stm32g473pc" ]
|
||||
stm32g473pe = [ "stm32-metapac/stm32g473pe" ]
|
||||
stm32g473qb = [ "stm32-metapac/stm32g473qb" ]
|
||||
stm32g473qc = [ "stm32-metapac/stm32g473qc" ]
|
||||
stm32g473qe = [ "stm32-metapac/stm32g473qe" ]
|
||||
stm32g473rb = [ "stm32-metapac/stm32g473rb" ]
|
||||
stm32g473rc = [ "stm32-metapac/stm32g473rc" ]
|
||||
stm32g473re = [ "stm32-metapac/stm32g473re" ]
|
||||
stm32g473vb = [ "stm32-metapac/stm32g473vb" ]
|
||||
stm32g473vc = [ "stm32-metapac/stm32g473vc" ]
|
||||
stm32g473ve = [ "stm32-metapac/stm32g473ve" ]
|
||||
stm32g474cb = [ "stm32-metapac/stm32g474cb" ]
|
||||
stm32g474cc = [ "stm32-metapac/stm32g474cc" ]
|
||||
stm32g474ce = [ "stm32-metapac/stm32g474ce" ]
|
||||
stm32g474mb = [ "stm32-metapac/stm32g474mb" ]
|
||||
stm32g474mc = [ "stm32-metapac/stm32g474mc" ]
|
||||
stm32g474me = [ "stm32-metapac/stm32g474me" ]
|
||||
stm32g474pb = [ "stm32-metapac/stm32g474pb" ]
|
||||
stm32g474pc = [ "stm32-metapac/stm32g474pc" ]
|
||||
stm32g474pe = [ "stm32-metapac/stm32g474pe" ]
|
||||
stm32g474qb = [ "stm32-metapac/stm32g474qb" ]
|
||||
stm32g474qc = [ "stm32-metapac/stm32g474qc" ]
|
||||
stm32g474qe = [ "stm32-metapac/stm32g474qe" ]
|
||||
stm32g474rb = [ "stm32-metapac/stm32g474rb" ]
|
||||
stm32g474rc = [ "stm32-metapac/stm32g474rc" ]
|
||||
stm32g474re = [ "stm32-metapac/stm32g474re" ]
|
||||
stm32g474vb = [ "stm32-metapac/stm32g474vb" ]
|
||||
stm32g474vc = [ "stm32-metapac/stm32g474vc" ]
|
||||
stm32g474ve = [ "stm32-metapac/stm32g474ve" ]
|
||||
stm32g483ce = [ "stm32-metapac/stm32g483ce" ]
|
||||
stm32g483me = [ "stm32-metapac/stm32g483me" ]
|
||||
stm32g483pe = [ "stm32-metapac/stm32g483pe" ]
|
||||
stm32g483qe = [ "stm32-metapac/stm32g483qe" ]
|
||||
stm32g483re = [ "stm32-metapac/stm32g483re" ]
|
||||
stm32g483ve = [ "stm32-metapac/stm32g483ve" ]
|
||||
stm32g484ce = [ "stm32-metapac/stm32g484ce" ]
|
||||
stm32g484me = [ "stm32-metapac/stm32g484me" ]
|
||||
stm32g484pe = [ "stm32-metapac/stm32g484pe" ]
|
||||
stm32g484qe = [ "stm32-metapac/stm32g484qe" ]
|
||||
stm32g484re = [ "stm32-metapac/stm32g484re" ]
|
||||
stm32g484ve = [ "stm32-metapac/stm32g484ve" ]
|
||||
stm32g491cc = [ "stm32-metapac/stm32g491cc" ]
|
||||
stm32g491ce = [ "stm32-metapac/stm32g491ce" ]
|
||||
stm32g491kc = [ "stm32-metapac/stm32g491kc" ]
|
||||
stm32g491ke = [ "stm32-metapac/stm32g491ke" ]
|
||||
stm32g491mc = [ "stm32-metapac/stm32g491mc" ]
|
||||
stm32g491me = [ "stm32-metapac/stm32g491me" ]
|
||||
stm32g491rc = [ "stm32-metapac/stm32g491rc" ]
|
||||
stm32g491re = [ "stm32-metapac/stm32g491re" ]
|
||||
stm32g491vc = [ "stm32-metapac/stm32g491vc" ]
|
||||
stm32g491ve = [ "stm32-metapac/stm32g491ve" ]
|
||||
stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce" ]
|
||||
stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ]
|
||||
stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ]
|
||||
stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ]
|
||||
stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ]
|
||||
stm32h503cb = [ "stm32-metapac/stm32h503cb" ]
|
||||
stm32h503eb = [ "stm32-metapac/stm32h503eb" ]
|
||||
stm32h503kb = [ "stm32-metapac/stm32h503kb" ]
|
||||
stm32h503rb = [ "stm32-metapac/stm32h503rb" ]
|
||||
stm32h562ag = [ "stm32-metapac/stm32h562ag" ]
|
||||
stm32h562ai = [ "stm32-metapac/stm32h562ai" ]
|
||||
stm32h562ig = [ "stm32-metapac/stm32h562ig" ]
|
||||
stm32h562ii = [ "stm32-metapac/stm32h562ii" ]
|
||||
stm32h562rg = [ "stm32-metapac/stm32h562rg" ]
|
||||
stm32h562ri = [ "stm32-metapac/stm32h562ri" ]
|
||||
stm32h562vg = [ "stm32-metapac/stm32h562vg" ]
|
||||
stm32h562vi = [ "stm32-metapac/stm32h562vi" ]
|
||||
stm32h562zg = [ "stm32-metapac/stm32h562zg" ]
|
||||
stm32h562zi = [ "stm32-metapac/stm32h562zi" ]
|
||||
stm32h563ag = [ "stm32-metapac/stm32h563ag" ]
|
||||
stm32h563ai = [ "stm32-metapac/stm32h563ai" ]
|
||||
stm32h563ig = [ "stm32-metapac/stm32h563ig" ]
|
||||
stm32h563ii = [ "stm32-metapac/stm32h563ii" ]
|
||||
stm32h563mi = [ "stm32-metapac/stm32h563mi" ]
|
||||
stm32h563rg = [ "stm32-metapac/stm32h563rg" ]
|
||||
stm32h563ri = [ "stm32-metapac/stm32h563ri" ]
|
||||
stm32h563vg = [ "stm32-metapac/stm32h563vg" ]
|
||||
stm32h563vi = [ "stm32-metapac/stm32h563vi" ]
|
||||
stm32h563zg = [ "stm32-metapac/stm32h563zg" ]
|
||||
stm32h563zi = [ "stm32-metapac/stm32h563zi" ]
|
||||
stm32h573ai = [ "stm32-metapac/stm32h573ai" ]
|
||||
stm32h573ii = [ "stm32-metapac/stm32h573ii" ]
|
||||
stm32h573mi = [ "stm32-metapac/stm32h573mi" ]
|
||||
stm32h573ri = [ "stm32-metapac/stm32h573ri" ]
|
||||
stm32h573vi = [ "stm32-metapac/stm32h573vi" ]
|
||||
stm32h573zi = [ "stm32-metapac/stm32h573zi" ]
|
||||
stm32h723ve = [ "stm32-metapac/stm32h723ve" ]
|
||||
stm32h723vg = [ "stm32-metapac/stm32h723vg" ]
|
||||
stm32h723ze = [ "stm32-metapac/stm32h723ze" ]
|
||||
stm32h723zg = [ "stm32-metapac/stm32h723zg" ]
|
||||
stm32h725ae = [ "stm32-metapac/stm32h725ae" ]
|
||||
stm32h725ag = [ "stm32-metapac/stm32h725ag" ]
|
||||
stm32h725ie = [ "stm32-metapac/stm32h725ie" ]
|
||||
stm32h725ig = [ "stm32-metapac/stm32h725ig" ]
|
||||
stm32h725re = [ "stm32-metapac/stm32h725re" ]
|
||||
stm32h725rg = [ "stm32-metapac/stm32h725rg" ]
|
||||
stm32h725ve = [ "stm32-metapac/stm32h725ve" ]
|
||||
stm32h725vg = [ "stm32-metapac/stm32h725vg" ]
|
||||
stm32h725ze = [ "stm32-metapac/stm32h725ze" ]
|
||||
stm32h725zg = [ "stm32-metapac/stm32h725zg" ]
|
||||
stm32h730ab = [ "stm32-metapac/stm32h730ab" ]
|
||||
stm32h730ib = [ "stm32-metapac/stm32h730ib" ]
|
||||
stm32h730vb = [ "stm32-metapac/stm32h730vb" ]
|
||||
stm32h730zb = [ "stm32-metapac/stm32h730zb" ]
|
||||
stm32h733vg = [ "stm32-metapac/stm32h733vg" ]
|
||||
stm32h733zg = [ "stm32-metapac/stm32h733zg" ]
|
||||
stm32h735ag = [ "stm32-metapac/stm32h735ag" ]
|
||||
stm32h735ig = [ "stm32-metapac/stm32h735ig" ]
|
||||
stm32h735rg = [ "stm32-metapac/stm32h735rg" ]
|
||||
stm32h735vg = [ "stm32-metapac/stm32h735vg" ]
|
||||
stm32h735zg = [ "stm32-metapac/stm32h735zg" ]
|
||||
stm32h742ag = [ "stm32-metapac/stm32h742ag" ]
|
||||
stm32h742ai = [ "stm32-metapac/stm32h742ai" ]
|
||||
stm32h742bg = [ "stm32-metapac/stm32h742bg" ]
|
||||
stm32h742bi = [ "stm32-metapac/stm32h742bi" ]
|
||||
stm32h742ig = [ "stm32-metapac/stm32h742ig" ]
|
||||
stm32h742ii = [ "stm32-metapac/stm32h742ii" ]
|
||||
stm32h742vg = [ "stm32-metapac/stm32h742vg" ]
|
||||
stm32h742vi = [ "stm32-metapac/stm32h742vi" ]
|
||||
stm32h742xg = [ "stm32-metapac/stm32h742xg" ]
|
||||
stm32h742xi = [ "stm32-metapac/stm32h742xi" ]
|
||||
stm32h742zg = [ "stm32-metapac/stm32h742zg" ]
|
||||
stm32h742zi = [ "stm32-metapac/stm32h742zi" ]
|
||||
stm32h743ag = [ "stm32-metapac/stm32h743ag" ]
|
||||
stm32h743ai = [ "stm32-metapac/stm32h743ai" ]
|
||||
stm32h743bg = [ "stm32-metapac/stm32h743bg" ]
|
||||
stm32h743bi = [ "stm32-metapac/stm32h743bi" ]
|
||||
stm32h743ig = [ "stm32-metapac/stm32h743ig" ]
|
||||
stm32h743ii = [ "stm32-metapac/stm32h743ii" ]
|
||||
stm32h743vg = [ "stm32-metapac/stm32h743vg" ]
|
||||
stm32h743vi = [ "stm32-metapac/stm32h743vi" ]
|
||||
stm32h743xg = [ "stm32-metapac/stm32h743xg" ]
|
||||
stm32h743xi = [ "stm32-metapac/stm32h743xi" ]
|
||||
stm32h743zg = [ "stm32-metapac/stm32h743zg" ]
|
||||
stm32h743zi = [ "stm32-metapac/stm32h743zi" ]
|
||||
stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7" ]
|
||||
stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4" ]
|
||||
stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7" ]
|
||||
stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4" ]
|
||||
stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7" ]
|
||||
stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4" ]
|
||||
stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7" ]
|
||||
stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4" ]
|
||||
stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7" ]
|
||||
stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4" ]
|
||||
stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7" ]
|
||||
stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4" ]
|
||||
stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7" ]
|
||||
stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4" ]
|
||||
stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7" ]
|
||||
stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4" ]
|
||||
stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7" ]
|
||||
stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4" ]
|
||||
stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7" ]
|
||||
stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4" ]
|
||||
stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7" ]
|
||||
stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4" ]
|
||||
stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7" ]
|
||||
stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4" ]
|
||||
stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7" ]
|
||||
stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4" ]
|
||||
stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7" ]
|
||||
stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4" ]
|
||||
stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7" ]
|
||||
stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4" ]
|
||||
stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7" ]
|
||||
stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4" ]
|
||||
stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7" ]
|
||||
stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4" ]
|
||||
stm32h750ib = [ "stm32-metapac/stm32h750ib" ]
|
||||
stm32h750vb = [ "stm32-metapac/stm32h750vb" ]
|
||||
stm32h750xb = [ "stm32-metapac/stm32h750xb" ]
|
||||
stm32h750zb = [ "stm32-metapac/stm32h750zb" ]
|
||||
stm32h753ai = [ "stm32-metapac/stm32h753ai" ]
|
||||
stm32h753bi = [ "stm32-metapac/stm32h753bi" ]
|
||||
stm32h753ii = [ "stm32-metapac/stm32h753ii" ]
|
||||
stm32h753vi = [ "stm32-metapac/stm32h753vi" ]
|
||||
stm32h753xi = [ "stm32-metapac/stm32h753xi" ]
|
||||
stm32h753zi = [ "stm32-metapac/stm32h753zi" ]
|
||||
stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7" ]
|
||||
stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4" ]
|
||||
stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7" ]
|
||||
stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4" ]
|
||||
stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7" ]
|
||||
stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4" ]
|
||||
stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7" ]
|
||||
stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4" ]
|
||||
stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7" ]
|
||||
stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4" ]
|
||||
stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7" ]
|
||||
stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4" ]
|
||||
stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7" ]
|
||||
stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4" ]
|
||||
stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7" ]
|
||||
stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4" ]
|
||||
stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7" ]
|
||||
stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4" ]
|
||||
stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ]
|
||||
stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ]
|
||||
stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ]
|
||||
stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii" ]
|
||||
stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg" ]
|
||||
stm32h7a3li = [ "stm32-metapac/stm32h7a3li" ]
|
||||
stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng" ]
|
||||
stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni" ]
|
||||
stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi" ]
|
||||
stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg" ]
|
||||
stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri" ]
|
||||
stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg" ]
|
||||
stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi" ]
|
||||
stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg" ]
|
||||
stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi" ]
|
||||
stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab" ]
|
||||
stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib" ]
|
||||
stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb" ]
|
||||
stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb" ]
|
||||
stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb" ]
|
||||
stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai" ]
|
||||
stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii" ]
|
||||
stm32h7b3li = [ "stm32-metapac/stm32h7b3li" ]
|
||||
stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni" ]
|
||||
stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi" ]
|
||||
stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri" ]
|
||||
stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi" ]
|
||||
stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi" ]
|
||||
stm32l010c6 = [ "stm32-metapac/stm32l010c6" ]
|
||||
stm32l010f4 = [ "stm32-metapac/stm32l010f4" ]
|
||||
stm32l010k4 = [ "stm32-metapac/stm32l010k4" ]
|
||||
|
@ -1388,86 +1393,86 @@ stm32l4s7zi = [ "stm32-metapac/stm32l4s7zi" ]
|
|||
stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ]
|
||||
stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ]
|
||||
stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ]
|
||||
stm32l552cc = [ "stm32-metapac/stm32l552cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552ce = [ "stm32-metapac/stm32l552ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552me = [ "stm32-metapac/stm32l552me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552qc = [ "stm32-metapac/stm32l552qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552qe = [ "stm32-metapac/stm32l552qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552rc = [ "stm32-metapac/stm32l552rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552re = [ "stm32-metapac/stm32l552re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552vc = [ "stm32-metapac/stm32l552vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552ve = [ "stm32-metapac/stm32l552ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552zc = [ "stm32-metapac/stm32l552zc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552ze = [ "stm32-metapac/stm32l552ze", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l562ce = [ "stm32-metapac/stm32l562ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l562me = [ "stm32-metapac/stm32l562me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l562qe = [ "stm32-metapac/stm32l562qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l562re = [ "stm32-metapac/stm32l562re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l562ve = [ "stm32-metapac/stm32l562ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l562ze = [ "stm32-metapac/stm32l562ze", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535cb = [ "stm32-metapac/stm32u535cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535cc = [ "stm32-metapac/stm32u535cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535ce = [ "stm32-metapac/stm32u535ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535je = [ "stm32-metapac/stm32u535je", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535nc = [ "stm32-metapac/stm32u535nc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535ne = [ "stm32-metapac/stm32u535ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535rb = [ "stm32-metapac/stm32u535rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535rc = [ "stm32-metapac/stm32u535rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535re = [ "stm32-metapac/stm32u535re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535vc = [ "stm32-metapac/stm32u535vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u535ve = [ "stm32-metapac/stm32u535ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u545ce = [ "stm32-metapac/stm32u545ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u545je = [ "stm32-metapac/stm32u545je", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u545ne = [ "stm32-metapac/stm32u545ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u545re = [ "stm32-metapac/stm32u545re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u545ve = [ "stm32-metapac/stm32u545ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575ag = [ "stm32-metapac/stm32u575ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575ai = [ "stm32-metapac/stm32u575ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575cg = [ "stm32-metapac/stm32u575cg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575ci = [ "stm32-metapac/stm32u575ci", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575og = [ "stm32-metapac/stm32u575og", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575oi = [ "stm32-metapac/stm32u575oi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575qg = [ "stm32-metapac/stm32u575qg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575qi = [ "stm32-metapac/stm32u575qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575rg = [ "stm32-metapac/stm32u575rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575ri = [ "stm32-metapac/stm32u575ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575vg = [ "stm32-metapac/stm32u575vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575vi = [ "stm32-metapac/stm32u575vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575zg = [ "stm32-metapac/stm32u575zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u575zi = [ "stm32-metapac/stm32u575zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u585ai = [ "stm32-metapac/stm32u585ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u585ci = [ "stm32-metapac/stm32u585ci", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u585oi = [ "stm32-metapac/stm32u585oi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u585qi = [ "stm32-metapac/stm32u585qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u585ri = [ "stm32-metapac/stm32u585ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u585vi = [ "stm32-metapac/stm32u585vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u585zi = [ "stm32-metapac/stm32u585zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595ai = [ "stm32-metapac/stm32u595ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595aj = [ "stm32-metapac/stm32u595aj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595qi = [ "stm32-metapac/stm32u595qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595qj = [ "stm32-metapac/stm32u595qj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595ri = [ "stm32-metapac/stm32u595ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595rj = [ "stm32-metapac/stm32u595rj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595vi = [ "stm32-metapac/stm32u595vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595vj = [ "stm32-metapac/stm32u595vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595zi = [ "stm32-metapac/stm32u595zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u595zj = [ "stm32-metapac/stm32u595zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u599bj = [ "stm32-metapac/stm32u599bj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u599ni = [ "stm32-metapac/stm32u599ni", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u599nj = [ "stm32-metapac/stm32u599nj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u599vi = [ "stm32-metapac/stm32u599vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u599vj = [ "stm32-metapac/stm32u599vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u599zi = [ "stm32-metapac/stm32u599zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u599zj = [ "stm32-metapac/stm32u599zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ]
|
||||
stm32l552cc = [ "stm32-metapac/stm32l552cc" ]
|
||||
stm32l552ce = [ "stm32-metapac/stm32l552ce" ]
|
||||
stm32l552me = [ "stm32-metapac/stm32l552me" ]
|
||||
stm32l552qc = [ "stm32-metapac/stm32l552qc" ]
|
||||
stm32l552qe = [ "stm32-metapac/stm32l552qe" ]
|
||||
stm32l552rc = [ "stm32-metapac/stm32l552rc" ]
|
||||
stm32l552re = [ "stm32-metapac/stm32l552re" ]
|
||||
stm32l552vc = [ "stm32-metapac/stm32l552vc" ]
|
||||
stm32l552ve = [ "stm32-metapac/stm32l552ve" ]
|
||||
stm32l552zc = [ "stm32-metapac/stm32l552zc" ]
|
||||
stm32l552ze = [ "stm32-metapac/stm32l552ze" ]
|
||||
stm32l562ce = [ "stm32-metapac/stm32l562ce" ]
|
||||
stm32l562me = [ "stm32-metapac/stm32l562me" ]
|
||||
stm32l562qe = [ "stm32-metapac/stm32l562qe" ]
|
||||
stm32l562re = [ "stm32-metapac/stm32l562re" ]
|
||||
stm32l562ve = [ "stm32-metapac/stm32l562ve" ]
|
||||
stm32l562ze = [ "stm32-metapac/stm32l562ze" ]
|
||||
stm32u535cb = [ "stm32-metapac/stm32u535cb" ]
|
||||
stm32u535cc = [ "stm32-metapac/stm32u535cc" ]
|
||||
stm32u535ce = [ "stm32-metapac/stm32u535ce" ]
|
||||
stm32u535je = [ "stm32-metapac/stm32u535je" ]
|
||||
stm32u535nc = [ "stm32-metapac/stm32u535nc" ]
|
||||
stm32u535ne = [ "stm32-metapac/stm32u535ne" ]
|
||||
stm32u535rb = [ "stm32-metapac/stm32u535rb" ]
|
||||
stm32u535rc = [ "stm32-metapac/stm32u535rc" ]
|
||||
stm32u535re = [ "stm32-metapac/stm32u535re" ]
|
||||
stm32u535vc = [ "stm32-metapac/stm32u535vc" ]
|
||||
stm32u535ve = [ "stm32-metapac/stm32u535ve" ]
|
||||
stm32u545ce = [ "stm32-metapac/stm32u545ce" ]
|
||||
stm32u545je = [ "stm32-metapac/stm32u545je" ]
|
||||
stm32u545ne = [ "stm32-metapac/stm32u545ne" ]
|
||||
stm32u545re = [ "stm32-metapac/stm32u545re" ]
|
||||
stm32u545ve = [ "stm32-metapac/stm32u545ve" ]
|
||||
stm32u575ag = [ "stm32-metapac/stm32u575ag" ]
|
||||
stm32u575ai = [ "stm32-metapac/stm32u575ai" ]
|
||||
stm32u575cg = [ "stm32-metapac/stm32u575cg" ]
|
||||
stm32u575ci = [ "stm32-metapac/stm32u575ci" ]
|
||||
stm32u575og = [ "stm32-metapac/stm32u575og" ]
|
||||
stm32u575oi = [ "stm32-metapac/stm32u575oi" ]
|
||||
stm32u575qg = [ "stm32-metapac/stm32u575qg" ]
|
||||
stm32u575qi = [ "stm32-metapac/stm32u575qi" ]
|
||||
stm32u575rg = [ "stm32-metapac/stm32u575rg" ]
|
||||
stm32u575ri = [ "stm32-metapac/stm32u575ri" ]
|
||||
stm32u575vg = [ "stm32-metapac/stm32u575vg" ]
|
||||
stm32u575vi = [ "stm32-metapac/stm32u575vi" ]
|
||||
stm32u575zg = [ "stm32-metapac/stm32u575zg" ]
|
||||
stm32u575zi = [ "stm32-metapac/stm32u575zi" ]
|
||||
stm32u585ai = [ "stm32-metapac/stm32u585ai" ]
|
||||
stm32u585ci = [ "stm32-metapac/stm32u585ci" ]
|
||||
stm32u585oi = [ "stm32-metapac/stm32u585oi" ]
|
||||
stm32u585qi = [ "stm32-metapac/stm32u585qi" ]
|
||||
stm32u585ri = [ "stm32-metapac/stm32u585ri" ]
|
||||
stm32u585vi = [ "stm32-metapac/stm32u585vi" ]
|
||||
stm32u585zi = [ "stm32-metapac/stm32u585zi" ]
|
||||
stm32u595ai = [ "stm32-metapac/stm32u595ai" ]
|
||||
stm32u595aj = [ "stm32-metapac/stm32u595aj" ]
|
||||
stm32u595qi = [ "stm32-metapac/stm32u595qi" ]
|
||||
stm32u595qj = [ "stm32-metapac/stm32u595qj" ]
|
||||
stm32u595ri = [ "stm32-metapac/stm32u595ri" ]
|
||||
stm32u595rj = [ "stm32-metapac/stm32u595rj" ]
|
||||
stm32u595vi = [ "stm32-metapac/stm32u595vi" ]
|
||||
stm32u595vj = [ "stm32-metapac/stm32u595vj" ]
|
||||
stm32u595zi = [ "stm32-metapac/stm32u595zi" ]
|
||||
stm32u595zj = [ "stm32-metapac/stm32u595zj" ]
|
||||
stm32u599bj = [ "stm32-metapac/stm32u599bj" ]
|
||||
stm32u599ni = [ "stm32-metapac/stm32u599ni" ]
|
||||
stm32u599nj = [ "stm32-metapac/stm32u599nj" ]
|
||||
stm32u599vi = [ "stm32-metapac/stm32u599vi" ]
|
||||
stm32u599vj = [ "stm32-metapac/stm32u599vj" ]
|
||||
stm32u599zi = [ "stm32-metapac/stm32u599zi" ]
|
||||
stm32u599zj = [ "stm32-metapac/stm32u599zj" ]
|
||||
stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ]
|
||||
stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ]
|
||||
stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ]
|
||||
stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ]
|
||||
stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ]
|
||||
stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ]
|
||||
stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ]
|
||||
stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ]
|
||||
stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ]
|
||||
stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ]
|
||||
stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ]
|
||||
stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ]
|
||||
|
|
438
embassy-stm32/src/can/fd/config.rs
Normal file
438
embassy-stm32/src/can/fd/config.rs
Normal file
|
@ -0,0 +1,438 @@
|
|||
//! Configuration for FDCAN Module
|
||||
//! Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
use core::num::{NonZeroU16, NonZeroU8};
|
||||
|
||||
/// Configures the bit timings.
|
||||
///
|
||||
/// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter
|
||||
/// parameters as follows:
|
||||
///
|
||||
/// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed).
|
||||
/// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1).
|
||||
/// - *Sample Point*: Should normally be left at the default value of 87.5%.
|
||||
/// - *SJW*: Should normally be left at the default value of 1.
|
||||
///
|
||||
/// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr`
|
||||
/// parameter to this method.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct NominalBitTiming {
|
||||
/// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit
|
||||
/// time is built up from a multiple of this quanta. Valid values are 1 to 512.
|
||||
pub prescaler: NonZeroU16,
|
||||
/// Valid values are 1 to 128.
|
||||
pub seg1: NonZeroU8,
|
||||
/// Valid values are 1 to 255.
|
||||
pub seg2: NonZeroU8,
|
||||
/// Valid values are 1 to 128.
|
||||
pub sync_jump_width: NonZeroU8,
|
||||
}
|
||||
impl NominalBitTiming {
|
||||
#[inline]
|
||||
pub(crate) fn nbrp(&self) -> u16 {
|
||||
u16::from(self.prescaler) & 0x1FF
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn ntseg1(&self) -> u8 {
|
||||
u8::from(self.seg1)
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn ntseg2(&self) -> u8 {
|
||||
u8::from(self.seg2) & 0x7F
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn nsjw(&self) -> u8 {
|
||||
u8::from(self.sync_jump_width) & 0x7F
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NominalBitTiming {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
// Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a NBTP
|
||||
// register value of 0x0600_0A03
|
||||
Self {
|
||||
prescaler: NonZeroU16::new(1).unwrap(),
|
||||
seg1: NonZeroU8::new(11).unwrap(),
|
||||
seg2: NonZeroU8::new(4).unwrap(),
|
||||
sync_jump_width: NonZeroU8::new(4).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the data bit timings for the FdCan Variable Bitrates.
|
||||
/// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct DataBitTiming {
|
||||
/// Tranceiver Delay Compensation
|
||||
pub transceiver_delay_compensation: bool,
|
||||
/// The value by which the oscillator frequency is divided to generate the bit time quanta. The bit
|
||||
/// time is built up from a multiple of this quanta. Valid values for the Baud Rate Prescaler are 1
|
||||
/// to 31.
|
||||
pub prescaler: NonZeroU16,
|
||||
/// Valid values are 1 to 31.
|
||||
pub seg1: NonZeroU8,
|
||||
/// Valid values are 1 to 15.
|
||||
pub seg2: NonZeroU8,
|
||||
/// Must always be smaller than DTSEG2, valid values are 1 to 15.
|
||||
pub sync_jump_width: NonZeroU8,
|
||||
}
|
||||
impl DataBitTiming {
|
||||
// #[inline]
|
||||
// fn tdc(&self) -> u8 {
|
||||
// let tsd = self.transceiver_delay_compensation as u8;
|
||||
// //TODO: stm32g4 does not export the TDC field
|
||||
// todo!()
|
||||
// }
|
||||
#[inline]
|
||||
pub(crate) fn dbrp(&self) -> u8 {
|
||||
(u16::from(self.prescaler) & 0x001F) as u8
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn dtseg1(&self) -> u8 {
|
||||
u8::from(self.seg1) & 0x1F
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn dtseg2(&self) -> u8 {
|
||||
u8::from(self.seg2) & 0x0F
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn dsjw(&self) -> u8 {
|
||||
u8::from(self.sync_jump_width) & 0x0F
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DataBitTiming {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
// Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a DBTP
|
||||
// register value of 0x0000_0A33
|
||||
Self {
|
||||
transceiver_delay_compensation: false,
|
||||
prescaler: NonZeroU16::new(1).unwrap(),
|
||||
seg1: NonZeroU8::new(11).unwrap(),
|
||||
seg2: NonZeroU8::new(4).unwrap(),
|
||||
sync_jump_width: NonZeroU8::new(4).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures which modes to use
|
||||
/// Individual headers can contain a desire to be send via FdCan
|
||||
/// or use Bit rate switching. But if this general setting does not allow
|
||||
/// that, only classic CAN is used instead.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum FrameTransmissionConfig {
|
||||
/// Only allow Classic CAN message Frames
|
||||
ClassicCanOnly,
|
||||
/// Allow (non-brs) FdCAN Message Frames
|
||||
AllowFdCan,
|
||||
/// Allow FdCAN Message Frames and allow Bit Rate Switching
|
||||
AllowFdCanAndBRS,
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ClockDivider {
|
||||
/// Divide by 1
|
||||
_1 = 0b0000,
|
||||
/// Divide by 2
|
||||
_2 = 0b0001,
|
||||
/// Divide by 4
|
||||
_4 = 0b0010,
|
||||
/// Divide by 6
|
||||
_6 = 0b0011,
|
||||
/// Divide by 8
|
||||
_8 = 0b0100,
|
||||
/// Divide by 10
|
||||
_10 = 0b0101,
|
||||
/// Divide by 12
|
||||
_12 = 0b0110,
|
||||
/// Divide by 14
|
||||
_14 = 0b0111,
|
||||
/// Divide by 16
|
||||
_16 = 0b1000,
|
||||
/// Divide by 18
|
||||
_18 = 0b1001,
|
||||
/// Divide by 20
|
||||
_20 = 0b1010,
|
||||
/// Divide by 22
|
||||
_22 = 0b1011,
|
||||
/// Divide by 24
|
||||
_24 = 0b1100,
|
||||
/// Divide by 26
|
||||
_26 = 0b1101,
|
||||
/// Divide by 28
|
||||
_28 = 0b1110,
|
||||
/// Divide by 30
|
||||
_30 = 0b1111,
|
||||
}
|
||||
|
||||
/// Prescaler of the Timestamp counter
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TimestampPrescaler {
|
||||
/// 1
|
||||
_1 = 1,
|
||||
/// 2
|
||||
_2 = 2,
|
||||
/// 3
|
||||
_3 = 3,
|
||||
/// 4
|
||||
_4 = 4,
|
||||
/// 5
|
||||
_5 = 5,
|
||||
/// 6
|
||||
_6 = 6,
|
||||
/// 7
|
||||
_7 = 7,
|
||||
/// 8
|
||||
_8 = 8,
|
||||
/// 9
|
||||
_9 = 9,
|
||||
/// 10
|
||||
_10 = 10,
|
||||
/// 11
|
||||
_11 = 11,
|
||||
/// 12
|
||||
_12 = 12,
|
||||
/// 13
|
||||
_13 = 13,
|
||||
/// 14
|
||||
_14 = 14,
|
||||
/// 15
|
||||
_15 = 15,
|
||||
/// 16
|
||||
_16 = 16,
|
||||
}
|
||||
|
||||
/// Selects the source of the Timestamp counter
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TimestampSource {
|
||||
/// The Timestamp counter is disabled
|
||||
None,
|
||||
/// Using the FdCan input clock as the Timstamp counter's source,
|
||||
/// and using a specific prescaler
|
||||
Prescaler(TimestampPrescaler),
|
||||
/// Using TIM3 as a source
|
||||
FromTIM3,
|
||||
}
|
||||
|
||||
/// How to handle frames in the global filter
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum NonMatchingFilter {
|
||||
/// Frames will go to Fifo0 when they do no match any specific filter
|
||||
IntoRxFifo0 = 0b00,
|
||||
/// Frames will go to Fifo1 when they do no match any specific filter
|
||||
IntoRxFifo1 = 0b01,
|
||||
/// Frames will be rejected when they do not match any specific filter
|
||||
Reject = 0b11,
|
||||
}
|
||||
|
||||
/// How to handle frames which do not match a specific filter
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct GlobalFilter {
|
||||
/// How to handle non-matching standard frames
|
||||
pub handle_standard_frames: NonMatchingFilter,
|
||||
|
||||
/// How to handle non-matching extended frames
|
||||
pub handle_extended_frames: NonMatchingFilter,
|
||||
|
||||
/// How to handle remote standard frames
|
||||
pub reject_remote_standard_frames: bool,
|
||||
|
||||
/// How to handle remote extended frames
|
||||
pub reject_remote_extended_frames: bool,
|
||||
}
|
||||
impl GlobalFilter {
|
||||
/// Reject all non-matching and remote frames
|
||||
pub const fn reject_all() -> Self {
|
||||
Self {
|
||||
handle_standard_frames: NonMatchingFilter::Reject,
|
||||
handle_extended_frames: NonMatchingFilter::Reject,
|
||||
reject_remote_standard_frames: true,
|
||||
reject_remote_extended_frames: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// How to handle non-matching standard frames
|
||||
pub const fn set_handle_standard_frames(mut self, filter: NonMatchingFilter) -> Self {
|
||||
self.handle_standard_frames = filter;
|
||||
self
|
||||
}
|
||||
/// How to handle non-matching exteded frames
|
||||
pub const fn set_handle_extended_frames(mut self, filter: NonMatchingFilter) -> Self {
|
||||
self.handle_extended_frames = filter;
|
||||
self
|
||||
}
|
||||
/// How to handle remote standard frames
|
||||
pub const fn set_reject_remote_standard_frames(mut self, filter: bool) -> Self {
|
||||
self.reject_remote_standard_frames = filter;
|
||||
self
|
||||
}
|
||||
/// How to handle remote extended frames
|
||||
pub const fn set_reject_remote_extended_frames(mut self, filter: bool) -> Self {
|
||||
self.reject_remote_extended_frames = filter;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Default for GlobalFilter {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
handle_standard_frames: NonMatchingFilter::IntoRxFifo0,
|
||||
handle_extended_frames: NonMatchingFilter::IntoRxFifo0,
|
||||
reject_remote_standard_frames: false,
|
||||
reject_remote_extended_frames: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// FdCan Config Struct
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct FdCanConfig {
|
||||
/// Nominal Bit Timings
|
||||
pub nbtr: NominalBitTiming,
|
||||
/// (Variable) Data Bit Timings
|
||||
pub dbtr: DataBitTiming,
|
||||
/// Enables or disables automatic retransmission of messages
|
||||
///
|
||||
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
||||
/// util it can be sent. Otherwise, it will try only once to send each frame.
|
||||
///
|
||||
/// Automatic retransmission is enabled by default.
|
||||
pub automatic_retransmit: bool,
|
||||
/// Enabled or disables the pausing between transmissions
|
||||
///
|
||||
/// This feature looses up burst transmissions coming from a single node and it protects against
|
||||
/// "babbling idiot" scenarios where the application program erroneously requests too many
|
||||
/// transmissions.
|
||||
pub transmit_pause: bool,
|
||||
/// Enabled or disables the pausing between transmissions
|
||||
///
|
||||
/// This feature looses up burst transmissions coming from a single node and it protects against
|
||||
/// "babbling idiot" scenarios where the application program erroneously requests too many
|
||||
/// transmissions.
|
||||
pub frame_transmit: FrameTransmissionConfig,
|
||||
/// Non Isoe Mode
|
||||
/// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
|
||||
/// FD Specification V1.0.
|
||||
pub non_iso_mode: bool,
|
||||
/// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization
|
||||
pub edge_filtering: bool,
|
||||
/// Enables protocol exception handling
|
||||
pub protocol_exception_handling: bool,
|
||||
/// Sets the general clock divider for this FdCAN instance
|
||||
pub clock_divider: ClockDivider,
|
||||
/// Sets the timestamp source
|
||||
pub timestamp_source: TimestampSource,
|
||||
/// Configures the Global Filter
|
||||
pub global_filter: GlobalFilter,
|
||||
}
|
||||
|
||||
impl FdCanConfig {
|
||||
/// Configures the bit timings.
|
||||
#[inline]
|
||||
pub const fn set_nominal_bit_timing(mut self, btr: NominalBitTiming) -> Self {
|
||||
self.nbtr = btr;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures the bit timings.
|
||||
#[inline]
|
||||
pub const fn set_data_bit_timing(mut self, btr: DataBitTiming) -> Self {
|
||||
self.dbtr = btr;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables automatic retransmission of messages
|
||||
///
|
||||
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
||||
/// util it can be sent. Otherwise, it will try only once to send each frame.
|
||||
///
|
||||
/// Automatic retransmission is enabled by default.
|
||||
#[inline]
|
||||
pub const fn set_automatic_retransmit(mut self, enabled: bool) -> Self {
|
||||
self.automatic_retransmit = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enabled or disables the pausing between transmissions
|
||||
///
|
||||
/// This feature looses up burst transmissions coming from a single node and it protects against
|
||||
/// "babbling idiot" scenarios where the application program erroneously requests too many
|
||||
/// transmissions.
|
||||
#[inline]
|
||||
pub const fn set_transmit_pause(mut self, enabled: bool) -> Self {
|
||||
self.transmit_pause = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
|
||||
/// FD Specification V1.0.
|
||||
#[inline]
|
||||
pub const fn set_non_iso_mode(mut self, enabled: bool) -> Self {
|
||||
self.non_iso_mode = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Two consecutive dominant tq required to detect an edge for hard synchronization
|
||||
#[inline]
|
||||
pub const fn set_edge_filtering(mut self, enabled: bool) -> Self {
|
||||
self.edge_filtering = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the allowed transmission types for messages.
|
||||
#[inline]
|
||||
pub const fn set_frame_transmit(mut self, fts: FrameTransmissionConfig) -> Self {
|
||||
self.frame_transmit = fts;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables protocol exception handling
|
||||
#[inline]
|
||||
pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self {
|
||||
self.protocol_exception_handling = peh;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the general clock divider for this FdCAN instance
|
||||
#[inline]
|
||||
pub const fn set_clock_divider(mut self, div: ClockDivider) -> Self {
|
||||
self.clock_divider = div;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the timestamp source
|
||||
#[inline]
|
||||
pub const fn set_timestamp_source(mut self, tss: TimestampSource) -> Self {
|
||||
self.timestamp_source = tss;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the global filter settings
|
||||
#[inline]
|
||||
pub const fn set_global_filter(mut self, filter: GlobalFilter) -> Self {
|
||||
self.global_filter = filter;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FdCanConfig {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
nbtr: NominalBitTiming::default(),
|
||||
dbtr: DataBitTiming::default(),
|
||||
automatic_retransmit: true,
|
||||
transmit_pause: false,
|
||||
frame_transmit: FrameTransmissionConfig::ClassicCanOnly,
|
||||
non_iso_mode: false,
|
||||
edge_filtering: false,
|
||||
protocol_exception_handling: true,
|
||||
clock_divider: ClockDivider::_1,
|
||||
timestamp_source: TimestampSource::None,
|
||||
global_filter: GlobalFilter::default(),
|
||||
}
|
||||
}
|
||||
}
|
379
embassy-stm32/src/can/fd/filter.rs
Normal file
379
embassy-stm32/src/can/fd/filter.rs
Normal file
|
@ -0,0 +1,379 @@
|
|||
//! Definition of Filter structs for FDCAN Module
|
||||
//! Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
use embedded_can::{ExtendedId, StandardId};
|
||||
|
||||
use crate::can::fd::message_ram;
|
||||
pub use crate::can::fd::message_ram::{EXTENDED_FILTER_MAX, STANDARD_FILTER_MAX};
|
||||
|
||||
/// A Standard Filter
|
||||
pub type StandardFilter = Filter<StandardId, u16>;
|
||||
/// An Extended Filter
|
||||
pub type ExtendedFilter = Filter<ExtendedId, u32>;
|
||||
|
||||
impl Default for StandardFilter {
|
||||
fn default() -> Self {
|
||||
StandardFilter::disable()
|
||||
}
|
||||
}
|
||||
impl Default for ExtendedFilter {
|
||||
fn default() -> Self {
|
||||
ExtendedFilter::disable()
|
||||
}
|
||||
}
|
||||
|
||||
impl StandardFilter {
|
||||
/// Accept all messages in FIFO 0
|
||||
pub fn accept_all_into_fifo0() -> StandardFilter {
|
||||
StandardFilter {
|
||||
filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },
|
||||
action: Action::StoreInFifo0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept all messages in FIFO 1
|
||||
pub fn accept_all_into_fifo1() -> StandardFilter {
|
||||
StandardFilter {
|
||||
filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },
|
||||
action: Action::StoreInFifo1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reject all messages
|
||||
pub fn reject_all() -> StandardFilter {
|
||||
StandardFilter {
|
||||
filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },
|
||||
action: Action::Reject,
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the filter
|
||||
pub fn disable() -> StandardFilter {
|
||||
StandardFilter {
|
||||
filter: FilterType::Disabled,
|
||||
action: Action::Disable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendedFilter {
|
||||
/// Accept all messages in FIFO 0
|
||||
pub fn accept_all_into_fifo0() -> ExtendedFilter {
|
||||
ExtendedFilter {
|
||||
filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },
|
||||
action: Action::StoreInFifo0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept all messages in FIFO 1
|
||||
pub fn accept_all_into_fifo1() -> ExtendedFilter {
|
||||
ExtendedFilter {
|
||||
filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },
|
||||
action: Action::StoreInFifo1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reject all messages
|
||||
pub fn reject_all() -> ExtendedFilter {
|
||||
ExtendedFilter {
|
||||
filter: FilterType::BitMask { filter: 0x0, mask: 0x0 },
|
||||
action: Action::Reject,
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the filter
|
||||
pub fn disable() -> ExtendedFilter {
|
||||
ExtendedFilter {
|
||||
filter: FilterType::Disabled,
|
||||
action: Action::Disable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter Type
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum FilterType<ID, UNIT>
|
||||
where
|
||||
ID: Copy + Clone + core::fmt::Debug,
|
||||
UNIT: Copy + Clone + core::fmt::Debug,
|
||||
{
|
||||
/// Match with a range between two messages
|
||||
Range {
|
||||
/// First Id of the range
|
||||
from: ID,
|
||||
/// Last Id of the range
|
||||
to: ID,
|
||||
},
|
||||
/// Match with a bitmask
|
||||
BitMask {
|
||||
/// Filter of the bitmask
|
||||
filter: UNIT,
|
||||
/// Mask of the bitmask
|
||||
mask: UNIT,
|
||||
},
|
||||
/// Match with a single ID
|
||||
DedicatedSingle(ID),
|
||||
/// Match with one of two ID's
|
||||
DedicatedDual(ID, ID),
|
||||
/// Filter is disabled
|
||||
Disabled,
|
||||
}
|
||||
impl<ID, UNIT> From<FilterType<ID, UNIT>> for message_ram::enums::FilterType
|
||||
where
|
||||
ID: Copy + Clone + core::fmt::Debug,
|
||||
UNIT: Copy + Clone + core::fmt::Debug,
|
||||
{
|
||||
fn from(f: FilterType<ID, UNIT>) -> Self {
|
||||
match f {
|
||||
FilterType::Range { to: _, from: _ } => Self::RangeFilter,
|
||||
FilterType::BitMask { filter: _, mask: _ } => Self::ClassicFilter,
|
||||
FilterType::DedicatedSingle(_) => Self::DualIdFilter,
|
||||
FilterType::DedicatedDual(_, _) => Self::DualIdFilter,
|
||||
FilterType::Disabled => Self::FilterDisabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter Action
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Action {
|
||||
/// No Action
|
||||
Disable = 0b000,
|
||||
/// Store an matching message in FIFO 0
|
||||
StoreInFifo0 = 0b001,
|
||||
/// Store an matching message in FIFO 1
|
||||
StoreInFifo1 = 0b010,
|
||||
/// Reject an matching message
|
||||
Reject = 0b011,
|
||||
/// Flag a matching message (But not store?!?)
|
||||
FlagHighPrio = 0b100,
|
||||
/// Flag a matching message as a High Priority message and store it in FIFO 0
|
||||
FlagHighPrioAndStoreInFifo0 = 0b101,
|
||||
/// Flag a matching message as a High Priority message and store it in FIFO 1
|
||||
FlagHighPrioAndStoreInFifo1 = 0b110,
|
||||
}
|
||||
impl From<Action> for message_ram::enums::FilterElementConfig {
|
||||
fn from(a: Action) -> Self {
|
||||
match a {
|
||||
Action::Disable => Self::DisableFilterElement,
|
||||
Action::StoreInFifo0 => Self::StoreInFifo0,
|
||||
Action::StoreInFifo1 => Self::StoreInFifo1,
|
||||
Action::Reject => Self::Reject,
|
||||
Action::FlagHighPrio => Self::SetPriority,
|
||||
Action::FlagHighPrioAndStoreInFifo0 => Self::SetPriorityAndStoreInFifo0,
|
||||
Action::FlagHighPrioAndStoreInFifo1 => Self::SetPriorityAndStoreInFifo1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Filter<ID, UNIT>
|
||||
where
|
||||
ID: Copy + Clone + core::fmt::Debug,
|
||||
UNIT: Copy + Clone + core::fmt::Debug,
|
||||
{
|
||||
/// How to match an incoming message
|
||||
pub filter: FilterType<ID, UNIT>,
|
||||
/// What to do with a matching message
|
||||
pub action: Action,
|
||||
}
|
||||
|
||||
/// Standard Filter Slot
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum StandardFilterSlot {
|
||||
/// 0
|
||||
_0 = 0,
|
||||
/// 1
|
||||
_1 = 1,
|
||||
/// 2
|
||||
_2 = 2,
|
||||
/// 3
|
||||
_3 = 3,
|
||||
/// 4
|
||||
_4 = 4,
|
||||
/// 5
|
||||
_5 = 5,
|
||||
/// 6
|
||||
_6 = 6,
|
||||
/// 7
|
||||
_7 = 7,
|
||||
/// 8
|
||||
_8 = 8,
|
||||
/// 9
|
||||
_9 = 9,
|
||||
/// 10
|
||||
_10 = 10,
|
||||
/// 11
|
||||
_11 = 11,
|
||||
/// 12
|
||||
_12 = 12,
|
||||
/// 13
|
||||
_13 = 13,
|
||||
/// 14
|
||||
_14 = 14,
|
||||
/// 15
|
||||
_15 = 15,
|
||||
/// 16
|
||||
_16 = 16,
|
||||
/// 17
|
||||
_17 = 17,
|
||||
/// 18
|
||||
_18 = 18,
|
||||
/// 19
|
||||
_19 = 19,
|
||||
/// 20
|
||||
_20 = 20,
|
||||
/// 21
|
||||
_21 = 21,
|
||||
/// 22
|
||||
_22 = 22,
|
||||
/// 23
|
||||
_23 = 23,
|
||||
/// 24
|
||||
_24 = 24,
|
||||
/// 25
|
||||
_25 = 25,
|
||||
/// 26
|
||||
_26 = 26,
|
||||
/// 27
|
||||
_27 = 27,
|
||||
}
|
||||
impl From<u8> for StandardFilterSlot {
|
||||
fn from(u: u8) -> Self {
|
||||
match u {
|
||||
0 => StandardFilterSlot::_0,
|
||||
1 => StandardFilterSlot::_1,
|
||||
2 => StandardFilterSlot::_2,
|
||||
3 => StandardFilterSlot::_3,
|
||||
4 => StandardFilterSlot::_4,
|
||||
5 => StandardFilterSlot::_5,
|
||||
6 => StandardFilterSlot::_6,
|
||||
7 => StandardFilterSlot::_7,
|
||||
8 => StandardFilterSlot::_8,
|
||||
9 => StandardFilterSlot::_9,
|
||||
10 => StandardFilterSlot::_10,
|
||||
11 => StandardFilterSlot::_11,
|
||||
12 => StandardFilterSlot::_12,
|
||||
13 => StandardFilterSlot::_13,
|
||||
14 => StandardFilterSlot::_14,
|
||||
15 => StandardFilterSlot::_15,
|
||||
16 => StandardFilterSlot::_16,
|
||||
17 => StandardFilterSlot::_17,
|
||||
18 => StandardFilterSlot::_18,
|
||||
19 => StandardFilterSlot::_19,
|
||||
20 => StandardFilterSlot::_20,
|
||||
21 => StandardFilterSlot::_21,
|
||||
22 => StandardFilterSlot::_22,
|
||||
23 => StandardFilterSlot::_23,
|
||||
24 => StandardFilterSlot::_24,
|
||||
25 => StandardFilterSlot::_25,
|
||||
26 => StandardFilterSlot::_26,
|
||||
27 => StandardFilterSlot::_27,
|
||||
_ => panic!("Standard Filter Slot Too High!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended Filter Slot
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ExtendedFilterSlot {
|
||||
/// 0
|
||||
_0 = 0,
|
||||
/// 1
|
||||
_1 = 1,
|
||||
/// 2
|
||||
_2 = 2,
|
||||
/// 3
|
||||
_3 = 3,
|
||||
/// 4
|
||||
_4 = 4,
|
||||
/// 5
|
||||
_5 = 5,
|
||||
/// 6
|
||||
_6 = 6,
|
||||
/// 7
|
||||
_7 = 7,
|
||||
}
|
||||
impl From<u8> for ExtendedFilterSlot {
|
||||
fn from(u: u8) -> Self {
|
||||
match u {
|
||||
0 => ExtendedFilterSlot::_0,
|
||||
1 => ExtendedFilterSlot::_1,
|
||||
2 => ExtendedFilterSlot::_2,
|
||||
3 => ExtendedFilterSlot::_3,
|
||||
4 => ExtendedFilterSlot::_4,
|
||||
5 => ExtendedFilterSlot::_5,
|
||||
6 => ExtendedFilterSlot::_6,
|
||||
7 => ExtendedFilterSlot::_7,
|
||||
_ => panic!("Extended Filter Slot Too High!"), // Should be unreachable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum over both Standard and Extended Filter ID's
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum FilterId {
|
||||
/// Standard Filter Slots
|
||||
Standard(StandardFilterSlot),
|
||||
/// Extended Filter Slots
|
||||
Extended(ExtendedFilterSlot),
|
||||
}
|
||||
|
||||
pub(crate) trait ActivateFilter<ID, UNIT>
|
||||
where
|
||||
ID: Copy + Clone + core::fmt::Debug,
|
||||
UNIT: Copy + Clone + core::fmt::Debug,
|
||||
{
|
||||
fn activate(&mut self, f: Filter<ID, UNIT>);
|
||||
// fn read(&self) -> Filter<ID, UNIT>;
|
||||
}
|
||||
|
||||
impl ActivateFilter<StandardId, u16> for message_ram::StandardFilter {
|
||||
fn activate(&mut self, f: Filter<StandardId, u16>) {
|
||||
let sft = f.filter.into();
|
||||
|
||||
let (sfid1, sfid2) = match f.filter {
|
||||
FilterType::Range { to, from } => (to.as_raw(), from.as_raw()),
|
||||
FilterType::DedicatedSingle(id) => (id.as_raw(), id.as_raw()),
|
||||
FilterType::DedicatedDual(id1, id2) => (id1.as_raw(), id2.as_raw()),
|
||||
FilterType::BitMask { filter, mask } => (filter, mask),
|
||||
FilterType::Disabled => (0x0, 0x0),
|
||||
};
|
||||
let sfec = f.action.into();
|
||||
self.write(|w| {
|
||||
unsafe { w.sfid1().bits(sfid1).sfid2().bits(sfid2) }
|
||||
.sft()
|
||||
.set_filter_type(sft)
|
||||
.sfec()
|
||||
.set_filter_element_config(sfec)
|
||||
});
|
||||
}
|
||||
// fn read(&self) -> Filter<StandardId, u16> {
|
||||
// todo!()
|
||||
// }
|
||||
}
|
||||
impl ActivateFilter<ExtendedId, u32> for message_ram::ExtendedFilter {
|
||||
fn activate(&mut self, f: Filter<ExtendedId, u32>) {
|
||||
let eft = f.filter.into();
|
||||
|
||||
let (efid1, efid2) = match f.filter {
|
||||
FilterType::Range { to, from } => (to.as_raw(), from.as_raw()),
|
||||
FilterType::DedicatedSingle(id) => (id.as_raw(), id.as_raw()),
|
||||
FilterType::DedicatedDual(id1, id2) => (id1.as_raw(), id2.as_raw()),
|
||||
FilterType::BitMask { filter, mask } => (filter, mask),
|
||||
FilterType::Disabled => (0x0, 0x0),
|
||||
};
|
||||
let efec = f.action.into();
|
||||
self.write(|w| {
|
||||
unsafe { w.efid1().bits(efid1).efid2().bits(efid2) }
|
||||
.eft()
|
||||
.set_filter_type(eft)
|
||||
.efec()
|
||||
.set_filter_element_config(efec)
|
||||
});
|
||||
}
|
||||
// fn read(&self) -> Filter<ExtendedId, u32> {
|
||||
// todo!()
|
||||
// }
|
||||
}
|
134
embassy-stm32/src/can/fd/message_ram/common.rs
Normal file
134
embassy-stm32/src/can/fd/message_ram/common.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use super::enums::{
|
||||
BitRateSwitching, ErrorStateIndicator, FilterElementConfig, FilterType, FrameFormat, IdType,
|
||||
RemoteTransmissionRequest,
|
||||
};
|
||||
use super::generic;
|
||||
|
||||
#[doc = "Reader of field `ID`"]
|
||||
pub type ID_R = generic::R<u32, u32>;
|
||||
|
||||
#[doc = "Reader of field `RTR`"]
|
||||
pub type RTR_R = generic::R<bool, RemoteTransmissionRequest>;
|
||||
impl RTR_R {
|
||||
pub fn rtr(&self) -> RemoteTransmissionRequest {
|
||||
match self.bits {
|
||||
false => RemoteTransmissionRequest::TransmitDataFrame,
|
||||
true => RemoteTransmissionRequest::TransmitRemoteFrame,
|
||||
}
|
||||
}
|
||||
pub fn is_transmit_remote_frame(&self) -> bool {
|
||||
*self == RemoteTransmissionRequest::TransmitRemoteFrame
|
||||
}
|
||||
pub fn is_transmit_data_frame(&self) -> bool {
|
||||
*self == RemoteTransmissionRequest::TransmitDataFrame
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `XTD`"]
|
||||
pub type XTD_R = generic::R<bool, IdType>;
|
||||
impl XTD_R {
|
||||
pub fn id_type(&self) -> IdType {
|
||||
match self.bits() {
|
||||
false => IdType::StandardId,
|
||||
true => IdType::ExtendedId,
|
||||
}
|
||||
}
|
||||
pub fn is_standard_id(&self) -> bool {
|
||||
*self == IdType::StandardId
|
||||
}
|
||||
pub fn is_exteded_id(&self) -> bool {
|
||||
*self == IdType::ExtendedId
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `ESI`"]
|
||||
pub type ESI_R = generic::R<bool, ErrorStateIndicator>;
|
||||
impl ESI_R {
|
||||
pub fn error_state(&self) -> ErrorStateIndicator {
|
||||
match self.bits() {
|
||||
false => ErrorStateIndicator::ErrorActive,
|
||||
true => ErrorStateIndicator::ErrorPassive,
|
||||
}
|
||||
}
|
||||
pub fn is_error_active(&self) -> bool {
|
||||
*self == ErrorStateIndicator::ErrorActive
|
||||
}
|
||||
pub fn is_error_passive(&self) -> bool {
|
||||
*self == ErrorStateIndicator::ErrorPassive
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `DLC`"]
|
||||
pub type DLC_R = generic::R<u8, u8>;
|
||||
|
||||
#[doc = "Reader of field `BRS`"]
|
||||
pub type BRS_R = generic::R<bool, BitRateSwitching>;
|
||||
impl BRS_R {
|
||||
pub fn bit_rate_switching(&self) -> BitRateSwitching {
|
||||
match self.bits() {
|
||||
true => BitRateSwitching::WithBRS,
|
||||
false => BitRateSwitching::WithoutBRS,
|
||||
}
|
||||
}
|
||||
pub fn is_with_brs(&self) -> bool {
|
||||
*self == BitRateSwitching::WithBRS
|
||||
}
|
||||
pub fn is_without_brs(&self) -> bool {
|
||||
*self == BitRateSwitching::WithoutBRS
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `FDF`"]
|
||||
pub type FDF_R = generic::R<bool, FrameFormat>;
|
||||
impl FDF_R {
|
||||
pub fn frame_format(&self) -> FrameFormat {
|
||||
match self.bits() {
|
||||
false => FrameFormat::Classic,
|
||||
true => FrameFormat::Fdcan,
|
||||
}
|
||||
}
|
||||
pub fn is_classic_format(&self) -> bool {
|
||||
*self == FrameFormat::Classic
|
||||
}
|
||||
pub fn is_fdcan_format(&self) -> bool {
|
||||
*self == FrameFormat::Fdcan
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `(X|S)FT`"]
|
||||
pub type ESFT_R = generic::R<u8, FilterType>;
|
||||
impl ESFT_R {
|
||||
#[doc = r"Gets the Filtertype"]
|
||||
#[inline(always)]
|
||||
pub fn to_filter_type(&self) -> FilterType {
|
||||
match self.bits() {
|
||||
0b00 => FilterType::RangeFilter,
|
||||
0b01 => FilterType::DualIdFilter,
|
||||
0b10 => FilterType::ClassicFilter,
|
||||
0b11 => FilterType::FilterDisabled,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `(E|S)FEC`"]
|
||||
pub type ESFEC_R = generic::R<u8, FilterElementConfig>;
|
||||
impl ESFEC_R {
|
||||
pub fn to_filter_element_config(&self) -> FilterElementConfig {
|
||||
match self.bits() {
|
||||
0b000 => FilterElementConfig::DisableFilterElement,
|
||||
0b001 => FilterElementConfig::StoreInFifo0,
|
||||
0b010 => FilterElementConfig::StoreInFifo1,
|
||||
0b011 => FilterElementConfig::Reject,
|
||||
0b100 => FilterElementConfig::SetPriority,
|
||||
0b101 => FilterElementConfig::SetPriorityAndStoreInFifo0,
|
||||
0b110 => FilterElementConfig::SetPriorityAndStoreInFifo1,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
233
embassy-stm32/src/can/fd/message_ram/enums.rs
Normal file
233
embassy-stm32/src/can/fd/message_ram/enums.rs
Normal file
|
@ -0,0 +1,233 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
/// Datalength is the message length generalised over
|
||||
/// the Standard (Classic) and FDCAN message types
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum DataLength {
|
||||
Classic(u8),
|
||||
Fdcan(u8),
|
||||
}
|
||||
impl DataLength {
|
||||
/// Creates a DataLength type
|
||||
///
|
||||
/// Uses the byte length and Type of frame as input
|
||||
pub fn new(len: u8, ff: FrameFormat) -> DataLength {
|
||||
match ff {
|
||||
FrameFormat::Classic => match len {
|
||||
0..=8 => DataLength::Classic(len),
|
||||
_ => panic!("DataLength > 8"),
|
||||
},
|
||||
FrameFormat::Fdcan => match len {
|
||||
0..=64 => DataLength::Fdcan(len),
|
||||
_ => panic!("DataLength > 64"),
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Specialised function to create classic frames
|
||||
pub fn new_classic(len: u8) -> DataLength {
|
||||
Self::new(len, FrameFormat::Classic)
|
||||
}
|
||||
/// Specialised function to create FDCAN frames
|
||||
pub fn new_fdcan(len: u8) -> DataLength {
|
||||
Self::new(len, FrameFormat::Fdcan)
|
||||
}
|
||||
|
||||
/// returns the length in bytes
|
||||
pub fn len(&self) -> u8 {
|
||||
match self {
|
||||
DataLength::Classic(l) | DataLength::Fdcan(l) => *l,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dlc(&self) -> u8 {
|
||||
match self {
|
||||
DataLength::Classic(l) => *l,
|
||||
// See RM0433 Rev 7 Table 475. DLC coding
|
||||
DataLength::Fdcan(l) => match l {
|
||||
0..=8 => *l,
|
||||
9..=12 => 9,
|
||||
13..=16 => 10,
|
||||
17..=20 => 11,
|
||||
21..=24 => 12,
|
||||
25..=32 => 13,
|
||||
33..=48 => 14,
|
||||
49..=64 => 15,
|
||||
_ => panic!("DataLength > 64"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<DataLength> for FrameFormat {
|
||||
fn from(dl: DataLength) -> FrameFormat {
|
||||
match dl {
|
||||
DataLength::Classic(_) => FrameFormat::Classic,
|
||||
DataLength::Fdcan(_) => FrameFormat::Fdcan,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wheter or not to generate an Tx Event
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Event {
|
||||
/// Do not generate an Tx Event
|
||||
NoEvent,
|
||||
/// Generate an Tx Event with a specified ID
|
||||
Event(u8),
|
||||
}
|
||||
|
||||
impl From<Event> for EventControl {
|
||||
fn from(e: Event) -> Self {
|
||||
match e {
|
||||
Event::NoEvent => EventControl::DoNotStore,
|
||||
Event::Event(_) => EventControl::Store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<u8>> for Event {
|
||||
fn from(mm: Option<u8>) -> Self {
|
||||
match mm {
|
||||
None => Event::NoEvent,
|
||||
Some(mm) => Event::Event(mm),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Event> for Option<u8> {
|
||||
fn from(e: Event) -> Option<u8> {
|
||||
match e {
|
||||
Event::NoEvent => None,
|
||||
Event::Event(mm) => Some(mm),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ErrorStateIndicator {
|
||||
/// TODO
|
||||
ErrorActive = 0,
|
||||
/// TODO
|
||||
ErrorPassive = 1,
|
||||
}
|
||||
impl From<ErrorStateIndicator> for bool {
|
||||
#[inline(always)]
|
||||
fn from(e: ErrorStateIndicator) -> Self {
|
||||
e as u8 != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of frame, standard (classic) or FdCAN
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum FrameFormat {
|
||||
Classic = 0,
|
||||
Fdcan = 1,
|
||||
}
|
||||
impl From<FrameFormat> for bool {
|
||||
#[inline(always)]
|
||||
fn from(e: FrameFormat) -> Self {
|
||||
e as u8 != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of Id, Standard or Extended
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum IdType {
|
||||
/// Standard ID
|
||||
StandardId = 0,
|
||||
/// Extended ID
|
||||
ExtendedId = 1,
|
||||
}
|
||||
impl From<IdType> for bool {
|
||||
#[inline(always)]
|
||||
fn from(e: IdType) -> Self {
|
||||
e as u8 != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the frame contains data or requests data
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum RemoteTransmissionRequest {
|
||||
/// Frame contains data
|
||||
TransmitDataFrame = 0,
|
||||
/// frame does not contain data
|
||||
TransmitRemoteFrame = 1,
|
||||
}
|
||||
impl From<RemoteTransmissionRequest> for bool {
|
||||
#[inline(always)]
|
||||
fn from(e: RemoteTransmissionRequest) -> Self {
|
||||
e as u8 != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether BitRateSwitching should be or was enabled
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum BitRateSwitching {
|
||||
/// disable bit rate switching
|
||||
WithoutBRS = 0,
|
||||
/// enable bit rate switching
|
||||
WithBRS = 1,
|
||||
}
|
||||
impl From<BitRateSwitching> for bool {
|
||||
#[inline(always)]
|
||||
fn from(e: BitRateSwitching) -> Self {
|
||||
e as u8 != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether to store transmit Events
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum EventControl {
|
||||
/// do not store an tx event
|
||||
DoNotStore,
|
||||
/// store transmit events
|
||||
Store,
|
||||
}
|
||||
impl From<EventControl> for bool {
|
||||
#[inline(always)]
|
||||
fn from(e: EventControl) -> Self {
|
||||
e as u8 != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// If an received message matched any filters
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum FilterFrameMatch {
|
||||
/// This did match filter <id>
|
||||
DidMatch(u8),
|
||||
/// This received frame did not match any specific filters
|
||||
DidNotMatch,
|
||||
}
|
||||
|
||||
/// Type of filter to be used
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum FilterType {
|
||||
/// Filter uses the range between two id's
|
||||
RangeFilter = 0b00,
|
||||
/// The filter matches on two specific id's (or one ID checked twice)
|
||||
DualIdFilter = 0b01,
|
||||
/// Filter is using a bitmask
|
||||
ClassicFilter = 0b10,
|
||||
/// Filter is disabled
|
||||
FilterDisabled = 0b11,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum FilterElementConfig {
|
||||
/// Filter is disabled
|
||||
DisableFilterElement = 0b000,
|
||||
/// Store a matching message in FIFO 0
|
||||
StoreInFifo0 = 0b001,
|
||||
/// Store a matching message in FIFO 1
|
||||
StoreInFifo1 = 0b010,
|
||||
/// Reject a matching message
|
||||
Reject = 0b011,
|
||||
/// Flag that a priority message has been received, *But do note store!*??
|
||||
SetPriority = 0b100,
|
||||
/// Flag and store message in FIFO 0
|
||||
SetPriorityAndStoreInFifo0 = 0b101,
|
||||
/// Flag and store message in FIFO 1
|
||||
SetPriorityAndStoreInFifo1 = 0b110,
|
||||
//_Unused = 0b111,
|
||||
}
|
136
embassy-stm32/src/can/fd/message_ram/extended_filter.rs
Normal file
136
embassy-stm32/src/can/fd/message_ram/extended_filter.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use super::common::{ESFEC_R, ESFT_R};
|
||||
use super::enums::{FilterElementConfig, FilterType};
|
||||
use super::generic;
|
||||
|
||||
#[doc = "Reader of register ExtendedFilter"]
|
||||
pub(crate) type R = generic::R<super::ExtendedFilterType, super::ExtendedFilter>;
|
||||
#[doc = "Writer for register ExtendedFilter"]
|
||||
pub(crate) type W = generic::W<super::ExtendedFilterType, super::ExtendedFilter>;
|
||||
#[doc = "Register ExtendedFilter `reset()`'s"]
|
||||
impl generic::ResetValue for super::ExtendedFilter {
|
||||
type Type = super::ExtendedFilterType;
|
||||
#[inline(always)]
|
||||
fn reset_value() -> Self::Type {
|
||||
// Sets filter element to Disabled
|
||||
[0x0, 0x0]
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `EFID2`"]
|
||||
pub(crate) type EFID2_R = generic::R<u32, u32>;
|
||||
#[doc = "Write proxy for field `EFID2`"]
|
||||
pub(crate) struct EFID2_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> EFID2_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u32) -> &'a mut W {
|
||||
self.w.bits[1] = (self.w.bits[1] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `EFID1`"]
|
||||
pub(crate) type EFID1_R = generic::R<u32, u32>;
|
||||
#[doc = "Write proxy for field `EFID1`"]
|
||||
pub(crate) struct EFID1_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> EFID1_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u32) -> &'a mut W {
|
||||
self.w.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `EFEC`"]
|
||||
pub(crate) struct EFEC_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> EFEC_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u8) -> &'a mut W {
|
||||
self.w.bits[0] = (self.w.bits[0] & !(0x07 << 29)) | (((value as u32) & 0x07) << 29);
|
||||
self.w
|
||||
}
|
||||
#[doc = r"Sets the field according to FilterElementConfig"]
|
||||
#[inline(always)]
|
||||
pub fn set_filter_element_config(self, fec: FilterElementConfig) -> &'a mut W {
|
||||
//SAFETY: FilterElementConfig only be valid options
|
||||
unsafe { self.bits(fec as u8) }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `EFT`"]
|
||||
pub(crate) struct EFT_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> EFT_W<'a> {
|
||||
#[doc = r"Sets the field according the FilterType"]
|
||||
#[inline(always)]
|
||||
pub fn set_filter_type(self, filter: FilterType) -> &'a mut W {
|
||||
//SAFETY: FilterType only be valid options
|
||||
unsafe { self.bits(filter as u8) }
|
||||
}
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u8) -> &'a mut W {
|
||||
self.w.bits[1] = (self.w.bits[1] & !(0x03 << 30)) | (((value as u32) & 0x03) << 30);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
impl R {
|
||||
#[doc = "Byte 0 - Bits 0:28 - EFID1"]
|
||||
#[inline(always)]
|
||||
pub fn sfid1(&self) -> EFID1_R {
|
||||
EFID1_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32)
|
||||
}
|
||||
#[doc = "Byte 0 - Bits 29:31 - EFEC"]
|
||||
#[inline(always)]
|
||||
pub fn efec(&self) -> ESFEC_R {
|
||||
ESFEC_R::new(((self.bits[0] >> 29) & 0x07) as u8)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 0:28 - EFID2"]
|
||||
#[inline(always)]
|
||||
pub fn sfid2(&self) -> EFID2_R {
|
||||
EFID2_R::new(((self.bits[1]) & 0x1FFFFFFF) as u32)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 30:31 - EFT"]
|
||||
#[inline(always)]
|
||||
pub fn eft(&self) -> ESFT_R {
|
||||
ESFT_R::new(((self.bits[1] >> 30) & 0x03) as u8)
|
||||
}
|
||||
}
|
||||
impl W {
|
||||
#[doc = "Byte 0 - Bits 0:28 - EFID1"]
|
||||
#[inline(always)]
|
||||
pub fn efid1(&mut self) -> EFID1_W {
|
||||
EFID1_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 0 - Bits 29:31 - EFEC"]
|
||||
#[inline(always)]
|
||||
pub fn efec(&mut self) -> EFEC_W {
|
||||
EFEC_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 0:28 - EFID2"]
|
||||
#[inline(always)]
|
||||
pub fn efid2(&mut self) -> EFID2_W {
|
||||
EFID2_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 30:31 - EFT"]
|
||||
#[inline(always)]
|
||||
pub fn eft(&mut self) -> EFT_W {
|
||||
EFT_W { w: self }
|
||||
}
|
||||
}
|
168
embassy-stm32/src/can/fd/message_ram/generic.rs
Normal file
168
embassy-stm32/src/can/fd/message_ram/generic.rs
Normal file
|
@ -0,0 +1,168 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
use core::marker;
|
||||
|
||||
///This trait shows that register has `read` method
|
||||
///
|
||||
///Registers marked with `Writable` can be also `modify`'ed
|
||||
pub trait Readable {}
|
||||
|
||||
///This trait shows that register has `write`, `write_with_zero` and `reset` method
|
||||
///
|
||||
///Registers marked with `Readable` can be also `modify`'ed
|
||||
pub trait Writable {}
|
||||
|
||||
///Reset value of the register
|
||||
///
|
||||
///This value is initial value for `write` method.
|
||||
///It can be also directly writed to register by `reset` method.
|
||||
pub trait ResetValue {
|
||||
///Register size
|
||||
type Type;
|
||||
///Reset value of the register
|
||||
fn reset_value() -> Self::Type;
|
||||
}
|
||||
|
||||
///This structure provides volatile access to register
|
||||
pub struct Reg<U, REG> {
|
||||
register: vcell::VolatileCell<U>,
|
||||
_marker: marker::PhantomData<REG>,
|
||||
}
|
||||
|
||||
unsafe impl<U: Send, REG> Send for Reg<U, REG> {}
|
||||
|
||||
impl<U, REG> Reg<U, REG>
|
||||
where
|
||||
Self: Readable,
|
||||
U: Copy,
|
||||
{
|
||||
///Reads the contents of `Readable` register
|
||||
///
|
||||
///You can read the contents of a register in such way:
|
||||
///```ignore
|
||||
///let bits = periph.reg.read().bits();
|
||||
///```
|
||||
///or get the content of a particular field of a register.
|
||||
///```ignore
|
||||
///let reader = periph.reg.read();
|
||||
///let bits = reader.field1().bits();
|
||||
///let flag = reader.field2().bit_is_set();
|
||||
///```
|
||||
#[inline(always)]
|
||||
pub fn read(&self) -> R<U, Self> {
|
||||
R {
|
||||
bits: self.register.get(),
|
||||
_reg: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<U, REG> Reg<U, REG>
|
||||
where
|
||||
Self: ResetValue<Type = U> + Writable,
|
||||
U: Copy,
|
||||
{
|
||||
///Writes the reset value to `Writable` register
|
||||
///
|
||||
///Resets the register to its initial state
|
||||
#[inline(always)]
|
||||
pub fn reset(&self) {
|
||||
self.register.set(Self::reset_value())
|
||||
}
|
||||
}
|
||||
|
||||
impl<U, REG> Reg<U, REG>
|
||||
where
|
||||
Self: ResetValue<Type = U> + Writable,
|
||||
U: Copy,
|
||||
{
|
||||
///Writes bits to `Writable` register
|
||||
///
|
||||
///You can write raw bits into a register:
|
||||
///```ignore
|
||||
///periph.reg.write(|w| unsafe { w.bits(rawbits) });
|
||||
///```
|
||||
///or write only the fields you need:
|
||||
///```ignore
|
||||
///periph.reg.write(|w| w
|
||||
/// .field1().bits(newfield1bits)
|
||||
/// .field2().set_bit()
|
||||
/// .field3().variant(VARIANT)
|
||||
///);
|
||||
///```
|
||||
///Other fields will have reset value.
|
||||
#[inline(always)]
|
||||
pub fn write<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce(&mut W<U, Self>) -> &mut W<U, Self>,
|
||||
{
|
||||
self.register.set(
|
||||
f(&mut W {
|
||||
bits: Self::reset_value(),
|
||||
_reg: marker::PhantomData,
|
||||
})
|
||||
.bits,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///Register/field reader
|
||||
///
|
||||
///Result of the [`read`](Reg::read) method of a register.
|
||||
///Also it can be used in the [`modify`](Reg::read) method
|
||||
pub struct R<U, T> {
|
||||
pub(crate) bits: U,
|
||||
_reg: marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<U, T> R<U, T>
|
||||
where
|
||||
U: Copy,
|
||||
{
|
||||
///Create new instance of reader
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(bits: U) -> Self {
|
||||
Self {
|
||||
bits,
|
||||
_reg: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
///Read raw bits from register/field
|
||||
#[inline(always)]
|
||||
pub fn bits(&self) -> U {
|
||||
self.bits
|
||||
}
|
||||
}
|
||||
|
||||
impl<U, T, FI> PartialEq<FI> for R<U, T>
|
||||
where
|
||||
U: PartialEq,
|
||||
FI: Copy + Into<U>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &FI) -> bool {
|
||||
self.bits.eq(&(*other).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<FI> R<bool, FI> {
|
||||
///Value of the field as raw bits
|
||||
#[inline(always)]
|
||||
pub fn bit(&self) -> bool {
|
||||
self.bits
|
||||
}
|
||||
///Returns `true` if the bit is clear (0)
|
||||
#[inline(always)]
|
||||
pub fn bit_is_clear(&self) -> bool {
|
||||
!self.bit()
|
||||
}
|
||||
}
|
||||
|
||||
///Register writer
|
||||
///
|
||||
///Used as an argument to the closures in the [`write`](Reg::write) and [`modify`](Reg::modify) methods of the register
|
||||
pub struct W<U, REG> {
|
||||
///Writable bits
|
||||
pub(crate) bits: U,
|
||||
_reg: marker::PhantomData<REG>,
|
||||
}
|
170
embassy-stm32/src/can/fd/message_ram/mod.rs
Normal file
170
embassy-stm32/src/can/fd/message_ram/mod.rs
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
use volatile_register::RW;
|
||||
|
||||
pub(crate) mod common;
|
||||
pub(crate) mod enums;
|
||||
pub(crate) mod generic;
|
||||
|
||||
/// Number of Receive Fifos configured by this module
|
||||
pub const RX_FIFOS_MAX: u8 = 2;
|
||||
/// Number of Receive Messages per RxFifo configured by this module
|
||||
pub const RX_FIFO_MAX: u8 = 3;
|
||||
/// Number of Transmit Messages configured by this module
|
||||
pub const TX_FIFO_MAX: u8 = 3;
|
||||
/// Number of Transmit Events configured by this module
|
||||
pub const TX_EVENT_MAX: u8 = 3;
|
||||
/// Number of Standard Filters configured by this module
|
||||
pub const STANDARD_FILTER_MAX: u8 = 28;
|
||||
/// Number of Extended Filters configured by this module
|
||||
pub const EXTENDED_FILTER_MAX: u8 = 8;
|
||||
|
||||
/// MessageRam Overlay
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
pub(crate) filters: Filters,
|
||||
pub(crate) receive: [Receive; RX_FIFOS_MAX as usize],
|
||||
pub(crate) transmit: Transmit,
|
||||
}
|
||||
impl RegisterBlock {
|
||||
pub fn reset(&mut self) {
|
||||
self.filters.reset();
|
||||
self.receive[0].reset();
|
||||
self.receive[1].reset();
|
||||
self.transmit.reset();
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct Filters {
|
||||
pub(crate) flssa: [StandardFilter; STANDARD_FILTER_MAX as usize],
|
||||
pub(crate) flesa: [ExtendedFilter; EXTENDED_FILTER_MAX as usize],
|
||||
}
|
||||
impl Filters {
|
||||
pub fn reset(&mut self) {
|
||||
for sf in &mut self.flssa {
|
||||
sf.reset();
|
||||
}
|
||||
for ef in &mut self.flesa {
|
||||
ef.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct Receive {
|
||||
pub(crate) fxsa: [RxFifoElement; RX_FIFO_MAX as usize],
|
||||
}
|
||||
impl Receive {
|
||||
pub fn reset(&mut self) {
|
||||
for fe in &mut self.fxsa {
|
||||
fe.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct Transmit {
|
||||
pub(crate) efsa: [TxEventElement; TX_EVENT_MAX as usize],
|
||||
pub(crate) tbsa: [TxBufferElement; TX_FIFO_MAX as usize],
|
||||
}
|
||||
impl Transmit {
|
||||
pub fn reset(&mut self) {
|
||||
for ee in &mut self.efsa {
|
||||
ee.reset();
|
||||
}
|
||||
for be in &mut self.tbsa {
|
||||
be.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod standard_filter;
|
||||
pub(crate) type StandardFilterType = u32;
|
||||
pub(crate) type StandardFilter = generic::Reg<StandardFilterType, _StandardFilter>;
|
||||
pub(crate) struct _StandardFilter;
|
||||
impl generic::Readable for StandardFilter {}
|
||||
impl generic::Writable for StandardFilter {}
|
||||
|
||||
pub(crate) mod extended_filter;
|
||||
pub(crate) type ExtendedFilterType = [u32; 2];
|
||||
pub(crate) type ExtendedFilter = generic::Reg<ExtendedFilterType, _ExtendedFilter>;
|
||||
pub(crate) struct _ExtendedFilter;
|
||||
impl generic::Readable for ExtendedFilter {}
|
||||
impl generic::Writable for ExtendedFilter {}
|
||||
|
||||
pub(crate) mod txevent_element;
|
||||
pub(crate) type TxEventElementType = [u32; 2];
|
||||
pub(crate) type TxEventElement = generic::Reg<TxEventElementType, _TxEventElement>;
|
||||
pub(crate) struct _TxEventElement;
|
||||
impl generic::Readable for TxEventElement {}
|
||||
impl generic::Writable for TxEventElement {}
|
||||
|
||||
pub(crate) mod rxfifo_element;
|
||||
#[repr(C)]
|
||||
pub(crate) struct RxFifoElement {
|
||||
pub(crate) header: RxFifoElementHeader,
|
||||
pub(crate) data: [RW<u32>; 16],
|
||||
}
|
||||
impl RxFifoElement {
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.header.reset();
|
||||
for byte in self.data.iter_mut() {
|
||||
unsafe { byte.write(0) };
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) type RxFifoElementHeaderType = [u32; 2];
|
||||
pub(crate) type RxFifoElementHeader = generic::Reg<RxFifoElementHeaderType, _RxFifoElement>;
|
||||
pub(crate) struct _RxFifoElement;
|
||||
impl generic::Readable for RxFifoElementHeader {}
|
||||
impl generic::Writable for RxFifoElementHeader {}
|
||||
|
||||
pub(crate) mod txbuffer_element;
|
||||
#[repr(C)]
|
||||
pub(crate) struct TxBufferElement {
|
||||
pub(crate) header: TxBufferElementHeader,
|
||||
pub(crate) data: [RW<u32>; 16],
|
||||
}
|
||||
impl TxBufferElement {
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.header.reset();
|
||||
for byte in self.data.iter_mut() {
|
||||
unsafe { byte.write(0) };
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) type TxBufferElementHeader = generic::Reg<TxBufferElementHeaderType, _TxBufferElement>;
|
||||
pub(crate) type TxBufferElementHeaderType = [u32; 2];
|
||||
pub(crate) struct _TxBufferElement;
|
||||
impl generic::Readable for TxBufferElementHeader {}
|
||||
impl generic::Writable for TxBufferElementHeader {}
|
||||
|
||||
/// FdCan Message RAM instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is only safe to implement this trait, when:
|
||||
///
|
||||
/// * The implementing type has ownership of the Message RAM, preventing any
|
||||
/// other accesses to the register block.
|
||||
/// * `MSG_RAM` is a pointer to the Message RAM block and can be safely accessed
|
||||
/// for as long as ownership or a borrow of the implementing type is present.
|
||||
pub unsafe trait Instance {
|
||||
const MSG_RAM: *mut RegisterBlock;
|
||||
fn msg_ram(&self) -> &RegisterBlock {
|
||||
unsafe { &*Self::MSG_RAM }
|
||||
}
|
||||
fn msg_ram_mut(&mut self) -> &mut RegisterBlock {
|
||||
unsafe { &mut *Self::MSG_RAM }
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the RegisterBlock is the same size as on pg 1957 of RM0440.
|
||||
static_assertions::assert_eq_size!(Filters, [u32; 28 + 16]);
|
||||
static_assertions::assert_eq_size!(Receive, [u32; 54]);
|
||||
static_assertions::assert_eq_size!(Transmit, [u32; 6 + 54]);
|
||||
static_assertions::assert_eq_size!(
|
||||
RegisterBlock,
|
||||
[u32; 28 /*Standard Filters*/ +16 /*Extended Filters*/ +54 /*RxFifo0*/ +54 /*RxFifo1*/ +6 /*TxEvent*/ +54 /*TxFifo */]
|
||||
);
|
122
embassy-stm32/src/can/fd/message_ram/rxfifo_element.rs
Normal file
122
embassy-stm32/src/can/fd/message_ram/rxfifo_element.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use super::common::{BRS_R, DLC_R, ESI_R, FDF_R, ID_R, RTR_R, XTD_R};
|
||||
use super::enums::{DataLength, FilterFrameMatch, FrameFormat};
|
||||
use super::generic;
|
||||
|
||||
#[doc = "Reader of register RxFifoElement"]
|
||||
pub(crate) type R = generic::R<super::RxFifoElementHeaderType, super::RxFifoElementHeader>;
|
||||
// #[doc = "Writer for register ExtendedFilter"]
|
||||
// pub(crate) type W = generic::W<super::RxFifoElementHeaderType, super::RxFifoElementHeader>;
|
||||
#[doc = "Register ExtendedFilter `reset()`'s"]
|
||||
impl generic::ResetValue for super::RxFifoElementHeader {
|
||||
type Type = super::RxFifoElementHeaderType;
|
||||
#[inline(always)]
|
||||
fn reset_value() -> Self::Type {
|
||||
[0x0, 0x0]
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `RXTS`"]
|
||||
pub(crate) type RXTS_R = generic::R<u16, u16>;
|
||||
|
||||
#[doc = "Reader of field `FIDX`"]
|
||||
pub(crate) type FIDX_R = generic::R<u8, u8>;
|
||||
|
||||
pub(crate) struct _ANMF;
|
||||
#[doc = "Reader of field `ANMF`"]
|
||||
pub(crate) type ANMF_R = generic::R<bool, _ANMF>;
|
||||
impl ANMF_R {
|
||||
pub fn is_matching_frame(&self) -> bool {
|
||||
self.bit_is_clear()
|
||||
}
|
||||
}
|
||||
|
||||
impl R {
|
||||
#[doc = "Byte 0 - Bits 0:28 - ID"]
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> ID_R {
|
||||
ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 29 - RTR"]
|
||||
#[inline(always)]
|
||||
pub fn rtr(&self) -> RTR_R {
|
||||
RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 30 - XTD"]
|
||||
#[inline(always)]
|
||||
pub fn xtd(&self) -> XTD_R {
|
||||
XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 30 - ESI"]
|
||||
#[inline(always)]
|
||||
pub fn esi(&self) -> ESI_R {
|
||||
ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 0:15 - RXTS"]
|
||||
#[inline(always)]
|
||||
pub fn txts(&self) -> RXTS_R {
|
||||
RXTS_R::new(((self.bits[1]) & 0xFFFF) as u16)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 16:19 - DLC"]
|
||||
#[inline(always)]
|
||||
pub fn dlc(&self) -> DLC_R {
|
||||
DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 20 - BRS"]
|
||||
#[inline(always)]
|
||||
pub fn brs(&self) -> BRS_R {
|
||||
BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 20 - FDF"]
|
||||
#[inline(always)]
|
||||
pub fn fdf(&self) -> FDF_R {
|
||||
FDF_R::new(((self.bits[1] >> 21) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 24:30 - FIDX"]
|
||||
#[inline(always)]
|
||||
pub fn fidx(&self) -> FIDX_R {
|
||||
FIDX_R::new(((self.bits[1] >> 24) & 0xFF) as u8)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 31 - ANMF"]
|
||||
#[inline(always)]
|
||||
pub fn anmf(&self) -> ANMF_R {
|
||||
ANMF_R::new(((self.bits[1] >> 31) & 0x01) != 0)
|
||||
}
|
||||
pub fn to_data_length(&self) -> DataLength {
|
||||
let dlc = self.dlc().bits();
|
||||
let ff = self.fdf().frame_format();
|
||||
let len = if ff == FrameFormat::Fdcan {
|
||||
// See RM0433 Rev 7 Table 475. DLC coding
|
||||
match dlc {
|
||||
0..=8 => dlc,
|
||||
9 => 12,
|
||||
10 => 16,
|
||||
11 => 20,
|
||||
12 => 24,
|
||||
13 => 32,
|
||||
14 => 48,
|
||||
15 => 64,
|
||||
_ => panic!("DLC > 15"),
|
||||
}
|
||||
} else {
|
||||
match dlc {
|
||||
0..=8 => dlc,
|
||||
9..=15 => 8,
|
||||
_ => panic!("DLC > 15"),
|
||||
}
|
||||
};
|
||||
DataLength::new(len, ff)
|
||||
}
|
||||
pub fn to_filter_match(&self) -> FilterFrameMatch {
|
||||
if self.anmf().is_matching_frame() {
|
||||
FilterFrameMatch::DidMatch(self.fidx().bits())
|
||||
} else {
|
||||
FilterFrameMatch::DidNotMatch
|
||||
}
|
||||
}
|
||||
}
|
136
embassy-stm32/src/can/fd/message_ram/standard_filter.rs
Normal file
136
embassy-stm32/src/can/fd/message_ram/standard_filter.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use super::common::{ESFEC_R, ESFT_R};
|
||||
use super::enums::{FilterElementConfig, FilterType};
|
||||
use super::generic;
|
||||
|
||||
#[doc = "Reader of register StandardFilter"]
|
||||
pub(crate) type R = generic::R<super::StandardFilterType, super::StandardFilter>;
|
||||
#[doc = "Writer for register StandardFilter"]
|
||||
pub(crate) type W = generic::W<super::StandardFilterType, super::StandardFilter>;
|
||||
#[doc = "Register StandardFilter `reset()`'s with value 0xC0000"]
|
||||
impl generic::ResetValue for super::StandardFilter {
|
||||
type Type = super::StandardFilterType;
|
||||
#[inline(always)]
|
||||
fn reset_value() -> Self::Type {
|
||||
// Sets filter element to Disabled
|
||||
0xC000
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `SFID2`"]
|
||||
pub(crate) type SFID2_R = generic::R<u16, u16>;
|
||||
#[doc = "Write proxy for field `SFID2`"]
|
||||
pub(crate) struct SFID2_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> SFID2_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u16) -> &'a mut W {
|
||||
self.w.bits = (self.w.bits & !(0x07ff)) | ((value as u32) & 0x07ff);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `SFID1`"]
|
||||
pub(crate) type SFID1_R = generic::R<u16, u16>;
|
||||
#[doc = "Write proxy for field `SFID1`"]
|
||||
pub(crate) struct SFID1_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> SFID1_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u16) -> &'a mut W {
|
||||
self.w.bits = (self.w.bits & !(0x07ff << 16)) | (((value as u32) & 0x07ff) << 16);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `SFEC`"]
|
||||
pub(crate) struct SFEC_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> SFEC_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u8) -> &'a mut W {
|
||||
self.w.bits = (self.w.bits & !(0x07 << 27)) | (((value as u32) & 0x07) << 27);
|
||||
self.w
|
||||
}
|
||||
#[doc = r"Sets the field according to FilterElementConfig"]
|
||||
#[inline(always)]
|
||||
pub fn set_filter_element_config(self, fec: FilterElementConfig) -> &'a mut W {
|
||||
//SAFETY: FilterElementConfig only be valid options
|
||||
unsafe { self.bits(fec as u8) }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `SFT`"]
|
||||
pub(crate) struct SFT_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> SFT_W<'a> {
|
||||
#[doc = r"Sets the field according the FilterType"]
|
||||
#[inline(always)]
|
||||
pub fn set_filter_type(self, filter: FilterType) -> &'a mut W {
|
||||
//SAFETY: FilterType only be valid options
|
||||
unsafe { self.bits(filter as u8) }
|
||||
}
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u8) -> &'a mut W {
|
||||
self.w.bits = (self.w.bits & !(0x03 << 30)) | (((value as u32) & 0x03) << 30);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
impl R {
|
||||
#[doc = "Bits 0:10 - SFID2"]
|
||||
#[inline(always)]
|
||||
pub fn sfid2(&self) -> SFID2_R {
|
||||
SFID2_R::new((self.bits & 0x07ff) as u16)
|
||||
}
|
||||
#[doc = "Bits 16:26 - SFID1"]
|
||||
#[inline(always)]
|
||||
pub fn sfid1(&self) -> SFID1_R {
|
||||
SFID1_R::new(((self.bits >> 16) & 0x07ff) as u16)
|
||||
}
|
||||
#[doc = "Bits 27:29 - SFEC"]
|
||||
#[inline(always)]
|
||||
pub fn sfec(&self) -> ESFEC_R {
|
||||
ESFEC_R::new(((self.bits >> 27) & 0x07) as u8)
|
||||
}
|
||||
#[doc = "Bits 30:31 - SFT"]
|
||||
#[inline(always)]
|
||||
pub fn sft(&self) -> ESFT_R {
|
||||
ESFT_R::new(((self.bits >> 30) & 0x03) as u8)
|
||||
}
|
||||
}
|
||||
impl W {
|
||||
#[doc = "Bits 0:10 - SFID2"]
|
||||
#[inline(always)]
|
||||
pub fn sfid2(&mut self) -> SFID2_W {
|
||||
SFID2_W { w: self }
|
||||
}
|
||||
#[doc = "Bits 16:26 - SFID1"]
|
||||
#[inline(always)]
|
||||
pub fn sfid1(&mut self) -> SFID1_W {
|
||||
SFID1_W { w: self }
|
||||
}
|
||||
#[doc = "Bits 27:29 - SFEC"]
|
||||
#[inline(always)]
|
||||
pub fn sfec(&mut self) -> SFEC_W {
|
||||
SFEC_W { w: self }
|
||||
}
|
||||
#[doc = "Bits 30:31 - SFT"]
|
||||
#[inline(always)]
|
||||
pub fn sft(&mut self) -> SFT_W {
|
||||
SFT_W { w: self }
|
||||
}
|
||||
}
|
433
embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs
Normal file
433
embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs
Normal file
|
@ -0,0 +1,433 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use super::common::{BRS_R, DLC_R, ESI_R, FDF_R, ID_R, RTR_R, XTD_R};
|
||||
use super::enums::{
|
||||
BitRateSwitching, DataLength, ErrorStateIndicator, Event, EventControl, FrameFormat, IdType,
|
||||
RemoteTransmissionRequest,
|
||||
};
|
||||
use super::generic;
|
||||
|
||||
#[doc = "Reader of register TxBufferElement"]
|
||||
pub(crate) type R = generic::R<super::TxBufferElementHeaderType, super::TxBufferElementHeader>;
|
||||
#[doc = "Writer for register TxBufferElement"]
|
||||
pub(crate) type W = generic::W<super::TxBufferElementHeaderType, super::TxBufferElementHeader>;
|
||||
impl generic::ResetValue for super::TxBufferElementHeader {
|
||||
type Type = super::TxBufferElementHeaderType;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[inline(always)]
|
||||
fn reset_value() -> Self::Type {
|
||||
[0; 2]
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `ESI`"]
|
||||
pub(crate) struct ESI_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> ESI_W<'a> {
|
||||
#[doc = r"Writes `variant` to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_error_indicator(self, esi: ErrorStateIndicator) -> &'a mut W {
|
||||
self.bit(esi as u8 != 0)
|
||||
}
|
||||
|
||||
#[doc = r"Sets the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_bit(self) -> &'a mut W {
|
||||
self.bit(true)
|
||||
}
|
||||
#[doc = r"Clears the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_bit(self) -> &'a mut W {
|
||||
self.bit(false)
|
||||
}
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn bit(self, value: bool) -> &'a mut W {
|
||||
self.w.bits[0] = (self.w.bits[0] & !(0x01 << 31)) | (((value as u32) & 0x01) << 31);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `XTD`"]
|
||||
pub(crate) struct XTD_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> XTD_W<'a> {
|
||||
#[doc = r"Writes `variant` to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_id_type(self, idt: IdType) -> &'a mut W {
|
||||
self.bit(idt as u8 != 0)
|
||||
}
|
||||
|
||||
#[doc = r"Sets the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_bit(self) -> &'a mut W {
|
||||
self.bit(true)
|
||||
}
|
||||
#[doc = r"Clears the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_bit(self) -> &'a mut W {
|
||||
self.bit(false)
|
||||
}
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn bit(self, value: bool) -> &'a mut W {
|
||||
self.w.bits[0] = (self.w.bits[0] & !(0x01 << 30)) | (((value as u32) & 0x01) << 30);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `RTR`"]
|
||||
pub(crate) struct RTR_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> RTR_W<'a> {
|
||||
#[doc = r"Writes `variant` to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_rtr(self, rtr: RemoteTransmissionRequest) -> &'a mut W {
|
||||
self.bit(rtr as u8 != 0)
|
||||
}
|
||||
|
||||
#[doc = r"Sets the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_bit(self) -> &'a mut W {
|
||||
self.bit(true)
|
||||
}
|
||||
#[doc = r"Clears the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_bit(self) -> &'a mut W {
|
||||
self.bit(false)
|
||||
}
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn bit(self, value: bool) -> &'a mut W {
|
||||
self.w.bits[0] = (self.w.bits[0] & !(0x01 << 29)) | (((value as u32) & 0x01) << 29);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `ID`"]
|
||||
pub(crate) struct ID_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> ID_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn bits(self, value: u32) -> &'a mut W {
|
||||
self.w.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `DLC`"]
|
||||
pub(crate) struct DLC_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> DLC_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn bits(self, value: u8) -> &'a mut W {
|
||||
self.w.bits[1] = (self.w.bits[1] & !(0x0F << 16)) | (((value as u32) & 0x0F) << 16);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `BRS`"]
|
||||
pub(crate) struct BRS_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> BRS_W<'a> {
|
||||
#[doc = r"Writes `variant` to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_brs(self, brs: BitRateSwitching) -> &'a mut W {
|
||||
self.bit(brs as u8 != 0)
|
||||
}
|
||||
|
||||
#[doc = r"Sets the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_bit(self) -> &'a mut W {
|
||||
self.bit(true)
|
||||
}
|
||||
#[doc = r"Clears the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_bit(self) -> &'a mut W {
|
||||
self.bit(false)
|
||||
}
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn bit(self, value: bool) -> &'a mut W {
|
||||
self.w.bits[1] = (self.w.bits[1] & !(0x01 << 20)) | (((value as u32) & 0x01) << 20);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Write proxy for field `FDF`"]
|
||||
pub(crate) struct FDF_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> FDF_W<'a> {
|
||||
#[doc = r"Writes `variant` to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_format(self, fdf: FrameFormat) -> &'a mut W {
|
||||
self.bit(fdf as u8 != 0)
|
||||
}
|
||||
|
||||
#[doc = r"Sets the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_bit(self) -> &'a mut W {
|
||||
self.bit(true)
|
||||
}
|
||||
#[doc = r"Clears the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_bit(self) -> &'a mut W {
|
||||
self.bit(false)
|
||||
}
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn bit(self, value: bool) -> &'a mut W {
|
||||
self.w.bits[1] = (self.w.bits[1] & !(0x01 << 21)) | (((value as u32) & 0x01) << 21);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `EFC`"]
|
||||
pub(crate) type EFC_R = generic::R<bool, EventControl>;
|
||||
impl EFC_R {
|
||||
pub fn to_event_control(&self) -> EventControl {
|
||||
match self.bit() {
|
||||
false => EventControl::DoNotStore,
|
||||
true => EventControl::Store,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[doc = "Write proxy for field `EFC`"]
|
||||
pub(crate) struct EFC_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> EFC_W<'a> {
|
||||
#[doc = r"Writes `variant` to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_event_control(self, efc: EventControl) -> &'a mut W {
|
||||
self.bit(match efc {
|
||||
EventControl::DoNotStore => false,
|
||||
EventControl::Store => true,
|
||||
})
|
||||
}
|
||||
|
||||
#[doc = r"Sets the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_bit(self) -> &'a mut W {
|
||||
self.bit(true)
|
||||
}
|
||||
#[doc = r"Clears the field bit"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_bit(self) -> &'a mut W {
|
||||
self.bit(false)
|
||||
}
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn bit(self, value: bool) -> &'a mut W {
|
||||
self.w.bits[1] = (self.w.bits[1] & !(0x01 << 23)) | (((value as u32) & 0x01) << 23);
|
||||
self.w
|
||||
}
|
||||
}
|
||||
|
||||
struct Marker(u8);
|
||||
impl From<Event> for Marker {
|
||||
fn from(e: Event) -> Marker {
|
||||
match e {
|
||||
Event::NoEvent => Marker(0),
|
||||
Event::Event(mm) => Marker(mm),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `MM`"]
|
||||
pub(crate) type MM_R = generic::R<u8, u8>;
|
||||
#[doc = "Write proxy for field `MM`"]
|
||||
pub(crate) struct MM_W<'a> {
|
||||
w: &'a mut W,
|
||||
}
|
||||
impl<'a> MM_W<'a> {
|
||||
#[doc = r"Writes raw bits to the field"]
|
||||
#[inline(always)]
|
||||
pub unsafe fn bits(self, value: u8) -> &'a mut W {
|
||||
self.w.bits[1] = (self.w.bits[1] & !(0x7F << 24)) | (((value as u32) & 0x7F) << 24);
|
||||
self.w
|
||||
}
|
||||
|
||||
fn set_message_marker(self, mm: Marker) -> &'a mut W {
|
||||
unsafe { self.bits(mm.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl R {
|
||||
#[doc = "Byte 0 - Bits 0:28 - ID"]
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> ID_R {
|
||||
ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 29 - RTR"]
|
||||
#[inline(always)]
|
||||
pub fn rtr(&self) -> RTR_R {
|
||||
RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 30 - XTD"]
|
||||
#[inline(always)]
|
||||
pub fn xtd(&self) -> XTD_R {
|
||||
XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 30 - ESI"]
|
||||
#[inline(always)]
|
||||
pub fn esi(&self) -> ESI_R {
|
||||
ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 16:19 - DLC"]
|
||||
#[inline(always)]
|
||||
pub fn dlc(&self) -> DLC_R {
|
||||
DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 20 - BRS"]
|
||||
#[inline(always)]
|
||||
pub fn brs(&self) -> BRS_R {
|
||||
BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 20 - FDF"]
|
||||
#[inline(always)]
|
||||
pub fn fdf(&self) -> FDF_R {
|
||||
FDF_R::new(((self.bits[1] >> 21) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 23 - EFC"]
|
||||
#[inline(always)]
|
||||
pub fn efc(&self) -> EFC_R {
|
||||
EFC_R::new(((self.bits[1] >> 23) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 24:31 - MM"]
|
||||
#[inline(always)]
|
||||
pub fn mm(&self) -> MM_R {
|
||||
MM_R::new(((self.bits[1] >> 24) & 0xFF) as u8)
|
||||
}
|
||||
pub fn to_data_length(&self) -> DataLength {
|
||||
let dlc = self.dlc().bits();
|
||||
let ff = self.fdf().frame_format();
|
||||
let len = if ff == FrameFormat::Fdcan {
|
||||
// See RM0433 Rev 7 Table 475. DLC coding
|
||||
match dlc {
|
||||
0..=8 => dlc,
|
||||
9 => 12,
|
||||
10 => 16,
|
||||
11 => 20,
|
||||
12 => 24,
|
||||
13 => 32,
|
||||
14 => 48,
|
||||
15 => 64,
|
||||
_ => panic!("DLC > 15"),
|
||||
}
|
||||
} else {
|
||||
match dlc {
|
||||
0..=8 => dlc,
|
||||
9..=15 => 8,
|
||||
_ => panic!("DLC > 15"),
|
||||
}
|
||||
};
|
||||
DataLength::new(len, ff)
|
||||
}
|
||||
pub fn to_event(&self) -> Event {
|
||||
let mm = self.mm().bits();
|
||||
let efc = self.efc().to_event_control();
|
||||
match efc {
|
||||
EventControl::DoNotStore => Event::NoEvent,
|
||||
EventControl::Store => Event::Event(mm),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl W {
|
||||
#[doc = "Byte 0 - Bits 0:28 - ID"]
|
||||
#[inline(always)]
|
||||
pub fn id(&mut self) -> ID_W {
|
||||
ID_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 29 - RTR"]
|
||||
#[inline(always)]
|
||||
pub fn rtr(&mut self) -> RTR_W {
|
||||
RTR_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 30 - XTD"]
|
||||
#[inline(always)]
|
||||
pub fn xtd(&mut self) -> XTD_W {
|
||||
XTD_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 31 - ESI"]
|
||||
#[inline(always)]
|
||||
pub fn esi(&mut self) -> ESI_W {
|
||||
ESI_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 1 - Bit 16:19 - DLC"]
|
||||
#[inline(always)]
|
||||
pub fn dlc(&mut self) -> DLC_W {
|
||||
DLC_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 1 - Bit 20 - BRS"]
|
||||
#[inline(always)]
|
||||
pub fn brs(&mut self) -> BRS_W {
|
||||
BRS_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 1 - Bit 21 - FDF"]
|
||||
#[inline(always)]
|
||||
pub fn fdf(&mut self) -> FDF_W {
|
||||
FDF_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 1 - Bit 23 - EFC"]
|
||||
#[inline(always)]
|
||||
pub fn efc(&mut self) -> EFC_W {
|
||||
EFC_W { w: self }
|
||||
}
|
||||
#[doc = "Byte 1 - Bit 24:31 - MM"]
|
||||
#[inline(always)]
|
||||
pub fn mm(&mut self) -> MM_W {
|
||||
MM_W { w: self }
|
||||
}
|
||||
#[doc = "Convenience function for setting the data length and frame format"]
|
||||
#[inline(always)]
|
||||
pub fn set_len(&mut self, dl: impl Into<DataLength>) -> &mut Self {
|
||||
let dl: DataLength = dl.into();
|
||||
self.fdf().set_format(dl.into());
|
||||
unsafe { self.dlc().bits(dl.dlc()) }
|
||||
}
|
||||
pub fn set_event(&mut self, event: Event) -> &mut Self {
|
||||
self.mm().set_message_marker(event.into());
|
||||
self.efc().set_event_control(event.into())
|
||||
}
|
||||
}
|
138
embassy-stm32/src/can/fd/message_ram/txevent_element.rs
Normal file
138
embassy-stm32/src/can/fd/message_ram/txevent_element.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(unused)]
|
||||
|
||||
use super::common::{BRS_R, DLC_R, ESI_R, RTR_R, XTD_R};
|
||||
use super::generic;
|
||||
|
||||
#[doc = "Reader of register TxEventElement"]
|
||||
pub(crate) type R = generic::R<super::TxEventElementType, super::TxEventElement>;
|
||||
// #[doc = "Writer for register TxEventElement"]
|
||||
// pub(crate) type W = generic::W<super::TxEventElementType, super::TxEventElement>;
|
||||
#[doc = "Register TxEventElement `reset()`'s"]
|
||||
impl generic::ResetValue for super::TxEventElement {
|
||||
type Type = super::TxEventElementType;
|
||||
#[inline(always)]
|
||||
fn reset_value() -> Self::Type {
|
||||
[0, 0]
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `ID`"]
|
||||
pub(crate) type ID_R = generic::R<u32, u32>;
|
||||
|
||||
#[doc = "Reader of field `TXTS`"]
|
||||
pub(crate) type TXTS_R = generic::R<u16, u16>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub(crate) enum DataLengthFormat {
|
||||
StandardLength = 0,
|
||||
FDCANLength = 1,
|
||||
}
|
||||
impl From<DataLengthFormat> for bool {
|
||||
#[inline(always)]
|
||||
fn from(dlf: DataLengthFormat) -> Self {
|
||||
dlf as u8 != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `EDL`"]
|
||||
pub(crate) type EDL_R = generic::R<bool, DataLengthFormat>;
|
||||
impl EDL_R {
|
||||
pub fn data_length_format(&self) -> DataLengthFormat {
|
||||
match self.bits() {
|
||||
false => DataLengthFormat::StandardLength,
|
||||
true => DataLengthFormat::FDCANLength,
|
||||
}
|
||||
}
|
||||
pub fn is_standard_length(&self) -> bool {
|
||||
*self == DataLengthFormat::StandardLength
|
||||
}
|
||||
pub fn is_fdcan_length(&self) -> bool {
|
||||
*self == DataLengthFormat::FDCANLength
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub(crate) enum EventType {
|
||||
//_Reserved = 0b00,
|
||||
TxEvent = 0b01,
|
||||
TxDespiteAbort = 0b10,
|
||||
//_Reserved = 0b10,
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `EFC`"]
|
||||
pub(crate) type EFC_R = generic::R<u8, EventType>;
|
||||
impl EFC_R {
|
||||
pub fn event_type(&self) -> EventType {
|
||||
match self.bits() {
|
||||
0b01 => EventType::TxEvent,
|
||||
0b10 => EventType::TxDespiteAbort,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
pub fn is_tx_event(&self) -> bool {
|
||||
self.event_type() == EventType::TxEvent
|
||||
}
|
||||
pub fn is_despite_abort(&self) -> bool {
|
||||
self.event_type() == EventType::TxDespiteAbort
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = "Reader of field `MM`"]
|
||||
pub(crate) type MM_R = generic::R<u8, u8>;
|
||||
|
||||
impl R {
|
||||
#[doc = "Byte 0 - Bits 0:28 - ID"]
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> ID_R {
|
||||
ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 29 - RTR"]
|
||||
#[inline(always)]
|
||||
pub fn rtr(&self) -> RTR_R {
|
||||
RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 30 - XTD"]
|
||||
#[inline(always)]
|
||||
pub fn xtd(&self) -> XTD_R {
|
||||
XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 0 - Bit 30 - ESI"]
|
||||
#[inline(always)]
|
||||
pub fn esi(&self) -> ESI_R {
|
||||
ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 0:15 - TXTS"]
|
||||
#[inline(always)]
|
||||
pub fn txts(&self) -> TXTS_R {
|
||||
TXTS_R::new(((self.bits[1]) & 0xFFFF) as u16)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 16:19 - DLC"]
|
||||
#[inline(always)]
|
||||
pub fn dlc(&self) -> DLC_R {
|
||||
DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 20 - BRS"]
|
||||
#[inline(always)]
|
||||
pub fn brs(&self) -> BRS_R {
|
||||
BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 21 - EDL"]
|
||||
#[inline(always)]
|
||||
pub fn edl(&self) -> EDL_R {
|
||||
EDL_R::new(((self.bits[1] >> 21) & 0x01) != 0)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 22:23 - EFC"]
|
||||
#[inline(always)]
|
||||
pub fn efc(&self) -> EFC_R {
|
||||
EFC_R::new(((self.bits[1] >> 22) & 0x03) as u8)
|
||||
}
|
||||
#[doc = "Byte 1 - Bits 24:31 - MM"]
|
||||
#[inline(always)]
|
||||
pub fn mm(&self) -> MM_R {
|
||||
MM_R::new(((self.bits[1] >> 24) & 0xFF) as u8)
|
||||
}
|
||||
}
|
6
embassy-stm32/src/can/fd/mod.rs
Normal file
6
embassy-stm32/src/can/fd/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
//! Module containing that which is specific to fdcan hardware variant
|
||||
|
||||
pub mod config;
|
||||
pub mod filter;
|
||||
pub(crate) mod message_ram;
|
||||
pub(crate) mod peripheral;
|
828
embassy-stm32/src/can/fd/peripheral.rs
Normal file
828
embassy-stm32/src/can/fd/peripheral.rs
Normal file
|
@ -0,0 +1,828 @@
|
|||
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
||||
|
||||
use core::convert::Infallible;
|
||||
use core::slice;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
use crate::can::enums::*;
|
||||
use crate::can::fd::config::*;
|
||||
use crate::can::fd::message_ram::enums::*;
|
||||
use crate::can::fd::message_ram::{RegisterBlock, RxFifoElement, TxBufferElement};
|
||||
use crate::can::frame::*;
|
||||
|
||||
/// Loopback Mode
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum LoopbackMode {
|
||||
None,
|
||||
Internal,
|
||||
External,
|
||||
}
|
||||
|
||||
pub struct Registers {
|
||||
pub regs: &'static crate::pac::can::Fdcan,
|
||||
pub msgram: &'static crate::pac::fdcanram::Fdcanram,
|
||||
}
|
||||
|
||||
impl Registers {
|
||||
fn tx_buffer_element(&self, bufidx: usize) -> &mut TxBufferElement {
|
||||
&mut self.msg_ram_mut().transmit.tbsa[bufidx]
|
||||
}
|
||||
pub fn msg_ram_mut(&self) -> &mut RegisterBlock {
|
||||
let ptr = self.msgram.as_ptr() as *mut RegisterBlock;
|
||||
unsafe { &mut (*ptr) }
|
||||
}
|
||||
|
||||
fn rx_fifo_element(&self, fifonr: usize, bufnum: usize) -> &mut RxFifoElement {
|
||||
&mut self.msg_ram_mut().receive[fifonr].fxsa[bufnum]
|
||||
}
|
||||
|
||||
pub fn read_classic(&self, fifonr: usize) -> Option<(ClassicFrame, u16)> {
|
||||
// Fill level - do we have a msg?
|
||||
if self.regs.rxfs(fifonr).read().ffl() < 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let read_idx = self.regs.rxfs(fifonr).read().fgi();
|
||||
let mailbox = self.rx_fifo_element(fifonr, read_idx as usize);
|
||||
|
||||
let mut buffer: [u8; 8] = [0; 8];
|
||||
let maybe_header = extract_frame(mailbox, &mut buffer);
|
||||
|
||||
// Clear FIFO, reduces count and increments read buf
|
||||
self.regs.rxfa(fifonr).modify(|w| w.set_fai(read_idx));
|
||||
|
||||
match maybe_header {
|
||||
Some((header, ts)) => {
|
||||
let data = ClassicData::new(&buffer[0..header.len() as usize]);
|
||||
Some((ClassicFrame::new(header, data.unwrap()), ts))
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_fd(&self, fifonr: usize) -> Option<(FdFrame, u16)> {
|
||||
// Fill level - do we have a msg?
|
||||
if self.regs.rxfs(fifonr).read().ffl() < 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let read_idx = self.regs.rxfs(fifonr).read().fgi();
|
||||
let mailbox = self.rx_fifo_element(fifonr, read_idx as usize);
|
||||
|
||||
let mut buffer: [u8; 64] = [0; 64];
|
||||
let maybe_header = extract_frame(mailbox, &mut buffer);
|
||||
|
||||
// Clear FIFO, reduces count and increments read buf
|
||||
self.regs.rxfa(fifonr).modify(|w| w.set_fai(read_idx));
|
||||
|
||||
match maybe_header {
|
||||
Some((header, ts)) => {
|
||||
let data = FdData::new(&buffer[0..header.len() as usize]);
|
||||
Some((FdFrame::new(header, data.unwrap()), ts))
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_tx_frame(&self, bufidx: usize, header: &Header, buffer: &[u8]) {
|
||||
// Fill level - do we have a msg?
|
||||
//if self.regs.rxfs(fifonr).read().ffl() < 1 { return None; }
|
||||
|
||||
//let read_idx = self.regs.rxfs(fifonr).read().fgi();
|
||||
|
||||
let mailbox = self.tx_buffer_element(bufidx);
|
||||
|
||||
mailbox.reset();
|
||||
put_tx_header(mailbox, header);
|
||||
put_tx_data(mailbox, &buffer[..header.len() as usize]);
|
||||
|
||||
// Set <idx as Mailbox> as ready to transmit
|
||||
self.regs.txbar().modify(|w| w.set_ar(bufidx, true));
|
||||
}
|
||||
|
||||
fn reg_to_error(value: u8) -> Option<BusError> {
|
||||
match value {
|
||||
//0b000 => None,
|
||||
0b001 => Some(BusError::Stuff),
|
||||
0b010 => Some(BusError::Form),
|
||||
0b011 => Some(BusError::Acknowledge),
|
||||
0b100 => Some(BusError::BitRecessive),
|
||||
0b101 => Some(BusError::BitDominant),
|
||||
0b110 => Some(BusError::Crc),
|
||||
//0b111 => Some(BusError::NoError),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn curr_error(&self) -> Option<BusError> {
|
||||
let err = { self.regs.psr().read() };
|
||||
if err.bo() {
|
||||
return Some(BusError::BusOff);
|
||||
} else if err.ep() {
|
||||
return Some(BusError::BusPassive);
|
||||
} else if err.ew() {
|
||||
return Some(BusError::BusWarning);
|
||||
} else {
|
||||
cfg_if! {
|
||||
if #[cfg(stm32h7)] {
|
||||
let lec = err.lec();
|
||||
} else {
|
||||
let lec = err.lec().to_bits();
|
||||
}
|
||||
}
|
||||
if let Some(err) = Self::reg_to_error(lec) {
|
||||
return Some(err);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
/// Returns if the tx queue is able to accept new messages without having to cancel an existing one
|
||||
#[inline]
|
||||
pub fn tx_queue_is_full(&self) -> bool {
|
||||
self.regs.txfqs().read().tfqf()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_pending_frame(&self, idx: usize) -> bool {
|
||||
self.regs.txbrp().read().trp(idx)
|
||||
}
|
||||
|
||||
/// Returns `Ok` when the mailbox is free or if it contains pending frame with a
|
||||
/// lower priority (higher ID) than the identifier `id`.
|
||||
#[inline]
|
||||
pub fn is_available(&self, bufidx: usize, id: &embedded_can::Id) -> bool {
|
||||
if self.has_pending_frame(bufidx) {
|
||||
let mailbox = self.tx_buffer_element(bufidx);
|
||||
|
||||
let header_reg = mailbox.header.read();
|
||||
let old_id = make_id(header_reg.id().bits(), header_reg.xtd().bits());
|
||||
|
||||
*id > old_id
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to abort the sending of a frame that is pending in a mailbox.
|
||||
///
|
||||
/// If there is no frame in the provided mailbox, or its transmission succeeds before it can be
|
||||
/// aborted, this function has no effect and returns `false`.
|
||||
///
|
||||
/// If there is a frame in the provided mailbox, and it is canceled successfully, this function
|
||||
/// returns `true`.
|
||||
#[inline]
|
||||
pub fn abort(&self, bufidx: usize) -> bool {
|
||||
let can = self.regs;
|
||||
|
||||
// Check if there is a request pending to abort
|
||||
if self.has_pending_frame(bufidx) {
|
||||
// Abort Request
|
||||
can.txbcr().write(|w| w.set_cr(bufidx, true));
|
||||
|
||||
// Wait for the abort request to be finished.
|
||||
loop {
|
||||
if can.txbcf().read().cf(bufidx) {
|
||||
// Return false when a transmission has occured
|
||||
break can.txbto().read().to(bufidx) == false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
//fn abort_pending_mailbox<PTX, R>(&mut self, idx: Mailbox, pending: PTX) -> Option<R>
|
||||
pub fn abort_pending_mailbox(&self, bufidx: usize) -> Option<ClassicFrame>
|
||||
//where
|
||||
// PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R,
|
||||
{
|
||||
if self.abort(bufidx) {
|
||||
let mailbox = self.tx_buffer_element(bufidx);
|
||||
|
||||
let header_reg = mailbox.header.read();
|
||||
let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());
|
||||
|
||||
let len = match header_reg.to_data_length() {
|
||||
DataLength::Fdcan(len) => len,
|
||||
DataLength::Classic(len) => len,
|
||||
};
|
||||
if len as usize > ClassicFrame::MAX_DATA_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
//let tx_ram = self.tx_msg_ram();
|
||||
let mut data = [0u8; 64];
|
||||
data_from_tx_buffer(&mut data, mailbox, len as usize);
|
||||
|
||||
let cd = ClassicData::new(&data).unwrap();
|
||||
Some(ClassicFrame::new(Header::new(id, len, header_reg.rtr().bit()), cd))
|
||||
} else {
|
||||
// Abort request failed because the frame was already sent (or being sent) on
|
||||
// the bus. All mailboxes are now free. This can happen for small prescaler
|
||||
// values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR
|
||||
// has preempted the execution.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
//fn abort_pending_mailbox<PTX, R>(&mut self, idx: Mailbox, pending: PTX) -> Option<R>
|
||||
pub fn abort_pending_fd_mailbox(&self, bufidx: usize) -> Option<FdFrame>
|
||||
//where
|
||||
// PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R,
|
||||
{
|
||||
if self.abort(bufidx) {
|
||||
let mailbox = self.tx_buffer_element(bufidx);
|
||||
|
||||
let header_reg = mailbox.header.read();
|
||||
let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());
|
||||
|
||||
let len = match header_reg.to_data_length() {
|
||||
DataLength::Fdcan(len) => len,
|
||||
DataLength::Classic(len) => len,
|
||||
};
|
||||
if len as usize > FdFrame::MAX_DATA_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
//let tx_ram = self.tx_msg_ram();
|
||||
let mut data = [0u8; 64];
|
||||
data_from_tx_buffer(&mut data, mailbox, len as usize);
|
||||
|
||||
let cd = FdData::new(&data).unwrap();
|
||||
|
||||
let header = if header_reg.fdf().frame_format() == FrameFormat::Fdcan {
|
||||
Header::new_fd(id, len, header_reg.rtr().bit(), header_reg.brs().bit())
|
||||
} else {
|
||||
Header::new(id, len, header_reg.rtr().bit())
|
||||
};
|
||||
|
||||
Some(FdFrame::new(header, cd))
|
||||
} else {
|
||||
// Abort request failed because the frame was already sent (or being sent) on
|
||||
// the bus. All mailboxes are now free. This can happen for small prescaler
|
||||
// values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR
|
||||
// has preempted the execution.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can
|
||||
/// be preserved.
|
||||
//pub fn transmit_preserve<PTX, P>(
|
||||
pub fn write_classic(&self, frame: &ClassicFrame) -> nb::Result<Option<ClassicFrame>, Infallible> {
|
||||
let queue_is_full = self.tx_queue_is_full();
|
||||
|
||||
let id = frame.header().id();
|
||||
|
||||
// If the queue is full,
|
||||
// Discard the first slot with a lower priority message
|
||||
let (idx, pending_frame) = if queue_is_full {
|
||||
if self.is_available(0, id) {
|
||||
(0, self.abort_pending_mailbox(0))
|
||||
} else if self.is_available(1, id) {
|
||||
(1, self.abort_pending_mailbox(1))
|
||||
} else if self.is_available(2, id) {
|
||||
(2, self.abort_pending_mailbox(2))
|
||||
} else {
|
||||
// For now we bail when there is no lower priority slot available
|
||||
// Can this lead to priority inversion?
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
} else {
|
||||
// Read the Write Pointer
|
||||
let idx = self.regs.txfqs().read().tfqpi();
|
||||
|
||||
(idx, None)
|
||||
};
|
||||
|
||||
self.put_tx_frame(idx as usize, frame.header(), frame.data());
|
||||
|
||||
Ok(pending_frame)
|
||||
}
|
||||
|
||||
/// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can
|
||||
/// be preserved.
|
||||
//pub fn transmit_preserve<PTX, P>(
|
||||
pub fn write_fd(&self, frame: &FdFrame) -> nb::Result<Option<FdFrame>, Infallible> {
|
||||
let queue_is_full = self.tx_queue_is_full();
|
||||
|
||||
let id = frame.header().id();
|
||||
|
||||
// If the queue is full,
|
||||
// Discard the first slot with a lower priority message
|
||||
let (idx, pending_frame) = if queue_is_full {
|
||||
if self.is_available(0, id) {
|
||||
(0, self.abort_pending_fd_mailbox(0))
|
||||
} else if self.is_available(1, id) {
|
||||
(1, self.abort_pending_fd_mailbox(1))
|
||||
} else if self.is_available(2, id) {
|
||||
(2, self.abort_pending_fd_mailbox(2))
|
||||
} else {
|
||||
// For now we bail when there is no lower priority slot available
|
||||
// Can this lead to priority inversion?
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
} else {
|
||||
// Read the Write Pointer
|
||||
let idx = self.regs.txfqs().read().tfqpi();
|
||||
|
||||
(idx, None)
|
||||
};
|
||||
|
||||
self.put_tx_frame(idx as usize, frame.header(), frame.data());
|
||||
|
||||
Ok(pending_frame)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reset_msg_ram(&mut self) {
|
||||
self.msg_ram_mut().reset();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn enter_init_mode(&mut self) {
|
||||
self.regs.cccr().modify(|w| w.set_init(true));
|
||||
while false == self.regs.cccr().read().init() {}
|
||||
self.regs.cccr().modify(|w| w.set_cce(true));
|
||||
}
|
||||
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
#[inline]
|
||||
fn set_loopback_mode(&mut self, mode: LoopbackMode) {
|
||||
let (test, mon, lbck) = match mode {
|
||||
LoopbackMode::None => (false, false, false),
|
||||
LoopbackMode::Internal => (true, true, true),
|
||||
LoopbackMode::External => (true, false, true),
|
||||
};
|
||||
|
||||
self.set_test_mode(test);
|
||||
self.set_bus_monitoring_mode(mon);
|
||||
|
||||
self.regs.test().modify(|w| w.set_lbck(lbck));
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
#[inline]
|
||||
fn set_bus_monitoring_mode(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_mon(enabled));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_restricted_operations(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_asm(enabled));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_normal_operations(&mut self, _enabled: bool) {
|
||||
self.set_loopback_mode(LoopbackMode::None);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_test_mode(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_test(enabled));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_power_down_mode(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_csr(enabled));
|
||||
while self.regs.cccr().read().csa() != enabled {}
|
||||
}
|
||||
|
||||
/// Moves out of PoweredDownMode and into ConfigMode
|
||||
#[inline]
|
||||
pub fn into_config_mode(mut self, _config: FdCanConfig) {
|
||||
self.set_power_down_mode(false);
|
||||
self.enter_init_mode();
|
||||
|
||||
self.reset_msg_ram();
|
||||
|
||||
// check the FDCAN core matches our expections
|
||||
assert!(
|
||||
self.regs.crel().read().rel() == 3,
|
||||
"Expected FDCAN core major release 3"
|
||||
);
|
||||
assert!(
|
||||
self.regs.endn().read().etv() == 0x87654321_u32,
|
||||
"Error reading endianness test value from FDCAN core"
|
||||
);
|
||||
|
||||
// Framework specific settings are set here
|
||||
|
||||
// set TxBuffer to Queue Mode
|
||||
self.regs.txbc().write(|w| w.set_tfqm(true));
|
||||
|
||||
// set standard filters list size to 28
|
||||
// set extended filters list size to 8
|
||||
// REQUIRED: we use the memory map as if these settings are set
|
||||
// instead of re-calculating them.
|
||||
#[cfg(not(stm32h7))]
|
||||
{
|
||||
self.regs.rxgfc().modify(|w| {
|
||||
w.set_lss(crate::can::fd::message_ram::STANDARD_FILTER_MAX);
|
||||
w.set_lse(crate::can::fd::message_ram::EXTENDED_FILTER_MAX);
|
||||
});
|
||||
}
|
||||
#[cfg(stm32h7)]
|
||||
{
|
||||
self.regs
|
||||
.sidfc()
|
||||
.modify(|w| w.set_lss(crate::can::fd::message_ram::STANDARD_FILTER_MAX));
|
||||
self.regs
|
||||
.xidfc()
|
||||
.modify(|w| w.set_lse(crate::can::fd::message_ram::EXTENDED_FILTER_MAX));
|
||||
}
|
||||
|
||||
/*
|
||||
for fid in 0..crate::can::message_ram::STANDARD_FILTER_MAX {
|
||||
self.set_standard_filter((fid as u8).into(), StandardFilter::disable());
|
||||
}
|
||||
for fid in 0..Ecrate::can::message_ram::XTENDED_FILTER_MAX {
|
||||
self.set_extended_filter(fid.into(), ExtendedFilter::disable());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/// Disables the CAN interface and returns back the raw peripheral it was created from.
|
||||
#[inline]
|
||||
pub fn free(mut self) {
|
||||
//self.disable_interrupts(Interrupts::all());
|
||||
|
||||
//TODO check this!
|
||||
self.enter_init_mode();
|
||||
self.set_power_down_mode(true);
|
||||
//self.control.instance
|
||||
}
|
||||
|
||||
/// Applies the settings of a new FdCanConfig See [`FdCanConfig`]
|
||||
#[inline]
|
||||
pub fn apply_config(&mut self, config: FdCanConfig) {
|
||||
self.set_data_bit_timing(config.dbtr);
|
||||
self.set_nominal_bit_timing(config.nbtr);
|
||||
self.set_automatic_retransmit(config.automatic_retransmit);
|
||||
self.set_transmit_pause(config.transmit_pause);
|
||||
self.set_frame_transmit(config.frame_transmit);
|
||||
//self.set_interrupt_line_config(config.interrupt_line_config);
|
||||
self.set_non_iso_mode(config.non_iso_mode);
|
||||
self.set_edge_filtering(config.edge_filtering);
|
||||
self.set_protocol_exception_handling(config.protocol_exception_handling);
|
||||
self.set_global_filter(config.global_filter);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn leave_init_mode(&mut self, config: FdCanConfig) {
|
||||
self.apply_config(config);
|
||||
|
||||
self.regs.cccr().modify(|w| w.set_cce(false));
|
||||
self.regs.cccr().modify(|w| w.set_init(false));
|
||||
while self.regs.cccr().read().init() == true {}
|
||||
}
|
||||
|
||||
/// Moves out of ConfigMode and into specified mode
|
||||
#[inline]
|
||||
pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::FdcanOperatingMode) {
|
||||
match mode {
|
||||
crate::can::FdcanOperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal),
|
||||
crate::can::FdcanOperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External),
|
||||
crate::can::FdcanOperatingMode::NormalOperationMode => self.set_normal_operations(true),
|
||||
crate::can::FdcanOperatingMode::RestrictedOperationMode => self.set_restricted_operations(true),
|
||||
crate::can::FdcanOperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true),
|
||||
}
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
||||
/// Moves out of ConfigMode and into InternalLoopbackMode
|
||||
#[inline]
|
||||
pub fn into_internal_loopback(mut self, config: FdCanConfig) {
|
||||
self.set_loopback_mode(LoopbackMode::Internal);
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
||||
/// Moves out of ConfigMode and into ExternalLoopbackMode
|
||||
#[inline]
|
||||
pub fn into_external_loopback(mut self, config: FdCanConfig) {
|
||||
self.set_loopback_mode(LoopbackMode::External);
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
||||
/// Moves out of ConfigMode and into RestrictedOperationMode
|
||||
#[inline]
|
||||
pub fn into_restricted(mut self, config: FdCanConfig) {
|
||||
self.set_restricted_operations(true);
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
||||
/// Moves out of ConfigMode and into NormalOperationMode
|
||||
#[inline]
|
||||
pub fn into_normal(mut self, config: FdCanConfig) {
|
||||
self.set_normal_operations(true);
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
||||
/// Moves out of ConfigMode and into BusMonitoringMode
|
||||
#[inline]
|
||||
pub fn into_bus_monitoring(mut self, config: FdCanConfig) {
|
||||
self.set_bus_monitoring_mode(true);
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
||||
/// Moves out of ConfigMode and into Testmode
|
||||
#[inline]
|
||||
pub fn into_test_mode(mut self, config: FdCanConfig) {
|
||||
self.set_test_mode(true);
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
||||
/// Moves out of ConfigMode and into PoweredDownmode
|
||||
#[inline]
|
||||
pub fn into_powered_down(mut self, config: FdCanConfig) {
|
||||
self.set_power_down_mode(true);
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
||||
/// Configures the bit timings.
|
||||
///
|
||||
/// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter
|
||||
/// parameters as follows:
|
||||
///
|
||||
/// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed).
|
||||
/// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1).
|
||||
/// - *Sample Point*: Should normally be left at the default value of 87.5%.
|
||||
/// - *SJW*: Should normally be left at the default value of 1.
|
||||
///
|
||||
/// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr`
|
||||
/// parameter to this method.
|
||||
#[inline]
|
||||
pub fn set_nominal_bit_timing(&mut self, btr: NominalBitTiming) {
|
||||
//self.control.config.nbtr = btr;
|
||||
|
||||
self.regs.nbtp().write(|w| {
|
||||
w.set_nbrp(btr.nbrp() - 1);
|
||||
w.set_ntseg1(btr.ntseg1() - 1);
|
||||
w.set_ntseg2(btr.ntseg2() - 1);
|
||||
w.set_nsjw(btr.nsjw() - 1);
|
||||
});
|
||||
}
|
||||
|
||||
/// Configures the data bit timings for the FdCan Variable Bitrates.
|
||||
/// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS.
|
||||
#[inline]
|
||||
pub fn set_data_bit_timing(&mut self, btr: DataBitTiming) {
|
||||
//self.control.config.dbtr = btr;
|
||||
|
||||
self.regs.dbtp().write(|w| {
|
||||
w.set_dbrp(btr.dbrp() - 1);
|
||||
w.set_dtseg1(btr.dtseg1() - 1);
|
||||
w.set_dtseg2(btr.dtseg2() - 1);
|
||||
w.set_dsjw(btr.dsjw() - 1);
|
||||
});
|
||||
}
|
||||
|
||||
/// Enables or disables automatic retransmission of messages
|
||||
///
|
||||
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
||||
/// util it can be sent. Otherwise, it will try only once to send each frame.
|
||||
///
|
||||
/// Automatic retransmission is enabled by default.
|
||||
#[inline]
|
||||
pub fn set_automatic_retransmit(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_dar(!enabled));
|
||||
//self.control.config.automatic_retransmit = enabled;
|
||||
}
|
||||
|
||||
/// Configures the transmit pause feature. See
|
||||
/// [`FdCanConfig::set_transmit_pause`]
|
||||
#[inline]
|
||||
pub fn set_transmit_pause(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_txp(!enabled));
|
||||
//self.control.config.transmit_pause = enabled;
|
||||
}
|
||||
|
||||
/// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`]
|
||||
#[inline]
|
||||
pub fn set_non_iso_mode(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_niso(enabled));
|
||||
//self.control.config.non_iso_mode = enabled;
|
||||
}
|
||||
|
||||
/// Configures edge filtering. See [`FdCanConfig::set_edge_filtering`]
|
||||
#[inline]
|
||||
pub fn set_edge_filtering(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_efbi(enabled));
|
||||
//self.control.config.edge_filtering = enabled;
|
||||
}
|
||||
|
||||
/// Configures frame transmission mode. See
|
||||
/// [`FdCanConfig::set_frame_transmit`]
|
||||
#[inline]
|
||||
pub fn set_frame_transmit(&mut self, fts: FrameTransmissionConfig) {
|
||||
let (fdoe, brse) = match fts {
|
||||
FrameTransmissionConfig::ClassicCanOnly => (false, false),
|
||||
FrameTransmissionConfig::AllowFdCan => (true, false),
|
||||
FrameTransmissionConfig::AllowFdCanAndBRS => (true, true),
|
||||
};
|
||||
|
||||
self.regs.cccr().modify(|w| {
|
||||
w.set_fdoe(fdoe);
|
||||
#[cfg(stm32h7)]
|
||||
w.set_bse(brse);
|
||||
#[cfg(not(stm32h7))]
|
||||
w.set_brse(brse);
|
||||
});
|
||||
|
||||
//self.control.config.frame_transmit = fts;
|
||||
}
|
||||
|
||||
/// Sets the protocol exception handling on/off
|
||||
#[inline]
|
||||
pub fn set_protocol_exception_handling(&mut self, enabled: bool) {
|
||||
self.regs.cccr().modify(|w| w.set_pxhd(!enabled));
|
||||
|
||||
//self.control.config.protocol_exception_handling = enabled;
|
||||
}
|
||||
|
||||
/// Configures and resets the timestamp counter
|
||||
#[inline]
|
||||
pub fn set_timestamp_counter_source(&mut self, select: TimestampSource) {
|
||||
#[cfg(stm32h7)]
|
||||
let (tcp, tss) = match select {
|
||||
TimestampSource::None => (0, 0),
|
||||
TimestampSource::Prescaler(p) => (p as u8, 1),
|
||||
TimestampSource::FromTIM3 => (0, 2),
|
||||
};
|
||||
|
||||
#[cfg(not(stm32h7))]
|
||||
let (tcp, tss) = match select {
|
||||
TimestampSource::None => (0, stm32_metapac::can::vals::Tss::ZERO),
|
||||
TimestampSource::Prescaler(p) => (p as u8, stm32_metapac::can::vals::Tss::INCREMENT),
|
||||
TimestampSource::FromTIM3 => (0, stm32_metapac::can::vals::Tss::EXTERNAL),
|
||||
};
|
||||
|
||||
self.regs.tscc().write(|w| {
|
||||
w.set_tcp(tcp);
|
||||
w.set_tss(tss);
|
||||
});
|
||||
|
||||
//self.control.config.timestamp_source = select;
|
||||
}
|
||||
|
||||
#[cfg(not(stm32h7))]
|
||||
/// Configures the global filter settings
|
||||
#[inline]
|
||||
pub fn set_global_filter(&mut self, filter: GlobalFilter) {
|
||||
let anfs = match filter.handle_standard_frames {
|
||||
crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => stm32_metapac::can::vals::Anfs::ACCEPT_FIFO_0,
|
||||
crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => stm32_metapac::can::vals::Anfs::ACCEPT_FIFO_1,
|
||||
crate::can::fd::config::NonMatchingFilter::Reject => stm32_metapac::can::vals::Anfs::REJECT,
|
||||
};
|
||||
let anfe = match filter.handle_extended_frames {
|
||||
crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => stm32_metapac::can::vals::Anfe::ACCEPT_FIFO_0,
|
||||
crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => stm32_metapac::can::vals::Anfe::ACCEPT_FIFO_1,
|
||||
crate::can::fd::config::NonMatchingFilter::Reject => stm32_metapac::can::vals::Anfe::REJECT,
|
||||
};
|
||||
|
||||
self.regs.rxgfc().modify(|w| {
|
||||
w.set_anfs(anfs);
|
||||
w.set_anfe(anfe);
|
||||
w.set_rrfs(filter.reject_remote_standard_frames);
|
||||
w.set_rrfe(filter.reject_remote_extended_frames);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
/// Configures the global filter settings
|
||||
#[inline]
|
||||
pub fn set_global_filter(&mut self, filter: GlobalFilter) {
|
||||
let anfs = match filter.handle_standard_frames {
|
||||
crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => 0,
|
||||
crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => 1,
|
||||
crate::can::fd::config::NonMatchingFilter::Reject => 2,
|
||||
};
|
||||
|
||||
let anfe = match filter.handle_extended_frames {
|
||||
crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => 0,
|
||||
crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => 1,
|
||||
crate::can::fd::config::NonMatchingFilter::Reject => 2,
|
||||
};
|
||||
|
||||
self.regs.gfc().modify(|w| {
|
||||
w.set_anfs(anfs);
|
||||
w.set_anfe(anfe);
|
||||
w.set_rrfs(filter.reject_remote_standard_frames);
|
||||
w.set_rrfe(filter.reject_remote_extended_frames);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn make_id(id: u32, extended: bool) -> embedded_can::Id {
|
||||
if extended {
|
||||
embedded_can::Id::from(unsafe { embedded_can::ExtendedId::new_unchecked(id & 0x1FFFFFFF) })
|
||||
} else {
|
||||
embedded_can::Id::from(unsafe { embedded_can::StandardId::new_unchecked((id & 0x000007FF) as u16) })
|
||||
}
|
||||
}
|
||||
|
||||
fn put_tx_header(mailbox: &mut TxBufferElement, header: &Header) {
|
||||
let (id, id_type) = match header.id() {
|
||||
embedded_can::Id::Standard(id) => (id.as_raw() as u32, IdType::StandardId),
|
||||
embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId),
|
||||
};
|
||||
|
||||
// Use FDCAN only for DLC > 8. FDCAN users can revise this if required.
|
||||
let frame_format = if header.len() > 8 || header.fdcan() {
|
||||
FrameFormat::Fdcan
|
||||
} else {
|
||||
FrameFormat::Classic
|
||||
};
|
||||
let brs = header.len() > 8 || header.bit_rate_switching();
|
||||
|
||||
mailbox.header.write(|w| {
|
||||
unsafe { w.id().bits(id) }
|
||||
.rtr()
|
||||
.bit(header.len() == 0 && header.rtr())
|
||||
.xtd()
|
||||
.set_id_type(id_type)
|
||||
.set_len(DataLength::new(header.len(), frame_format))
|
||||
.set_event(Event::NoEvent)
|
||||
.fdf()
|
||||
.set_format(frame_format)
|
||||
.brs()
|
||||
.bit(brs)
|
||||
//esi.set_error_indicator(//TODO//)
|
||||
});
|
||||
}
|
||||
|
||||
fn put_tx_data(mailbox: &mut TxBufferElement, buffer: &[u8]) {
|
||||
let mut lbuffer = [0_u32; 16];
|
||||
let len = buffer.len();
|
||||
let data = unsafe { slice::from_raw_parts_mut(lbuffer.as_mut_ptr() as *mut u8, len) };
|
||||
data[..len].copy_from_slice(&buffer[..len]);
|
||||
let data_len = ((len) + 3) / 4;
|
||||
for (register, byte) in mailbox.data.iter_mut().zip(lbuffer[..data_len].iter()) {
|
||||
unsafe { register.write(*byte) };
|
||||
}
|
||||
}
|
||||
|
||||
fn data_from_fifo(buffer: &mut [u8], mailbox: &RxFifoElement, len: usize) {
|
||||
for (i, register) in mailbox.data.iter().enumerate() {
|
||||
let register_value = register.read();
|
||||
let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) };
|
||||
let num_bytes = (len) - i * 4;
|
||||
if num_bytes <= 4 {
|
||||
buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]);
|
||||
break;
|
||||
}
|
||||
buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
fn data_from_tx_buffer(buffer: &mut [u8], mailbox: &TxBufferElement, len: usize) {
|
||||
for (i, register) in mailbox.data.iter().enumerate() {
|
||||
let register_value = register.read();
|
||||
let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) };
|
||||
let num_bytes = (len) - i * 4;
|
||||
if num_bytes <= 4 {
|
||||
buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]);
|
||||
break;
|
||||
}
|
||||
buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&RxFifoElement> for ClassicFrame {
|
||||
fn from(mailbox: &RxFifoElement) -> Self {
|
||||
let header_reg = mailbox.header.read();
|
||||
|
||||
let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());
|
||||
let dlc = header_reg.to_data_length().len();
|
||||
let len = dlc as usize;
|
||||
|
||||
let mut buffer: [u8; 64] = [0; 64];
|
||||
data_from_fifo(&mut buffer, mailbox, len);
|
||||
let data = ClassicData::new(&buffer[0..len]);
|
||||
let header = Header::new(id, dlc, header_reg.rtr().bits());
|
||||
ClassicFrame::new(header, data.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_frame(mailbox: &RxFifoElement, buffer: &mut [u8]) -> Option<(Header, u16)> {
|
||||
let header_reg = mailbox.header.read();
|
||||
|
||||
let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());
|
||||
let dlc = header_reg.to_data_length().len();
|
||||
let len = dlc as usize;
|
||||
let timestamp = header_reg.txts().bits;
|
||||
if len > buffer.len() {
|
||||
return None;
|
||||
}
|
||||
data_from_fifo(buffer, mailbox, len);
|
||||
let header = if header_reg.fdf().bits {
|
||||
Header::new_fd(id, dlc, header_reg.rtr().bits(), header_reg.brs().bits())
|
||||
} else {
|
||||
Header::new(id, dlc, header_reg.rtr().bits())
|
||||
};
|
||||
Some((header, timestamp))
|
||||
}
|
File diff suppressed because it is too large
Load diff
370
embassy-stm32/src/can/frame.rs
Normal file
370
embassy-stm32/src/can/frame.rs
Normal file
|
@ -0,0 +1,370 @@
|
|||
//! Definition for CAN Frames
|
||||
use bit_field::BitField;
|
||||
|
||||
/// CAN Header, without meta data
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Header {
|
||||
id: embedded_can::Id,
|
||||
len: u8,
|
||||
flags: u8,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
const FLAG_RTR: usize = 0; // Remote
|
||||
const FLAG_FDCAN: usize = 1; // FDCan vs Classic CAN
|
||||
const FLAG_BRS: usize = 2; // Bit-rate switching, ignored for Classic CAN
|
||||
|
||||
/// Create new CAN Header
|
||||
pub fn new(id: embedded_can::Id, len: u8, rtr: bool) -> Header {
|
||||
let mut flags = 0u8;
|
||||
flags.set_bit(Self::FLAG_RTR, rtr);
|
||||
Header { id, len, flags }
|
||||
}
|
||||
|
||||
/// Create new CAN FD Header
|
||||
pub fn new_fd(id: embedded_can::Id, len: u8, rtr: bool, brs: bool) -> Header {
|
||||
let mut flags = 0u8;
|
||||
flags.set_bit(Self::FLAG_RTR, rtr);
|
||||
flags.set_bit(Self::FLAG_FDCAN, true);
|
||||
flags.set_bit(Self::FLAG_BRS, brs);
|
||||
Header { id, len, flags }
|
||||
}
|
||||
|
||||
/// Return ID
|
||||
pub fn id(&self) -> &embedded_can::Id {
|
||||
&self.id
|
||||
}
|
||||
|
||||
/// Return length as u8
|
||||
pub fn len(&self) -> u8 {
|
||||
self.len
|
||||
}
|
||||
|
||||
/// Is remote frame
|
||||
pub fn rtr(&self) -> bool {
|
||||
self.flags.get_bit(Self::FLAG_RTR)
|
||||
}
|
||||
|
||||
/// Request/is FDCAN frame
|
||||
pub fn fdcan(&self) -> bool {
|
||||
self.flags.get_bit(Self::FLAG_FDCAN)
|
||||
}
|
||||
|
||||
/// Request/is Flexible Data Rate
|
||||
pub fn bit_rate_switching(&self) -> bool {
|
||||
self.flags.get_bit(Self::FLAG_BRS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Payload of a classic CAN data frame.
|
||||
///
|
||||
/// Contains 0 to 8 Bytes of data.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ClassicData {
|
||||
pub(crate) bytes: [u8; 8],
|
||||
}
|
||||
|
||||
impl ClassicData {
|
||||
/// Creates a data payload from a raw byte slice.
|
||||
///
|
||||
/// Returns `None` if `data` is more than 64 bytes (which is the maximum) or
|
||||
/// cannot be represented with an FDCAN DLC.
|
||||
pub fn new(data: &[u8]) -> Option<Self> {
|
||||
if !FdData::is_valid_len(data.len()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut bytes = [0; 8];
|
||||
bytes[..data.len()].copy_from_slice(data);
|
||||
|
||||
Some(Self { bytes })
|
||||
}
|
||||
|
||||
/// Raw read access to data.
|
||||
pub fn raw(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
/// Checks if the length can be encoded in FDCAN DLC field.
|
||||
pub const fn is_valid_len(len: usize) -> bool {
|
||||
match len {
|
||||
0..=8 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an empty data payload containing 0 bytes.
|
||||
#[inline]
|
||||
pub const fn empty() -> Self {
|
||||
Self { bytes: [0; 8] }
|
||||
}
|
||||
}
|
||||
|
||||
/// Frame with up to 8 bytes of data payload as per Classic CAN
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ClassicFrame {
|
||||
can_header: Header,
|
||||
data: ClassicData,
|
||||
}
|
||||
|
||||
impl ClassicFrame {
|
||||
pub(crate) const MAX_DATA_LEN: usize = 8;
|
||||
|
||||
/// Create a new CAN classic Frame
|
||||
pub fn new(can_header: Header, data: ClassicData) -> ClassicFrame {
|
||||
ClassicFrame { can_header, data }
|
||||
}
|
||||
|
||||
/// Create new extended frame
|
||||
pub fn new_extended(raw_id: u32, raw_data: &[u8]) -> Option<Self> {
|
||||
if let Some(id) = embedded_can::ExtendedId::new(raw_id) {
|
||||
match ClassicData::new(raw_data) {
|
||||
Some(data) => Some(ClassicFrame::new(
|
||||
Header::new(id.into(), raw_data.len() as u8, false),
|
||||
data,
|
||||
)),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new standard frame
|
||||
pub fn new_standard(raw_id: u16, raw_data: &[u8]) -> Option<Self> {
|
||||
if let Some(id) = embedded_can::StandardId::new(raw_id) {
|
||||
match ClassicData::new(raw_data) {
|
||||
Some(data) => Some(ClassicFrame::new(
|
||||
Header::new(id.into(), raw_data.len() as u8, false),
|
||||
data,
|
||||
)),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new remote frame
|
||||
pub fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> {
|
||||
if len <= 8usize {
|
||||
Some(ClassicFrame::new(
|
||||
Header::new(id.into(), len as u8, true),
|
||||
ClassicData::empty(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get reference to data
|
||||
pub fn header(&self) -> &Header {
|
||||
&self.can_header
|
||||
}
|
||||
|
||||
/// Return ID
|
||||
pub fn id(&self) -> &embedded_can::Id {
|
||||
&self.can_header.id
|
||||
}
|
||||
|
||||
/// Get reference to data
|
||||
pub fn data(&self) -> &[u8] {
|
||||
&self.data.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_can::Frame for ClassicFrame {
|
||||
fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> {
|
||||
match ClassicData::new(raw_data) {
|
||||
Some(data) => Some(ClassicFrame::new(
|
||||
Header::new(id.into(), raw_data.len() as u8, false),
|
||||
data,
|
||||
)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> {
|
||||
if len <= 8 {
|
||||
Some(ClassicFrame::new(
|
||||
Header::new(id.into(), len as u8, true),
|
||||
ClassicData::empty(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn is_extended(&self) -> bool {
|
||||
match self.can_header.id {
|
||||
embedded_can::Id::Extended(_) => true,
|
||||
embedded_can::Id::Standard(_) => true,
|
||||
}
|
||||
}
|
||||
fn is_remote_frame(&self) -> bool {
|
||||
self.can_header.rtr()
|
||||
}
|
||||
fn id(&self) -> embedded_can::Id {
|
||||
self.can_header.id
|
||||
}
|
||||
fn dlc(&self) -> usize {
|
||||
self.can_header.len as usize
|
||||
}
|
||||
fn data(&self) -> &[u8] {
|
||||
&self.data.raw()
|
||||
}
|
||||
}
|
||||
|
||||
/// Payload of a (FD)CAN data frame.
|
||||
///
|
||||
/// Contains 0 to 64 Bytes of data.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FdData {
|
||||
pub(crate) bytes: [u8; 64],
|
||||
}
|
||||
|
||||
impl FdData {
|
||||
/// Creates a data payload from a raw byte slice.
|
||||
///
|
||||
/// Returns `None` if `data` is more than 64 bytes (which is the maximum) or
|
||||
/// cannot be represented with an FDCAN DLC.
|
||||
pub fn new(data: &[u8]) -> Option<Self> {
|
||||
if !FdData::is_valid_len(data.len()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut bytes = [0; 64];
|
||||
bytes[..data.len()].copy_from_slice(data);
|
||||
|
||||
Some(Self { bytes })
|
||||
}
|
||||
|
||||
/// Raw read access to data.
|
||||
pub fn raw(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
|
||||
/// Checks if the length can be encoded in FDCAN DLC field.
|
||||
pub const fn is_valid_len(len: usize) -> bool {
|
||||
match len {
|
||||
0..=8 => true,
|
||||
12 => true,
|
||||
16 => true,
|
||||
20 => true,
|
||||
24 => true,
|
||||
32 => true,
|
||||
48 => true,
|
||||
64 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an empty data payload containing 0 bytes.
|
||||
#[inline]
|
||||
pub const fn empty() -> Self {
|
||||
Self { bytes: [0; 64] }
|
||||
}
|
||||
}
|
||||
|
||||
/// Frame with up to 8 bytes of data payload as per Fd CAN
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct FdFrame {
|
||||
can_header: Header,
|
||||
data: FdData,
|
||||
}
|
||||
|
||||
impl FdFrame {
|
||||
pub(crate) const MAX_DATA_LEN: usize = 64;
|
||||
|
||||
/// Create a new CAN classic Frame
|
||||
pub fn new(can_header: Header, data: FdData) -> FdFrame {
|
||||
FdFrame { can_header, data }
|
||||
}
|
||||
|
||||
/// Create new extended frame
|
||||
pub fn new_extended(raw_id: u32, raw_data: &[u8]) -> Option<Self> {
|
||||
if let Some(id) = embedded_can::ExtendedId::new(raw_id) {
|
||||
match FdData::new(raw_data) {
|
||||
Some(data) => Some(FdFrame::new(Header::new(id.into(), raw_data.len() as u8, false), data)),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new standard frame
|
||||
pub fn new_standard(raw_id: u16, raw_data: &[u8]) -> Option<Self> {
|
||||
if let Some(id) = embedded_can::StandardId::new(raw_id) {
|
||||
match FdData::new(raw_data) {
|
||||
Some(data) => Some(FdFrame::new(Header::new(id.into(), raw_data.len() as u8, false), data)),
|
||||
None => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new remote frame
|
||||
pub fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> {
|
||||
if len <= 8 {
|
||||
Some(FdFrame::new(Header::new(id.into(), len as u8, true), FdData::empty()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get reference to data
|
||||
pub fn header(&self) -> &Header {
|
||||
&self.can_header
|
||||
}
|
||||
|
||||
/// Return ID
|
||||
pub fn id(&self) -> &embedded_can::Id {
|
||||
&self.can_header.id
|
||||
}
|
||||
|
||||
/// Get reference to data
|
||||
pub fn data(&self) -> &[u8] {
|
||||
&self.data.raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_can::Frame for FdFrame {
|
||||
fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> {
|
||||
match FdData::new(raw_data) {
|
||||
Some(data) => Some(FdFrame::new(
|
||||
Header::new_fd(id.into(), raw_data.len() as u8, false, true),
|
||||
data,
|
||||
)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> {
|
||||
if len <= 8 {
|
||||
Some(FdFrame::new(
|
||||
Header::new_fd(id.into(), len as u8, true, true),
|
||||
FdData::empty(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn is_extended(&self) -> bool {
|
||||
match self.can_header.id {
|
||||
embedded_can::Id::Extended(_) => true,
|
||||
embedded_can::Id::Standard(_) => true,
|
||||
}
|
||||
}
|
||||
fn is_remote_frame(&self) -> bool {
|
||||
self.can_header.rtr()
|
||||
}
|
||||
fn id(&self) -> embedded_can::Id {
|
||||
self.can_header.id
|
||||
}
|
||||
// Returns length in bytes even for CANFD packets which embedded-can does not really mention.
|
||||
fn dlc(&self) -> usize {
|
||||
self.can_header.len as usize
|
||||
}
|
||||
fn data(&self) -> &[u8] {
|
||||
&self.data.raw()
|
||||
}
|
||||
}
|
|
@ -299,7 +299,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||
STATE.complete_count[this.channel.index()].store(0, Ordering::Release);
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
super::dmamux::configure_dmamux(&*this.channel, _request);
|
||||
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
ch.mar().write_value(mem_addr as u32);
|
||||
|
@ -483,7 +483,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
super::dmamux::configure_dmamux(&*this.channel, _request);
|
||||
|
||||
let ch = dma.ch(channel_number);
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
|
@ -641,7 +641,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
super::dmamux::configure_dmamux(&*this.channel, _request);
|
||||
|
||||
let ch = dma.ch(channel_number);
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
|
|
|
@ -366,7 +366,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
super::dmamux::configure_dmamux(&*this.channel, _request);
|
||||
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
ch.m0ar().write_value(mem_addr as u32);
|
||||
|
@ -522,7 +522,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
|
|||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
super::dmamux::configure_dmamux(&*this.channel, _request);
|
||||
|
||||
let ch = dma.st(channel_number);
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
|
@ -726,7 +726,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> {
|
|||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
super::dmamux::configure_dmamux(&*this.channel, _request);
|
||||
|
||||
let ch = dma.st(channel_number);
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
|
@ -901,7 +901,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> {
|
|||
this.clear_irqs();
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, _request);
|
||||
super::dmamux::configure_dmamux(&*this.channel, _request);
|
||||
|
||||
let ch = dma.st(channel_number);
|
||||
ch.par().write_value(peri_addr as u32);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::{pac, peripherals};
|
||||
|
||||
pub(crate) fn configure_dmamux<M: MuxChannel>(channel: &mut M, request: u8) {
|
||||
pub(crate) fn configure_dmamux<M: MuxChannel>(channel: &M, request: u8) {
|
||||
let ch_mux_regs = channel.mux_regs().ccr(channel.mux_num());
|
||||
ch_mux_regs.write(|reg| {
|
||||
reg.set_nbreq(0);
|
||||
|
|
|
@ -259,7 +259,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
|
|||
let this = Self { channel };
|
||||
|
||||
#[cfg(dmamux)]
|
||||
super::dmamux::configure_dmamux(&mut *this.channel, request);
|
||||
super::dmamux::configure_dmamux(&*this.channel, request);
|
||||
|
||||
ch.cr().write(|w| w.set_reset(true));
|
||||
ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs
|
||||
|
|
124
embassy-stm32/src/flash/h50.rs
Normal file
124
embassy-stm32/src/flash/h50.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
/// STM32H50 series flash impl. See RM0492
|
||||
use core::{
|
||||
ptr::write_volatile,
|
||||
sync::atomic::{fence, Ordering},
|
||||
};
|
||||
|
||||
use cortex_m::interrupt;
|
||||
use pac::flash::regs::Nssr;
|
||||
use pac::flash::vals::Bksel;
|
||||
|
||||
use super::{Error, FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||
use crate::pac;
|
||||
|
||||
pub(crate) const fn is_default_layout() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
|
||||
&FLASH_REGIONS
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn lock() {
|
||||
pac::FLASH.nscr().modify(|w| w.set_lock(true));
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unlock() {
|
||||
while busy() {}
|
||||
|
||||
if pac::FLASH.nscr().read().lock() {
|
||||
pac::FLASH.nskeyr().write_value(0x4567_0123);
|
||||
pac::FLASH.nskeyr().write_value(0xCDEF_89AB);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn enable_blocking_write() {
|
||||
assert_eq!(0, WRITE_SIZE % 4);
|
||||
pac::FLASH.nscr().write(|w| w.set_pg(true));
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn disable_blocking_write() {
|
||||
pac::FLASH.nscr().write(|w| w.set_pg(false));
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
|
||||
let mut address = start_address;
|
||||
for val in buf.chunks(4) {
|
||||
write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
|
||||
address += val.len() as u32;
|
||||
|
||||
// prevents parallelism errors
|
||||
fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
wait_ready_blocking()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
|
||||
assert!(sector.bank != FlashBank::Otp);
|
||||
assert!(sector.index_in_bank < 8);
|
||||
|
||||
while busy() {}
|
||||
|
||||
interrupt::free(|_| {
|
||||
pac::FLASH.nscr().modify(|w| {
|
||||
w.set_bksel(match sector.bank {
|
||||
FlashBank::Bank1 => Bksel::B_0X0,
|
||||
FlashBank::Bank2 => Bksel::B_0X1,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
w.set_snb(sector.index_in_bank);
|
||||
w.set_ser(true);
|
||||
w.set_strt(true);
|
||||
})
|
||||
});
|
||||
|
||||
let ret = wait_ready_blocking();
|
||||
pac::FLASH.nscr().modify(|w| w.set_ser(false));
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {
|
||||
loop {
|
||||
let sr = pac::FLASH.nssr().read();
|
||||
|
||||
if !sr_busy(sr) {
|
||||
if sr.wrperr() {
|
||||
return Err(Error::Protected);
|
||||
}
|
||||
if sr.pgserr() {
|
||||
return Err(Error::Seq);
|
||||
}
|
||||
if sr.strberr() {
|
||||
// writing several times to the same byte in the write buffer
|
||||
return Err(Error::Prog);
|
||||
}
|
||||
if sr.incerr() {
|
||||
// attempting write operation before completion of previous
|
||||
// write operation
|
||||
return Err(Error::Seq);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn clear_all_err() {
|
||||
pac::FLASH.nsccr().modify(|w| {
|
||||
w.set_clr_wrperr(true);
|
||||
w.set_clr_pgserr(true);
|
||||
w.set_clr_strberr(true);
|
||||
w.set_clr_incerr(true);
|
||||
})
|
||||
}
|
||||
|
||||
fn sr_busy(sr: Nssr) -> bool {
|
||||
// Note: RM0492 sometimes incorrectly refers to WBNE as NSWBNE
|
||||
sr.bsy() || sr.dbne() || sr.wbne()
|
||||
}
|
||||
|
||||
fn busy() -> bool {
|
||||
let sr = pac::FLASH.nssr().read();
|
||||
sr_busy(sr)
|
||||
}
|
|
@ -102,10 +102,11 @@ pub enum FlashBank {
|
|||
#[cfg_attr(flash_h7, path = "h7.rs")]
|
||||
#[cfg_attr(flash_h7ab, path = "h7.rs")]
|
||||
#[cfg_attr(flash_u5, path = "u5.rs")]
|
||||
#[cfg_attr(flash_h50, path = "h50.rs")]
|
||||
#[cfg_attr(
|
||||
not(any(
|
||||
flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f1, flash_f3, flash_f4, flash_f7, flash_g0,
|
||||
flash_g4, flash_h7, flash_h7ab, flash_u5
|
||||
flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50
|
||||
)),
|
||||
path = "other.rs"
|
||||
)]
|
||||
|
|
|
@ -201,11 +201,18 @@ impl LsConfig {
|
|||
bdcr().modify(|w| w.set_bdrst(true));
|
||||
bdcr().modify(|w| w.set_bdrst(false));
|
||||
}
|
||||
#[cfg(any(stm32h5))]
|
||||
{
|
||||
bdcr().modify(|w| w.set_vswrst(true));
|
||||
bdcr().modify(|w| w.set_vswrst(false));
|
||||
}
|
||||
// H5 has a terrible, terrible errata: 'SRAM2 is erased when the backup domain is reset'
|
||||
// pending a more sane sane way to handle this, just don't reset BD for now.
|
||||
// This means the RTCSEL write below will have no effect, only if it has already been written
|
||||
// after last power-on. Since it's uncommon to dynamically change RTCSEL, this is better than
|
||||
// letting half our RAM go magically *poof*.
|
||||
// STM32H503CB/EB/KB/RB device errata - 2.2.8 SRAM2 unduly erased upon a backup domain reset
|
||||
// STM32H562xx/563xx/573xx device errata - 2.2.14 SRAM2 is erased when the backup domain is reset
|
||||
//#[cfg(any(stm32h5))]
|
||||
//{
|
||||
// bdcr().modify(|w| w.set_vswrst(true));
|
||||
// bdcr().modify(|w| w.set_vswrst(false));
|
||||
//}
|
||||
#[cfg(any(stm32c0, stm32l0))]
|
||||
{
|
||||
bdcr().modify(|w| w.set_rtcrst(true));
|
||||
|
|
|
@ -1,134 +1,83 @@
|
|||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange, Plldiv, Pllm, Plln, Ppre as APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Msirgsel, Pllmboost, Pllrge, Pllsrc, Sw};
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
pub use crate::pac::rcc::vals::{
|
||||
Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
|
||||
Pllsrc as PllSource, Ppre as APBPrescaler, Sw as ClockSrc,
|
||||
};
|
||||
use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum ClockSrc {
|
||||
/// Use an internal medium speed oscillator (MSIS) as the system clock.
|
||||
MSI(Msirange),
|
||||
/// Use the external high speed clock as the system clock.
|
||||
///
|
||||
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
|
||||
/// never exceed 50 MHz.
|
||||
HSE(Hertz),
|
||||
/// Use the 16 MHz internal high speed oscillator as the system clock.
|
||||
HSI,
|
||||
/// Use PLL1 as the system clock.
|
||||
PLL1_R(PllConfig),
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum HseMode {
|
||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||
Oscillator,
|
||||
/// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
|
||||
Bypass,
|
||||
/// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
|
||||
BypassDigital,
|
||||
}
|
||||
|
||||
impl Default for ClockSrc {
|
||||
fn default() -> Self {
|
||||
// The default system clock source is MSIS @ 4 MHz, per RM0456 § 11.4.9
|
||||
ClockSrc::MSI(Msirange::RANGE_4MHZ)
|
||||
}
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Hse {
|
||||
/// HSE frequency.
|
||||
pub freq: Hertz,
|
||||
/// HSE mode.
|
||||
pub mode: HseMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PllConfig {
|
||||
pub struct Pll {
|
||||
/// The clock source for the PLL.
|
||||
pub source: PllSource,
|
||||
/// The PLL prescaler.
|
||||
/// The PLL pre-divider.
|
||||
///
|
||||
/// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
|
||||
pub m: Pllm,
|
||||
pub prediv: PllPreDiv,
|
||||
/// The PLL multiplier.
|
||||
///
|
||||
/// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544
|
||||
/// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.
|
||||
pub n: Plln,
|
||||
pub mul: PllMul,
|
||||
/// The divider for the P output.
|
||||
///
|
||||
/// The P output is one of several options
|
||||
/// that can be used to feed the SAI/MDF/ADF Clock mux's.
|
||||
pub p: Plldiv,
|
||||
pub divp: Option<PllDiv>,
|
||||
/// The divider for the Q output.
|
||||
///
|
||||
/// The Q ouput is one of severals options that can be used to feed the 48MHz clocks
|
||||
/// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's.
|
||||
pub q: Plldiv,
|
||||
pub divq: Option<PllDiv>,
|
||||
/// The divider for the R output.
|
||||
///
|
||||
/// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`
|
||||
/// must not exceed 160 MHz. System clocks above 55 MHz require a non-default
|
||||
/// `Config { voltage_range }`.
|
||||
pub r: Plldiv,
|
||||
}
|
||||
|
||||
impl PllConfig {
|
||||
/// A configuration for HSI / 1 * 10 / 1 = 160 MHz
|
||||
pub const fn hsi_160mhz() -> Self {
|
||||
PllConfig {
|
||||
source: PllSource::HSI,
|
||||
m: Pllm::DIV1,
|
||||
n: Plln::MUL10,
|
||||
p: Plldiv::DIV3,
|
||||
q: Plldiv::DIV2,
|
||||
r: Plldiv::DIV1,
|
||||
}
|
||||
}
|
||||
|
||||
/// A configuration for MSIS @ 48 MHz / 3 * 10 / 1 = 160 MHz
|
||||
pub const fn msis_160mhz() -> Self {
|
||||
PllConfig {
|
||||
source: PllSource::MSIS(Msirange::RANGE_48MHZ),
|
||||
m: Pllm::DIV3,
|
||||
n: Plln::MUL10,
|
||||
p: Plldiv::DIV3,
|
||||
q: Plldiv::DIV2,
|
||||
r: Plldiv::DIV1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllSource {
|
||||
/// Use an internal medium speed oscillator as the PLL source.
|
||||
MSIS(Msirange),
|
||||
/// Use the external high speed clock as the system PLL source.
|
||||
///
|
||||
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
|
||||
/// never exceed 50 MHz.
|
||||
HSE(Hertz),
|
||||
/// Use the 16 MHz internal high speed oscillator as the PLL source.
|
||||
HSI,
|
||||
}
|
||||
|
||||
impl Into<Pllsrc> for PllSource {
|
||||
fn into(self) -> Pllsrc {
|
||||
match self {
|
||||
PllSource::MSIS(..) => Pllsrc::MSIS,
|
||||
PllSource::HSE(..) => Pllsrc::HSE,
|
||||
PllSource::HSI => Pllsrc::HSI,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Sw> for ClockSrc {
|
||||
fn into(self) -> Sw {
|
||||
match self {
|
||||
ClockSrc::MSI(..) => Sw::MSIS,
|
||||
ClockSrc::HSE(..) => Sw::HSE,
|
||||
ClockSrc::HSI => Sw::HSI,
|
||||
ClockSrc::PLL1_R(..) => Sw::PLL1_R,
|
||||
}
|
||||
}
|
||||
pub divr: Option<PllDiv>,
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
// base clock sources
|
||||
pub msi: Option<MSIRange>,
|
||||
pub hsi: bool,
|
||||
pub hse: Option<Hse>,
|
||||
pub hsi48: Option<super::Hsi48Config>,
|
||||
|
||||
// pll
|
||||
pub pll1: Option<Pll>,
|
||||
pub pll2: Option<Pll>,
|
||||
pub pll3: Option<Pll>,
|
||||
|
||||
// sysclk, buses.
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub apb3_pre: APBPrescaler,
|
||||
pub hsi48: Option<super::Hsi48Config>,
|
||||
|
||||
/// The voltage range influences the maximum clock frequencies for different parts of the
|
||||
/// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks
|
||||
/// exceeding 55 MHz require at least `RANGE2`.
|
||||
|
@ -138,35 +87,35 @@ pub struct Config {
|
|||
pub ls: super::LsConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
unsafe fn init_hsi(&self) -> Hertz {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI_FREQ
|
||||
}
|
||||
|
||||
unsafe fn init_hse(&self, frequency: Hertz) -> Hertz {
|
||||
// Check frequency limits per RM456 § 11.4.10
|
||||
match self.voltage_range {
|
||||
VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => {
|
||||
assert!(frequency.0 <= 50_000_000);
|
||||
}
|
||||
VoltageScale::RANGE4 => {
|
||||
assert!(frequency.0 <= 25_000_000);
|
||||
}
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
msi: Some(Msirange::RANGE_4MHZ),
|
||||
hse: None,
|
||||
hsi: false,
|
||||
hsi48: Some(Default::default()),
|
||||
pll1: None,
|
||||
pll2: None,
|
||||
pll3: None,
|
||||
mux: ClockSrc::MSIS,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb3_pre: APBPrescaler::DIV1,
|
||||
voltage_range: VoltageScale::RANGE1,
|
||||
ls: Default::default(),
|
||||
}
|
||||
|
||||
// Enable HSE, and wait for it to stabilize
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
frequency
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn init_msis(&self, range: Msirange) -> Hertz {
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// Set the requested power mode
|
||||
PWR.vosr().modify(|w| w.set_vos(config.voltage_range));
|
||||
while !PWR.vosr().read().vosrdy() {}
|
||||
|
||||
let msi = config.msi.map(|range| {
|
||||
// Check MSI output per RM0456 § 11.4.10
|
||||
match self.voltage_range {
|
||||
match config.voltage_range {
|
||||
VoltageScale::RANGE4 => {
|
||||
assert!(msirange_to_hertz(range).0 <= 24_000_000);
|
||||
}
|
||||
|
@ -191,223 +140,98 @@ impl Config {
|
|||
});
|
||||
while !RCC.cr().read().msisrdy() {}
|
||||
msirange_to_hertz(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mux: ClockSrc::default(),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb3_pre: APBPrescaler::DIV1,
|
||||
hsi48: Some(Default::default()),
|
||||
voltage_range: VoltageScale::RANGE3,
|
||||
ls: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// Ensure PWR peripheral clock is enabled
|
||||
RCC.ahb3enr().modify(|w| {
|
||||
w.set_pwren(true);
|
||||
});
|
||||
RCC.ahb3enr().read(); // synchronize
|
||||
|
||||
// Set the requested power mode
|
||||
PWR.vosr().modify(|w| {
|
||||
w.set_vos(config.voltage_range);
|
||||
let hsi = config.hsi.then(|| {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI_FREQ
|
||||
});
|
||||
while !PWR.vosr().read().vosrdy() {}
|
||||
|
||||
let sys_clk = match config.mux {
|
||||
ClockSrc::MSI(range) => config.init_msis(range),
|
||||
ClockSrc::HSE(freq) => config.init_hse(freq),
|
||||
ClockSrc::HSI => config.init_hsi(),
|
||||
ClockSrc::PLL1_R(pll) => {
|
||||
// Configure the PLL source
|
||||
let source_clk = match pll.source {
|
||||
PllSource::MSIS(range) => config.init_msis(range),
|
||||
PllSource::HSE(hertz) => config.init_hse(hertz),
|
||||
PllSource::HSI => config.init_hsi(),
|
||||
};
|
||||
|
||||
// Calculate the reference clock, which is the source divided by m
|
||||
let reference_clk = source_clk / pll.m;
|
||||
|
||||
// Check limits per RM0456 § 11.4.6
|
||||
assert!(Hertz::mhz(4) <= reference_clk && reference_clk <= Hertz::mhz(16));
|
||||
|
||||
// Calculate the PLL1 VCO clock and PLL1 R output clock
|
||||
let pll1_clk = reference_clk * pll.n;
|
||||
let pll1r_clk = pll1_clk / pll.r;
|
||||
|
||||
// Check system clock per RM0456 § 11.4.9
|
||||
assert!(pll1r_clk <= Hertz::mhz(160));
|
||||
|
||||
// Check PLL clocks per RM0456 § 11.4.10
|
||||
match config.voltage_range {
|
||||
VoltageScale::RANGE1 => {
|
||||
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
|
||||
assert!(pll1r_clk <= Hertz::mhz(208));
|
||||
}
|
||||
VoltageScale::RANGE2 => {
|
||||
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
|
||||
assert!(pll1r_clk <= Hertz::mhz(110));
|
||||
}
|
||||
VoltageScale::RANGE3 => {
|
||||
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(330));
|
||||
assert!(pll1r_clk <= Hertz::mhz(55));
|
||||
}
|
||||
VoltageScale::RANGE4 => {
|
||||
panic!("PLL is unavailable in voltage range 4");
|
||||
}
|
||||
let hse = config.hse.map(|hse| {
|
||||
// Check frequency limits per RM456 § 11.4.10
|
||||
match config.voltage_range {
|
||||
VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => {
|
||||
assert!(hse.freq.0 <= 50_000_000);
|
||||
}
|
||||
|
||||
// § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler
|
||||
// value that results in an output between 4 and 16 MHz for the PWR EPOD boost
|
||||
let mboost = if pll1r_clk >= Hertz::mhz(55) {
|
||||
// source_clk can be up to 50 MHz, so there's just a few cases:
|
||||
if source_clk > Hertz::mhz(32) {
|
||||
// Divide by 4, giving EPOD 8-12.5 MHz
|
||||
Pllmboost::DIV4
|
||||
} else if source_clk > Hertz::mhz(16) {
|
||||
// Divide by 2, giving EPOD 8-16 MHz
|
||||
Pllmboost::DIV2
|
||||
} else {
|
||||
// Bypass, giving EPOD 4-16 MHz
|
||||
Pllmboost::DIV1
|
||||
}
|
||||
} else {
|
||||
// Nothing to do
|
||||
Pllmboost::DIV1
|
||||
};
|
||||
|
||||
// Disable the PLL, and wait for it to disable
|
||||
RCC.cr().modify(|w| w.set_pllon(0, false));
|
||||
while RCC.cr().read().pllrdy(0) {}
|
||||
|
||||
// Configure the PLL
|
||||
RCC.pll1cfgr().write(|w| {
|
||||
// Configure PLL1 source and prescaler
|
||||
w.set_pllsrc(pll.source.into());
|
||||
w.set_pllm(pll.m);
|
||||
|
||||
// Configure PLL1 input frequncy range
|
||||
let input_range = if reference_clk <= Hertz::mhz(8) {
|
||||
Pllrge::FREQ_4TO8MHZ
|
||||
} else {
|
||||
Pllrge::FREQ_8TO16MHZ
|
||||
};
|
||||
w.set_pllrge(input_range);
|
||||
|
||||
// Set the prescaler for PWR EPOD
|
||||
w.set_pllmboost(mboost);
|
||||
|
||||
// Enable PLL1_R output
|
||||
w.set_pllren(true);
|
||||
});
|
||||
|
||||
// Configure the PLL divisors
|
||||
RCC.pll1divr().modify(|w| {
|
||||
// Set the VCO multiplier
|
||||
w.set_plln(pll.n);
|
||||
w.set_pllp(pll.p);
|
||||
w.set_pllq(pll.q);
|
||||
// Set the R output divisor
|
||||
w.set_pllr(pll.r);
|
||||
});
|
||||
|
||||
// Do we need the EPOD booster to reach the target clock speed per § 10.5.4?
|
||||
if pll1r_clk >= Hertz::mhz(55) {
|
||||
// Enable the booster
|
||||
PWR.vosr().modify(|w| {
|
||||
w.set_boosten(true);
|
||||
});
|
||||
while !PWR.vosr().read().boostrdy() {}
|
||||
VoltageScale::RANGE4 => {
|
||||
assert!(hse.freq.0 <= 25_000_000);
|
||||
}
|
||||
|
||||
// Enable the PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(0, true));
|
||||
while !RCC.cr().read().pllrdy(0) {}
|
||||
|
||||
pll1r_clk
|
||||
}
|
||||
};
|
||||
|
||||
// Enable HSE, and wait for it to stabilize
|
||||
RCC.cr().write(|w| {
|
||||
w.set_hseon(true);
|
||||
w.set_hsebyp(hse.mode != HseMode::Oscillator);
|
||||
w.set_hseext(match hse.mode {
|
||||
HseMode::Oscillator | HseMode::Bypass => Hseext::ANALOG,
|
||||
HseMode::BypassDigital => Hseext::DIGITAL,
|
||||
});
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
hse.freq
|
||||
});
|
||||
|
||||
let hsi48 = config.hsi48.map(super::init_hsi48);
|
||||
|
||||
let pll_input = PllInput { hse, hsi, msi };
|
||||
let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range);
|
||||
let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range);
|
||||
let pll3 = init_pll(PllInstance::Pll3, config.pll3, &pll_input, config.voltage_range);
|
||||
|
||||
let sys_clk = match config.mux {
|
||||
ClockSrc::HSE => hse.unwrap(),
|
||||
ClockSrc::HSI => hsi.unwrap(),
|
||||
ClockSrc::MSIS => msi.unwrap(),
|
||||
ClockSrc::PLL1_R => pll1.r.unwrap(),
|
||||
};
|
||||
|
||||
// Do we need the EPOD booster to reach the target clock speed per § 10.5.4?
|
||||
if sys_clk >= Hertz::mhz(55) {
|
||||
// Enable the booster
|
||||
PWR.vosr().modify(|w| w.set_boosten(true));
|
||||
while !PWR.vosr().read().boostrdy() {}
|
||||
}
|
||||
|
||||
// The clock source is ready
|
||||
// Calculate and set the flash wait states
|
||||
let wait_states = match config.voltage_range {
|
||||
// VOS 1 range VCORE 1.26V - 1.40V
|
||||
VoltageScale::RANGE1 => {
|
||||
if sys_clk.0 < 32_000_000 {
|
||||
0
|
||||
} else if sys_clk.0 < 64_000_000 {
|
||||
1
|
||||
} else if sys_clk.0 < 96_000_000 {
|
||||
2
|
||||
} else if sys_clk.0 < 128_000_000 {
|
||||
3
|
||||
} else {
|
||||
4
|
||||
}
|
||||
}
|
||||
VoltageScale::RANGE1 => match sys_clk.0 {
|
||||
..=32_000_000 => 0,
|
||||
..=64_000_000 => 1,
|
||||
..=96_000_000 => 2,
|
||||
..=128_000_000 => 3,
|
||||
_ => 4,
|
||||
},
|
||||
// VOS 2 range VCORE 1.15V - 1.26V
|
||||
VoltageScale::RANGE2 => {
|
||||
if sys_clk.0 < 30_000_000 {
|
||||
0
|
||||
} else if sys_clk.0 < 60_000_000 {
|
||||
1
|
||||
} else if sys_clk.0 < 90_000_000 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
}
|
||||
}
|
||||
VoltageScale::RANGE2 => match sys_clk.0 {
|
||||
..=30_000_000 => 0,
|
||||
..=60_000_000 => 1,
|
||||
..=90_000_000 => 2,
|
||||
_ => 3,
|
||||
},
|
||||
// VOS 3 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::RANGE3 => {
|
||||
if sys_clk.0 < 24_000_000 {
|
||||
0
|
||||
} else if sys_clk.0 < 48_000_000 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
}
|
||||
VoltageScale::RANGE3 => match sys_clk.0 {
|
||||
..=24_000_000 => 0,
|
||||
..=48_000_000 => 1,
|
||||
_ => 2,
|
||||
},
|
||||
// VOS 4 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::RANGE4 => {
|
||||
if sys_clk.0 < 12_000_000 {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
VoltageScale::RANGE4 => match sys_clk.0 {
|
||||
..=12_000_000 => 0,
|
||||
_ => 1,
|
||||
},
|
||||
};
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(wait_states);
|
||||
});
|
||||
|
||||
// Switch the system clock source
|
||||
RCC.cfgr1().modify(|w| {
|
||||
w.set_sw(config.mux.into());
|
||||
});
|
||||
|
||||
// RM0456 § 11.4.9 specifies maximum bus frequencies per voltage range, but the maximum bus
|
||||
// frequency for each voltage range exactly matches the maximum permitted PLL output frequency.
|
||||
// Given that:
|
||||
//
|
||||
// 1. Any bus frequency can never exceed the system clock frequency;
|
||||
// 2. We checked the PLL output frequency if we're using it as a system clock;
|
||||
// 3. The maximum HSE frequencies at each voltage range are lower than the bus limits, and
|
||||
// we checked the HSE frequency if configured as a system clock; and
|
||||
// 4. The maximum frequencies from the other clock sources are lower than the lowest bus
|
||||
// frequency limit
|
||||
//
|
||||
// ...then we do not need to perform additional bus-related frequency checks.
|
||||
RCC.cfgr1().modify(|w| w.set_sw(config.mux));
|
||||
while RCC.cfgr1().read().sws() != config.mux {}
|
||||
|
||||
// Configure the bus prescalers
|
||||
RCC.cfgr2().modify(|w| {
|
||||
|
@ -419,64 +243,52 @@ pub(crate) unsafe fn init(config: Config) {
|
|||
w.set_ppre3(config.apb3_pre);
|
||||
});
|
||||
|
||||
let ahb_freq = sys_clk / config.ahb_pre;
|
||||
let hclk = sys_clk / config.ahb_pre;
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
let hclk_max = match config.voltage_range {
|
||||
VoltageScale::RANGE1 => Hertz::mhz(160),
|
||||
VoltageScale::RANGE2 => Hertz::mhz(110),
|
||||
VoltageScale::RANGE3 => Hertz::mhz(55),
|
||||
VoltageScale::RANGE4 => Hertz::mhz(25),
|
||||
};
|
||||
assert!(hclk <= hclk_max);
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);
|
||||
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);
|
||||
let (pclk3, _) = super::util::calc_pclk(hclk, config.apb3_pre);
|
||||
|
||||
let rtc = config.ls.init();
|
||||
|
||||
set_clocks!(
|
||||
sys: Some(sys_clk),
|
||||
hclk1: Some(ahb_freq),
|
||||
hclk2: Some(ahb_freq),
|
||||
hclk3: Some(ahb_freq),
|
||||
pclk1: Some(apb1_freq),
|
||||
pclk2: Some(apb2_freq),
|
||||
pclk3: Some(apb3_freq),
|
||||
pclk1_tim: Some(apb1_tim_freq),
|
||||
pclk2_tim: Some(apb2_tim_freq),
|
||||
hclk1: Some(hclk),
|
||||
hclk2: Some(hclk),
|
||||
hclk3: Some(hclk),
|
||||
pclk1: Some(pclk1),
|
||||
pclk2: Some(pclk2),
|
||||
pclk3: Some(pclk3),
|
||||
pclk1_tim: Some(pclk1_tim),
|
||||
pclk2_tim: Some(pclk2_tim),
|
||||
hsi48: hsi48,
|
||||
rtc: rtc,
|
||||
hse: hse,
|
||||
hsi: hsi,
|
||||
pll1_p: pll1.p,
|
||||
pll1_q: pll1.q,
|
||||
pll1_r: pll1.r,
|
||||
pll2_p: pll2.p,
|
||||
pll2_q: pll2.q,
|
||||
pll2_r: pll2.r,
|
||||
pll3_p: pll3.p,
|
||||
pll3_q: pll3.q,
|
||||
pll3_r: pll3.r,
|
||||
|
||||
// TODO
|
||||
hse: None,
|
||||
hsi: None,
|
||||
audioclk: None,
|
||||
hsi48_div_2: None,
|
||||
lse: None,
|
||||
lsi: None,
|
||||
msik: None,
|
||||
pll1_p: None,
|
||||
pll1_q: None,
|
||||
pll1_r: None,
|
||||
pll2_p: None,
|
||||
pll2_q: None,
|
||||
pll2_r: None,
|
||||
pll3_p: None,
|
||||
pll3_q: None,
|
||||
pll3_r: None,
|
||||
iclk: None,
|
||||
);
|
||||
}
|
||||
|
@ -501,3 +313,126 @@ fn msirange_to_hertz(range: Msirange) -> Hertz {
|
|||
Msirange::RANGE_100KHZ => Hertz(100_000),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct PllInput {
|
||||
pub hsi: Option<Hertz>,
|
||||
pub hse: Option<Hertz>,
|
||||
pub msi: Option<Hertz>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Default)]
|
||||
pub(super) struct PllOutput {
|
||||
pub p: Option<Hertz>,
|
||||
pub q: Option<Hertz>,
|
||||
pub r: Option<Hertz>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
enum PllInstance {
|
||||
Pll1 = 0,
|
||||
Pll2 = 1,
|
||||
Pll3 = 2,
|
||||
}
|
||||
|
||||
fn pll_enable(instance: PllInstance, enabled: bool) {
|
||||
RCC.cr().modify(|w| w.set_pllon(instance as _, enabled));
|
||||
while RCC.cr().read().pllrdy(instance as _) != enabled {}
|
||||
}
|
||||
|
||||
fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput {
|
||||
// Disable PLL
|
||||
pll_enable(instance, false);
|
||||
|
||||
let Some(pll) = config else { return PllOutput::default() };
|
||||
|
||||
let src_freq = match pll.source {
|
||||
PllSource::DISABLE => panic!("must not select PLL source as DISABLE"),
|
||||
PllSource::HSE => unwrap!(input.hse),
|
||||
PllSource::HSI => unwrap!(input.hsi),
|
||||
PllSource::MSIS => unwrap!(input.msi),
|
||||
};
|
||||
|
||||
// Calculate the reference clock, which is the source divided by m
|
||||
let ref_freq = src_freq / pll.prediv;
|
||||
// Check limits per RM0456 § 11.4.6
|
||||
assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16));
|
||||
|
||||
// Check PLL clocks per RM0456 § 11.4.10
|
||||
let (vco_min, vco_max, out_max) = match voltage_range {
|
||||
VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(208)),
|
||||
VoltageScale::RANGE2 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(110)),
|
||||
VoltageScale::RANGE3 => (Hertz::mhz(128), Hertz::mhz(330), Hertz::mhz(55)),
|
||||
VoltageScale::RANGE4 => panic!("PLL is unavailable in voltage range 4"),
|
||||
};
|
||||
|
||||
// Calculate the PLL VCO clock
|
||||
let vco_freq = ref_freq * pll.mul;
|
||||
assert!(vco_freq >= vco_min && vco_freq <= vco_max);
|
||||
|
||||
// Calculate output clocks.
|
||||
let p = pll.divp.map(|div| vco_freq / div);
|
||||
let q = pll.divq.map(|div| vco_freq / div);
|
||||
let r = pll.divr.map(|div| vco_freq / div);
|
||||
for freq in [p, q, r] {
|
||||
if let Some(freq) = freq {
|
||||
assert!(freq <= out_max);
|
||||
}
|
||||
}
|
||||
|
||||
let divr = match instance {
|
||||
PllInstance::Pll1 => RCC.pll1divr(),
|
||||
PllInstance::Pll2 => RCC.pll2divr(),
|
||||
PllInstance::Pll3 => RCC.pll3divr(),
|
||||
};
|
||||
divr.write(|w| {
|
||||
w.set_plln(pll.mul);
|
||||
w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1));
|
||||
w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1));
|
||||
w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1));
|
||||
});
|
||||
|
||||
let input_range = match ref_freq.0 {
|
||||
..=8_000_000 => Pllrge::FREQ_4TO8MHZ,
|
||||
_ => Pllrge::FREQ_8TO16MHZ,
|
||||
};
|
||||
|
||||
macro_rules! write_fields {
|
||||
($w:ident) => {
|
||||
$w.set_pllpen(pll.divp.is_some());
|
||||
$w.set_pllqen(pll.divq.is_some());
|
||||
$w.set_pllren(pll.divr.is_some());
|
||||
$w.set_pllm(pll.prediv);
|
||||
$w.set_pllsrc(pll.source);
|
||||
$w.set_pllrge(input_range);
|
||||
};
|
||||
}
|
||||
|
||||
match instance {
|
||||
PllInstance::Pll1 => RCC.pll1cfgr().write(|w| {
|
||||
// § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler
|
||||
// value that results in an output between 4 and 16 MHz for the PWR EPOD boost
|
||||
if r.unwrap() >= Hertz::mhz(55) {
|
||||
// source_clk can be up to 50 MHz, so there's just a few cases:
|
||||
let mboost = match src_freq.0 {
|
||||
..=16_000_000 => Pllmboost::DIV1, // Bypass, giving EPOD 4-16 MHz
|
||||
..=32_000_000 => Pllmboost::DIV2, // Divide by 2, giving EPOD 8-16 MHz
|
||||
_ => Pllmboost::DIV4, // Divide by 4, giving EPOD 8-12.5 MHz
|
||||
};
|
||||
w.set_pllmboost(mboost);
|
||||
}
|
||||
write_fields!(w);
|
||||
}),
|
||||
PllInstance::Pll2 => RCC.pll2cfgr().write(|w| {
|
||||
write_fields!(w);
|
||||
}),
|
||||
PllInstance::Pll3 => RCC.pll3cfgr().write(|w| {
|
||||
write_fields!(w);
|
||||
}),
|
||||
}
|
||||
|
||||
// Enable PLL
|
||||
pll_enable(instance, true);
|
||||
|
||||
PllOutput { p, q, r }
|
||||
}
|
||||
|
|
|
@ -65,17 +65,17 @@ pub(crate) mod sealed {
|
|||
fn regs_core() -> crate::pac::timer::TimCore;
|
||||
|
||||
/// Start the timer.
|
||||
fn start(&mut self) {
|
||||
fn start(&self) {
|
||||
Self::regs_core().cr1().modify(|r| r.set_cen(true));
|
||||
}
|
||||
|
||||
/// Stop the timer.
|
||||
fn stop(&mut self) {
|
||||
fn stop(&self) {
|
||||
Self::regs_core().cr1().modify(|r| r.set_cen(false));
|
||||
}
|
||||
|
||||
/// Reset the counter value to 0
|
||||
fn reset(&mut self) {
|
||||
fn reset(&self) {
|
||||
Self::regs_core().cnt().write(|r| r.set_cnt(0));
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ pub(crate) mod sealed {
|
|||
/// the timer counter will wrap around at the same frequency as is being set.
|
||||
/// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved
|
||||
/// because it needs to count up and down.
|
||||
fn set_frequency(&mut self, frequency: Hertz) {
|
||||
fn set_frequency(&self, frequency: Hertz) {
|
||||
let f = frequency.0;
|
||||
let timer_f = Self::frequency().0;
|
||||
assert!(f > 0);
|
||||
|
@ -108,7 +108,7 @@ pub(crate) mod sealed {
|
|||
/// Clear update interrupt.
|
||||
///
|
||||
/// Returns whether the update interrupt flag was set.
|
||||
fn clear_update_interrupt(&mut self) -> bool {
|
||||
fn clear_update_interrupt(&self) -> bool {
|
||||
let regs = Self::regs_core();
|
||||
let sr = regs.sr().read();
|
||||
if sr.uif() {
|
||||
|
@ -122,12 +122,12 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Enable/disable the update interrupt.
|
||||
fn enable_update_interrupt(&mut self, enable: bool) {
|
||||
fn enable_update_interrupt(&self, enable: bool) {
|
||||
Self::regs_core().dier().modify(|r| r.set_uie(enable));
|
||||
}
|
||||
|
||||
/// Enable/disable autoreload preload.
|
||||
fn set_autoreload_preload(&mut self, enable: bool) {
|
||||
fn set_autoreload_preload(&self, enable: bool) {
|
||||
Self::regs_core().cr1().modify(|r| r.set_arpe(enable));
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ pub(crate) mod sealed {
|
|||
fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2;
|
||||
|
||||
/// Enable/disable the update dma.
|
||||
fn enable_update_dma(&mut self, enable: bool) {
|
||||
fn enable_update_dma(&self, enable: bool) {
|
||||
Self::regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable));
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ pub(crate) mod sealed {
|
|||
fn regs_1ch() -> crate::pac::timer::Tim1ch;
|
||||
|
||||
/// Set clock divider.
|
||||
fn set_clock_division(&mut self, ckd: vals::Ckd) {
|
||||
fn set_clock_division(&self, ckd: vals::Ckd) {
|
||||
Self::regs_1ch().cr1().modify(|r| r.set_ckd(ckd));
|
||||
}
|
||||
|
||||
|
@ -218,7 +218,7 @@ pub(crate) mod sealed {
|
|||
fn regs_gp16() -> crate::pac::timer::TimGp16;
|
||||
|
||||
/// Set counting mode.
|
||||
fn set_counting_mode(&mut self, mode: CountingMode) {
|
||||
fn set_counting_mode(&self, mode: CountingMode) {
|
||||
let (cms, dir) = mode.into();
|
||||
|
||||
let timer_enabled = Self::regs_core().cr1().read().cen();
|
||||
|
@ -237,7 +237,7 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set input capture filter.
|
||||
fn set_input_capture_filter(&mut self, channel: Channel, icf: vals::FilterValue) {
|
||||
fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) {
|
||||
let raw_channel = channel.index();
|
||||
Self::regs_gp16()
|
||||
.ccmr_input(raw_channel / 2)
|
||||
|
@ -245,17 +245,17 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Clear input interrupt.
|
||||
fn clear_input_interrupt(&mut self, channel: Channel) {
|
||||
fn clear_input_interrupt(&self, channel: Channel) {
|
||||
Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false));
|
||||
}
|
||||
|
||||
/// Enable input interrupt.
|
||||
fn enable_input_interrupt(&mut self, channel: Channel, enable: bool) {
|
||||
fn enable_input_interrupt(&self, channel: Channel, enable: bool) {
|
||||
Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable));
|
||||
}
|
||||
|
||||
/// Set input capture prescaler.
|
||||
fn set_input_capture_prescaler(&mut self, channel: Channel, factor: u8) {
|
||||
fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) {
|
||||
let raw_channel = channel.index();
|
||||
Self::regs_gp16()
|
||||
.ccmr_input(raw_channel / 2)
|
||||
|
@ -263,7 +263,7 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set input TI selection.
|
||||
fn set_input_ti_selection(&mut self, channel: Channel, tisel: InputTISelection) {
|
||||
fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) {
|
||||
let raw_channel = channel.index();
|
||||
Self::regs_gp16()
|
||||
.ccmr_input(raw_channel / 2)
|
||||
|
@ -271,7 +271,7 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set input capture mode.
|
||||
fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) {
|
||||
fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) {
|
||||
Self::regs_gp16().ccer().modify(|r| match mode {
|
||||
InputCaptureMode::Rising => {
|
||||
r.set_ccnp(channel.index(), false);
|
||||
|
@ -289,7 +289,7 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set output compare mode.
|
||||
fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) {
|
||||
fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) {
|
||||
let raw_channel: usize = channel.index();
|
||||
Self::regs_gp16()
|
||||
.ccmr_output(raw_channel / 2)
|
||||
|
@ -297,14 +297,14 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set output polarity.
|
||||
fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
||||
fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
|
||||
Self::regs_gp16()
|
||||
.ccer()
|
||||
.modify(|w| w.set_ccp(channel.index(), polarity.into()));
|
||||
}
|
||||
|
||||
/// Enable/disable a channel.
|
||||
fn enable_channel(&mut self, channel: Channel, enable: bool) {
|
||||
fn enable_channel(&self, channel: Channel, enable: bool) {
|
||||
Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable));
|
||||
}
|
||||
|
||||
|
@ -314,12 +314,12 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set compare value for a channel.
|
||||
fn set_compare_value(&mut self, channel: Channel, value: u16) {
|
||||
fn set_compare_value(&self, channel: Channel, value: u16) {
|
||||
Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value));
|
||||
}
|
||||
|
||||
/// Get capture value for a channel.
|
||||
fn get_capture_value(&mut self, channel: Channel) -> u16 {
|
||||
fn get_capture_value(&self, channel: Channel) -> u16 {
|
||||
Self::regs_gp16().ccr(channel.index()).read().ccr()
|
||||
}
|
||||
|
||||
|
@ -329,7 +329,7 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set output compare preload.
|
||||
fn set_output_compare_preload(&mut self, channel: Channel, preload: bool) {
|
||||
fn set_output_compare_preload(&self, channel: Channel, preload: bool) {
|
||||
let channel_index = channel.index();
|
||||
Self::regs_gp16()
|
||||
.ccmr_output(channel_index / 2)
|
||||
|
@ -342,7 +342,7 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set capture compare DMA selection
|
||||
fn set_cc_dma_selection(&mut self, ccds: super::vals::Ccds) {
|
||||
fn set_cc_dma_selection(&self, ccds: super::vals::Ccds) {
|
||||
Self::regs_gp16().cr2().modify(|w| w.set_ccds(ccds))
|
||||
}
|
||||
|
||||
|
@ -352,7 +352,7 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set capture compare DMA enable state
|
||||
fn set_cc_dma_enable_state(&mut self, channel: Channel, ccde: bool) {
|
||||
fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) {
|
||||
Self::regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde))
|
||||
}
|
||||
}
|
||||
|
@ -369,7 +369,7 @@ pub(crate) mod sealed {
|
|||
fn regs_gp32() -> crate::pac::timer::TimGp32;
|
||||
|
||||
/// Set timer frequency.
|
||||
fn set_frequency(&mut self, frequency: Hertz) {
|
||||
fn set_frequency(&self, frequency: Hertz) {
|
||||
let f = frequency.0;
|
||||
assert!(f > 0);
|
||||
let timer_f = Self::frequency().0;
|
||||
|
@ -398,12 +398,12 @@ pub(crate) mod sealed {
|
|||
}
|
||||
|
||||
/// Set comapre value for a channel.
|
||||
fn set_compare_value(&mut self, channel: Channel, value: u32) {
|
||||
fn set_compare_value(&self, channel: Channel, value: u32) {
|
||||
Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value));
|
||||
}
|
||||
|
||||
/// Get capture value for a channel.
|
||||
fn get_capture_value(&mut self, channel: Channel) -> u32 {
|
||||
fn get_capture_value(&self, channel: Channel) -> u32 {
|
||||
Self::regs_gp32().ccr(channel.index()).read().ccr()
|
||||
}
|
||||
|
||||
|
@ -430,17 +430,17 @@ pub(crate) mod sealed {
|
|||
fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp;
|
||||
|
||||
/// Set clock divider for the dead time.
|
||||
fn set_dead_time_clock_division(&mut self, value: vals::Ckd) {
|
||||
fn set_dead_time_clock_division(&self, value: vals::Ckd) {
|
||||
Self::regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value));
|
||||
}
|
||||
|
||||
/// Set dead time, as a fraction of the max duty value.
|
||||
fn set_dead_time_value(&mut self, value: u8) {
|
||||
fn set_dead_time_value(&self, value: u8) {
|
||||
Self::regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value));
|
||||
}
|
||||
|
||||
/// Enable timer outputs.
|
||||
fn enable_outputs(&mut self) {
|
||||
fn enable_outputs(&self) {
|
||||
Self::regs_1ch_cmp().bdtr().modify(|w| w.set_moe(true));
|
||||
}
|
||||
}
|
||||
|
@ -468,14 +468,14 @@ pub(crate) mod sealed {
|
|||
fn regs_advanced() -> crate::pac::timer::TimAdv;
|
||||
|
||||
/// Set complementary output polarity.
|
||||
fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) {
|
||||
fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
|
||||
Self::regs_advanced()
|
||||
.ccer()
|
||||
.modify(|w| w.set_ccnp(channel.index(), polarity.into()));
|
||||
}
|
||||
|
||||
/// Enable/disable a complementary channel.
|
||||
fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
|
||||
fn enable_complementary_channel(&self, channel: Channel, enable: bool) {
|
||||
Self::regs_advanced()
|
||||
.ccer()
|
||||
.modify(|w| w.set_ccne(channel.index(), enable));
|
||||
|
|
|
@ -264,7 +264,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
|||
|
||||
let regs = T::regs();
|
||||
|
||||
#[cfg(any(stm32l5, stm32wb))]
|
||||
#[cfg(any(stm32l4, stm32l5, stm32wb))]
|
||||
crate::pac::PWR.cr2().modify(|w| w.set_usv(true));
|
||||
|
||||
#[cfg(pwr_h5)]
|
||||
|
|
182
examples/rp/src/bin/usb_hid_mouse.rs
Normal file
182
examples/rp/src/bin/usb_hid_mouse.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::clocks::RoscRng;
|
||||
use embassy_rp::gpio::{Input, Pull};
|
||||
use embassy_rp::peripherals::USB;
|
||||
use embassy_rp::usb::{Driver, InterruptHandler};
|
||||
use embassy_time::Timer;
|
||||
use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State};
|
||||
use embassy_usb::control::OutResponse;
|
||||
use embassy_usb::{Builder, Config, Handler};
|
||||
use rand::Rng;
|
||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
USBCTRL_IRQ => InterruptHandler<USB>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
// Create the driver, from the HAL.
|
||||
let driver = Driver::new(p.USB, Irqs);
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
config.manufacturer = Some("Embassy");
|
||||
config.product = Some("HID keyboard example");
|
||||
config.serial_number = Some("12345678");
|
||||
config.max_power = 100;
|
||||
config.max_packet_size_0 = 64;
|
||||
|
||||
// Create embassy-usb DeviceBuilder using the driver and config.
|
||||
// It needs some buffers for building the descriptors.
|
||||
let mut device_descriptor = [0; 256];
|
||||
let mut config_descriptor = [0; 256];
|
||||
let mut bos_descriptor = [0; 256];
|
||||
// You can also add a Microsoft OS descriptor.
|
||||
let mut msos_descriptor = [0; 256];
|
||||
let mut control_buf = [0; 64];
|
||||
let request_handler = MyRequestHandler {};
|
||||
let mut device_handler = MyDeviceHandler::new();
|
||||
|
||||
let mut state = State::new();
|
||||
|
||||
let mut builder = Builder::new(
|
||||
driver,
|
||||
config,
|
||||
&mut device_descriptor,
|
||||
&mut config_descriptor,
|
||||
&mut bos_descriptor,
|
||||
&mut msos_descriptor,
|
||||
&mut control_buf,
|
||||
);
|
||||
|
||||
builder.handler(&mut device_handler);
|
||||
|
||||
// Create classes on the builder.
|
||||
let config = embassy_usb::class::hid::Config {
|
||||
report_descriptor: MouseReport::desc(),
|
||||
request_handler: Some(&request_handler),
|
||||
poll_ms: 60,
|
||||
max_packet_size: 64,
|
||||
};
|
||||
let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
|
||||
|
||||
// Build the builder.
|
||||
let mut usb = builder.build();
|
||||
|
||||
// Run the USB device.
|
||||
let usb_fut = usb.run();
|
||||
|
||||
// Set up the signal pin that will be used to trigger the keyboard.
|
||||
let mut signal_pin = Input::new(p.PIN_16, Pull::None);
|
||||
|
||||
// Enable the schmitt trigger to slightly debounce.
|
||||
signal_pin.set_schmitt(true);
|
||||
|
||||
let (reader, mut writer) = hid.split();
|
||||
|
||||
// Do stuff with the class!
|
||||
let in_fut = async {
|
||||
let mut rng = RoscRng;
|
||||
|
||||
loop {
|
||||
// every 1 second
|
||||
_ = Timer::after_secs(1).await;
|
||||
let report = MouseReport {
|
||||
buttons: 0,
|
||||
x: rng.gen_range(-100..100), // random small x movement
|
||||
y: rng.gen_range(-100..100), // random small y movement
|
||||
wheel: 0,
|
||||
pan: 0,
|
||||
};
|
||||
// Send the report.
|
||||
match writer.write_serialize(&report).await {
|
||||
Ok(()) => {}
|
||||
Err(e) => warn!("Failed to send report: {:?}", e),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let out_fut = async {
|
||||
reader.run(false, &request_handler).await;
|
||||
};
|
||||
|
||||
// Run everything concurrently.
|
||||
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
|
||||
join(usb_fut, join(in_fut, out_fut)).await;
|
||||
}
|
||||
|
||||
struct MyRequestHandler {}
|
||||
|
||||
impl RequestHandler for MyRequestHandler {
|
||||
fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
|
||||
info!("Get report for {:?}", id);
|
||||
None
|
||||
}
|
||||
|
||||
fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse {
|
||||
info!("Set report for {:?}: {=[u8]}", id, data);
|
||||
OutResponse::Accepted
|
||||
}
|
||||
|
||||
fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) {
|
||||
info!("Set idle rate for {:?} to {:?}", id, dur);
|
||||
}
|
||||
|
||||
fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> {
|
||||
info!("Get idle rate for {:?}", id);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct MyDeviceHandler {
|
||||
configured: AtomicBool,
|
||||
}
|
||||
|
||||
impl MyDeviceHandler {
|
||||
fn new() -> Self {
|
||||
MyDeviceHandler {
|
||||
configured: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for MyDeviceHandler {
|
||||
fn enabled(&mut self, enabled: bool) {
|
||||
self.configured.store(false, Ordering::Relaxed);
|
||||
if enabled {
|
||||
info!("Device enabled");
|
||||
} else {
|
||||
info!("Device disabled");
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.configured.store(false, Ordering::Relaxed);
|
||||
info!("Bus reset, the Vbus current limit is 100mA");
|
||||
}
|
||||
|
||||
fn addressed(&mut self, addr: u8) {
|
||||
self.configured.store(false, Ordering::Relaxed);
|
||||
info!("USB address set to: {}", addr);
|
||||
}
|
||||
|
||||
fn configured(&mut self, configured: bool) {
|
||||
self.configured.store(configured, Ordering::Relaxed);
|
||||
if configured {
|
||||
info!("Device configured, it may now draw up to the configured current limit from Vbus.")
|
||||
} else {
|
||||
info!("Device is no longer configured, the Vbus current limit is 100mA.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,9 +20,11 @@ defmt-rtt = "0.4"
|
|||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
embedded-hal = "0.2.6"
|
||||
embedded-can = { version = "0.4" }
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
static_cell = "2.0.0"
|
||||
|
||||
[profile.release]
|
||||
debug = 2
|
||||
|
|
|
@ -5,6 +5,7 @@ use embassy_executor::Spawner;
|
|||
use embassy_stm32::peripherals::*;
|
||||
use embassy_stm32::{bind_interrupts, can, Config};
|
||||
use embassy_time::Timer;
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
|
@ -18,39 +19,190 @@ async fn main(_spawner: Spawner) {
|
|||
|
||||
let peripherals = embassy_stm32::init(config);
|
||||
|
||||
let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
|
||||
can.set_extended_filter(
|
||||
can::fd::filter::ExtendedFilterSlot::_0,
|
||||
can::fd::filter::ExtendedFilter::accept_all_into_fifo1(),
|
||||
);
|
||||
|
||||
// 250k bps
|
||||
can.set_bitrate(250_000);
|
||||
|
||||
let use_fd = false;
|
||||
|
||||
// 1M bps
|
||||
if use_fd {
|
||||
can.set_fd_data_bitrate(1_000_000, false);
|
||||
}
|
||||
|
||||
info!("Configured");
|
||||
|
||||
//let mut can = can.into_external_loopback_mode();
|
||||
let mut can = can.into_normal_mode();
|
||||
let mut can = can.start(match use_fd {
|
||||
true => can::FdcanOperatingMode::InternalLoopbackMode,
|
||||
false => can::FdcanOperatingMode::NormalOperationMode,
|
||||
});
|
||||
|
||||
let mut i = 0;
|
||||
let mut last_read_ts = embassy_time::Instant::now();
|
||||
|
||||
loop {
|
||||
let frame = can::TxFrame::new(
|
||||
can::TxFrameHeader {
|
||||
len: 1,
|
||||
frame_format: can::FrameFormat::Standard,
|
||||
id: can::StandardId::new(0x123).unwrap().into(),
|
||||
bit_rate_switching: false,
|
||||
marker: None,
|
||||
},
|
||||
&[i],
|
||||
)
|
||||
.unwrap();
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
|
||||
_ = can.write(&frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]),
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {} {:02x} --- {}ms",
|
||||
rx_frame.header().len(),
|
||||
rx_frame.data()[0..rx_frame.header().len() as usize],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
Timer::after_millis(250).await;
|
||||
|
||||
i += 1;
|
||||
if i > 2 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the FD API's even if we don't get FD packets.
|
||||
|
||||
loop {
|
||||
if use_fd {
|
||||
let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 16]).unwrap();
|
||||
info!("Writing frame using FD API");
|
||||
_ = can.write_fd(&frame).await;
|
||||
} else {
|
||||
let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame using FD API");
|
||||
_ = can.write_fd(&frame).await;
|
||||
}
|
||||
|
||||
match can.read_fd().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {} {:02x} --- using FD API {}ms",
|
||||
rx_frame.header().len(),
|
||||
rx_frame.data()[0..rx_frame.header().len() as usize],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
Timer::after_millis(250).await;
|
||||
|
||||
i += 1;
|
||||
if i > 4 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
i = 0;
|
||||
let (mut tx, mut rx) = can.split();
|
||||
// With split
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = tx.write(&frame).await;
|
||||
|
||||
match rx.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {} {:02x} --- {}ms",
|
||||
rx_frame.header().len(),
|
||||
rx_frame.data()[0..rx_frame.header().len() as usize],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
Timer::after_millis(250).await;
|
||||
|
||||
i += 1;
|
||||
|
||||
if i > 2 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let can = can::Fdcan::join(tx, rx);
|
||||
|
||||
info!("\n\n\nBuffered\n");
|
||||
if use_fd {
|
||||
static TX_BUF: StaticCell<can::TxFdBuf<8>> = StaticCell::new();
|
||||
static RX_BUF: StaticCell<can::RxFdBuf<10>> = StaticCell::new();
|
||||
let mut can = can.buffered_fd(
|
||||
TX_BUF.init(can::TxFdBuf::<8>::new()),
|
||||
RX_BUF.init(can::RxFdBuf::<10>::new()),
|
||||
);
|
||||
loop {
|
||||
let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 16]).unwrap();
|
||||
info!("Writing frame");
|
||||
|
||||
_ = can.write(frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {} {:02x} --- {}ms",
|
||||
rx_frame.header().len(),
|
||||
rx_frame.data()[0..rx_frame.header().len() as usize],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
Timer::after_millis(250).await;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
} else {
|
||||
static TX_BUF: StaticCell<can::TxBuf<8>> = StaticCell::new();
|
||||
static RX_BUF: StaticCell<can::RxBuf<10>> = StaticCell::new();
|
||||
let mut can = can.buffered(
|
||||
TX_BUF.init(can::TxBuf::<8>::new()),
|
||||
RX_BUF.init(can::RxBuf::<10>::new()),
|
||||
);
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
|
||||
_ = can.write(frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {} {:02x} --- {}ms",
|
||||
rx_frame.header().len(),
|
||||
rx_frame.data()[0..rx_frame.header().len() as usize],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
Timer::after_millis(250).await;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,54 +16,76 @@ bind_interrupts!(struct Irqs {
|
|||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
|
||||
// configure FDCAN to use PLL1_Q at 64 MHz
|
||||
config.rcc.pll1 = Some(rcc::Pll {
|
||||
source: rcc::PllSource::HSI,
|
||||
prediv: rcc::PllPreDiv::DIV4,
|
||||
mul: rcc::PllMul::MUL8,
|
||||
divp: None,
|
||||
divq: Some(rcc::PllDiv::DIV2),
|
||||
divr: None,
|
||||
config.rcc.hse = Some(rcc::Hse {
|
||||
freq: embassy_stm32::time::Hertz(25_000_000),
|
||||
mode: rcc::HseMode::Oscillator,
|
||||
});
|
||||
config.rcc.fdcan_clock_source = rcc::FdCanClockSource::PLL1_Q;
|
||||
config.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE;
|
||||
|
||||
let peripherals = embassy_stm32::init(config);
|
||||
|
||||
let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
|
||||
can.can.apply_config(
|
||||
can::config::FdCanConfig::default().set_nominal_bit_timing(can::config::NominalBitTiming {
|
||||
sync_jump_width: 1.try_into().unwrap(),
|
||||
prescaler: 8.try_into().unwrap(),
|
||||
seg1: 13.try_into().unwrap(),
|
||||
seg2: 2.try_into().unwrap(),
|
||||
}),
|
||||
);
|
||||
// 250k bps
|
||||
can.set_bitrate(250_000);
|
||||
|
||||
info!("Configured");
|
||||
//let mut can = can.into_internal_loopback_mode();
|
||||
let mut can = can.into_normal_mode();
|
||||
|
||||
let mut can = can.into_external_loopback_mode();
|
||||
//let mut can = can.into_normal_mode();
|
||||
info!("CAN Configured");
|
||||
|
||||
let mut i = 0;
|
||||
let mut last_read_ts = embassy_time::Instant::now();
|
||||
|
||||
loop {
|
||||
let frame = can::TxFrame::new(
|
||||
can::TxFrameHeader {
|
||||
len: 1,
|
||||
frame_format: can::FrameFormat::Standard,
|
||||
id: can::StandardId::new(0x123).unwrap().into(),
|
||||
bit_rate_switching: false,
|
||||
marker: None,
|
||||
},
|
||||
&[i],
|
||||
)
|
||||
.unwrap();
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = can.write(&frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]),
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {:x} {:x} {:x} {:x} --- NEW {}",
|
||||
rx_frame.data()[0],
|
||||
rx_frame.data()[1],
|
||||
rx_frame.data()[2],
|
||||
rx_frame.data()[3],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
Timer::after_millis(250).await;
|
||||
|
||||
i += 1;
|
||||
if i > 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let (mut tx, mut rx) = can.split();
|
||||
// With split
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = tx.write(&frame).await;
|
||||
|
||||
match rx.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {:x} {:x} {:x} {:x} --- NEW {}",
|
||||
rx_frame.data()[0],
|
||||
rx_frame.data()[1],
|
||||
rx_frame.data()[2],
|
||||
rx_frame.data()[3],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
|
|
|
@ -16,54 +16,76 @@ bind_interrupts!(struct Irqs {
|
|||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
|
||||
// configure FDCAN to use PLL1_Q at 64 MHz
|
||||
config.rcc.pll1 = Some(rcc::Pll {
|
||||
source: rcc::PllSource::HSI,
|
||||
prediv: rcc::PllPreDiv::DIV4,
|
||||
mul: rcc::PllMul::MUL8,
|
||||
divp: None,
|
||||
divq: Some(rcc::PllDiv::DIV2),
|
||||
divr: None,
|
||||
config.rcc.hse = Some(rcc::Hse {
|
||||
freq: embassy_stm32::time::Hertz(25_000_000),
|
||||
mode: rcc::HseMode::Oscillator,
|
||||
});
|
||||
config.rcc.fdcan_clock_source = rcc::FdCanClockSource::PLL1_Q;
|
||||
config.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE;
|
||||
|
||||
let peripherals = embassy_stm32::init(config);
|
||||
|
||||
let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
|
||||
can.can.apply_config(
|
||||
can::config::FdCanConfig::default().set_nominal_bit_timing(can::config::NominalBitTiming {
|
||||
sync_jump_width: 1.try_into().unwrap(),
|
||||
prescaler: 8.try_into().unwrap(),
|
||||
seg1: 13.try_into().unwrap(),
|
||||
seg2: 2.try_into().unwrap(),
|
||||
}),
|
||||
);
|
||||
// 250k bps
|
||||
can.set_bitrate(250_000);
|
||||
|
||||
info!("Configured");
|
||||
//let mut can = can.into_internal_loopback_mode();
|
||||
let mut can = can.into_normal_mode();
|
||||
|
||||
let mut can = can.into_external_loopback_mode();
|
||||
//let mut can = can.into_normal_mode();
|
||||
info!("CAN Configured");
|
||||
|
||||
let mut i = 0;
|
||||
let mut last_read_ts = embassy_time::Instant::now();
|
||||
|
||||
loop {
|
||||
let frame = can::TxFrame::new(
|
||||
can::TxFrameHeader {
|
||||
len: 1,
|
||||
frame_format: can::FrameFormat::Standard,
|
||||
id: can::StandardId::new(0x123).unwrap().into(),
|
||||
bit_rate_switching: false,
|
||||
marker: None,
|
||||
},
|
||||
&[i],
|
||||
)
|
||||
.unwrap();
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = can.write(&frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]),
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {:x} {:x} {:x} {:x} --- NEW {}",
|
||||
rx_frame.data()[0],
|
||||
rx_frame.data()[1],
|
||||
rx_frame.data()[2],
|
||||
rx_frame.data()[3],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
Timer::after_millis(250).await;
|
||||
|
||||
i += 1;
|
||||
if i > 3 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let (mut tx, mut rx) = can.split();
|
||||
// With split
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = tx.write(&frame).await;
|
||||
|
||||
match rx.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
"Rx: {:x} {:x} {:x} {:x} --- NEW {}",
|
||||
rx_frame.data()[0],
|
||||
rx_frame.data()[1],
|
||||
rx_frame.data()[2],
|
||||
rx_frame.data()[3],
|
||||
delta,
|
||||
)
|
||||
}
|
||||
Err(_err) => error!("Error in frame"),
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
use defmt::{panic, *};
|
||||
use defmt_rtt as _; // global logger
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::rcc::*;
|
||||
use embassy_stm32::usb_otg::{Driver, Instance};
|
||||
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
|
@ -22,22 +21,28 @@ async fn main(_spawner: Spawner) {
|
|||
info!("Hello World!");
|
||||
|
||||
let mut config = Config::default();
|
||||
config.rcc.mux = ClockSrc::PLL1_R(PllConfig {
|
||||
source: PllSource::HSI,
|
||||
m: Pllm::DIV2,
|
||||
n: Plln::MUL10,
|
||||
p: Plldiv::DIV1,
|
||||
q: Plldiv::DIV1,
|
||||
r: Plldiv::DIV1,
|
||||
});
|
||||
config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.hsi = true;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
source: PllSource::HSI, // 16 MHz
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL10,
|
||||
divp: None,
|
||||
divq: None,
|
||||
divr: Some(PllDiv::DIV1), // 160 MHz
|
||||
});
|
||||
config.rcc.mux = ClockSrc::PLL1_R;
|
||||
config.rcc.voltage_range = VoltageScale::RANGE1;
|
||||
config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
|
||||
}
|
||||
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
// Create the driver, from the HAL.
|
||||
let mut ep_out_buffer = [0u8; 256];
|
||||
let mut config = embassy_stm32::usb_otg::Config::default();
|
||||
config.vbus_detection = true;
|
||||
config.vbus_detection = false;
|
||||
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
|
||||
|
||||
// Create embassy-usb Config
|
||||
|
|
|
@ -15,7 +15,7 @@ bind_interrupts!(struct Irqs {
|
|||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_nrf::init(Default::default());
|
||||
let mut p = embassy_nrf::init(Default::default());
|
||||
let mut config = uarte::Config::default();
|
||||
config.parity = uarte::Parity::EXCLUDED;
|
||||
config.baudrate = uarte::Baudrate::BAUD1M;
|
||||
|
@ -23,55 +23,58 @@ async fn main(_spawner: Spawner) {
|
|||
let mut tx_buffer = [0u8; 1024];
|
||||
let mut rx_buffer = [0u8; 1024];
|
||||
|
||||
let mut u = BufferedUarte::new(
|
||||
p.UARTE0,
|
||||
p.TIMER0,
|
||||
p.PPI_CH0,
|
||||
p.PPI_CH1,
|
||||
p.PPI_GROUP0,
|
||||
Irqs,
|
||||
p.P1_03,
|
||||
p.P1_02,
|
||||
config.clone(),
|
||||
&mut rx_buffer,
|
||||
&mut tx_buffer,
|
||||
);
|
||||
// test teardown + recreate of the buffereduarte works fine.
|
||||
for _ in 0..2 {
|
||||
let u = BufferedUarte::new(
|
||||
&mut p.UARTE0,
|
||||
&mut p.TIMER0,
|
||||
&mut p.PPI_CH0,
|
||||
&mut p.PPI_CH1,
|
||||
&mut p.PPI_GROUP0,
|
||||
Irqs,
|
||||
&mut p.P1_03,
|
||||
&mut p.P1_02,
|
||||
config.clone(),
|
||||
&mut rx_buffer,
|
||||
&mut tx_buffer,
|
||||
);
|
||||
|
||||
info!("uarte initialized!");
|
||||
info!("uarte initialized!");
|
||||
|
||||
let (mut rx, mut tx) = u.split();
|
||||
let (mut rx, mut tx) = u.split();
|
||||
|
||||
const COUNT: usize = 40_000;
|
||||
const COUNT: usize = 40_000;
|
||||
|
||||
let tx_fut = async {
|
||||
let mut tx_buf = [0; 215];
|
||||
let mut i = 0;
|
||||
while i < COUNT {
|
||||
let n = tx_buf.len().min(COUNT - i);
|
||||
let tx_buf = &mut tx_buf[..n];
|
||||
for (j, b) in tx_buf.iter_mut().enumerate() {
|
||||
*b = (i + j) as u8;
|
||||
let tx_fut = async {
|
||||
let mut tx_buf = [0; 215];
|
||||
let mut i = 0;
|
||||
while i < COUNT {
|
||||
let n = tx_buf.len().min(COUNT - i);
|
||||
let tx_buf = &mut tx_buf[..n];
|
||||
for (j, b) in tx_buf.iter_mut().enumerate() {
|
||||
*b = (i + j) as u8;
|
||||
}
|
||||
let n = unwrap!(tx.write(tx_buf).await);
|
||||
i += n;
|
||||
}
|
||||
let n = unwrap!(tx.write(tx_buf).await);
|
||||
i += n;
|
||||
}
|
||||
};
|
||||
let rx_fut = async {
|
||||
let mut i = 0;
|
||||
while i < COUNT {
|
||||
let buf = unwrap!(rx.fill_buf().await);
|
||||
};
|
||||
let rx_fut = async {
|
||||
let mut i = 0;
|
||||
while i < COUNT {
|
||||
let buf = unwrap!(rx.fill_buf().await);
|
||||
|
||||
for &b in buf {
|
||||
assert_eq!(b, i as u8);
|
||||
i = i + 1;
|
||||
for &b in buf {
|
||||
assert_eq!(b, i as u8);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
let n = buf.len();
|
||||
rx.consume(n);
|
||||
}
|
||||
};
|
||||
|
||||
let n = buf.len();
|
||||
rx.consume(n);
|
||||
}
|
||||
};
|
||||
|
||||
join(rx_fut, tx_fut).await;
|
||||
join(rx_fut, tx_fut).await;
|
||||
}
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
|
|
|
@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) {
|
|||
let mut tx_buffer = [0u8; 1024];
|
||||
let mut rx_buffer = [0u8; 1024];
|
||||
|
||||
let mut u = BufferedUarte::new(
|
||||
let u = BufferedUarte::new(
|
||||
p.UARTE0,
|
||||
p.TIMER0,
|
||||
p.PPI_CH0,
|
||||
|
|
82
tests/nrf52840/src/bin/buffered_uart_halves.rs
Normal file
82
tests/nrf52840/src/bin/buffered_uart_halves.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
teleprobe_meta::target!(b"nrf52840-dk");
|
||||
|
||||
use defmt::{assert_eq, *};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_nrf::buffered_uarte::{self, BufferedUarteRx, BufferedUarteTx};
|
||||
use embassy_nrf::{bind_interrupts, peripherals, uarte};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
UARTE0_UART0 => buffered_uarte::InterruptHandler<peripherals::UARTE0>;
|
||||
UARTE1 => buffered_uarte::InterruptHandler<peripherals::UARTE1>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut p = embassy_nrf::init(Default::default());
|
||||
let mut config = uarte::Config::default();
|
||||
config.parity = uarte::Parity::EXCLUDED;
|
||||
config.baudrate = uarte::Baudrate::BAUD1M;
|
||||
|
||||
let mut tx_buffer = [0u8; 1024];
|
||||
let mut rx_buffer = [0u8; 1024];
|
||||
|
||||
// test teardown + recreate of the buffereduarte works fine.
|
||||
for _ in 0..2 {
|
||||
const COUNT: usize = 40_000;
|
||||
|
||||
let mut tx = BufferedUarteTx::new(&mut p.UARTE1, Irqs, &mut p.P1_02, config.clone(), &mut tx_buffer);
|
||||
|
||||
let mut rx = BufferedUarteRx::new(
|
||||
&mut p.UARTE0,
|
||||
&mut p.TIMER0,
|
||||
&mut p.PPI_CH0,
|
||||
&mut p.PPI_CH1,
|
||||
&mut p.PPI_GROUP0,
|
||||
Irqs,
|
||||
&mut p.P1_03,
|
||||
config.clone(),
|
||||
&mut rx_buffer,
|
||||
);
|
||||
|
||||
let tx_fut = async {
|
||||
info!("tx initialized!");
|
||||
|
||||
let mut tx_buf = [0; 215];
|
||||
let mut i = 0;
|
||||
while i < COUNT {
|
||||
let n = tx_buf.len().min(COUNT - i);
|
||||
let tx_buf = &mut tx_buf[..n];
|
||||
for (j, b) in tx_buf.iter_mut().enumerate() {
|
||||
*b = (i + j) as u8;
|
||||
}
|
||||
let n = unwrap!(tx.write(tx_buf).await);
|
||||
i += n;
|
||||
}
|
||||
};
|
||||
let rx_fut = async {
|
||||
info!("rx initialized!");
|
||||
|
||||
let mut i = 0;
|
||||
while i < COUNT {
|
||||
let buf = unwrap!(rx.fill_buf().await);
|
||||
|
||||
for &b in buf {
|
||||
assert_eq!(b, i as u8);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
let n = buf.len();
|
||||
rx.consume(n);
|
||||
}
|
||||
};
|
||||
|
||||
join(rx_fut, tx_fut).await;
|
||||
}
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
|
@ -14,6 +14,7 @@ embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = [ "defmt
|
|||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
|
||||
embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] }
|
||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal/"}
|
||||
cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] }
|
||||
cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] }
|
||||
perf-client = { path = "../perf-client" }
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
teleprobe_meta::target!(b"rpi-pico");
|
||||
|
||||
use defmt::{assert_eq, info, panic, unwrap};
|
||||
use embassy_executor::Executor;
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_executor::{Executor, Spawner};
|
||||
use embassy_rp::clocks::{PllConfig, XoscConfig};
|
||||
use embassy_rp::config::Config as rpConfig;
|
||||
use embassy_rp::multicore::{spawn_core1, Stack};
|
||||
use embassy_rp::peripherals::{I2C0, I2C1};
|
||||
use embassy_rp::{bind_interrupts, i2c, i2c_slave};
|
||||
|
@ -13,7 +16,6 @@ use static_cell::StaticCell;
|
|||
use {defmt_rtt as _, panic_probe as _, panic_probe as _, panic_probe as _};
|
||||
|
||||
static mut CORE1_STACK: Stack<1024> = Stack::new();
|
||||
static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
|
||||
static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
|
||||
|
||||
use crate::i2c::AbortReason;
|
||||
|
@ -44,10 +46,7 @@ async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {
|
|||
Ok(x) => match x {
|
||||
i2c_slave::ReadStatus::Done => break,
|
||||
i2c_slave::ReadStatus::NeedMoreBytes => count += 1,
|
||||
i2c_slave::ReadStatus::LeftoverBytes(x) => {
|
||||
info!("tried to write {} extra bytes", x);
|
||||
break;
|
||||
}
|
||||
i2c_slave::ReadStatus::LeftoverBytes(x) => panic!("tried to write {} extra bytes", x),
|
||||
},
|
||||
Err(e) => match e {
|
||||
embassy_rp::i2c_slave::Error::Abort(AbortReason::Other(n)) => panic!("Other {:b}", n),
|
||||
|
@ -92,6 +91,8 @@ async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {
|
|||
resp_buff[i] = i as u8;
|
||||
}
|
||||
dev.respond_to_read(&resp_buff).await.unwrap();
|
||||
// reset count for next round of tests
|
||||
count = 0xD0;
|
||||
}
|
||||
x => panic!("Invalid Write Read {:x}", x),
|
||||
}
|
||||
|
@ -104,8 +105,7 @@ async fn device_task(mut dev: i2c_slave::I2cSlave<'static, I2C1>) -> ! {
|
|||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {
|
||||
async fn controller_task(con: &mut i2c::I2c<'static, I2C0, i2c::Async>) {
|
||||
info!("Device start");
|
||||
|
||||
{
|
||||
|
@ -179,33 +179,55 @@ async fn controller_task(mut con: i2c::I2c<'static, I2C0, i2c::Async>) {
|
|||
info!("large write_read - OK")
|
||||
}
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
||||
|
||||
#[cortex_m_rt::entry]
|
||||
fn main() -> ! {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
let d_sda = p.PIN_19;
|
||||
let d_scl = p.PIN_18;
|
||||
let mut config = i2c_slave::Config::default();
|
||||
config.addr = DEV_ADDR as u16;
|
||||
let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);
|
||||
|
||||
spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
|
||||
let executor1 = EXECUTOR1.init(Executor::new());
|
||||
executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device))));
|
||||
});
|
||||
|
||||
let executor0 = EXECUTOR0.init(Executor::new());
|
||||
|
||||
let c_sda = p.PIN_21;
|
||||
let c_scl = p.PIN_20;
|
||||
let mut config = i2c::Config::default();
|
||||
config.frequency = 5_000;
|
||||
let controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, config);
|
||||
|
||||
executor0.run(|spawner| unwrap!(spawner.spawn(controller_task(controller))));
|
||||
#[embassy_executor::main]
|
||||
async fn main(_core0_spawner: Spawner) {
|
||||
let mut config = rpConfig::default();
|
||||
// Configure clk_sys to 48MHz to support 1kHz scl.
|
||||
// In theory it can go lower, but we won't bother to test below 1kHz.
|
||||
config.clocks.xosc = Some(XoscConfig {
|
||||
hz: 12_000_000,
|
||||
delay_multiplier: 128,
|
||||
sys_pll: Some(PllConfig {
|
||||
refdiv: 1,
|
||||
fbdiv: 120,
|
||||
post_div1: 6,
|
||||
post_div2: 5,
|
||||
}),
|
||||
usb_pll: Some(PllConfig {
|
||||
refdiv: 1,
|
||||
fbdiv: 120,
|
||||
post_div1: 6,
|
||||
post_div2: 5,
|
||||
}),
|
||||
});
|
||||
|
||||
let p = embassy_rp::init(config);
|
||||
info!("Hello World!");
|
||||
|
||||
let d_sda = p.PIN_19;
|
||||
let d_scl = p.PIN_18;
|
||||
let mut config = i2c_slave::Config::default();
|
||||
config.addr = DEV_ADDR as u16;
|
||||
let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config);
|
||||
|
||||
spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
|
||||
let executor1 = EXECUTOR1.init(Executor::new());
|
||||
executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device))));
|
||||
});
|
||||
|
||||
let c_sda = p.PIN_21;
|
||||
let c_scl = p.PIN_20;
|
||||
let mut controller = i2c::I2c::new_async(p.I2C0, c_sda, c_scl, Irqs, Default::default());
|
||||
|
||||
for freq in [1000, 100_000, 400_000, 1_000_000] {
|
||||
info!("testing at {}hz", freq);
|
||||
let mut config = i2c::Config::default();
|
||||
config.frequency = freq;
|
||||
controller.set_config(&config).unwrap();
|
||||
controller_task(&mut controller).await;
|
||||
}
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"
|
|||
stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"]
|
||||
stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"]
|
||||
stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"]
|
||||
stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"]
|
||||
stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"]
|
||||
stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"]
|
||||
stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"]
|
||||
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
|
||||
|
@ -69,6 +69,7 @@ cortex-m-rt = "0.7.0"
|
|||
embedded-hal = "0.2.6"
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
embedded-hal-async = { version = "1.0" }
|
||||
embedded-can = { version = "0.4" }
|
||||
micromath = "2.0.0"
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
rand_core = { version = "0.6", default-features = false }
|
||||
|
|
|
@ -16,6 +16,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
feature = "stm32l073rz",
|
||||
// wrong ram size in stm32-data
|
||||
feature = "stm32wl55jc",
|
||||
feature = "stm32u5a5zj",
|
||||
// no VTOR, so interrupts can't work when running from RAM
|
||||
feature = "stm32f091rc",
|
||||
)) {
|
||||
|
|
|
@ -36,7 +36,7 @@ fn options() -> TestOptions {
|
|||
c.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE;
|
||||
TestOptions {
|
||||
config: c,
|
||||
max_latency: Duration::from_micros(3800),
|
||||
max_latency: Duration::from_micros(1200),
|
||||
second_fifo_working: false,
|
||||
}
|
||||
}
|
||||
|
@ -53,12 +53,12 @@ fn options() -> TestOptions {
|
|||
c.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE;
|
||||
TestOptions {
|
||||
config: c,
|
||||
max_latency: Duration::from_micros(5500),
|
||||
max_latency: Duration::from_micros(1200),
|
||||
second_fifo_working: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "stm32g491re"))]
|
||||
#[cfg(any(feature = "stm32g491re", feature = "stm32g431cb"))]
|
||||
fn options() -> TestOptions {
|
||||
info!("G4 config");
|
||||
TestOptions {
|
||||
|
@ -75,14 +75,14 @@ async fn main(_spawner: Spawner) {
|
|||
let options = options();
|
||||
let peripherals = embassy_stm32::init(options.config);
|
||||
|
||||
let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs);
|
||||
let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs);
|
||||
|
||||
// 250k bps
|
||||
can.set_bitrate(250_000);
|
||||
|
||||
can.can.set_extended_filter(
|
||||
can::filter::ExtendedFilterSlot::_0,
|
||||
can::filter::ExtendedFilter::accept_all_into_fifo1(),
|
||||
can.set_extended_filter(
|
||||
can::fd::filter::ExtendedFilterSlot::_0,
|
||||
can::fd::filter::ExtendedFilter::accept_all_into_fifo1(),
|
||||
);
|
||||
|
||||
let mut can = can.into_internal_loopback_mode();
|
||||
|
@ -91,31 +91,21 @@ async fn main(_spawner: Spawner) {
|
|||
|
||||
let mut i: u8 = 0;
|
||||
loop {
|
||||
let tx_frame = can::TxFrame::new(
|
||||
can::TxFrameHeader {
|
||||
len: 1,
|
||||
frame_format: can::FrameFormat::Standard,
|
||||
id: can::StandardId::new(0x123).unwrap().into(),
|
||||
bit_rate_switching: false,
|
||||
marker: None,
|
||||
},
|
||||
&[i],
|
||||
)
|
||||
.unwrap();
|
||||
let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap();
|
||||
|
||||
info!("Transmitting frame...");
|
||||
let tx_ts = Instant::now();
|
||||
can.write(&tx_frame).await;
|
||||
|
||||
let envelope = can.read().await.unwrap();
|
||||
let (frame, timestamp) = can.read().await.unwrap();
|
||||
info!("Frame received!");
|
||||
|
||||
// Check data.
|
||||
assert!(i == envelope.data()[0], "{} == {}", i, envelope.data()[0]);
|
||||
assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]);
|
||||
|
||||
info!("loopback time {}", envelope.header.time_stamp);
|
||||
info!("loopback frame {=u8}", envelope.data()[0]);
|
||||
let latency = envelope.timestamp.saturating_duration_since(tx_ts);
|
||||
info!("loopback time {}", timestamp);
|
||||
info!("loopback frame {=u8}", frame.data()[0]);
|
||||
let latency = timestamp.saturating_duration_since(tx_ts);
|
||||
info!("loopback latency {} us", latency.as_micros());
|
||||
|
||||
// Theoretical minimum latency is 55us, actual is usually ~80us
|
||||
|
@ -143,47 +133,26 @@ async fn main(_spawner: Spawner) {
|
|||
// in each FIFO so make sure we write enough to fill them both up before reading.
|
||||
for i in 0..3 {
|
||||
// Try filling up the RX FIFO0 buffers with standard packets
|
||||
let tx_frame = can::TxFrame::new(
|
||||
can::TxFrameHeader {
|
||||
len: 1,
|
||||
frame_format: can::FrameFormat::Standard,
|
||||
id: can::StandardId::new(0x123).unwrap().into(),
|
||||
bit_rate_switching: false,
|
||||
marker: None,
|
||||
},
|
||||
&[i],
|
||||
)
|
||||
.unwrap();
|
||||
let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap();
|
||||
info!("Transmitting frame {}", i);
|
||||
can.write(&tx_frame).await;
|
||||
}
|
||||
for i in 3..max_buffered {
|
||||
// Try filling up the RX FIFO0 buffers with extended packets
|
||||
let tx_frame = can::TxFrame::new(
|
||||
can::TxFrameHeader {
|
||||
len: 1,
|
||||
frame_format: can::FrameFormat::Standard,
|
||||
id: can::ExtendedId::new(0x1232344).unwrap().into(),
|
||||
bit_rate_switching: false,
|
||||
marker: None,
|
||||
},
|
||||
&[i],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap();
|
||||
info!("Transmitting frame {}", i);
|
||||
can.write(&tx_frame).await;
|
||||
}
|
||||
|
||||
// Try and receive all 6 packets
|
||||
for i in 0..max_buffered {
|
||||
let envelope = can.read().await.unwrap();
|
||||
match envelope.header.id {
|
||||
can::Id::Extended(id) => {
|
||||
info!("Extended received! {:x} {} {}", id.as_raw(), envelope.data()[0], i);
|
||||
let (frame, _ts) = can.read().await.unwrap();
|
||||
match frame.id() {
|
||||
embedded_can::Id::Extended(id) => {
|
||||
info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
can::Id::Standard(id) => {
|
||||
info!("Standard received! {:x} {} {}", id.as_raw(), envelope.data()[0], i);
|
||||
embedded_can::Id::Standard(id) => {
|
||||
info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,48 +161,26 @@ async fn main(_spawner: Spawner) {
|
|||
let (mut tx, mut rx) = can.split();
|
||||
for i in 0..3 {
|
||||
// Try filling up the RX FIFO0 buffers with standard packets
|
||||
let tx_frame = can::TxFrame::new(
|
||||
can::TxFrameHeader {
|
||||
len: 1,
|
||||
frame_format: can::FrameFormat::Standard,
|
||||
id: can::StandardId::new(0x123).unwrap().into(),
|
||||
bit_rate_switching: false,
|
||||
marker: None,
|
||||
},
|
||||
&[i],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap();
|
||||
info!("Transmitting frame {}", i);
|
||||
tx.write(&tx_frame).await;
|
||||
}
|
||||
for i in 3..max_buffered {
|
||||
// Try filling up the RX FIFO0 buffers with extended packets
|
||||
let tx_frame = can::TxFrame::new(
|
||||
can::TxFrameHeader {
|
||||
len: 1,
|
||||
frame_format: can::FrameFormat::Standard,
|
||||
id: can::ExtendedId::new(0x1232344).unwrap().into(),
|
||||
bit_rate_switching: false,
|
||||
marker: None,
|
||||
},
|
||||
&[i],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap();
|
||||
info!("Transmitting frame {}", i);
|
||||
tx.write(&tx_frame).await;
|
||||
}
|
||||
|
||||
// Try and receive all 6 packets
|
||||
for i in 0..max_buffered {
|
||||
let envelope = rx.read().await.unwrap();
|
||||
match envelope.header.id {
|
||||
can::Id::Extended(id) => {
|
||||
info!("Extended received! {:x} {} {}", id.as_raw(), envelope.data()[0], i);
|
||||
let (frame, _ts) = rx.read().await.unwrap();
|
||||
match frame.id() {
|
||||
embedded_can::Id::Extended(id) => {
|
||||
info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
can::Id::Standard(id) => {
|
||||
info!("Standard received! {:x} {} {}", id.as_raw(), envelope.data()[0], i);
|
||||
embedded_can::Id::Standard(id) => {
|
||||
info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -577,7 +577,18 @@ pub fn config() -> Config {
|
|||
#[cfg(any(feature = "stm32u585ai", feature = "stm32u5a5zj"))]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.mux = ClockSrc::MSI(Msirange::RANGE_48MHZ);
|
||||
config.rcc.hsi = true;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
source: PllSource::HSI, // 16 MHz
|
||||
prediv: PllPreDiv::DIV1,
|
||||
mul: PllMul::MUL10,
|
||||
divp: None,
|
||||
divq: None,
|
||||
divr: Some(PllDiv::DIV1), // 160 MHz
|
||||
});
|
||||
config.rcc.mux = ClockSrc::PLL1_R;
|
||||
config.rcc.voltage_range = VoltageScale::RANGE1;
|
||||
config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32wba52cg")]
|
||||
|
|
Loading…
Add table
Reference in a new issue