mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 22:32:29 +00:00
Merge branch 'main' of https://github.com/GustavToft/embassy
This commit is contained in:
commit
a373633d0d
1
ci.sh
1
ci.sh
@ -124,6 +124,7 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-tim1,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -208,3 +208,26 @@ Tools like `cargo size` and `cargo nm` can tell you the size of any globals or o
|
||||
=== For Max Stack Usage
|
||||
|
||||
Check out link:https://github.com/Dirbaio/cargo-call-stack/[`cargo-call-stack`] for statically calculating worst-case stack usage. There are some caveats and inaccuracies possible with this, but this is a good way to get the general idea. See link:https://github.com/dirbaio/cargo-call-stack#known-limitations[the README] for more details.
|
||||
|
||||
== The memory definition for my STM chip seems wrong, how do I define a `memory.x` file?
|
||||
|
||||
It could happen that your project compiles, flashes but fails to run. The following situation can be true for your setup:
|
||||
|
||||
The `memory.x` is generated automatically when enabling the `memory-x` feature on the `embassy-stm32` crate in the `Cargo.toml` file.
|
||||
This, in turn, uses `stm32-metapac` to generate the `memory.x` file for you. Unfortunately, more often than not this memory definition is not correct.
|
||||
|
||||
You can override this by adding your own `memory.x` file. Such a file could look like this:
|
||||
```
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K
|
||||
}
|
||||
|
||||
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||
```
|
||||
|
||||
Please refer to the STM32 documentation for the specific values suitable for your board and setup. The STM32 Cube examples often contain a linker script `.ld` file.
|
||||
Look for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start.
|
||||
|
||||
If you find a case where the memory.x is wrong, please report it on [this Github issue](https://github.com/embassy-rs/stm32-data/issues/301) so other users are not caught by surprise.
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -183,29 +183,29 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// |-----------|------------|--------|--------|--------|--------|
|
||||
/// | Active | 0 | 1 | 2 | 3 | - |
|
||||
/// | DFU | 0 | 3 | 2 | 1 | X |
|
||||
/// | DFU | 0 | 4 | 5 | 6 | X |
|
||||
///
|
||||
/// The algorithm starts by copying 'backwards', and after the first step, the layout is
|
||||
/// as follows:
|
||||
///
|
||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// |-----------|------------|--------|--------|--------|--------|
|
||||
/// | Active | 1 | 1 | 2 | 1 | - |
|
||||
/// | DFU | 1 | 3 | 2 | 1 | 3 |
|
||||
/// | Active | 1 | 1 | 2 | 6 | - |
|
||||
/// | DFU | 1 | 4 | 5 | 6 | 3 |
|
||||
///
|
||||
/// The next iteration performs the same steps
|
||||
///
|
||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// |-----------|------------|--------|--------|--------|--------|
|
||||
/// | Active | 2 | 1 | 2 | 1 | - |
|
||||
/// | DFU | 2 | 3 | 2 | 2 | 3 |
|
||||
/// | Active | 2 | 1 | 5 | 6 | - |
|
||||
/// | DFU | 2 | 4 | 5 | 2 | 3 |
|
||||
///
|
||||
/// And again until we're done
|
||||
///
|
||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// |-----------|------------|--------|--------|--------|--------|
|
||||
/// | Active | 3 | 3 | 2 | 1 | - |
|
||||
/// | DFU | 3 | 3 | 1 | 2 | 3 |
|
||||
/// | Active | 3 | 4 | 5 | 6 | - |
|
||||
/// | DFU | 3 | 4 | 1 | 2 | 3 |
|
||||
///
|
||||
/// ## REVERTING
|
||||
///
|
||||
@ -220,19 +220,19 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
||||
///
|
||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// |-----------|--------------|--------|--------|--------|--------|
|
||||
/// | Active | 3 | 1 | 2 | 1 | - |
|
||||
/// | DFU | 3 | 3 | 1 | 2 | 3 |
|
||||
/// | Active | 3 | 1 | 5 | 6 | - |
|
||||
/// | DFU | 3 | 4 | 1 | 2 | 3 |
|
||||
///
|
||||
///
|
||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// |-----------|--------------|--------|--------|--------|--------|
|
||||
/// | Active | 3 | 1 | 2 | 1 | - |
|
||||
/// | DFU | 3 | 3 | 2 | 2 | 3 |
|
||||
/// | Active | 3 | 1 | 2 | 6 | - |
|
||||
/// | DFU | 3 | 4 | 5 | 2 | 3 |
|
||||
///
|
||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// |-----------|--------------|--------|--------|--------|--------|
|
||||
/// | Active | 3 | 1 | 2 | 3 | - |
|
||||
/// | DFU | 3 | 3 | 2 | 1 | 3 |
|
||||
/// | DFU | 3 | 4 | 5 | 6 | 3 |
|
||||
///
|
||||
pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {
|
||||
// Ensure we have enough progress pages to store copy progress
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -106,6 +106,11 @@ impl<'a, M: RawMutex, BUS: SetConfig> I2cDeviceWithConfig<'a, M, BUS> {
|
||||
pub fn new(bus: &'a Mutex<M, BUS>, config: BUS::Config) -> Self {
|
||||
Self { bus, config }
|
||||
}
|
||||
|
||||
/// Change the device's config at runtime
|
||||
pub fn set_config(&mut self, config: BUS::Config) {
|
||||
self.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, M, BUS> i2c::ErrorType for I2cDeviceWithConfig<'a, M, BUS>
|
||||
|
@ -122,6 +122,11 @@ impl<'a, M: RawMutex, BUS: SetConfig, CS> SpiDeviceWithConfig<'a, M, BUS, CS> {
|
||||
pub fn new(bus: &'a Mutex<M, BUS>, cs: CS, config: BUS::Config) -> Self {
|
||||
Self { bus, cs, config }
|
||||
}
|
||||
|
||||
/// Change the device's config at runtime
|
||||
pub fn set_config(&mut self, config: BUS::Config) {
|
||||
self.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, M, BUS, CS> spi::ErrorType for SpiDeviceWithConfig<'a, M, BUS, CS>
|
||||
|
@ -132,6 +132,11 @@ impl<'a, M: RawMutex, BUS: SetConfig> I2cDeviceWithConfig<'a, M, BUS> {
|
||||
pub fn new(bus: &'a Mutex<M, RefCell<BUS>>, config: BUS::Config) -> Self {
|
||||
Self { bus, config }
|
||||
}
|
||||
|
||||
/// Change the device's config at runtime
|
||||
pub fn set_config(&mut self, config: BUS::Config) {
|
||||
self.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, M, BUS> ErrorType for I2cDeviceWithConfig<'a, M, BUS>
|
||||
|
@ -147,6 +147,11 @@ impl<'a, M: RawMutex, BUS: SetConfig, CS> SpiDeviceWithConfig<'a, M, BUS, CS> {
|
||||
pub fn new(bus: &'a Mutex<M, RefCell<BUS>>, cs: CS, config: BUS::Config) -> Self {
|
||||
Self { bus, cs, config }
|
||||
}
|
||||
|
||||
/// Change the device's config at runtime
|
||||
pub fn set_config(&mut self, config: BUS::Config) {
|
||||
self.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, M, BUS, CS> spi::ErrorType for SpiDeviceWithConfig<'a, M, BUS, CS>
|
||||
|
@ -93,10 +93,21 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
|
||||
#[cfg(feature = "nightly")]
|
||||
let mut task_outer: ItemFn = parse_quote! {
|
||||
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
|
||||
type Fut = impl ::core::future::Future + 'static;
|
||||
trait _EmbassyInternalTaskTrait {
|
||||
type Fut: ::core::future::Future + 'static;
|
||||
fn construct(#fargs) -> Self::Fut;
|
||||
}
|
||||
|
||||
impl _EmbassyInternalTaskTrait for () {
|
||||
type Fut = impl core::future::Future + 'static;
|
||||
fn construct(#fargs) -> Self::Fut {
|
||||
#task_inner_ident(#(#full_args,)*)
|
||||
}
|
||||
}
|
||||
|
||||
const POOL_SIZE: usize = #pool_size;
|
||||
static POOL: ::embassy_executor::raw::TaskPool<Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new();
|
||||
unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) }
|
||||
static POOL: ::embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new();
|
||||
unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) }
|
||||
}
|
||||
};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
|
@ -7,7 +7,6 @@ use std::thread;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn;
|
||||
|
||||
/// A type to collect errors together and format them.
|
||||
///
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -30,7 +30,7 @@ use core::ptr::NonNull;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
use embassy_time_driver::{self, AlarmHandle};
|
||||
use embassy_time_driver::AlarmHandle;
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
use rtos_trace::trace;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
|
||||
|
||||
use std::boxed::Box;
|
||||
use std::future::poll_fn;
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -30,14 +30,12 @@ macro_rules! interrupt_mod {
|
||||
pub mod typelevel {
|
||||
use super::InterruptExt;
|
||||
|
||||
mod sealed {
|
||||
pub trait Interrupt {}
|
||||
}
|
||||
trait SealedInterrupt {}
|
||||
|
||||
/// Type-level interrupt.
|
||||
///
|
||||
/// This trait is implemented for all typelevel interrupt types in this module.
|
||||
pub trait Interrupt: sealed::Interrupt {
|
||||
pub trait Interrupt: SealedInterrupt {
|
||||
|
||||
/// Interrupt enum variant.
|
||||
///
|
||||
@ -105,7 +103,7 @@ macro_rules! interrupt_mod {
|
||||
#[doc=stringify!($irqs)]
|
||||
#[doc=" typelevel interrupt."]
|
||||
pub enum $irqs {}
|
||||
impl sealed::Interrupt for $irqs{}
|
||||
impl SealedInterrupt for $irqs{}
|
||||
impl Interrupt for $irqs {
|
||||
const IRQ: super::Interrupt = super::Interrupt::$irqs;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -83,14 +83,17 @@ macro_rules! todo {
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
::core::unreachable!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unreachable!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
@ -113,7 +116,7 @@ macro_rules! trace {
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ignored = ($( & $x ),*);
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -126,7 +129,7 @@ macro_rules! debug {
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ignored = ($( & $x ),*);
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -139,7 +142,7 @@ macro_rules! info {
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ignored = ($( & $x ),*);
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -152,7 +155,7 @@ macro_rules! warn {
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ignored = ($( & $x ),*);
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -165,7 +168,7 @@ macro_rules! error {
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ignored = ($( & $x ),*);
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -226,7 +229,7 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bytes<'a>(pub &'a [u8]);
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -17,7 +17,6 @@ mod phy;
|
||||
mod traits;
|
||||
|
||||
use core::cmp;
|
||||
use core::convert::TryInto;
|
||||
|
||||
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
|
||||
use embassy_time::Duration;
|
||||
@ -645,8 +644,8 @@ where
|
||||
Self: 'a;
|
||||
|
||||
fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
let rx_buf = unsafe { &mut RX_BUF };
|
||||
let tx_buf = unsafe { &mut TX_BUF };
|
||||
let rx_buf = unsafe { &mut *core::ptr::addr_of_mut!(RX_BUF) };
|
||||
let tx_buf = unsafe { &mut *core::ptr::addr_of_mut!(TX_BUF) };
|
||||
if let Some(n) = self.receive(rx_buf) {
|
||||
Some((RxToken { buf: &mut rx_buf[..n] }, TxToken { buf: tx_buf, eth: self }))
|
||||
} else {
|
||||
@ -656,7 +655,7 @@ where
|
||||
}
|
||||
|
||||
fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
|
||||
let tx_buf = unsafe { &mut TX_BUF };
|
||||
let tx_buf = unsafe { &mut *core::ptr::addr_of_mut!(TX_BUF) };
|
||||
Some(TxToken { buf: tx_buf, eth: self })
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -6,7 +6,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::task::Context;
|
||||
|
||||
use async_io::Async;
|
||||
use embassy_net_driver::{self, Capabilities, Driver, HardwareAddress, LinkState};
|
||||
use embassy_net_driver::{Capabilities, Driver, HardwareAddress, LinkState};
|
||||
use log::*;
|
||||
|
||||
/// Get the MTU of the given interface.
|
||||
|
@ -2,49 +2,40 @@
|
||||
mod w5500;
|
||||
pub use w5500::W5500;
|
||||
mod w5100s;
|
||||
use embedded_hal_async::spi::SpiDevice;
|
||||
pub use w5100s::W5100S;
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use embedded_hal_async::spi::SpiDevice;
|
||||
pub(crate) trait SealedChip {
|
||||
type Address;
|
||||
|
||||
pub trait Chip {
|
||||
type Address;
|
||||
const COMMON_MODE: Self::Address;
|
||||
const COMMON_MAC: Self::Address;
|
||||
const COMMON_SOCKET_INTR: Self::Address;
|
||||
const COMMON_PHY_CFG: Self::Address;
|
||||
const SOCKET_MODE: Self::Address;
|
||||
const SOCKET_COMMAND: Self::Address;
|
||||
const SOCKET_RXBUF_SIZE: Self::Address;
|
||||
const SOCKET_TXBUF_SIZE: Self::Address;
|
||||
const SOCKET_TX_FREE_SIZE: Self::Address;
|
||||
const SOCKET_TX_DATA_WRITE_PTR: Self::Address;
|
||||
const SOCKET_RECVD_SIZE: Self::Address;
|
||||
const SOCKET_RX_DATA_READ_PTR: Self::Address;
|
||||
const SOCKET_INTR_MASK: Self::Address;
|
||||
const SOCKET_INTR: Self::Address;
|
||||
|
||||
const COMMON_MODE: Self::Address;
|
||||
const COMMON_MAC: Self::Address;
|
||||
const COMMON_SOCKET_INTR: Self::Address;
|
||||
const COMMON_PHY_CFG: Self::Address;
|
||||
const SOCKET_MODE: Self::Address;
|
||||
const SOCKET_COMMAND: Self::Address;
|
||||
const SOCKET_RXBUF_SIZE: Self::Address;
|
||||
const SOCKET_TXBUF_SIZE: Self::Address;
|
||||
const SOCKET_TX_FREE_SIZE: Self::Address;
|
||||
const SOCKET_TX_DATA_WRITE_PTR: Self::Address;
|
||||
const SOCKET_RECVD_SIZE: Self::Address;
|
||||
const SOCKET_RX_DATA_READ_PTR: Self::Address;
|
||||
const SOCKET_INTR_MASK: Self::Address;
|
||||
const SOCKET_INTR: Self::Address;
|
||||
const SOCKET_MODE_VALUE: u8;
|
||||
|
||||
const SOCKET_MODE_VALUE: u8;
|
||||
const BUF_SIZE: u16;
|
||||
const AUTO_WRAP: bool;
|
||||
|
||||
const BUF_SIZE: u16;
|
||||
const AUTO_WRAP: bool;
|
||||
fn rx_addr(addr: u16) -> Self::Address;
|
||||
fn tx_addr(addr: u16) -> Self::Address;
|
||||
|
||||
fn rx_addr(addr: u16) -> Self::Address;
|
||||
fn tx_addr(addr: u16) -> Self::Address;
|
||||
|
||||
async fn bus_read<SPI: SpiDevice>(
|
||||
spi: &mut SPI,
|
||||
address: Self::Address,
|
||||
data: &mut [u8],
|
||||
) -> Result<(), SPI::Error>;
|
||||
async fn bus_write<SPI: SpiDevice>(
|
||||
spi: &mut SPI,
|
||||
address: Self::Address,
|
||||
data: &[u8],
|
||||
) -> Result<(), SPI::Error>;
|
||||
}
|
||||
async fn bus_read<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &mut [u8])
|
||||
-> Result<(), SPI::Error>;
|
||||
async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error>;
|
||||
}
|
||||
|
||||
/// Trait for Wiznet chips.
|
||||
pub trait Chip: sealed::Chip {}
|
||||
#[allow(private_bounds)]
|
||||
pub trait Chip: SealedChip {}
|
||||
|
@ -8,7 +8,7 @@ const RX_BASE: u16 = 0x6000;
|
||||
pub enum W5100S {}
|
||||
|
||||
impl super::Chip for W5100S {}
|
||||
impl super::sealed::Chip for W5100S {
|
||||
impl super::SealedChip for W5100S {
|
||||
type Address = u16;
|
||||
|
||||
const COMMON_MODE: Self::Address = 0x00;
|
||||
|
@ -12,7 +12,7 @@ pub enum RegisterBlock {
|
||||
pub enum W5500 {}
|
||||
|
||||
impl super::Chip for W5500 {}
|
||||
impl super::sealed::Chip for W5500 {
|
||||
impl super::SealedChip for W5500 {
|
||||
type Address = (RegisterBlock, u16);
|
||||
|
||||
const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00);
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -473,10 +473,12 @@ impl sealed::Pin for AnyPin {
|
||||
|
||||
// ====================
|
||||
|
||||
#[cfg(not(feature = "_nrf51"))]
|
||||
pub(crate) trait PselBits {
|
||||
fn psel_bits(&self) -> u32;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "_nrf51"))]
|
||||
impl<'a, P: Pin> PselBits for Option<PeripheralRef<'a, P>> {
|
||||
#[inline]
|
||||
fn psel_bits(&self) -> u32 {
|
||||
|
@ -167,8 +167,10 @@ unsafe fn handle_gpiote_interrupt() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "_nrf51"))]
|
||||
struct BitIter(u32);
|
||||
|
||||
#[cfg(not(feature = "_nrf51"))]
|
||||
impl Iterator for BitIter {
|
||||
type Item = u32;
|
||||
|
||||
|
@ -225,10 +225,31 @@ pub mod config {
|
||||
/// Config for the first stage DCDC (VDDH -> VDD), if disabled LDO will be used.
|
||||
#[cfg(feature = "nrf52840")]
|
||||
pub reg0: bool,
|
||||
/// Configure the voltage of the first stage DCDC. It is stored in non-volatile memory (UICR.REGOUT0 register); pass None to not touch it.
|
||||
#[cfg(feature = "nrf52840")]
|
||||
pub reg0_voltage: Option<Reg0Voltage>,
|
||||
/// Config for the second stage DCDC (VDD -> DEC4), if disabled LDO will be used.
|
||||
pub reg1: bool,
|
||||
}
|
||||
|
||||
/// Output voltage setting for REG0 regulator stage.
|
||||
#[cfg(feature = "nrf52840")]
|
||||
pub enum Reg0Voltage {
|
||||
/// 1.8 V
|
||||
_1V8 = 0,
|
||||
/// 2.1 V
|
||||
_2V1 = 1,
|
||||
/// 2.4 V
|
||||
_2V4 = 2,
|
||||
/// 2.7 V
|
||||
_2V7 = 3,
|
||||
/// 3.0 V
|
||||
_3V0 = 4,
|
||||
/// 3.3 V
|
||||
_3v3 = 5,
|
||||
//ERASED = 7, means 1.8V
|
||||
}
|
||||
|
||||
/// Settings for enabling the built in DCDC converters.
|
||||
#[cfg(feature = "_nrf5340-app")]
|
||||
pub struct DcdcConfig {
|
||||
@ -279,6 +300,8 @@ pub mod config {
|
||||
dcdc: DcdcConfig {
|
||||
#[cfg(feature = "nrf52840")]
|
||||
reg0: false,
|
||||
#[cfg(feature = "nrf52840")]
|
||||
reg0_voltage: None,
|
||||
reg1: false,
|
||||
},
|
||||
#[cfg(feature = "_nrf5340-app")]
|
||||
@ -337,6 +360,7 @@ mod consts {
|
||||
pub const UICR_PSELRESET2: *mut u32 = 0x10001204 as *mut u32;
|
||||
pub const UICR_NFCPINS: *mut u32 = 0x1000120C as *mut u32;
|
||||
pub const UICR_APPROTECT: *mut u32 = 0x10001208 as *mut u32;
|
||||
pub const UICR_REGOUT0: *mut u32 = 0x10001304 as *mut u32;
|
||||
pub const APPROTECT_ENABLED: u32 = 0x0000_0000;
|
||||
pub const APPROTECT_DISABLED: u32 = 0x0000_005a;
|
||||
}
|
||||
@ -493,6 +517,21 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nrf52840")]
|
||||
unsafe {
|
||||
if let Some(value) = config.dcdc.reg0_voltage {
|
||||
let value = value as u32;
|
||||
let res = uicr_write_masked(consts::UICR_REGOUT0, value, 0b00000000_00000000_00000000_00000111);
|
||||
needs_reset |= res == WriteResult::Written;
|
||||
if res == WriteResult::Failed {
|
||||
warn!(
|
||||
"Failed to set regulator voltage, as UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
||||
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if needs_reset {
|
||||
cortex_m::peripheral::SCB::sys_reset();
|
||||
}
|
||||
|
@ -21,8 +21,6 @@ pub(crate) mod sealed {
|
||||
fn regs() -> &'static pac::timer0::RegisterBlock;
|
||||
}
|
||||
pub trait ExtendedInstance {}
|
||||
|
||||
pub trait TimerType {}
|
||||
}
|
||||
|
||||
/// Basic Timer instance.
|
||||
|
@ -19,14 +19,9 @@ static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
/// ADC config.
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
pub struct Config {}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
enum Source<'p> {
|
||||
Pin(PeripheralRef<'p, AnyPin>),
|
||||
TempSensor(PeripheralRef<'p, ADC_TEMP_SENSOR>),
|
||||
@ -175,7 +170,7 @@ impl<'d, M: Mode> Adc<'d, M> {
|
||||
while !r.cs().read().ready() {}
|
||||
match r.cs().read().err() {
|
||||
true => Err(Error::ConversionFailed),
|
||||
false => Ok(r.result().read().result().into()),
|
||||
false => Ok(r.result().read().result()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -221,7 +216,7 @@ impl<'d> Adc<'d, Async> {
|
||||
Self::wait_for_ready().await;
|
||||
match r.cs().read().err() {
|
||||
true => Err(Error::ConversionFailed),
|
||||
false => Ok(r.result().read().result().into()),
|
||||
false => Ok(r.result().read().result()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -737,7 +737,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 {
|
||||
assert!(config.refdiv >= 1 && config.refdiv <= 63);
|
||||
assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000);
|
||||
let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32);
|
||||
assert!(vco_freq >= 750_000_000 && vco_freq <= 1800_000_000);
|
||||
assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
|
||||
|
||||
// Load VCO-related dividers before starting VCO
|
||||
p.cs().write(|w| w.set_refdiv(config.refdiv as _));
|
||||
|
@ -96,7 +96,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
|
||||
) -> Transfer<'a, C> {
|
||||
copy_inner(
|
||||
ch,
|
||||
&mut DUMMY as *const u32,
|
||||
core::ptr::addr_of_mut!(DUMMY) as *const u32,
|
||||
to as *mut u32,
|
||||
len,
|
||||
W::size(),
|
||||
|
@ -326,9 +326,9 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {
|
||||
// If the destination address is already aligned, then we can just DMA directly
|
||||
if (bytes.as_ptr() as u32) % 4 == 0 {
|
||||
// Safety: alignment and size have been checked for compatibility
|
||||
let mut buf: &mut [u32] =
|
||||
let buf: &mut [u32] =
|
||||
unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u32, bytes.len() / 4) };
|
||||
self.background_read(offset, &mut buf)?.await;
|
||||
self.background_read(offset, buf)?.await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -420,8 +420,6 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod ram_helpers {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use super::*;
|
||||
use crate::rom_data;
|
||||
|
||||
|
@ -89,6 +89,7 @@ pub(crate) trait Float:
|
||||
}
|
||||
|
||||
/// Returns true if `self` is infinity
|
||||
#[allow(unused)]
|
||||
fn is_infinity(self) -> bool {
|
||||
(self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -225,8 +225,8 @@ fn irq_handler<const N: usize>(bank: pac::io::Io, wakers: &[AtomicWaker; N]) {
|
||||
// The status register is divided into groups of four, one group for
|
||||
// each pin. Each group consists of four trigger levels LEVEL_LOW,
|
||||
// LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin.
|
||||
let pin_group = (pin % 8) as usize;
|
||||
let event = (intsx.read().0 >> pin_group * 4) & 0xf as u32;
|
||||
let pin_group = pin % 8;
|
||||
let event = (intsx.read().0 >> (pin_group * 4)) & 0xf;
|
||||
|
||||
// no more than one event can be awaited per pin at any given time, so
|
||||
// we can just clear all interrupt enables for that pin without having
|
||||
@ -238,7 +238,7 @@ fn irq_handler<const N: usize>(bank: pac::io::Io, wakers: &[AtomicWaker; N]) {
|
||||
w.set_level_high(pin_group, true);
|
||||
w.set_level_low(pin_group, true);
|
||||
});
|
||||
wakers[pin as usize].wake();
|
||||
wakers[pin].wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -976,8 +976,6 @@ impl_pin!(PIN_QSPI_SD3, Bank::Qspi, 5);
|
||||
// ====================
|
||||
|
||||
mod eh02 {
|
||||
use core::convert::Infallible;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d> embedded_hal_02::digital::v2::InputPin for Input<'d> {
|
||||
|
@ -352,7 +352,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_up_i2c_pin<'d, P, T>(pin: &P)
|
||||
pub(crate) fn set_up_i2c_pin<P, T>(pin: &P)
|
||||
where
|
||||
P: core::ops::Deref<Target = T>,
|
||||
T: crate::gpio::Pin,
|
||||
@ -749,7 +749,7 @@ where
|
||||
|
||||
let addr: u16 = address.into();
|
||||
|
||||
if operations.len() > 0 {
|
||||
if !operations.is_empty() {
|
||||
Self::setup(addr)?;
|
||||
}
|
||||
let mut iterator = operations.iter_mut();
|
||||
@ -762,7 +762,7 @@ where
|
||||
self.read_async_internal(buffer, false, last).await?;
|
||||
}
|
||||
Operation::Write(buffer) => {
|
||||
self.write_async_internal(buffer.into_iter().cloned(), last).await?;
|
||||
self.write_async_internal(buffer.iter().cloned(), last).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> {
|
||||
let p = T::regs();
|
||||
|
||||
if buffer.len() == 0 {
|
||||
if buffer.is_empty() {
|
||||
return Err(Error::InvalidResponseBufferLength);
|
||||
}
|
||||
|
||||
@ -318,15 +318,13 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
} else if stat.rx_done() {
|
||||
p.ic_clr_rx_done().read();
|
||||
Poll::Ready(Ok(ReadStatus::Done))
|
||||
} else if stat.rd_req() && stat.tx_empty() {
|
||||
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
|
||||
} else {
|
||||
if stat.rx_done() {
|
||||
p.ic_clr_rx_done().read();
|
||||
Poll::Ready(Ok(ReadStatus::Done))
|
||||
} else if stat.rd_req() && stat.tx_empty() {
|
||||
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
},
|
||||
|_me| {
|
||||
|
@ -183,14 +183,14 @@ embassy_hal_internal::peripherals! {
|
||||
DMA_CH10,
|
||||
DMA_CH11,
|
||||
|
||||
PWM_CH0,
|
||||
PWM_CH1,
|
||||
PWM_CH2,
|
||||
PWM_CH3,
|
||||
PWM_CH4,
|
||||
PWM_CH5,
|
||||
PWM_CH6,
|
||||
PWM_CH7,
|
||||
PWM_SLICE0,
|
||||
PWM_SLICE1,
|
||||
PWM_SLICE2,
|
||||
PWM_SLICE3,
|
||||
PWM_SLICE4,
|
||||
PWM_SLICE5,
|
||||
PWM_SLICE6,
|
||||
PWM_SLICE7,
|
||||
|
||||
USB,
|
||||
|
||||
@ -274,7 +274,7 @@ pub fn install_core0_stack_guard() -> Result<(), ()> {
|
||||
extern "C" {
|
||||
static mut _stack_end: usize;
|
||||
}
|
||||
unsafe { install_stack_guard(&mut _stack_end as *mut usize) }
|
||||
unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -354,6 +354,7 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
|
||||
/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes.
|
||||
trait RegExt<T: Copy> {
|
||||
#[allow(unused)]
|
||||
fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||
fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||
fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||
|
@ -59,7 +59,7 @@ static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[inline(always)]
|
||||
fn core1_setup(stack_bottom: *mut usize) {
|
||||
if let Err(_) = install_stack_guard(stack_bottom) {
|
||||
if install_stack_guard(stack_bottom).is_err() {
|
||||
// currently only happens if the MPU was already set up, which
|
||||
// would indicate that the core is already in use from outside
|
||||
// embassy, somehow. trap if so since we can't deal with that.
|
||||
|
@ -268,7 +268,7 @@ impl<'l, PIO: Instance> Pin<'l, PIO> {
|
||||
}
|
||||
|
||||
/// Set the pin's input sync bypass.
|
||||
pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) {
|
||||
pub fn set_input_sync_bypass(&mut self, bypass: bool) {
|
||||
let mask = 1 << self.pin();
|
||||
if bypass {
|
||||
PIO::PIO.input_sync_bypass().write_set(|w| *w = mask);
|
||||
@ -463,7 +463,7 @@ impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> {
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) {
|
||||
fn assert_consecutive<PIO: Instance>(pins: &[&Pin<PIO>]) {
|
||||
for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) {
|
||||
// purposely does not allow wrap-around because we can't claim pins 30 and 31.
|
||||
assert!(p1.pin() + 1 == p2.pin(), "pins must be consecutive");
|
||||
@ -764,7 +764,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
|
||||
w.set_set_count(1);
|
||||
});
|
||||
// SET PINS, (dir)
|
||||
unsafe { sm.exec_instr(0b111_00000_000_00000 | level as u16) };
|
||||
unsafe { sm.exec_instr(0b11100_000_000_00000 | level as u16) };
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -867,9 +867,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
|
||||
prog: &Program<SIZE>,
|
||||
) -> Result<LoadedProgram<'d, PIO>, LoadError> {
|
||||
match prog.origin {
|
||||
Some(origin) => self
|
||||
.try_load_program_at(prog, origin)
|
||||
.map_err(|a| LoadError::AddressInUse(a)),
|
||||
Some(origin) => self.try_load_program_at(prog, origin).map_err(LoadError::AddressInUse),
|
||||
None => {
|
||||
// naively search for free space, allowing wraparound since
|
||||
// PIO does support that. with only 32 instruction slots it
|
||||
|
@ -82,13 +82,13 @@ impl From<InputMode> for Divmode {
|
||||
}
|
||||
|
||||
/// PWM driver.
|
||||
pub struct Pwm<'d, T: Channel> {
|
||||
pub struct Pwm<'d, T: Slice> {
|
||||
inner: PeripheralRef<'d, T>,
|
||||
pin_a: Option<PeripheralRef<'d, AnyPin>>,
|
||||
pin_b: Option<PeripheralRef<'d, AnyPin>>,
|
||||
}
|
||||
|
||||
impl<'d, T: Channel> Pwm<'d, T> {
|
||||
impl<'d, T: Slice> Pwm<'d, T> {
|
||||
fn new_inner(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
a: Option<PeripheralRef<'d, AnyPin>>,
|
||||
@ -114,8 +114,8 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
||||
}
|
||||
Self {
|
||||
inner,
|
||||
pin_a: a.into(),
|
||||
pin_b: b.into(),
|
||||
pin_a: a,
|
||||
pin_b: b,
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
||||
#[inline]
|
||||
pub fn new_output_a(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
|
||||
a: impl Peripheral<P = impl ChannelAPin<T>> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(a);
|
||||
@ -140,7 +140,7 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
||||
#[inline]
|
||||
pub fn new_output_b(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
|
||||
b: impl Peripheral<P = impl ChannelBPin<T>> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(b);
|
||||
@ -151,8 +151,8 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
||||
#[inline]
|
||||
pub fn new_output_ab(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
|
||||
b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
|
||||
a: impl Peripheral<P = impl ChannelAPin<T>> + 'd,
|
||||
b: impl Peripheral<P = impl ChannelBPin<T>> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(a, b);
|
||||
@ -163,7 +163,7 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
||||
#[inline]
|
||||
pub fn new_input(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
|
||||
b: impl Peripheral<P = impl ChannelBPin<T>> + 'd,
|
||||
mode: InputMode,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
@ -175,8 +175,8 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
||||
#[inline]
|
||||
pub fn new_output_input(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
a: impl Peripheral<P = impl PwmPinA<T>> + 'd,
|
||||
b: impl Peripheral<P = impl PwmPinB<T>> + 'd,
|
||||
a: impl Peripheral<P = impl ChannelAPin<T>> + 'd,
|
||||
b: impl Peripheral<P = impl ChannelBPin<T>> + 'd,
|
||||
mode: InputMode,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
@ -190,7 +190,7 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
||||
}
|
||||
|
||||
fn configure(p: pac::pwm::Channel, config: &Config) {
|
||||
if config.divider > FixedU16::<fixed::types::extra::U4>::from_bits(0xFF_F) {
|
||||
if config.divider > FixedU16::<fixed::types::extra::U4>::from_bits(0xFFF) {
|
||||
panic!("Requested divider is too large");
|
||||
}
|
||||
|
||||
@ -265,18 +265,18 @@ impl<'d, T: Channel> Pwm<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Batch representation of PWM channels.
|
||||
/// Batch representation of PWM slices.
|
||||
pub struct PwmBatch(u32);
|
||||
|
||||
impl PwmBatch {
|
||||
#[inline]
|
||||
/// Enable a PWM channel in this batch.
|
||||
pub fn enable(&mut self, pwm: &Pwm<'_, impl Channel>) {
|
||||
/// Enable a PWM slice in this batch.
|
||||
pub fn enable(&mut self, pwm: &Pwm<'_, impl Slice>) {
|
||||
self.0 |= pwm.bit();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Enable channels in this batch in a PWM.
|
||||
/// Enable slices in this batch in a PWM.
|
||||
pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) {
|
||||
let mut en = PwmBatch(0);
|
||||
batch(&mut en);
|
||||
@ -288,7 +288,7 @@ impl PwmBatch {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Channel> Drop for Pwm<'d, T> {
|
||||
impl<'d, T: Slice> Drop for Pwm<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.regs().csr().write_clear(|w| w.set_en(false));
|
||||
if let Some(pin) = &self.pin_a {
|
||||
@ -301,24 +301,24 @@ impl<'d, T: Channel> Drop for Pwm<'d, T> {
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Channel {}
|
||||
pub trait Slice {}
|
||||
}
|
||||
|
||||
/// PWM Channel.
|
||||
pub trait Channel: Peripheral<P = Self> + sealed::Channel + Sized + 'static {
|
||||
/// Channel number.
|
||||
/// PWM Slice.
|
||||
pub trait Slice: Peripheral<P = Self> + sealed::Slice + Sized + 'static {
|
||||
/// Slice number.
|
||||
fn number(&self) -> u8;
|
||||
|
||||
/// Channel register block.
|
||||
/// Slice register block.
|
||||
fn regs(&self) -> pac::pwm::Channel {
|
||||
pac::PWM.ch(self.number() as _)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! channel {
|
||||
macro_rules! slice {
|
||||
($name:ident, $num:expr) => {
|
||||
impl sealed::Channel for peripherals::$name {}
|
||||
impl Channel for peripherals::$name {
|
||||
impl sealed::Slice for peripherals::$name {}
|
||||
impl Slice for peripherals::$name {
|
||||
fn number(&self) -> u8 {
|
||||
$num
|
||||
}
|
||||
@ -326,19 +326,19 @@ macro_rules! channel {
|
||||
};
|
||||
}
|
||||
|
||||
channel!(PWM_CH0, 0);
|
||||
channel!(PWM_CH1, 1);
|
||||
channel!(PWM_CH2, 2);
|
||||
channel!(PWM_CH3, 3);
|
||||
channel!(PWM_CH4, 4);
|
||||
channel!(PWM_CH5, 5);
|
||||
channel!(PWM_CH6, 6);
|
||||
channel!(PWM_CH7, 7);
|
||||
slice!(PWM_SLICE0, 0);
|
||||
slice!(PWM_SLICE1, 1);
|
||||
slice!(PWM_SLICE2, 2);
|
||||
slice!(PWM_SLICE3, 3);
|
||||
slice!(PWM_SLICE4, 4);
|
||||
slice!(PWM_SLICE5, 5);
|
||||
slice!(PWM_SLICE6, 6);
|
||||
slice!(PWM_SLICE7, 7);
|
||||
|
||||
/// PWM Pin A.
|
||||
pub trait PwmPinA<T: Channel>: GpioPin {}
|
||||
/// PWM Pin B.
|
||||
pub trait PwmPinB<T: Channel>: GpioPin {}
|
||||
/// PWM Channel A.
|
||||
pub trait ChannelAPin<T: Slice>: GpioPin {}
|
||||
/// PWM Channel B.
|
||||
pub trait ChannelBPin<T: Slice>: GpioPin {}
|
||||
|
||||
macro_rules! impl_pin {
|
||||
($pin:ident, $channel:ident, $kind:ident) => {
|
||||
@ -346,33 +346,33 @@ macro_rules! impl_pin {
|
||||
};
|
||||
}
|
||||
|
||||
impl_pin!(PIN_0, PWM_CH0, PwmPinA);
|
||||
impl_pin!(PIN_1, PWM_CH0, PwmPinB);
|
||||
impl_pin!(PIN_2, PWM_CH1, PwmPinA);
|
||||
impl_pin!(PIN_3, PWM_CH1, PwmPinB);
|
||||
impl_pin!(PIN_4, PWM_CH2, PwmPinA);
|
||||
impl_pin!(PIN_5, PWM_CH2, PwmPinB);
|
||||
impl_pin!(PIN_6, PWM_CH3, PwmPinA);
|
||||
impl_pin!(PIN_7, PWM_CH3, PwmPinB);
|
||||
impl_pin!(PIN_8, PWM_CH4, PwmPinA);
|
||||
impl_pin!(PIN_9, PWM_CH4, PwmPinB);
|
||||
impl_pin!(PIN_10, PWM_CH5, PwmPinA);
|
||||
impl_pin!(PIN_11, PWM_CH5, PwmPinB);
|
||||
impl_pin!(PIN_12, PWM_CH6, PwmPinA);
|
||||
impl_pin!(PIN_13, PWM_CH6, PwmPinB);
|
||||
impl_pin!(PIN_14, PWM_CH7, PwmPinA);
|
||||
impl_pin!(PIN_15, PWM_CH7, PwmPinB);
|
||||
impl_pin!(PIN_16, PWM_CH0, PwmPinA);
|
||||
impl_pin!(PIN_17, PWM_CH0, PwmPinB);
|
||||
impl_pin!(PIN_18, PWM_CH1, PwmPinA);
|
||||
impl_pin!(PIN_19, PWM_CH1, PwmPinB);
|
||||
impl_pin!(PIN_20, PWM_CH2, PwmPinA);
|
||||
impl_pin!(PIN_21, PWM_CH2, PwmPinB);
|
||||
impl_pin!(PIN_22, PWM_CH3, PwmPinA);
|
||||
impl_pin!(PIN_23, PWM_CH3, PwmPinB);
|
||||
impl_pin!(PIN_24, PWM_CH4, PwmPinA);
|
||||
impl_pin!(PIN_25, PWM_CH4, PwmPinB);
|
||||
impl_pin!(PIN_26, PWM_CH5, PwmPinA);
|
||||
impl_pin!(PIN_27, PWM_CH5, PwmPinB);
|
||||
impl_pin!(PIN_28, PWM_CH6, PwmPinA);
|
||||
impl_pin!(PIN_29, PWM_CH6, PwmPinB);
|
||||
impl_pin!(PIN_0, PWM_SLICE0, ChannelAPin);
|
||||
impl_pin!(PIN_1, PWM_SLICE0, ChannelBPin);
|
||||
impl_pin!(PIN_2, PWM_SLICE1, ChannelAPin);
|
||||
impl_pin!(PIN_3, PWM_SLICE1, ChannelBPin);
|
||||
impl_pin!(PIN_4, PWM_SLICE2, ChannelAPin);
|
||||
impl_pin!(PIN_5, PWM_SLICE2, ChannelBPin);
|
||||
impl_pin!(PIN_6, PWM_SLICE3, ChannelAPin);
|
||||
impl_pin!(PIN_7, PWM_SLICE3, ChannelBPin);
|
||||
impl_pin!(PIN_8, PWM_SLICE4, ChannelAPin);
|
||||
impl_pin!(PIN_9, PWM_SLICE4, ChannelBPin);
|
||||
impl_pin!(PIN_10, PWM_SLICE5, ChannelAPin);
|
||||
impl_pin!(PIN_11, PWM_SLICE5, ChannelBPin);
|
||||
impl_pin!(PIN_12, PWM_SLICE6, ChannelAPin);
|
||||
impl_pin!(PIN_13, PWM_SLICE6, ChannelBPin);
|
||||
impl_pin!(PIN_14, PWM_SLICE7, ChannelAPin);
|
||||
impl_pin!(PIN_15, PWM_SLICE7, ChannelBPin);
|
||||
impl_pin!(PIN_16, PWM_SLICE0, ChannelAPin);
|
||||
impl_pin!(PIN_17, PWM_SLICE0, ChannelBPin);
|
||||
impl_pin!(PIN_18, PWM_SLICE1, ChannelAPin);
|
||||
impl_pin!(PIN_19, PWM_SLICE1, ChannelBPin);
|
||||
impl_pin!(PIN_20, PWM_SLICE2, ChannelAPin);
|
||||
impl_pin!(PIN_21, PWM_SLICE2, ChannelBPin);
|
||||
impl_pin!(PIN_22, PWM_SLICE3, ChannelAPin);
|
||||
impl_pin!(PIN_23, PWM_SLICE3, ChannelBPin);
|
||||
impl_pin!(PIN_24, PWM_SLICE4, ChannelAPin);
|
||||
impl_pin!(PIN_25, PWM_SLICE4, ChannelBPin);
|
||||
impl_pin!(PIN_26, PWM_SLICE5, ChannelAPin);
|
||||
impl_pin!(PIN_27, PWM_SLICE5, ChannelBPin);
|
||||
impl_pin!(PIN_28, PWM_SLICE6, ChannelAPin);
|
||||
impl_pin!(PIN_29, PWM_SLICE6, ChannelBPin);
|
||||
|
@ -1,5 +1,3 @@
|
||||
use core::iter::Iterator;
|
||||
|
||||
use pio::{Program, SideSet, Wrap};
|
||||
|
||||
pub struct CodeIterator<'a, I>
|
||||
@ -22,15 +20,15 @@ where
|
||||
{
|
||||
type Item = u16;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().and_then(|&instr| {
|
||||
Some(if instr & 0b1110_0000_0000_0000 == 0 {
|
||||
self.iter.next().map(|&instr| {
|
||||
if instr & 0b1110_0000_0000_0000 == 0 {
|
||||
// this is a JMP instruction -> add offset to address
|
||||
let address = (instr & 0b1_1111) as u8;
|
||||
let address = address.wrapping_add(self.offset) % 32;
|
||||
instr & (!0b11111) | address as u16
|
||||
} else {
|
||||
instr
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,7 @@ impl<'d, T: Instance> Rtc<'d, T> {
|
||||
// Set the RTC divider
|
||||
inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1));
|
||||
|
||||
let result = Self { inner };
|
||||
result
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
|
||||
|
@ -1,17 +1,11 @@
|
||||
//! Buffered UART driver.
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::future::Future;
|
||||
use core::slice;
|
||||
use core::task::Poll;
|
||||
|
||||
use atomic_polyfill::{AtomicU8, Ordering};
|
||||
use atomic_polyfill::AtomicU8;
|
||||
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embassy_time::Timer;
|
||||
|
||||
use super::*;
|
||||
use crate::clocks::clk_peri_freq;
|
||||
use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::{interrupt, RegExt};
|
||||
|
||||
pub struct State {
|
||||
tx_waker: AtomicWaker,
|
||||
@ -467,7 +461,7 @@ impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> {
|
||||
|
||||
// TX is inactive if the the buffer is not available.
|
||||
// We can now unregister the interrupt handler
|
||||
if state.tx_buf.len() == 0 {
|
||||
if state.tx_buf.is_empty() {
|
||||
T::Interrupt::disable();
|
||||
}
|
||||
}
|
||||
@ -480,7 +474,7 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> {
|
||||
|
||||
// RX is inactive if the the buffer is not available.
|
||||
// We can now unregister the interrupt handler
|
||||
if state.rx_buf.len() == 0 {
|
||||
if state.rx_buf.is_empty() {
|
||||
T::Interrupt::disable();
|
||||
}
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> {
|
||||
|
||||
impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(_) = self.rx_dma {
|
||||
if self.rx_dma.is_some() {
|
||||
T::Interrupt::disable();
|
||||
// clear dma flags. irq handlers use these to disambiguate among themselves.
|
||||
T::regs().uartdmacr().write_clear(|reg| {
|
||||
|
@ -465,7 +465,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
|
||||
trait Dir {
|
||||
fn dir() -> Direction;
|
||||
fn waker(i: usize) -> &'static AtomicWaker;
|
||||
}
|
||||
|
||||
/// Type for In direction.
|
||||
@ -474,11 +473,6 @@ impl Dir for In {
|
||||
fn dir() -> Direction {
|
||||
Direction::In
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn waker(i: usize) -> &'static AtomicWaker {
|
||||
&EP_IN_WAKERS[i]
|
||||
}
|
||||
}
|
||||
|
||||
/// Type for Out direction.
|
||||
@ -487,11 +481,6 @@ impl Dir for Out {
|
||||
fn dir() -> Direction {
|
||||
Direction::Out
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn waker(i: usize) -> &'static AtomicWaker {
|
||||
&EP_OUT_WAKERS[i]
|
||||
}
|
||||
}
|
||||
|
||||
/// Endpoint for RP USB driver.
|
||||
|
@ -1,5 +1,3 @@
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::evt::CsEvt;
|
||||
use crate::PacketHeader;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -70,7 +70,7 @@ rand_core = "0.6.3"
|
||||
sdio-host = "0.5.0"
|
||||
critical-section = "1.1"
|
||||
#stm32-metapac = { version = "15" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c8b32ecae7d70cea2705095c4fc6bd5f59d238d5" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f84633553331c2d154ee72de779a40cbb10fd1bd" }
|
||||
vcell = "0.1.3"
|
||||
nb = "1.0.0"
|
||||
stm32-fmc = "0.3.0"
|
||||
@ -94,7 +94,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c8b32ecae7d70cea2705095c4fc6bd5f59d238d5", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f84633553331c2d154ee72de779a40cbb10fd1bd", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
|
@ -584,7 +584,7 @@ fn main() {
|
||||
};
|
||||
|
||||
g.extend(quote! {
|
||||
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
||||
impl crate::rcc::SealedRccPeripheral for peripherals::#pname {
|
||||
fn frequency() -> crate::time::Hertz {
|
||||
#clock_frequency
|
||||
}
|
||||
@ -1486,7 +1486,7 @@ fn main() {
|
||||
#[crate::interrupt]
|
||||
unsafe fn #irq () {
|
||||
#(
|
||||
<crate::peripherals::#channels as crate::dma::sealed::ChannelInterrupt>::on_irq();
|
||||
<crate::peripherals::#channels as crate::dma::ChannelInterrupt>::on_irq();
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
|
||||
pub struct Vref;
|
||||
impl<T: Instance> AdcPin<T> for Vref {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Vref {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
@ -41,7 +41,7 @@ impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
16
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
|
||||
|
||||
pub struct Vref;
|
||||
impl<T: Instance> AdcPin<T> for Vref {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vref {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Vref {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
@ -48,7 +48,7 @@ impl Vref {
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
16
|
||||
}
|
||||
@ -102,7 +102,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
}
|
||||
|
||||
fn freq() -> Hertz {
|
||||
<T as crate::rcc::sealed::RccPeripheral>::frequency()
|
||||
<T as crate::rcc::SealedRccPeripheral>::frequency()
|
||||
}
|
||||
|
||||
pub fn sample_time_for_us(&self, us: u32) -> SampleTime {
|
||||
|
@ -65,7 +65,7 @@ fn update_vref<T: Instance>(op: i8) {
|
||||
|
||||
pub struct Vref<T: Instance>(core::marker::PhantomData<T>);
|
||||
impl<T: Instance> AdcPin<T> for Vref<T> {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vref<T> {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Vref<T> {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
@ -124,7 +124,7 @@ impl<T: Instance> Drop for Vref<T> {
|
||||
|
||||
pub struct Temperature<T: Instance>(core::marker::PhantomData<T>);
|
||||
impl<T: Instance> AdcPin<T> for Temperature<T> {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature<T> {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Temperature<T> {
|
||||
fn channel(&self) -> u8 {
|
||||
16
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ mod _version;
|
||||
#[allow(unused)]
|
||||
#[cfg(not(adc_f3_v2))]
|
||||
pub use _version::*;
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
#[cfg(not(any(adc_f1, adc_f3_v2)))]
|
||||
pub use crate::pac::adc::vals::Res as Resolution;
|
||||
@ -31,63 +33,65 @@ pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
pub struct State {
|
||||
pub waker: AtomicWaker,
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
pub struct State {
|
||||
pub waker: AtomicWaker,
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InterruptableInstance {
|
||||
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
trait SealedInstance {
|
||||
#[allow(unused)]
|
||||
fn regs() -> crate::pac::adc::Adc;
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
|
||||
pub trait Instance: InterruptableInstance {
|
||||
fn regs() -> crate::pac::adc::Adc;
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
|
||||
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
pub(crate) trait SealedAdcPin<T: Instance> {
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||
fn set_as_analog(&mut self) {}
|
||||
|
||||
pub trait AdcPin<T: Instance> {
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||
fn set_as_analog(&mut self) {}
|
||||
#[allow(unused)]
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
|
||||
pub trait InternalChannel<T> {
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
trait SealedInternalChannel<T> {
|
||||
#[allow(unused)]
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
|
||||
/// ADC instance.
|
||||
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + crate::Peripheral<P = Self> {
|
||||
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
/// ADC instance.
|
||||
#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))]
|
||||
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {
|
||||
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
/// ADC pin.
|
||||
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
||||
#[allow(private_bounds)]
|
||||
pub trait AdcPin<T: Instance>: SealedAdcPin<T> {}
|
||||
/// ADC internal channel.
|
||||
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
#[allow(private_bounds)]
|
||||
pub trait InternalChannel<T>: SealedInternalChannel<T> {}
|
||||
|
||||
foreach_adc!(
|
||||
($inst:ident, $common_inst:ident, $clock:ident) => {
|
||||
impl crate::adc::sealed::Instance for peripherals::$inst {
|
||||
impl crate::adc::SealedInstance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::adc::Adc {
|
||||
crate::pac::$inst
|
||||
}
|
||||
@ -98,21 +102,15 @@ foreach_adc!(
|
||||
}
|
||||
|
||||
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
|
||||
fn state() -> &'static sealed::State {
|
||||
static STATE: sealed::State = sealed::State::new();
|
||||
fn state() -> &'static State {
|
||||
static STATE: State = State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
foreach_interrupt!(
|
||||
($inst,adc,ADC,GLOBAL,$irq:ident) => {
|
||||
impl sealed::InterruptableInstance for peripherals::$inst {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
impl crate::adc::Instance for peripherals::$inst {}
|
||||
impl crate::adc::Instance for peripherals::$inst {
|
||||
type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
@ -120,10 +118,10 @@ macro_rules! impl_adc_pin {
|
||||
($inst:ident, $pin:ident, $ch:expr) => {
|
||||
impl crate::adc::AdcPin<peripherals::$inst> for crate::peripherals::$pin {}
|
||||
|
||||
impl crate::adc::sealed::AdcPin<peripherals::$inst> for crate::peripherals::$pin {
|
||||
impl crate::adc::SealedAdcPin<peripherals::$inst> for crate::peripherals::$pin {
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||
fn set_as_analog(&mut self) {
|
||||
<Self as crate::gpio::sealed::Pin>::set_as_analog(self);
|
||||
<Self as crate::gpio::SealedPin>::set_as_analog(self);
|
||||
}
|
||||
|
||||
fn channel(&self) -> u8 {
|
||||
|
@ -39,7 +39,7 @@ pub struct Vbat;
|
||||
impl AdcPin<ADC> for Vbat {}
|
||||
|
||||
#[cfg(not(adc_l0))]
|
||||
impl super::sealed::AdcPin<ADC> for Vbat {
|
||||
impl super::SealedAdcPin<ADC> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
@ -47,7 +47,7 @@ impl super::sealed::AdcPin<ADC> for Vbat {
|
||||
|
||||
pub struct Vref;
|
||||
impl AdcPin<ADC> for Vref {}
|
||||
impl super::sealed::AdcPin<ADC> for Vref {
|
||||
impl super::SealedAdcPin<ADC> for Vref {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
@ -55,7 +55,7 @@ impl super::sealed::AdcPin<ADC> for Vref {
|
||||
|
||||
pub struct Temperature;
|
||||
impl AdcPin<ADC> for Temperature {}
|
||||
impl super::sealed::AdcPin<ADC> for Temperature {
|
||||
impl super::SealedAdcPin<ADC> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
16
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ pub const ADC_POWERUP_TIME_US: u32 = 3;
|
||||
|
||||
pub struct VrefInt;
|
||||
impl AdcPin<ADC1> for VrefInt {}
|
||||
impl super::sealed::AdcPin<ADC1> for VrefInt {
|
||||
impl super::SealedAdcPin<ADC1> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
@ -31,7 +31,7 @@ impl VrefInt {
|
||||
|
||||
pub struct Temperature;
|
||||
impl AdcPin<ADC1> for Temperature {}
|
||||
impl super::sealed::AdcPin<ADC1> for Temperature {
|
||||
impl super::SealedAdcPin<ADC1> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(stm32f2, stm32f40, stm32f41))] {
|
||||
@ -52,7 +52,7 @@ impl Temperature {
|
||||
|
||||
pub struct Vbat;
|
||||
impl AdcPin<ADC1> for Vbat {}
|
||||
impl super::sealed::AdcPin<ADC1> for Vbat {
|
||||
impl super::SealedAdcPin<ADC1> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ pub const VREF_CALIB_MV: u32 = 3000;
|
||||
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> AdcPin<T> for VrefInt {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for VrefInt {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
cfg_if! {
|
||||
if #[cfg(adc_g0)] {
|
||||
@ -29,7 +29,7 @@ impl<T: Instance> super::sealed::AdcPin<T> for VrefInt {
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
cfg_if! {
|
||||
if #[cfg(adc_g0)] {
|
||||
@ -46,7 +46,7 @@ impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
|
||||
pub struct Vbat;
|
||||
impl<T: Instance> AdcPin<T> for Vbat {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
cfg_if! {
|
||||
if #[cfg(adc_g0)] {
|
||||
@ -65,7 +65,7 @@ cfg_if! {
|
||||
if #[cfg(adc_h5)] {
|
||||
pub struct VddCore;
|
||||
impl<T: Instance> AdcPin<T> for VddCore {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for VddCore {
|
||||
impl<T: Instance> super::SealedAdcPin<T> for VddCore {
|
||||
fn channel(&self) -> u8 {
|
||||
6
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ const VBAT_CHANNEL: u8 = 17;
|
||||
/// Internal voltage reference channel.
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> InternalChannel<T> for VrefInt {}
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
VREF_CHANNEL
|
||||
}
|
||||
@ -44,7 +44,7 @@ impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
|
||||
/// Internal temperature channel.
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> InternalChannel<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
TEMP_CHANNEL
|
||||
}
|
||||
@ -53,7 +53,7 @@ impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
|
||||
/// Internal battery voltage channel.
|
||||
pub struct Vbat;
|
||||
impl<T: Instance> InternalChannel<T> for Vbat {}
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
|
||||
impl<T: Instance> super::SealedInternalChannel<T> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
VBAT_CHANNEL
|
||||
}
|
||||
@ -276,7 +276,7 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
pub fn read<P>(&mut self, pin: &mut P) -> u16
|
||||
where
|
||||
P: AdcPin<T>,
|
||||
P: crate::gpio::sealed::Pin,
|
||||
P: crate::gpio::Pin,
|
||||
{
|
||||
pin.set_as_analog();
|
||||
|
||||
|
@ -1,971 +0,0 @@
|
||||
//! Driver for the STM32 bxCAN peripheral.
|
||||
//!
|
||||
//! This crate provides a reusable driver for the bxCAN peripheral found in many low- to middle-end
|
||||
//! STM32 microcontrollers. HALs for compatible chips can reexport this crate and implement its
|
||||
//! traits to easily expose a featureful CAN driver.
|
||||
//!
|
||||
//! # Features
|
||||
//!
|
||||
//! - Supports both single- and dual-peripheral configurations (where one bxCAN instance manages the
|
||||
//! filters of a secondary instance).
|
||||
//! - Handles standard and extended frames, and data and remote frames.
|
||||
//! - Support for interrupts emitted by the bxCAN peripheral.
|
||||
//! - Transmission respects CAN IDs and protects against priority inversion (a lower-priority frame
|
||||
//! may be dequeued when enqueueing a higher-priority one).
|
||||
//! - Implements the [`embedded-hal`] traits for interoperability.
|
||||
//! - Support for both RX FIFOs (as [`Rx0`] and [`Rx1`]).
|
||||
//!
|
||||
//! # Limitations
|
||||
//!
|
||||
//! - Support for querying error states and handling error interrupts is incomplete.
|
||||
//!
|
||||
|
||||
// Deny a few warnings in doctests, since rustdoc `allow`s many warnings by default
|
||||
#![allow(clippy::unnecessary_operation)] // lint is bugged
|
||||
|
||||
//mod embedded_hal;
|
||||
pub mod filter;
|
||||
|
||||
#[allow(clippy::all)] // generated code
|
||||
use core::cmp::{Ord, Ordering};
|
||||
use core::convert::{Infallible, Into, TryInto};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
|
||||
pub use embedded_can::{ExtendedId, Id, StandardId};
|
||||
|
||||
/// CAN Header: includes ID and length
|
||||
pub type Header = crate::can::frame::Header;
|
||||
|
||||
/// Data for a CAN Frame
|
||||
pub type Data = crate::can::frame::ClassicData;
|
||||
|
||||
/// CAN Frame
|
||||
pub type Frame = crate::can::frame::ClassicFrame;
|
||||
|
||||
use crate::can::bx::filter::MasterFilters;
|
||||
|
||||
/// A bxCAN peripheral instance.
|
||||
///
|
||||
/// This trait is meant to be implemented for a HAL-specific type that represent ownership of
|
||||
/// the CAN peripheral (and any pins required by it, although that is entirely up to the HAL).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is only safe to implement this trait, when:
|
||||
///
|
||||
/// * The implementing type has ownership of the peripheral, preventing any other accesses to the
|
||||
/// register block.
|
||||
/// * `REGISTERS` is a pointer to that peripheral's register block and can be safely accessed for as
|
||||
/// long as ownership or a borrow of the implementing type is present.
|
||||
pub unsafe trait Instance {}
|
||||
|
||||
/// A bxCAN instance that owns filter banks.
|
||||
///
|
||||
/// In master-slave-instance setups, only the master instance owns the filter banks, and needs to
|
||||
/// split some of them off for use by the slave instance. In that case, the master instance should
|
||||
/// implement [`FilterOwner`] and [`MasterInstance`], while the slave instance should only implement
|
||||
/// [`Instance`].
|
||||
///
|
||||
/// In single-instance configurations, the instance owns all filter banks and they can not be split
|
||||
/// off. In that case, the instance should implement [`Instance`] and [`FilterOwner`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented if the instance does, in fact, own its associated filter
|
||||
/// banks, and `NUM_FILTER_BANKS` must be correct.
|
||||
pub unsafe trait FilterOwner: Instance {
|
||||
/// The total number of filter banks available to the instance.
|
||||
///
|
||||
/// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet.
|
||||
const NUM_FILTER_BANKS: u8;
|
||||
}
|
||||
|
||||
/// A bxCAN master instance that shares filter banks with a slave instance.
|
||||
///
|
||||
/// In master-slave-instance setups, this trait should be implemented for the master instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented when there is actually an associated slave instance.
|
||||
pub unsafe trait MasterInstance: FilterOwner {}
|
||||
|
||||
// TODO: what to do with these?
|
||||
/*
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Format)]
|
||||
pub enum Error {
|
||||
Stuff,
|
||||
Form,
|
||||
Acknowledgement,
|
||||
BitRecessive,
|
||||
BitDominant,
|
||||
Crc,
|
||||
Software,
|
||||
}*/
|
||||
|
||||
/// Error that indicates that an incoming message has been lost due to buffer overrun.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct OverrunError {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
/// Identifier of a CAN message.
|
||||
///
|
||||
/// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a
|
||||
/// extendended identifier (29bit , Range: 0..0x1FFFFFFF).
|
||||
///
|
||||
/// The `Ord` trait can be used to determine the frame’s priority this ID
|
||||
/// belongs to.
|
||||
/// Lower identifier values have a higher priority. Additionally standard frames
|
||||
/// have a higher priority than extended frames and data frames have a higher
|
||||
/// priority than remote frames.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub(crate) struct IdReg(u32);
|
||||
|
||||
impl IdReg {
|
||||
const STANDARD_SHIFT: u32 = 21;
|
||||
|
||||
const EXTENDED_SHIFT: u32 = 3;
|
||||
|
||||
const IDE_MASK: u32 = 0x0000_0004;
|
||||
|
||||
const RTR_MASK: u32 = 0x0000_0002;
|
||||
|
||||
/// Creates a new standard identifier (11bit, Range: 0..0x7FF)
|
||||
///
|
||||
/// Panics for IDs outside the allowed range.
|
||||
fn new_standard(id: StandardId) -> Self {
|
||||
Self(u32::from(id.as_raw()) << Self::STANDARD_SHIFT)
|
||||
}
|
||||
|
||||
/// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF).
|
||||
///
|
||||
/// Panics for IDs outside the allowed range.
|
||||
fn new_extended(id: ExtendedId) -> IdReg {
|
||||
Self(id.as_raw() << Self::EXTENDED_SHIFT | Self::IDE_MASK)
|
||||
}
|
||||
|
||||
fn from_register(reg: u32) -> IdReg {
|
||||
Self(reg & 0xFFFF_FFFE)
|
||||
}
|
||||
|
||||
/// Returns the identifier.
|
||||
fn to_id(self) -> Id {
|
||||
if self.is_extended() {
|
||||
Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::EXTENDED_SHIFT) })
|
||||
} else {
|
||||
Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::STANDARD_SHIFT) as u16) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the identifier.
|
||||
fn id(self) -> embedded_can::Id {
|
||||
if self.is_extended() {
|
||||
embedded_can::ExtendedId::new(self.0 >> Self::EXTENDED_SHIFT)
|
||||
.unwrap()
|
||||
.into()
|
||||
} else {
|
||||
embedded_can::StandardId::new((self.0 >> Self::STANDARD_SHIFT) as u16)
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the identifier is an extended identifier.
|
||||
fn is_extended(self) -> bool {
|
||||
self.0 & Self::IDE_MASK != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the identifer is part of a remote frame (RTR bit set).
|
||||
fn rtr(self) -> bool {
|
||||
self.0 & Self::RTR_MASK != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&embedded_can::Id> for IdReg {
|
||||
fn from(eid: &embedded_can::Id) -> Self {
|
||||
match eid {
|
||||
embedded_can::Id::Standard(id) => IdReg::new_standard(StandardId::new(id.as_raw()).unwrap()),
|
||||
embedded_can::Id::Extended(id) => IdReg::new_extended(ExtendedId::new(id.as_raw()).unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IdReg> for embedded_can::Id {
|
||||
fn from(idr: IdReg) -> Self {
|
||||
idr.id()
|
||||
}
|
||||
}
|
||||
|
||||
/// `IdReg` is ordered by priority.
|
||||
impl Ord for IdReg {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
// When the IDs match, data frames have priority over remote frames.
|
||||
let rtr = self.rtr().cmp(&other.rtr()).reverse();
|
||||
|
||||
let id_a = self.to_id();
|
||||
let id_b = other.to_id();
|
||||
match (id_a, id_b) {
|
||||
(Id::Standard(a), Id::Standard(b)) => {
|
||||
// Lower IDs have priority over higher IDs.
|
||||
a.as_raw().cmp(&b.as_raw()).reverse().then(rtr)
|
||||
}
|
||||
(Id::Extended(a), Id::Extended(b)) => a.as_raw().cmp(&b.as_raw()).reverse().then(rtr),
|
||||
(Id::Standard(a), Id::Extended(b)) => {
|
||||
// Standard frames have priority over extended frames if their Base IDs match.
|
||||
a.as_raw()
|
||||
.cmp(&b.standard_id().as_raw())
|
||||
.reverse()
|
||||
.then(Ordering::Greater)
|
||||
}
|
||||
(Id::Extended(a), Id::Standard(b)) => {
|
||||
a.standard_id().as_raw().cmp(&b.as_raw()).reverse().then(Ordering::Less)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for IdReg {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration proxy returned by [`Can::modify_config`].
|
||||
#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"]
|
||||
pub struct CanConfig<'a, I: Instance> {
|
||||
can: &'a mut Can<I>,
|
||||
}
|
||||
|
||||
impl<I: Instance> CanConfig<'_, I> {
|
||||
/// 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.
|
||||
pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self {
|
||||
self.can.set_bit_timing(bt);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(self, enabled: bool) -> Self {
|
||||
self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(self, enabled: bool) -> Self {
|
||||
let mode = match enabled {
|
||||
false => stm32_metapac::can::vals::Silm::NORMAL,
|
||||
true => stm32_metapac::can::vals::Silm::SILENT,
|
||||
};
|
||||
self.can.canregs.btr().modify(|reg| reg.set_silm(mode));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables automatic retransmission of messages.
|
||||
///
|
||||
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
||||
/// until it can be sent. Otherwise, it will try only once to send each frame.
|
||||
///
|
||||
/// Automatic retransmission is enabled by default.
|
||||
pub fn set_automatic_retransmit(self, enabled: bool) -> Self {
|
||||
self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// Leaves initialization mode and enables the peripheral.
|
||||
///
|
||||
/// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected
|
||||
/// on the bus.
|
||||
///
|
||||
/// If you want to finish configuration without enabling the peripheral, you can call
|
||||
/// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead.
|
||||
pub fn enable(mut self) {
|
||||
self.leave_init_mode();
|
||||
|
||||
match nb::block!(self.can.enable_non_blocking()) {
|
||||
Ok(()) => {}
|
||||
Err(void) => match void {},
|
||||
}
|
||||
|
||||
// Don't run the destructor.
|
||||
mem::forget(self);
|
||||
}
|
||||
|
||||
/// Leaves initialization mode, but keeps the peripheral in sleep mode.
|
||||
///
|
||||
/// Before the [`Can`] instance can be used, you have to enable it by calling
|
||||
/// [`Can::enable_non_blocking`].
|
||||
pub fn leave_disabled(mut self) {
|
||||
self.leave_init_mode();
|
||||
}
|
||||
|
||||
/// Leaves initialization mode, enters sleep mode.
|
||||
fn leave_init_mode(&mut self) {
|
||||
self.can.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.can.canregs.msr().read();
|
||||
if msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Instance> Drop for CanConfig<'_, I> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.leave_init_mode();
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder returned by [`Can::builder`].
|
||||
#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"]
|
||||
pub struct CanBuilder<I: Instance> {
|
||||
can: Can<I>,
|
||||
}
|
||||
|
||||
impl<I: Instance> CanBuilder<I> {
|
||||
/// 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.
|
||||
pub fn set_bit_timing(mut self, bt: crate::can::util::NominalBitTiming) -> Self {
|
||||
self.can.set_bit_timing(bt);
|
||||
self
|
||||
}
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(self, enabled: bool) -> Self {
|
||||
self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(self, enabled: bool) -> Self {
|
||||
let mode = match enabled {
|
||||
false => stm32_metapac::can::vals::Silm::NORMAL,
|
||||
true => stm32_metapac::can::vals::Silm::SILENT,
|
||||
};
|
||||
self.can.canregs.btr().modify(|reg| reg.set_silm(mode));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables automatic retransmission of messages.
|
||||
///
|
||||
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
||||
/// until it can be sent. Otherwise, it will try only once to send each frame.
|
||||
///
|
||||
/// Automatic retransmission is enabled by default.
|
||||
pub fn set_automatic_retransmit(self, enabled: bool) -> Self {
|
||||
self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// Leaves initialization mode and enables the peripheral.
|
||||
///
|
||||
/// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected
|
||||
/// on the bus.
|
||||
///
|
||||
/// If you want to finish configuration without enabling the peripheral, you can call
|
||||
/// [`CanBuilder::leave_disabled`] instead.
|
||||
pub fn enable(mut self) -> Can<I> {
|
||||
self.leave_init_mode();
|
||||
|
||||
match nb::block!(self.can.enable_non_blocking()) {
|
||||
Ok(()) => self.can,
|
||||
Err(void) => match void {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Can`] interface without enabling it.
|
||||
///
|
||||
/// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling
|
||||
/// it.
|
||||
///
|
||||
/// Before the [`Can`] instance can be used, you have to enable it by calling
|
||||
/// [`Can::enable_non_blocking`].
|
||||
pub fn leave_disabled(mut self) -> Can<I> {
|
||||
self.leave_init_mode();
|
||||
self.can
|
||||
}
|
||||
|
||||
/// Leaves initialization mode, enters sleep mode.
|
||||
fn leave_init_mode(&mut self) {
|
||||
self.can.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.can.canregs.msr().read();
|
||||
if msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to a bxCAN peripheral.
|
||||
pub struct Can<I: Instance> {
|
||||
instance: I,
|
||||
canregs: crate::pac::can::Can,
|
||||
}
|
||||
|
||||
impl<I> Can<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
/// Creates a [`CanBuilder`] for constructing a CAN interface.
|
||||
pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder<I> {
|
||||
let can_builder = CanBuilder {
|
||||
can: Can { instance, canregs },
|
||||
};
|
||||
|
||||
canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(false);
|
||||
reg.set_inrq(true);
|
||||
});
|
||||
loop {
|
||||
let msr = canregs.msr().read();
|
||||
if !msr.slak() && msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
can_builder
|
||||
}
|
||||
|
||||
fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) {
|
||||
let prescaler = u16::from(bt.prescaler) & 0x1FF;
|
||||
let seg1 = u8::from(bt.seg1);
|
||||
let seg2 = u8::from(bt.seg2) & 0x7F;
|
||||
let sync_jump_width = u8::from(bt.sync_jump_width) & 0x7F;
|
||||
self.canregs.btr().modify(|reg| {
|
||||
reg.set_brp(prescaler - 1);
|
||||
reg.set_ts(0, seg1 - 1);
|
||||
reg.set_ts(1, seg2 - 1);
|
||||
reg.set_sjw(sync_jump_width - 1);
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a reference to the peripheral instance.
|
||||
///
|
||||
/// This allows accessing HAL-specific data stored in the instance type.
|
||||
pub fn instance(&mut self) -> &mut I {
|
||||
&mut self.instance
|
||||
}
|
||||
|
||||
/// Disables the CAN interface and returns back the raw peripheral it was created from.
|
||||
///
|
||||
/// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to
|
||||
/// enter sleep mode.
|
||||
pub fn free(self) -> I {
|
||||
self.canregs.mcr().write(|reg| reg.set_reset(true));
|
||||
self.instance
|
||||
}
|
||||
|
||||
/// Configure bit timings and silent/loop-back mode.
|
||||
///
|
||||
/// Calling this method will enter initialization mode.
|
||||
pub fn modify_config(&mut self) -> CanConfig<'_, I> {
|
||||
self.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(false);
|
||||
reg.set_inrq(true);
|
||||
});
|
||||
loop {
|
||||
let msr = self.canregs.msr().read();
|
||||
if !msr.slak() && msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CanConfig { can: self }
|
||||
}
|
||||
|
||||
/// Configures the automatic wake-up feature.
|
||||
///
|
||||
/// This is turned off by default.
|
||||
///
|
||||
/// When turned on, an incoming frame will cause the peripheral to wake up from sleep and
|
||||
/// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming
|
||||
/// frame.
|
||||
pub fn set_automatic_wakeup(&mut self, enabled: bool) {
|
||||
self.canregs.mcr().modify(|reg| reg.set_awum(enabled));
|
||||
}
|
||||
|
||||
/// Leaves initialization mode and enables the peripheral (non-blocking version).
|
||||
///
|
||||
/// Usually, it is recommended to call [`CanConfig::enable`] instead. This method is only needed
|
||||
/// if you want non-blocking initialization.
|
||||
///
|
||||
/// If this returns [`WouldBlock`][nb::Error::WouldBlock], the peripheral will enable itself
|
||||
/// in the background. The peripheral is enabled and ready to use when this method returns
|
||||
/// successfully.
|
||||
pub fn enable_non_blocking(&mut self) -> nb::Result<(), Infallible> {
|
||||
let msr = self.canregs.msr().read();
|
||||
if msr.slak() {
|
||||
self.canregs.mcr().modify(|reg| {
|
||||
reg.set_abom(true);
|
||||
reg.set_sleep(false);
|
||||
});
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Puts the peripheral in a sleep mode to save power.
|
||||
///
|
||||
/// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled.
|
||||
pub fn sleep(&mut self) {
|
||||
self.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.canregs.msr().read();
|
||||
if msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wakes up from sleep mode.
|
||||
///
|
||||
/// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN
|
||||
/// frame will cause that interrupt.
|
||||
pub fn wakeup(&mut self) {
|
||||
self.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(false);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.canregs.msr().read();
|
||||
if !msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Puts a CAN frame in a free transmit mailbox for transmission on the bus.
|
||||
///
|
||||
/// Frames are transmitted to the bus based on their priority (see [`FramePriority`]).
|
||||
/// Transmit order is preserved for frames with identical priority.
|
||||
///
|
||||
/// If all transmit mailboxes are full, and `frame` has a higher priority than the
|
||||
/// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is
|
||||
/// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as
|
||||
/// [`TransmitStatus::dequeued_frame`].
|
||||
pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> {
|
||||
// Safety: We have a `&mut self` and have unique access to the peripheral.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).transmit(frame) }
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_transmitter_idle(&self) -> bool {
|
||||
// Safety: Read-only operation.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).is_idle() }
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
pub fn abort(&mut self, mailbox: Mailbox) -> bool {
|
||||
// Safety: We have a `&mut self` and have unique access to the peripheral.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).abort(mailbox) }
|
||||
}
|
||||
|
||||
/// Returns a received frame if available.
|
||||
///
|
||||
/// This will first check FIFO 0 for a message or error. If none are available, FIFO 1 is
|
||||
/// checked.
|
||||
///
|
||||
/// Returns `Err` when a frame was lost due to buffer overrun.
|
||||
pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> {
|
||||
// Safety: We have a `&mut self` and have unique access to the peripheral.
|
||||
let mut rx0 = unsafe { Rx0::<I>::conjure(self.canregs) };
|
||||
let mut rx1 = unsafe { Rx1::<I>::conjure(self.canregs) };
|
||||
|
||||
match rx0.receive() {
|
||||
Err(nb::Error::WouldBlock) => rx1.receive(),
|
||||
result => result,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the RX FIFO 0.
|
||||
pub fn rx0(&mut self) -> Rx0<I> {
|
||||
// Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime.
|
||||
unsafe { Rx0::conjure(self.canregs) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the RX FIFO 1.
|
||||
pub fn rx1(&mut self) -> Rx1<I> {
|
||||
// Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime.
|
||||
unsafe { Rx1::conjure(self.canregs) }
|
||||
}
|
||||
|
||||
pub(crate) fn split_by_ref(&mut self) -> (Tx<I>, Rx0<I>, Rx1<I>) {
|
||||
// Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime.
|
||||
let tx = unsafe { Tx::conjure(self.canregs) };
|
||||
let rx0 = unsafe { Rx0::conjure(self.canregs) };
|
||||
let rx1 = unsafe { Rx1::conjure(self.canregs) };
|
||||
(tx, rx0, rx1)
|
||||
}
|
||||
|
||||
/// Consumes this `Can` instance and splits it into transmitting and receiving halves.
|
||||
pub fn split(self) -> (Tx<I>, Rx0<I>, Rx1<I>) {
|
||||
// Safety: `Self` is not `Copy` and is destroyed by moving it into this method.
|
||||
unsafe {
|
||||
(
|
||||
Tx::conjure(self.canregs),
|
||||
Rx0::conjure(self.canregs),
|
||||
Rx1::conjure(self.canregs),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: FilterOwner> Can<I> {
|
||||
/// Accesses the filter banks owned by this CAN peripheral.
|
||||
///
|
||||
/// To modify filters of a slave peripheral, `modify_filters` has to be called on the master
|
||||
/// peripheral instead.
|
||||
pub fn modify_filters(&mut self) -> MasterFilters<'_, I> {
|
||||
unsafe { MasterFilters::new(self.canregs) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to the CAN transmitter part.
|
||||
pub struct Tx<I> {
|
||||
_can: PhantomData<I>,
|
||||
canregs: crate::pac::can::Can,
|
||||
}
|
||||
|
||||
impl<I> Tx<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
unsafe fn conjure(canregs: crate::pac::can::Can) -> Self {
|
||||
Self {
|
||||
_can: PhantomData,
|
||||
canregs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Puts a CAN frame in a transmit mailbox for transmission on the bus.
|
||||
///
|
||||
/// Frames are transmitted to the bus based on their priority (see [`FramePriority`]).
|
||||
/// Transmit order is preserved for frames with identical priority.
|
||||
///
|
||||
/// If all transmit mailboxes are full, and `frame` has a higher priority than the
|
||||
/// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is
|
||||
/// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as
|
||||
/// [`TransmitStatus::dequeued_frame`].
|
||||
pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> {
|
||||
// Get the index of the next free mailbox or the one with the lowest priority.
|
||||
let tsr = self.canregs.tsr().read();
|
||||
let idx = tsr.code() as usize;
|
||||
|
||||
let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2);
|
||||
let pending_frame = if frame_is_pending {
|
||||
// High priority frames are transmitted first by the mailbox system.
|
||||
// Frames with identical identifier shall be transmitted in FIFO order.
|
||||
// The controller schedules pending frames of same priority based on the
|
||||
// mailbox index instead. As a workaround check all pending mailboxes
|
||||
// and only accept higher priority frames.
|
||||
self.check_priority(0, frame.id().into())?;
|
||||
self.check_priority(1, frame.id().into())?;
|
||||
self.check_priority(2, frame.id().into())?;
|
||||
|
||||
let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2);
|
||||
if all_frames_are_pending {
|
||||
// No free mailbox is available. This can only happen when three frames with
|
||||
// ascending priority (descending IDs) were requested for transmission and all
|
||||
// of them are blocked by bus traffic with even higher priority.
|
||||
// To prevent a priority inversion abort and replace the lowest priority frame.
|
||||
self.read_pending_mailbox(idx)
|
||||
} else {
|
||||
// There was a free mailbox.
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// All mailboxes are available: Send frame without performing any checks.
|
||||
None
|
||||
};
|
||||
|
||||
self.write_mailbox(idx, frame);
|
||||
|
||||
let mailbox = match idx {
|
||||
0 => Mailbox::Mailbox0,
|
||||
1 => Mailbox::Mailbox1,
|
||||
2 => Mailbox::Mailbox2,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(TransmitStatus {
|
||||
dequeued_frame: pending_frame,
|
||||
mailbox,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `Ok` when the mailbox is free or if it contains pending frame with a
|
||||
/// lower priority (higher ID) than the identifier `id`.
|
||||
fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> {
|
||||
// Read the pending frame's id to check its priority.
|
||||
assert!(idx < 3);
|
||||
let tir = &self.canregs.tx(idx).tir().read();
|
||||
//let tir = &can.tx[idx].tir.read();
|
||||
|
||||
// Check the priority by comparing the identifiers. But first make sure the
|
||||
// frame has not finished the transmission (`TXRQ` == 0) in the meantime.
|
||||
if tir.txrq() && id <= IdReg::from_register(tir.0) {
|
||||
// There's a mailbox whose priority is higher or equal
|
||||
// the priority of the new frame.
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_mailbox(&mut self, idx: usize, frame: &Frame) {
|
||||
debug_assert!(idx < 3);
|
||||
|
||||
let mb = self.canregs.tx(idx);
|
||||
mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8));
|
||||
|
||||
mb.tdlr()
|
||||
.write(|w| w.0 = u32::from_ne_bytes(frame.data()[0..4].try_into().unwrap()));
|
||||
mb.tdhr()
|
||||
.write(|w| w.0 = u32::from_ne_bytes(frame.data()[4..8].try_into().unwrap()));
|
||||
let id: IdReg = frame.id().into();
|
||||
mb.tir().write(|w| {
|
||||
w.0 = id.0;
|
||||
w.set_txrq(true);
|
||||
});
|
||||
}
|
||||
|
||||
fn read_pending_mailbox(&mut self, idx: usize) -> Option<Frame> {
|
||||
if self.abort_by_index(idx) {
|
||||
debug_assert!(idx < 3);
|
||||
|
||||
let mb = self.canregs.tx(idx);
|
||||
|
||||
let id = IdReg(mb.tir().read().0).id();
|
||||
let mut data = [0xff; 8];
|
||||
data[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes());
|
||||
let len = mb.tdtr().read().dlc();
|
||||
|
||||
Some(Frame::new(Header::new(id, len, false), &data).unwrap())
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to abort a pending frame. Returns `true` when aborted.
|
||||
fn abort_by_index(&mut self, idx: usize) -> bool {
|
||||
self.canregs.tsr().write(|reg| reg.set_abrq(idx, true));
|
||||
|
||||
// Wait for the abort request to be finished.
|
||||
loop {
|
||||
let tsr = self.canregs.tsr().read();
|
||||
if false == tsr.abrq(idx) {
|
||||
break tsr.txok(idx) == false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
pub fn abort(&mut self, mailbox: Mailbox) -> bool {
|
||||
// If the mailbox is empty, the value of TXOKx depends on what happened with the previous
|
||||
// frame in that mailbox. Only call abort_by_index() if the mailbox is not empty.
|
||||
let tsr = self.canregs.tsr().read();
|
||||
let mailbox_empty = match mailbox {
|
||||
Mailbox::Mailbox0 => tsr.tme(0),
|
||||
Mailbox::Mailbox1 => tsr.tme(1),
|
||||
Mailbox::Mailbox2 => tsr.tme(2),
|
||||
};
|
||||
if mailbox_empty {
|
||||
false
|
||||
} else {
|
||||
self.abort_by_index(mailbox as usize)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_idle(&self) -> bool {
|
||||
let tsr = self.canregs.tsr().read();
|
||||
tsr.tme(0) && tsr.tme(1) && tsr.tme(2)
|
||||
}
|
||||
|
||||
/// Clears the request complete flag for all mailboxes.
|
||||
pub fn clear_interrupt_flags(&mut self) {
|
||||
self.canregs.tsr().write(|reg| {
|
||||
reg.set_rqcp(0, true);
|
||||
reg.set_rqcp(1, true);
|
||||
reg.set_rqcp(2, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to receiver FIFO 0.
|
||||
pub struct Rx0<I> {
|
||||
_can: PhantomData<I>,
|
||||
canregs: crate::pac::can::Can,
|
||||
}
|
||||
|
||||
impl<I> Rx0<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
unsafe fn conjure(canregs: crate::pac::can::Can) -> Self {
|
||||
Self {
|
||||
_can: PhantomData,
|
||||
canregs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a received frame if available.
|
||||
///
|
||||
/// Returns `Err` when a frame was lost due to buffer overrun.
|
||||
pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> {
|
||||
receive_fifo(self.canregs, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to receiver FIFO 1.
|
||||
pub struct Rx1<I> {
|
||||
_can: PhantomData<I>,
|
||||
canregs: crate::pac::can::Can,
|
||||
}
|
||||
|
||||
impl<I> Rx1<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
unsafe fn conjure(canregs: crate::pac::can::Can) -> Self {
|
||||
Self {
|
||||
_can: PhantomData,
|
||||
canregs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a received frame if available.
|
||||
///
|
||||
/// Returns `Err` when a frame was lost due to buffer overrun.
|
||||
pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> {
|
||||
receive_fifo(self.canregs, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result<Frame, OverrunError> {
|
||||
assert!(fifo_nr < 2);
|
||||
let rfr = canregs.rfr(fifo_nr);
|
||||
let rx = canregs.rx(fifo_nr);
|
||||
|
||||
//let rfr = &can.rfr[fifo_nr];
|
||||
//let rx = &can.rx[fifo_nr];
|
||||
|
||||
// Check if a frame is available in the mailbox.
|
||||
let rfr_read = rfr.read();
|
||||
if rfr_read.fmp() == 0 {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
// Check for RX FIFO overrun.
|
||||
if rfr_read.fovr() {
|
||||
rfr.write(|w| w.set_fovr(true));
|
||||
return Err(nb::Error::Other(OverrunError { _priv: () }));
|
||||
}
|
||||
|
||||
// Read the frame.
|
||||
let id = IdReg(rx.rir().read().0).id();
|
||||
let mut data = [0xff; 8];
|
||||
data[0..4].copy_from_slice(&rx.rdlr().read().0.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&rx.rdhr().read().0.to_ne_bytes());
|
||||
let len = rx.rdtr().read().dlc();
|
||||
|
||||
// Release the mailbox.
|
||||
rfr.write(|w| w.set_rfom(true));
|
||||
|
||||
Ok(Frame::new(Header::new(id, len, false), &data).unwrap())
|
||||
}
|
||||
|
||||
/// Identifies one of the two receive FIFOs.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Fifo {
|
||||
/// First receive FIFO
|
||||
Fifo0 = 0,
|
||||
/// Second receive FIFO
|
||||
Fifo1 = 1,
|
||||
}
|
||||
|
||||
/// Identifies one of the three transmit mailboxes.
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Mailbox {
|
||||
/// Transmit mailbox 0
|
||||
Mailbox0 = 0,
|
||||
/// Transmit mailbox 1
|
||||
Mailbox1 = 1,
|
||||
/// Transmit mailbox 2
|
||||
Mailbox2 = 2,
|
||||
}
|
||||
|
||||
/// Contains information about a frame enqueued for transmission via [`Can::transmit`] or
|
||||
/// [`Tx::transmit`].
|
||||
pub struct TransmitStatus {
|
||||
dequeued_frame: Option<Frame>,
|
||||
mailbox: Mailbox,
|
||||
}
|
||||
|
||||
impl TransmitStatus {
|
||||
/// Returns the lower-priority frame that was dequeued to make space for the new frame.
|
||||
#[inline]
|
||||
pub fn dequeued_frame(&self) -> Option<&Frame> {
|
||||
self.dequeued_frame.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the [`Mailbox`] the frame was enqueued in.
|
||||
#[inline]
|
||||
pub fn mailbox(&self) -> Mailbox {
|
||||
self.mailbox
|
||||
}
|
||||
}
|
@ -1,627 +0,0 @@
|
||||
use core::convert::AsMut;
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::task::Poll;
|
||||
|
||||
pub mod bx;
|
||||
|
||||
pub use bx::{filter, Data, ExtendedId, Fifo, Frame, Header, Id, StandardId};
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use futures::FutureExt;
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::pac::can::vals::{Ide, Lec};
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{interrupt, peripherals, Peripheral};
|
||||
|
||||
pub mod enums;
|
||||
use enums::*;
|
||||
pub mod frame;
|
||||
pub mod util;
|
||||
|
||||
/// Contains CAN frame and additional metadata.
|
||||
///
|
||||
/// Timestamp is available if `time` feature is enabled.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Envelope {
|
||||
/// Reception time.
|
||||
#[cfg(feature = "time")]
|
||||
pub ts: embassy_time::Instant,
|
||||
/// The actual CAN frame.
|
||||
pub frame: Frame,
|
||||
}
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct TxInterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::TXInterrupt> for TxInterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
T::regs().tsr().write(|v| {
|
||||
v.set_rqcp(0, true);
|
||||
v.set_rqcp(1, true);
|
||||
v.set_rqcp(2, true);
|
||||
});
|
||||
|
||||
T::state().tx_waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
/// RX0 interrupt handler.
|
||||
pub struct Rx0InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
// info!("rx0 irq");
|
||||
Can::<T>::receive_fifo(RxFifo::Fifo0);
|
||||
}
|
||||
}
|
||||
|
||||
/// RX1 interrupt handler.
|
||||
pub struct Rx1InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
// info!("rx1 irq");
|
||||
Can::<T>::receive_fifo(RxFifo::Fifo1);
|
||||
}
|
||||
}
|
||||
|
||||
/// SCE interrupt handler.
|
||||
pub struct SceInterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
// info!("sce irq");
|
||||
let msr = T::regs().msr();
|
||||
let msr_val = msr.read();
|
||||
|
||||
if msr_val.erri() {
|
||||
msr.modify(|v| v.set_erri(true));
|
||||
T::state().err_waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver
|
||||
pub struct Can<'d, T: Instance> {
|
||||
can: crate::can::bx::Can<BxcanInstance<'d, T>>,
|
||||
}
|
||||
|
||||
/// Error returned by `try_read`
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TryReadError {
|
||||
/// Bus error
|
||||
BusError(BusError),
|
||||
/// Receive buffer is empty
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Error returned by `try_write`
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TryWriteError {
|
||||
/// All transmit mailboxes are full
|
||||
Full,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Can<'d, T> {
|
||||
/// Creates a new Bxcan instance, keeping the peripheral in sleep mode.
|
||||
/// You must call [Can::enable_non_blocking] to use the peripheral.
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
_irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>>
|
||||
+ 'd,
|
||||
) -> Self {
|
||||
into_ref!(peri, rx, tx);
|
||||
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
|
||||
T::enable_and_reset();
|
||||
|
||||
{
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_errie(true);
|
||||
w.set_fmpie(0, true);
|
||||
w.set_fmpie(1, true);
|
||||
w.set_tmeie(true);
|
||||
});
|
||||
|
||||
T::regs().mcr().write(|w| {
|
||||
// Enable timestamps on rx messages
|
||||
|
||||
w.set_ttcm(true);
|
||||
});
|
||||
}
|
||||
|
||||
unsafe {
|
||||
T::TXInterrupt::unpend();
|
||||
T::TXInterrupt::enable();
|
||||
|
||||
T::RX0Interrupt::unpend();
|
||||
T::RX0Interrupt::enable();
|
||||
|
||||
T::RX1Interrupt::unpend();
|
||||
T::RX1Interrupt::enable();
|
||||
|
||||
T::SCEInterrupt::unpend();
|
||||
T::SCEInterrupt::enable();
|
||||
}
|
||||
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
|
||||
let can = crate::can::bx::Can::builder(BxcanInstance(peri), T::regs()).leave_disabled();
|
||||
Self { can }
|
||||
}
|
||||
|
||||
/// Set CAN bit rate.
|
||||
pub fn set_bitrate(&mut self, bitrate: u32) {
|
||||
let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap();
|
||||
self.can.modify_config().set_bit_timing(bit_timing).leave_disabled();
|
||||
}
|
||||
|
||||
/// Enables the peripheral and synchronizes with the bus.
|
||||
///
|
||||
/// This will wait for 11 consecutive recessive bits (bus idle state).
|
||||
/// Contrary to enable method from bxcan library, this will not freeze the executor while waiting.
|
||||
pub async fn enable(&mut self) {
|
||||
while self.enable_non_blocking().is_err() {
|
||||
// SCE interrupt is only generated for entering sleep mode, but not leaving.
|
||||
// Yield to allow other tasks to execute while can bus is initializing.
|
||||
embassy_futures::yield_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues the message to be sent.
|
||||
///
|
||||
/// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
|
||||
pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus {
|
||||
self.split().0.write(frame).await
|
||||
}
|
||||
|
||||
/// Attempts to transmit a frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
||||
pub fn try_write(&mut self, frame: &Frame) -> Result<crate::can::bx::TransmitStatus, TryWriteError> {
|
||||
self.split().0.try_write(frame)
|
||||
}
|
||||
|
||||
/// Waits for a specific transmit mailbox to become empty
|
||||
pub async fn flush(&self, mb: crate::can::bx::Mailbox) {
|
||||
CanTx::<T>::flush_inner(mb).await
|
||||
}
|
||||
|
||||
/// Waits until any of the transmit mailboxes become empty
|
||||
pub async fn flush_any(&self) {
|
||||
CanTx::<T>::flush_any_inner().await
|
||||
}
|
||||
|
||||
/// Waits until all of the transmit mailboxes become empty
|
||||
pub async fn flush_all(&self) {
|
||||
CanTx::<T>::flush_all_inner().await
|
||||
}
|
||||
|
||||
/// Read a CAN frame.
|
||||
///
|
||||
/// If no CAN frame is in the RX buffer, this will wait until there is one.
|
||||
///
|
||||
/// Returns a tuple of the time the message was received and the message frame
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
self.split().1.read().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
self.split().1.try_read()
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
self.split().1.wait_not_empty().await
|
||||
}
|
||||
|
||||
unsafe fn receive_fifo(fifo: RxFifo) {
|
||||
// Generate timestamp as early as possible
|
||||
#[cfg(feature = "time")]
|
||||
let ts = embassy_time::Instant::now();
|
||||
|
||||
let state = T::state();
|
||||
let regs = T::regs();
|
||||
let fifo_idx = match fifo {
|
||||
RxFifo::Fifo0 => 0usize,
|
||||
RxFifo::Fifo1 => 1usize,
|
||||
};
|
||||
let rfr = regs.rfr(fifo_idx);
|
||||
let fifo = regs.rx(fifo_idx);
|
||||
|
||||
loop {
|
||||
// If there are no pending messages, there is nothing to do
|
||||
if rfr.read().fmp() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let rir = fifo.rir().read();
|
||||
let id: embedded_can::Id = if rir.ide() == Ide::STANDARD {
|
||||
embedded_can::StandardId::new(rir.stid()).unwrap().into()
|
||||
} else {
|
||||
let stid = (rir.stid() & 0x7FF) as u32;
|
||||
let exid = rir.exid() & 0x3FFFF;
|
||||
let id = (stid << 18) | (exid);
|
||||
embedded_can::ExtendedId::new(id).unwrap().into()
|
||||
};
|
||||
let data_len = fifo.rdtr().read().dlc();
|
||||
let mut data: [u8; 8] = [0; 8];
|
||||
data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes());
|
||||
|
||||
let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap();
|
||||
let envelope = Envelope {
|
||||
#[cfg(feature = "time")]
|
||||
ts,
|
||||
frame,
|
||||
};
|
||||
|
||||
rfr.modify(|v| v.set_rfom(true));
|
||||
|
||||
/*
|
||||
NOTE: consensus was reached that if rx_queue is full, packets should be dropped
|
||||
*/
|
||||
let _ = state.rx_queue.try_send(envelope);
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the CAN driver into transmit and receive halves.
|
||||
///
|
||||
/// Useful for doing separate transmit/receive tasks.
|
||||
pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) {
|
||||
let (tx, rx0, rx1) = self.can.split_by_ref();
|
||||
(CanTx { tx }, CanRx { rx0, rx1 })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> AsMut<crate::can::bx::Can<BxcanInstance<'d, T>>> for Can<'d, T> {
|
||||
/// Get mutable access to the lower-level driver from the `bxcan` crate.
|
||||
fn as_mut(&mut self) -> &mut crate::can::bx::Can<BxcanInstance<'d, T>> {
|
||||
&mut self.can
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver, transmit half.
|
||||
pub struct CanTx<'d, T: Instance> {
|
||||
tx: crate::can::bx::Tx<BxcanInstance<'d, T>>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> CanTx<'d, T> {
|
||||
/// Queues the message to be sent.
|
||||
///
|
||||
/// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
|
||||
pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
if let Ok(status) = self.tx.transmit(frame) {
|
||||
return Poll::Ready(status);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Attempts to transmit a frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
||||
pub fn try_write(&mut self, frame: &Frame) -> Result<crate::can::bx::TransmitStatus, TryWriteError> {
|
||||
self.tx.transmit(frame).map_err(|_| TryWriteError::Full)
|
||||
}
|
||||
|
||||
async fn flush_inner(mb: crate::can::bx::Mailbox) {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
if T::regs().tsr().read().tme(mb.index()) {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Waits for a specific transmit mailbox to become empty
|
||||
pub async fn flush(&self, mb: crate::can::bx::Mailbox) {
|
||||
Self::flush_inner(mb).await
|
||||
}
|
||||
|
||||
async fn flush_any_inner() {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
|
||||
let tsr = T::regs().tsr().read();
|
||||
if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index())
|
||||
|| tsr.tme(crate::can::bx::Mailbox::Mailbox1.index())
|
||||
|| tsr.tme(crate::can::bx::Mailbox::Mailbox2.index())
|
||||
{
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Waits until any of the transmit mailboxes become empty
|
||||
pub async fn flush_any(&self) {
|
||||
Self::flush_any_inner().await
|
||||
}
|
||||
|
||||
async fn flush_all_inner() {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
|
||||
let tsr = T::regs().tsr().read();
|
||||
if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index())
|
||||
&& tsr.tme(crate::can::bx::Mailbox::Mailbox1.index())
|
||||
&& tsr.tme(crate::can::bx::Mailbox::Mailbox2.index())
|
||||
{
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Waits until all of the transmit mailboxes become empty
|
||||
pub async fn flush_all(&self) {
|
||||
Self::flush_all_inner().await
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver, receive half.
|
||||
#[allow(dead_code)]
|
||||
pub struct CanRx<'d, T: Instance> {
|
||||
rx0: crate::can::bx::Rx0<BxcanInstance<'d, T>>,
|
||||
rx1: crate::can::bx::Rx1<BxcanInstance<'d, T>>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> CanRx<'d, T> {
|
||||
/// Read a CAN frame.
|
||||
///
|
||||
/// If no CAN frame is in the RX buffer, this will wait until there is one.
|
||||
///
|
||||
/// Returns a tuple of the time the message was received and the message frame
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
poll_fn(|cx| {
|
||||
T::state().err_waker.register(cx.waker());
|
||||
if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) {
|
||||
return Poll::Ready(Ok(envelope));
|
||||
} else if let Some(err) = self.curr_error() {
|
||||
return Poll::Ready(Err(err));
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
if let Ok(envelope) = T::state().rx_queue.try_receive() {
|
||||
return Ok(envelope);
|
||||
}
|
||||
|
||||
if let Some(err) = self.curr_error() {
|
||||
return Err(TryReadError::BusError(err));
|
||||
}
|
||||
|
||||
Err(TryReadError::Empty)
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await
|
||||
}
|
||||
|
||||
fn curr_error(&self) -> Option<BusError> {
|
||||
let err = { T::regs().esr().read() };
|
||||
if err.boff() {
|
||||
return Some(BusError::BusOff);
|
||||
} else if err.epvf() {
|
||||
return Some(BusError::BusPassive);
|
||||
} else if err.ewgf() {
|
||||
return Some(BusError::BusWarning);
|
||||
} else if let Some(err) = err.lec().into_bus_err() {
|
||||
return Some(err);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
enum RxFifo {
|
||||
Fifo0,
|
||||
Fifo1,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Can<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
// Cannot call `free()` because it moves the instance.
|
||||
// Manually reset the peripheral.
|
||||
T::regs().mcr().write(|w| w.set_reset(true));
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Deref for Can<'d, T> {
|
||||
type Target = crate::can::bx::Can<BxcanInstance<'d, T>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.can
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> DerefMut for Can<'d, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.can
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use super::Envelope;
|
||||
|
||||
pub struct State {
|
||||
pub tx_waker: AtomicWaker,
|
||||
pub err_waker: AtomicWaker,
|
||||
pub rx_queue: Channel<CriticalSectionRawMutex, Envelope, 32>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
tx_waker: AtomicWaker::new(),
|
||||
err_waker: AtomicWaker::new(),
|
||||
rx_queue: Channel::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
fn regs() -> crate::pac::can::Can;
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN instance trait.
|
||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {
|
||||
/// TX interrupt for this instance.
|
||||
type TXInterrupt: crate::interrupt::typelevel::Interrupt;
|
||||
/// RX0 interrupt for this instance.
|
||||
type RX0Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
/// RX1 interrupt for this instance.
|
||||
type RX1Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
/// SCE interrupt for this instance.
|
||||
type SCEInterrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
/// BXCAN instance newtype.
|
||||
pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>);
|
||||
|
||||
unsafe impl<'d, T: Instance> crate::can::bx::Instance for BxcanInstance<'d, T> {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(can, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
|
||||
fn regs() -> crate::pac::can::Can {
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
||||
fn state() -> &'static sealed::State {
|
||||
static STATE: sealed::State = sealed::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {
|
||||
type TXInterrupt = crate::_generated::peripheral_interrupts::$inst::TX;
|
||||
type RX0Interrupt = crate::_generated::peripheral_interrupts::$inst::RX0;
|
||||
type RX1Interrupt = crate::_generated::peripheral_interrupts::$inst::RX1;
|
||||
type SCEInterrupt = crate::_generated::peripheral_interrupts::$inst::SCE;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
foreach_peripheral!(
|
||||
(can, CAN) => {
|
||||
unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN> {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
};
|
||||
// CAN1 and CAN2 is a combination of master and slave instance.
|
||||
// CAN1 owns the filter bank and needs to be enabled in order
|
||||
// for CAN2 to receive messages.
|
||||
(can, CAN1) => {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(
|
||||
any(stm32l4, stm32f72, stm32f73),
|
||||
not(any(stm32l49, stm32l4a))
|
||||
))] {
|
||||
// Most L4 devices and some F7 devices use the name "CAN1"
|
||||
// even if there is no "CAN2" peripheral.
|
||||
unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN1> {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
} else {
|
||||
unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN1> {
|
||||
const NUM_FILTER_BANKS: u8 = 28;
|
||||
}
|
||||
unsafe impl<'d> crate::can::bx::MasterInstance for BxcanInstance<'d, peripherals::CAN1> {}
|
||||
}
|
||||
}
|
||||
};
|
||||
(can, CAN3) => {
|
||||
unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN3> {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
pin_trait!(RxPin, Instance);
|
||||
pin_trait!(TxPin, Instance);
|
||||
|
||||
trait Index {
|
||||
fn index(&self) -> usize;
|
||||
}
|
||||
|
||||
impl Index for crate::can::bx::Mailbox {
|
||||
fn index(&self) -> usize {
|
||||
match self {
|
||||
crate::can::bx::Mailbox::Mailbox0 => 0,
|
||||
crate::can::bx::Mailbox::Mailbox1 => 1,
|
||||
crate::can::bx::Mailbox::Mailbox2 => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait IntoBusError {
|
||||
fn into_bus_err(self) -> Option<BusError>;
|
||||
}
|
||||
|
||||
impl IntoBusError for Lec {
|
||||
fn into_bus_err(self) -> Option<BusError> {
|
||||
match self {
|
||||
Lec::STUFF => Some(BusError::Stuff),
|
||||
Lec::FORM => Some(BusError::Form),
|
||||
Lec::ACK => Some(BusError::Acknowledge),
|
||||
Lec::BITRECESSIVE => Some(BusError::BitRecessive),
|
||||
Lec::BITDOMINANT => Some(BusError::BitDominant),
|
||||
Lec::CRC => Some(BusError::Crc),
|
||||
Lec::CUSTOM => Some(BusError::Software),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::can::bx::{ExtendedId, Fifo, FilterOwner, Id, Instance, MasterInstance, StandardId};
|
||||
use super::{ExtendedId, Fifo, FilterOwner, Id, Instance, MasterInstance, StandardId};
|
||||
|
||||
const F32_RTR: u32 = 0b010; // set the RTR bit to match remote frames
|
||||
const F32_IDE: u32 = 0b100; // set the IDE bit to match extended identifiers
|
989
embassy-stm32/src/can/bxcan/mod.rs
Normal file
989
embassy-stm32/src/can/bxcan/mod.rs
Normal file
@ -0,0 +1,989 @@
|
||||
pub mod filter;
|
||||
mod registers;
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
pub use embedded_can::{ExtendedId, Id, StandardId};
|
||||
|
||||
use self::filter::MasterFilters;
|
||||
use self::registers::{Registers, RxFifo};
|
||||
pub use super::common::{BufferedCanReceiver, BufferedCanSender};
|
||||
use super::frame::{Envelope, Frame};
|
||||
use super::util;
|
||||
use crate::can::enums::{BusError, TryReadError};
|
||||
use crate::gpio::AFType;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{interrupt, peripherals, Peripheral};
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct TxInterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::TXInterrupt> for TxInterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
T::regs().tsr().write(|v| {
|
||||
v.set_rqcp(0, true);
|
||||
v.set_rqcp(1, true);
|
||||
v.set_rqcp(2, true);
|
||||
});
|
||||
T::state().tx_mode.on_interrupt::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
/// RX0 interrupt handler.
|
||||
pub struct Rx0InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo0);
|
||||
}
|
||||
}
|
||||
|
||||
/// RX1 interrupt handler.
|
||||
pub struct Rx1InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo1);
|
||||
}
|
||||
}
|
||||
|
||||
/// SCE interrupt handler.
|
||||
pub struct SceInterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
// info!("sce irq");
|
||||
let msr = T::regs().msr();
|
||||
let msr_val = msr.read();
|
||||
|
||||
if msr_val.erri() {
|
||||
msr.modify(|v| v.set_erri(true));
|
||||
T::state().err_waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration proxy returned by [`Can::modify_config`].
|
||||
pub struct CanConfig<'a, T: Instance> {
|
||||
can: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> CanConfig<'_, T> {
|
||||
/// 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.
|
||||
pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self {
|
||||
Registers(T::regs()).set_bit_timing(bt);
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure the CAN bit rate.
|
||||
///
|
||||
/// This is a helper that internally calls `set_bit_timing()`[Self::set_bit_timing].
|
||||
pub fn set_bitrate(self, bitrate: u32) -> Self {
|
||||
let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap();
|
||||
self.set_bit_timing(bit_timing)
|
||||
}
|
||||
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(self, enabled: bool) -> Self {
|
||||
Registers(T::regs()).set_loopback(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(self, enabled: bool) -> Self {
|
||||
Registers(T::regs()).set_silent(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables automatic retransmission of messages.
|
||||
///
|
||||
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
||||
/// until it can be sent. Otherwise, it will try only once to send each frame.
|
||||
///
|
||||
/// Automatic retransmission is enabled by default.
|
||||
pub fn set_automatic_retransmit(self, enabled: bool) -> Self {
|
||||
Registers(T::regs()).set_automatic_retransmit(enabled);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> Drop for CanConfig<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
Registers(T::regs()).leave_init_mode();
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver
|
||||
pub struct Can<'d, T: Instance> {
|
||||
peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
/// Error returned by `try_write`
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TryWriteError {
|
||||
/// All transmit mailboxes are full
|
||||
Full,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Can<'d, T> {
|
||||
/// Creates a new Bxcan instance, keeping the peripheral in sleep mode.
|
||||
/// You must call [Can::enable_non_blocking] to use the peripheral.
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
_irqs: impl interrupt::typelevel::Binding<T::TXInterrupt, TxInterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::RX0Interrupt, Rx0InterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::RX1Interrupt, Rx1InterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::SCEInterrupt, SceInterruptHandler<T>>
|
||||
+ 'd,
|
||||
) -> Self {
|
||||
into_ref!(peri, rx, tx);
|
||||
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
|
||||
T::enable_and_reset();
|
||||
|
||||
{
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_errie(true);
|
||||
w.set_fmpie(0, true);
|
||||
w.set_fmpie(1, true);
|
||||
w.set_tmeie(true);
|
||||
});
|
||||
|
||||
T::regs().mcr().write(|w| {
|
||||
// Enable timestamps on rx messages
|
||||
|
||||
w.set_ttcm(true);
|
||||
});
|
||||
}
|
||||
|
||||
unsafe {
|
||||
T::TXInterrupt::unpend();
|
||||
T::TXInterrupt::enable();
|
||||
|
||||
T::RX0Interrupt::unpend();
|
||||
T::RX0Interrupt::enable();
|
||||
|
||||
T::RX1Interrupt::unpend();
|
||||
T::RX1Interrupt::enable();
|
||||
|
||||
T::SCEInterrupt::unpend();
|
||||
T::SCEInterrupt::enable();
|
||||
}
|
||||
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
|
||||
Registers(T::regs()).leave_init_mode();
|
||||
|
||||
Self { peri }
|
||||
}
|
||||
|
||||
/// Set CAN bit rate.
|
||||
pub fn set_bitrate(&mut self, bitrate: u32) {
|
||||
let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap();
|
||||
self.modify_config().set_bit_timing(bit_timing);
|
||||
}
|
||||
|
||||
/// Configure bit timings and silent/loop-back mode.
|
||||
///
|
||||
/// Calling this method will enter initialization mode. You must enable the peripheral
|
||||
/// again afterwards with [`enable`](Self::enable).
|
||||
pub fn modify_config(&mut self) -> CanConfig<'_, T> {
|
||||
Registers(T::regs()).enter_init_mode();
|
||||
|
||||
CanConfig { can: PhantomData }
|
||||
}
|
||||
|
||||
/// Enables the peripheral and synchronizes with the bus.
|
||||
///
|
||||
/// This will wait for 11 consecutive recessive bits (bus idle state).
|
||||
/// Contrary to enable method from bxcan library, this will not freeze the executor while waiting.
|
||||
pub async fn enable(&mut self) {
|
||||
while Registers(T::regs()).enable_non_blocking().is_err() {
|
||||
// SCE interrupt is only generated for entering sleep mode, but not leaving.
|
||||
// Yield to allow other tasks to execute while can bus is initializing.
|
||||
embassy_futures::yield_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues the message to be sent.
|
||||
///
|
||||
/// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
|
||||
pub async fn write(&mut self, frame: &Frame) -> TransmitStatus {
|
||||
self.split().0.write(frame).await
|
||||
}
|
||||
|
||||
/// Attempts to transmit a frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
||||
pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> {
|
||||
self.split().0.try_write(frame)
|
||||
}
|
||||
|
||||
/// Waits for a specific transmit mailbox to become empty
|
||||
pub async fn flush(&self, mb: Mailbox) {
|
||||
CanTx::<T>::flush_inner(mb).await
|
||||
}
|
||||
|
||||
/// Waits until any of the transmit mailboxes become empty
|
||||
pub async fn flush_any(&self) {
|
||||
CanTx::<T>::flush_any_inner().await
|
||||
}
|
||||
|
||||
/// Waits until all of the transmit mailboxes become empty
|
||||
pub async fn flush_all(&self) {
|
||||
CanTx::<T>::flush_all_inner().await
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
pub fn abort(&mut self, mailbox: Mailbox) -> bool {
|
||||
Registers(T::regs()).abort(mailbox)
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_transmitter_idle(&self) -> bool {
|
||||
Registers(T::regs()).is_idle()
|
||||
}
|
||||
|
||||
/// Read a CAN frame.
|
||||
///
|
||||
/// If no CAN frame is in the RX buffer, this will wait until there is one.
|
||||
///
|
||||
/// Returns a tuple of the time the message was received and the message frame
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
T::state().rx_mode.read::<T>().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
T::state().rx_mode.try_read::<T>()
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
T::state().rx_mode.wait_not_empty::<T>().await
|
||||
}
|
||||
|
||||
/// Split the CAN driver into transmit and receive halves.
|
||||
///
|
||||
/// Useful for doing separate transmit/receive tasks.
|
||||
pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) {
|
||||
(
|
||||
CanTx {
|
||||
_peri: unsafe { self.peri.clone_unchecked() },
|
||||
},
|
||||
CanRx {
|
||||
peri: unsafe { self.peri.clone_unchecked() },
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a buffered instance of driver. User must supply Buffers
|
||||
pub fn buffered<'c, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>(
|
||||
&'c mut self,
|
||||
txb: &'static mut TxBuf<TX_BUF_SIZE>,
|
||||
rxb: &'static mut RxBuf<RX_BUF_SIZE>,
|
||||
) -> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> {
|
||||
let (tx, rx) = self.split();
|
||||
BufferedCan {
|
||||
tx: tx.buffered(txb),
|
||||
rx: rx.buffered(rxb),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: FilterOwner> Can<'d, T> {
|
||||
/// Accesses the filter banks owned by this CAN peripheral.
|
||||
///
|
||||
/// To modify filters of a slave peripheral, `modify_filters` has to be called on the master
|
||||
/// peripheral instead.
|
||||
pub fn modify_filters(&mut self) -> MasterFilters<'_, T> {
|
||||
unsafe { MasterFilters::new(T::regs()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffered CAN driver.
|
||||
pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
|
||||
tx: BufferedCanTx<'d, T, TX_BUF_SIZE>,
|
||||
rx: BufferedCanRx<'d, T, RX_BUF_SIZE>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> {
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: &Frame) {
|
||||
self.tx.write(frame).await
|
||||
}
|
||||
|
||||
/// Returns a sender that can be used for sending CAN frames.
|
||||
pub fn writer(&self) -> BufferedCanSender {
|
||||
self.tx.writer()
|
||||
}
|
||||
|
||||
/// Async read frame from RX buffer.
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
self.rx.read().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
self.rx.try_read()
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
self.rx.wait_not_empty().await
|
||||
}
|
||||
|
||||
/// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub fn reader(&self) -> BufferedCanReceiver {
|
||||
self.rx.reader()
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver, transmit half.
|
||||
pub struct CanTx<'d, T: Instance> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> CanTx<'d, T> {
|
||||
/// Queues the message to be sent.
|
||||
///
|
||||
/// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
|
||||
pub async fn write(&mut self, frame: &Frame) -> TransmitStatus {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
if let Ok(status) = Registers(T::regs()).transmit(frame) {
|
||||
return Poll::Ready(status);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Attempts to transmit a frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
||||
pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> {
|
||||
Registers(T::regs()).transmit(frame).map_err(|_| TryWriteError::Full)
|
||||
}
|
||||
|
||||
async fn flush_inner(mb: Mailbox) {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
if T::regs().tsr().read().tme(mb.index()) {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Waits for a specific transmit mailbox to become empty
|
||||
pub async fn flush(&self, mb: Mailbox) {
|
||||
Self::flush_inner(mb).await
|
||||
}
|
||||
|
||||
async fn flush_any_inner() {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
|
||||
let tsr = T::regs().tsr().read();
|
||||
if tsr.tme(Mailbox::Mailbox0.index())
|
||||
|| tsr.tme(Mailbox::Mailbox1.index())
|
||||
|| tsr.tme(Mailbox::Mailbox2.index())
|
||||
{
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Waits until any of the transmit mailboxes become empty
|
||||
pub async fn flush_any(&self) {
|
||||
Self::flush_any_inner().await
|
||||
}
|
||||
|
||||
async fn flush_all_inner() {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
|
||||
let tsr = T::regs().tsr().read();
|
||||
if tsr.tme(Mailbox::Mailbox0.index())
|
||||
&& tsr.tme(Mailbox::Mailbox1.index())
|
||||
&& tsr.tme(Mailbox::Mailbox2.index())
|
||||
{
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Waits until all of the transmit mailboxes become empty
|
||||
pub async fn flush_all(&self) {
|
||||
Self::flush_all_inner().await
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
pub fn abort(&mut self, mailbox: Mailbox) -> bool {
|
||||
Registers(T::regs()).abort(mailbox)
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_idle(&self) -> bool {
|
||||
Registers(T::regs()).is_idle()
|
||||
}
|
||||
|
||||
/// Return a buffered instance of driver. User must supply Buffers
|
||||
pub fn buffered<const TX_BUF_SIZE: usize>(
|
||||
self,
|
||||
txb: &'static mut TxBuf<TX_BUF_SIZE>,
|
||||
) -> BufferedCanTx<'d, T, TX_BUF_SIZE> {
|
||||
BufferedCanTx::new(self, txb)
|
||||
}
|
||||
}
|
||||
|
||||
/// User supplied buffer for TX buffering
|
||||
pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>;
|
||||
|
||||
/// Buffered CAN driver, transmit half.
|
||||
pub struct BufferedCanTx<'d, T: Instance, const TX_BUF_SIZE: usize> {
|
||||
_tx: CanTx<'d, T>,
|
||||
tx_buf: &'static TxBuf<TX_BUF_SIZE>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX_BUF_SIZE: usize> BufferedCanTx<'d, T, TX_BUF_SIZE> {
|
||||
fn new(_tx: CanTx<'d, T>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self {
|
||||
Self { _tx, tx_buf }.setup()
|
||||
}
|
||||
|
||||
fn setup(self) -> Self {
|
||||
// We don't want interrupts being processed while we change modes.
|
||||
critical_section::with(|_| unsafe {
|
||||
let tx_inner = super::common::ClassicBufferedTxInner {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().tx_mode = TxMode::Buffered(tx_inner);
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: &Frame) {
|
||||
self.tx_buf.send(*frame).await;
|
||||
T::TXInterrupt::pend(); // Wake for Tx
|
||||
}
|
||||
|
||||
/// Returns a sender that can be used for sending CAN frames.
|
||||
pub fn writer(&self) -> BufferedCanSender {
|
||||
BufferedCanSender {
|
||||
tx_buf: self.tx_buf.sender().into(),
|
||||
waker: T::TXInterrupt::pend,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, T, TX_BUF_SIZE> {
|
||||
fn drop(&mut self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver, receive half.
|
||||
#[allow(dead_code)]
|
||||
pub struct CanRx<'d, T: Instance> {
|
||||
peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> CanRx<'d, T> {
|
||||
/// Read a CAN frame.
|
||||
///
|
||||
/// If no CAN frame is in the RX buffer, this will wait until there is one.
|
||||
///
|
||||
/// Returns a tuple of the time the message was received and the message frame
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
T::state().rx_mode.read::<T>().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
T::state().rx_mode.try_read::<T>()
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
T::state().rx_mode.wait_not_empty::<T>().await
|
||||
}
|
||||
|
||||
/// Return a buffered instance of driver. User must supply Buffers
|
||||
pub fn buffered<const RX_BUF_SIZE: usize>(
|
||||
self,
|
||||
rxb: &'static mut RxBuf<RX_BUF_SIZE>,
|
||||
) -> BufferedCanRx<'d, T, RX_BUF_SIZE> {
|
||||
BufferedCanRx::new(self, rxb)
|
||||
}
|
||||
}
|
||||
|
||||
/// User supplied buffer for RX Buffering
|
||||
pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>;
|
||||
|
||||
/// CAN driver, receive half in Buffered mode.
|
||||
pub struct BufferedCanRx<'d, T: Instance, const RX_BUF_SIZE: usize> {
|
||||
_rx: CanRx<'d, T>,
|
||||
rx_buf: &'static RxBuf<RX_BUF_SIZE>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE> {
|
||||
fn new(_rx: CanRx<'d, T>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self {
|
||||
BufferedCanRx { _rx, rx_buf }.setup()
|
||||
}
|
||||
|
||||
fn setup(self) -> Self {
|
||||
// We don't want interrupts being processed while we change modes.
|
||||
critical_section::with(|_| unsafe {
|
||||
let rx_inner = super::common::ClassicBufferedRxInner {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = RxMode::Buffered(rx_inner);
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Async read frame from RX buffer.
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
self.rx_buf.receive().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
match &T::state().rx_mode {
|
||||
RxMode::Buffered(_) => {
|
||||
if let Ok(result) = self.rx_buf.try_receive() {
|
||||
match result {
|
||||
Ok(envelope) => Ok(envelope),
|
||||
Err(e) => Err(TryReadError::BusError(e)),
|
||||
}
|
||||
} else {
|
||||
if let Some(err) = Registers(T::regs()).curr_error() {
|
||||
return Err(TryReadError::BusError(err));
|
||||
} else {
|
||||
Err(TryReadError::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
poll_fn(|cx| self.rx_buf.poll_ready_to_receive(cx)).await
|
||||
}
|
||||
|
||||
/// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub fn reader(&self) -> BufferedCanReceiver {
|
||||
self.rx_buf.receiver().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, T, RX_BUF_SIZE> {
|
||||
fn drop(&mut self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Can<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
// Cannot call `free()` because it moves the instance.
|
||||
// Manually reset the peripheral.
|
||||
T::regs().mcr().write(|w| w.set_reset(true));
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifies one of the two receive FIFOs.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Fifo {
|
||||
/// First receive FIFO
|
||||
Fifo0 = 0,
|
||||
/// Second receive FIFO
|
||||
Fifo1 = 1,
|
||||
}
|
||||
|
||||
/// Identifies one of the three transmit mailboxes.
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Mailbox {
|
||||
/// Transmit mailbox 0
|
||||
Mailbox0 = 0,
|
||||
/// Transmit mailbox 1
|
||||
Mailbox1 = 1,
|
||||
/// Transmit mailbox 2
|
||||
Mailbox2 = 2,
|
||||
}
|
||||
|
||||
/// Contains information about a frame enqueued for transmission via [`Can::transmit`] or
|
||||
/// [`Tx::transmit`].
|
||||
pub struct TransmitStatus {
|
||||
dequeued_frame: Option<Frame>,
|
||||
mailbox: Mailbox,
|
||||
}
|
||||
|
||||
impl TransmitStatus {
|
||||
/// Returns the lower-priority frame that was dequeued to make space for the new frame.
|
||||
#[inline]
|
||||
pub fn dequeued_frame(&self) -> Option<&Frame> {
|
||||
self.dequeued_frame.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the [`Mailbox`] the frame was enqueued in.
|
||||
#[inline]
|
||||
pub fn mailbox(&self) -> Mailbox {
|
||||
self.mailbox
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum RxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
Buffered(super::common::ClassicBufferedRxInner),
|
||||
}
|
||||
|
||||
impl RxMode {
|
||||
pub fn on_interrupt<T: Instance>(&self, fifo: RxFifo) {
|
||||
match self {
|
||||
Self::NonBuffered(waker) => {
|
||||
// Disable interrupts until read
|
||||
let fifo_idx = match fifo {
|
||||
RxFifo::Fifo0 => 0usize,
|
||||
RxFifo::Fifo1 => 1usize,
|
||||
};
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(fifo_idx, false);
|
||||
});
|
||||
waker.wake();
|
||||
}
|
||||
Self::Buffered(buf) => {
|
||||
loop {
|
||||
match Registers(T::regs()).receive_fifo(fifo) {
|
||||
Some(envelope) => {
|
||||
// NOTE: consensus was reached that if rx_queue is full, packets should be dropped
|
||||
let _ = buf.rx_sender.try_send(Ok(envelope));
|
||||
}
|
||||
None => return,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read<T: Instance>(&self) -> Result<Envelope, BusError> {
|
||||
match self {
|
||||
Self::NonBuffered(waker) => {
|
||||
poll_fn(|cx| {
|
||||
T::state().err_waker.register(cx.waker());
|
||||
waker.register(cx.waker());
|
||||
match self.try_read::<T>() {
|
||||
Ok(result) => Poll::Ready(Ok(result)),
|
||||
Err(TryReadError::Empty) => Poll::Pending,
|
||||
Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)),
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn try_read<T: Instance>(&self) -> Result<Envelope, TryReadError> {
|
||||
match self {
|
||||
Self::NonBuffered(_) => {
|
||||
let registers = Registers(T::regs());
|
||||
if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) {
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(0, true);
|
||||
});
|
||||
Ok(msg)
|
||||
} else if let Some(msg) = registers.receive_fifo(RxFifo::Fifo1) {
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(1, true);
|
||||
});
|
||||
Ok(msg)
|
||||
} else if let Some(err) = registers.curr_error() {
|
||||
Err(TryReadError::BusError(err))
|
||||
} else {
|
||||
Err(TryReadError::Empty)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn wait_not_empty<T: Instance>(&self) {
|
||||
match &T::state().rx_mode {
|
||||
Self::NonBuffered(waker) => {
|
||||
poll_fn(|cx| {
|
||||
waker.register(cx.waker());
|
||||
if Registers(T::regs()).receive_frame_available() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
Buffered(super::common::ClassicBufferedTxInner),
|
||||
}
|
||||
|
||||
impl TxMode {
|
||||
pub fn buffer_free<T: Instance>(&self) -> bool {
|
||||
let tsr = T::regs().tsr().read();
|
||||
tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index())
|
||||
}
|
||||
pub fn on_interrupt<T: Instance>(&self) {
|
||||
match &T::state().tx_mode {
|
||||
TxMode::NonBuffered(waker) => waker.wake(),
|
||||
TxMode::Buffered(buf) => {
|
||||
while self.buffer_free::<T>() {
|
||||
match buf.tx_receiver.try_receive() {
|
||||
Ok(frame) => {
|
||||
_ = Registers(T::regs()).transmit(&frame);
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn register(&self, arg: &core::task::Waker) {
|
||||
match self {
|
||||
TxMode::NonBuffered(waker) => {
|
||||
waker.register(arg);
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
pub(crate) rx_mode: RxMode,
|
||||
pub(crate) tx_mode: TxMode,
|
||||
pub err_waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
|
||||
tx_mode: TxMode::NonBuffered(AtomicWaker::new()),
|
||||
err_waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait SealedInstance {
|
||||
fn regs() -> crate::pac::can::Can;
|
||||
fn state() -> &'static State;
|
||||
unsafe fn mut_state() -> &'static mut State;
|
||||
}
|
||||
|
||||
/// CAN instance trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral + 'static {
|
||||
/// TX interrupt for this instance.
|
||||
type TXInterrupt: crate::interrupt::typelevel::Interrupt;
|
||||
/// RX0 interrupt for this instance.
|
||||
type RX0Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
/// RX1 interrupt for this instance.
|
||||
type RX1Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
/// SCE interrupt for this instance.
|
||||
type SCEInterrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
/// A bxCAN instance that owns filter banks.
|
||||
///
|
||||
/// In master-slave-instance setups, only the master instance owns the filter banks, and needs to
|
||||
/// split some of them off for use by the slave instance. In that case, the master instance should
|
||||
/// implement [`FilterOwner`] and [`MasterInstance`], while the slave instance should only implement
|
||||
/// [`Instance`].
|
||||
///
|
||||
/// In single-instance configurations, the instance owns all filter banks and they can not be split
|
||||
/// off. In that case, the instance should implement [`Instance`] and [`FilterOwner`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented if the instance does, in fact, own its associated filter
|
||||
/// banks, and `NUM_FILTER_BANKS` must be correct.
|
||||
pub unsafe trait FilterOwner: Instance {
|
||||
/// The total number of filter banks available to the instance.
|
||||
///
|
||||
/// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet.
|
||||
const NUM_FILTER_BANKS: u8;
|
||||
}
|
||||
|
||||
/// A bxCAN master instance that shares filter banks with a slave instance.
|
||||
///
|
||||
/// In master-slave-instance setups, this trait should be implemented for the master instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented when there is actually an associated slave instance.
|
||||
pub unsafe trait MasterInstance: FilterOwner {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(can, $inst:ident) => {
|
||||
impl SealedInstance for peripherals::$inst {
|
||||
|
||||
fn regs() -> crate::pac::can::Can {
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
||||
unsafe fn mut_state() -> & 'static mut State {
|
||||
static mut STATE: State = State::new();
|
||||
&mut *core::ptr::addr_of_mut!(STATE)
|
||||
}
|
||||
fn state() -> &'static State {
|
||||
unsafe { peripherals::$inst::mut_state() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {
|
||||
type TXInterrupt = crate::_generated::peripheral_interrupts::$inst::TX;
|
||||
type RX0Interrupt = crate::_generated::peripheral_interrupts::$inst::RX0;
|
||||
type RX1Interrupt = crate::_generated::peripheral_interrupts::$inst::RX1;
|
||||
type SCEInterrupt = crate::_generated::peripheral_interrupts::$inst::SCE;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
foreach_peripheral!(
|
||||
(can, CAN) => {
|
||||
unsafe impl FilterOwner for peripherals::CAN {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
};
|
||||
// CAN1 and CAN2 is a combination of master and slave instance.
|
||||
// CAN1 owns the filter bank and needs to be enabled in order
|
||||
// for CAN2 to receive messages.
|
||||
(can, CAN1) => {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(
|
||||
any(stm32l4, stm32f72, stm32f73),
|
||||
not(any(stm32l49, stm32l4a))
|
||||
))] {
|
||||
// Most L4 devices and some F7 devices use the name "CAN1"
|
||||
// even if there is no "CAN2" peripheral.
|
||||
unsafe impl FilterOwner for peripherals::CAN1 {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
} else {
|
||||
unsafe impl FilterOwner for peripherals::CAN1 {
|
||||
const NUM_FILTER_BANKS: u8 = 28;
|
||||
}
|
||||
unsafe impl MasterInstance for peripherals::CAN1 {}
|
||||
}
|
||||
}
|
||||
};
|
||||
(can, CAN3) => {
|
||||
unsafe impl FilterOwner for peripherals::CAN3 {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
pin_trait!(RxPin, Instance);
|
||||
pin_trait!(TxPin, Instance);
|
||||
|
||||
trait Index {
|
||||
fn index(&self) -> usize;
|
||||
}
|
||||
|
||||
impl Index for Mailbox {
|
||||
fn index(&self) -> usize {
|
||||
match self {
|
||||
Mailbox::Mailbox0 => 0,
|
||||
Mailbox::Mailbox1 => 1,
|
||||
Mailbox::Mailbox2 => 2,
|
||||
}
|
||||
}
|
||||
}
|
510
embassy-stm32/src/can/bxcan/registers.rs
Normal file
510
embassy-stm32/src/can/bxcan/registers.rs
Normal file
@ -0,0 +1,510 @@
|
||||
use core::cmp::Ordering;
|
||||
use core::convert::Infallible;
|
||||
|
||||
pub use embedded_can::{ExtendedId, Id, StandardId};
|
||||
use stm32_metapac::can::vals::Lec;
|
||||
|
||||
use super::{Mailbox, TransmitStatus};
|
||||
use crate::can::enums::BusError;
|
||||
use crate::can::frame::{Envelope, Frame, Header};
|
||||
|
||||
pub(crate) struct Registers(pub crate::pac::can::Can);
|
||||
|
||||
impl Registers {
|
||||
pub fn enter_init_mode(&mut self) {
|
||||
self.0.mcr().modify(|reg| {
|
||||
reg.set_sleep(false);
|
||||
reg.set_inrq(true);
|
||||
});
|
||||
loop {
|
||||
let msr = self.0.msr().read();
|
||||
if !msr.slak() && msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Leaves initialization mode, enters sleep mode.
|
||||
pub fn leave_init_mode(&mut self) {
|
||||
self.0.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.0.msr().read();
|
||||
if msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) {
|
||||
let prescaler = u16::from(bt.prescaler) & 0x1FF;
|
||||
let seg1 = u8::from(bt.seg1);
|
||||
let seg2 = u8::from(bt.seg2) & 0x7F;
|
||||
let sync_jump_width = u8::from(bt.sync_jump_width) & 0x7F;
|
||||
self.0.btr().modify(|reg| {
|
||||
reg.set_brp(prescaler - 1);
|
||||
reg.set_ts(0, seg1 - 1);
|
||||
reg.set_ts(1, seg2 - 1);
|
||||
reg.set_sjw(sync_jump_width - 1);
|
||||
});
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(&self, enabled: bool) {
|
||||
let mode = match enabled {
|
||||
false => stm32_metapac::can::vals::Silm::NORMAL,
|
||||
true => stm32_metapac::can::vals::Silm::SILENT,
|
||||
};
|
||||
self.0.btr().modify(|reg| reg.set_silm(mode));
|
||||
}
|
||||
|
||||
/// Enables or disables automatic retransmission of messages.
|
||||
///
|
||||
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
||||
/// until it can be sent. Otherwise, it will try only once to send each frame.
|
||||
///
|
||||
/// Automatic retransmission is enabled by default.
|
||||
pub fn set_automatic_retransmit(&self, enabled: bool) {
|
||||
self.0.mcr().modify(|reg| reg.set_nart(enabled));
|
||||
}
|
||||
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(&self, enabled: bool) {
|
||||
self.0.btr().modify(|reg| reg.set_lbkm(enabled));
|
||||
}
|
||||
|
||||
/// Configures the automatic wake-up feature.
|
||||
///
|
||||
/// This is turned off by default.
|
||||
///
|
||||
/// When turned on, an incoming frame will cause the peripheral to wake up from sleep and
|
||||
/// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming
|
||||
/// frame.
|
||||
#[allow(dead_code)]
|
||||
pub fn set_automatic_wakeup(&mut self, enabled: bool) {
|
||||
self.0.mcr().modify(|reg| reg.set_awum(enabled));
|
||||
}
|
||||
|
||||
/// Leaves initialization mode and enables the peripheral (non-blocking version).
|
||||
///
|
||||
/// Usually, it is recommended to call [`CanConfig::enable`] instead. This method is only needed
|
||||
/// if you want non-blocking initialization.
|
||||
///
|
||||
/// If this returns [`WouldBlock`][nb::Error::WouldBlock], the peripheral will enable itself
|
||||
/// in the background. The peripheral is enabled and ready to use when this method returns
|
||||
/// successfully.
|
||||
pub fn enable_non_blocking(&mut self) -> nb::Result<(), Infallible> {
|
||||
let msr = self.0.msr().read();
|
||||
if msr.slak() {
|
||||
self.0.mcr().modify(|reg| {
|
||||
reg.set_abom(true);
|
||||
reg.set_sleep(false);
|
||||
});
|
||||
Err(nb::Error::WouldBlock)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Puts the peripheral in a sleep mode to save power.
|
||||
///
|
||||
/// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled.
|
||||
#[allow(dead_code)]
|
||||
pub fn sleep(&mut self) {
|
||||
self.0.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.0.msr().read();
|
||||
if msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wakes up from sleep mode.
|
||||
///
|
||||
/// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN
|
||||
/// frame will cause that interrupt.
|
||||
#[allow(dead_code)]
|
||||
pub fn wakeup(&mut self) {
|
||||
self.0.mcr().modify(|reg| {
|
||||
reg.set_sleep(false);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.0.msr().read();
|
||||
if !msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn curr_error(&self) -> Option<BusError> {
|
||||
let err = { self.0.esr().read() };
|
||||
if err.boff() {
|
||||
return Some(BusError::BusOff);
|
||||
} else if err.epvf() {
|
||||
return Some(BusError::BusPassive);
|
||||
} else if err.ewgf() {
|
||||
return Some(BusError::BusWarning);
|
||||
} else if err.lec() != Lec::NOERROR {
|
||||
return Some(match err.lec() {
|
||||
Lec::STUFF => BusError::Stuff,
|
||||
Lec::FORM => BusError::Form,
|
||||
Lec::ACK => BusError::Acknowledge,
|
||||
Lec::BITRECESSIVE => BusError::BitRecessive,
|
||||
Lec::BITDOMINANT => BusError::BitDominant,
|
||||
Lec::CRC => BusError::Crc,
|
||||
Lec::CUSTOM => BusError::Software,
|
||||
Lec::NOERROR => unreachable!(),
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Puts a CAN frame in a transmit mailbox for transmission on the bus.
|
||||
///
|
||||
/// Frames are transmitted to the bus based on their priority (see [`FramePriority`]).
|
||||
/// Transmit order is preserved for frames with identical priority.
|
||||
///
|
||||
/// If all transmit mailboxes are full, and `frame` has a higher priority than the
|
||||
/// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is
|
||||
/// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as
|
||||
/// [`TransmitStatus::dequeued_frame`].
|
||||
pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> {
|
||||
// Get the index of the next free mailbox or the one with the lowest priority.
|
||||
let tsr = self.0.tsr().read();
|
||||
let idx = tsr.code() as usize;
|
||||
|
||||
let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2);
|
||||
let pending_frame = if frame_is_pending {
|
||||
// High priority frames are transmitted first by the mailbox system.
|
||||
// Frames with identical identifier shall be transmitted in FIFO order.
|
||||
// The controller schedules pending frames of same priority based on the
|
||||
// mailbox index instead. As a workaround check all pending mailboxes
|
||||
// and only accept higher priority frames.
|
||||
self.check_priority(0, frame.id().into())?;
|
||||
self.check_priority(1, frame.id().into())?;
|
||||
self.check_priority(2, frame.id().into())?;
|
||||
|
||||
let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2);
|
||||
if all_frames_are_pending {
|
||||
// No free mailbox is available. This can only happen when three frames with
|
||||
// ascending priority (descending IDs) were requested for transmission and all
|
||||
// of them are blocked by bus traffic with even higher priority.
|
||||
// To prevent a priority inversion abort and replace the lowest priority frame.
|
||||
self.read_pending_mailbox(idx)
|
||||
} else {
|
||||
// There was a free mailbox.
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// All mailboxes are available: Send frame without performing any checks.
|
||||
None
|
||||
};
|
||||
|
||||
self.write_mailbox(idx, frame);
|
||||
|
||||
let mailbox = match idx {
|
||||
0 => Mailbox::Mailbox0,
|
||||
1 => Mailbox::Mailbox1,
|
||||
2 => Mailbox::Mailbox2,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(TransmitStatus {
|
||||
dequeued_frame: pending_frame,
|
||||
mailbox,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `Ok` when the mailbox is free or if it contains pending frame with a
|
||||
/// lower priority (higher ID) than the identifier `id`.
|
||||
fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> {
|
||||
// Read the pending frame's id to check its priority.
|
||||
assert!(idx < 3);
|
||||
let tir = &self.0.tx(idx).tir().read();
|
||||
//let tir = &can.tx[idx].tir.read();
|
||||
|
||||
// Check the priority by comparing the identifiers. But first make sure the
|
||||
// frame has not finished the transmission (`TXRQ` == 0) in the meantime.
|
||||
if tir.txrq() && id <= IdReg::from_register(tir.0) {
|
||||
// There's a mailbox whose priority is higher or equal
|
||||
// the priority of the new frame.
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_mailbox(&mut self, idx: usize, frame: &Frame) {
|
||||
debug_assert!(idx < 3);
|
||||
|
||||
let mb = self.0.tx(idx);
|
||||
mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8));
|
||||
|
||||
mb.tdlr()
|
||||
.write(|w| w.0 = u32::from_ne_bytes(frame.data()[0..4].try_into().unwrap()));
|
||||
mb.tdhr()
|
||||
.write(|w| w.0 = u32::from_ne_bytes(frame.data()[4..8].try_into().unwrap()));
|
||||
let id: IdReg = frame.id().into();
|
||||
mb.tir().write(|w| {
|
||||
w.0 = id.0;
|
||||
w.set_txrq(true);
|
||||
});
|
||||
}
|
||||
|
||||
fn read_pending_mailbox(&mut self, idx: usize) -> Option<Frame> {
|
||||
if self.abort_by_index(idx) {
|
||||
debug_assert!(idx < 3);
|
||||
|
||||
let mb = self.0.tx(idx);
|
||||
|
||||
let id = IdReg(mb.tir().read().0);
|
||||
let mut data = [0xff; 8];
|
||||
data[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes());
|
||||
let len = mb.tdtr().read().dlc();
|
||||
|
||||
Some(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap())
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to abort a pending frame. Returns `true` when aborted.
|
||||
fn abort_by_index(&mut self, idx: usize) -> bool {
|
||||
self.0.tsr().write(|reg| reg.set_abrq(idx, true));
|
||||
|
||||
// Wait for the abort request to be finished.
|
||||
loop {
|
||||
let tsr = self.0.tsr().read();
|
||||
if false == tsr.abrq(idx) {
|
||||
break tsr.txok(idx) == false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
pub fn abort(&mut self, mailbox: Mailbox) -> bool {
|
||||
// If the mailbox is empty, the value of TXOKx depends on what happened with the previous
|
||||
// frame in that mailbox. Only call abort_by_index() if the mailbox is not empty.
|
||||
let tsr = self.0.tsr().read();
|
||||
let mailbox_empty = match mailbox {
|
||||
Mailbox::Mailbox0 => tsr.tme(0),
|
||||
Mailbox::Mailbox1 => tsr.tme(1),
|
||||
Mailbox::Mailbox2 => tsr.tme(2),
|
||||
};
|
||||
if mailbox_empty {
|
||||
false
|
||||
} else {
|
||||
self.abort_by_index(mailbox as usize)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_idle(&self) -> bool {
|
||||
let tsr = self.0.tsr().read();
|
||||
tsr.tme(0) && tsr.tme(1) && tsr.tme(2)
|
||||
}
|
||||
|
||||
pub fn receive_frame_available(&self) -> bool {
|
||||
if self.0.rfr(0).read().fmp() != 0 {
|
||||
true
|
||||
} else if self.0.rfr(1).read().fmp() != 0 {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_fifo(&self, fifo: RxFifo) -> Option<Envelope> {
|
||||
// Generate timestamp as early as possible
|
||||
#[cfg(feature = "time")]
|
||||
let ts = embassy_time::Instant::now();
|
||||
|
||||
use crate::pac::can::vals::Ide;
|
||||
|
||||
let fifo_idx = match fifo {
|
||||
RxFifo::Fifo0 => 0usize,
|
||||
RxFifo::Fifo1 => 1usize,
|
||||
};
|
||||
let rfr = self.0.rfr(fifo_idx);
|
||||
let fifo = self.0.rx(fifo_idx);
|
||||
|
||||
// If there are no pending messages, there is nothing to do
|
||||
if rfr.read().fmp() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let rir = fifo.rir().read();
|
||||
let id: embedded_can::Id = if rir.ide() == Ide::STANDARD {
|
||||
embedded_can::StandardId::new(rir.stid()).unwrap().into()
|
||||
} else {
|
||||
let stid = (rir.stid() & 0x7FF) as u32;
|
||||
let exid = rir.exid() & 0x3FFFF;
|
||||
let id = (stid << 18) | (exid);
|
||||
embedded_can::ExtendedId::new(id).unwrap().into()
|
||||
};
|
||||
let rdtr = fifo.rdtr().read();
|
||||
let data_len = rdtr.dlc();
|
||||
let rtr = rir.rtr() == stm32_metapac::can::vals::Rtr::REMOTE;
|
||||
|
||||
#[cfg(not(feature = "time"))]
|
||||
let ts = rdtr.time();
|
||||
|
||||
let mut data: [u8; 8] = [0; 8];
|
||||
data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes());
|
||||
|
||||
let frame = Frame::new(Header::new(id, data_len, rtr), &data).unwrap();
|
||||
let envelope = Envelope { ts, frame };
|
||||
|
||||
rfr.modify(|v| v.set_rfom(true));
|
||||
|
||||
Some(envelope)
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier of a CAN message.
|
||||
///
|
||||
/// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a
|
||||
/// extendended identifier (29bit , Range: 0..0x1FFFFFFF).
|
||||
///
|
||||
/// The `Ord` trait can be used to determine the frame’s priority this ID
|
||||
/// belongs to.
|
||||
/// Lower identifier values have a higher priority. Additionally standard frames
|
||||
/// have a higher priority than extended frames and data frames have a higher
|
||||
/// priority than remote frames.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub(crate) struct IdReg(u32);
|
||||
|
||||
impl IdReg {
|
||||
const STANDARD_SHIFT: u32 = 21;
|
||||
|
||||
const EXTENDED_SHIFT: u32 = 3;
|
||||
|
||||
const IDE_MASK: u32 = 0x0000_0004;
|
||||
|
||||
const RTR_MASK: u32 = 0x0000_0002;
|
||||
|
||||
/// Creates a new standard identifier (11bit, Range: 0..0x7FF)
|
||||
///
|
||||
/// Panics for IDs outside the allowed range.
|
||||
fn new_standard(id: StandardId) -> Self {
|
||||
Self(u32::from(id.as_raw()) << Self::STANDARD_SHIFT)
|
||||
}
|
||||
|
||||
/// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF).
|
||||
///
|
||||
/// Panics for IDs outside the allowed range.
|
||||
fn new_extended(id: ExtendedId) -> IdReg {
|
||||
Self(id.as_raw() << Self::EXTENDED_SHIFT | Self::IDE_MASK)
|
||||
}
|
||||
|
||||
fn from_register(reg: u32) -> IdReg {
|
||||
Self(reg & 0xFFFF_FFFE)
|
||||
}
|
||||
|
||||
/// Returns the identifier.
|
||||
fn to_id(self) -> Id {
|
||||
if self.is_extended() {
|
||||
Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::EXTENDED_SHIFT) })
|
||||
} else {
|
||||
Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::STANDARD_SHIFT) as u16) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the identifier.
|
||||
fn id(self) -> embedded_can::Id {
|
||||
if self.is_extended() {
|
||||
embedded_can::ExtendedId::new(self.0 >> Self::EXTENDED_SHIFT)
|
||||
.unwrap()
|
||||
.into()
|
||||
} else {
|
||||
embedded_can::StandardId::new((self.0 >> Self::STANDARD_SHIFT) as u16)
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the identifier is an extended identifier.
|
||||
fn is_extended(self) -> bool {
|
||||
self.0 & Self::IDE_MASK != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the identifer is part of a remote frame (RTR bit set).
|
||||
fn rtr(self) -> bool {
|
||||
self.0 & Self::RTR_MASK != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&embedded_can::Id> for IdReg {
|
||||
fn from(eid: &embedded_can::Id) -> Self {
|
||||
match eid {
|
||||
embedded_can::Id::Standard(id) => IdReg::new_standard(StandardId::new(id.as_raw()).unwrap()),
|
||||
embedded_can::Id::Extended(id) => IdReg::new_extended(ExtendedId::new(id.as_raw()).unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IdReg> for embedded_can::Id {
|
||||
fn from(idr: IdReg) -> Self {
|
||||
idr.id()
|
||||
}
|
||||
}
|
||||
|
||||
/// `IdReg` is ordered by priority.
|
||||
impl Ord for IdReg {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
// When the IDs match, data frames have priority over remote frames.
|
||||
let rtr = self.rtr().cmp(&other.rtr()).reverse();
|
||||
|
||||
let id_a = self.to_id();
|
||||
let id_b = other.to_id();
|
||||
match (id_a, id_b) {
|
||||
(Id::Standard(a), Id::Standard(b)) => {
|
||||
// Lower IDs have priority over higher IDs.
|
||||
a.as_raw().cmp(&b.as_raw()).reverse().then(rtr)
|
||||
}
|
||||
(Id::Extended(a), Id::Extended(b)) => a.as_raw().cmp(&b.as_raw()).reverse().then(rtr),
|
||||
(Id::Standard(a), Id::Extended(b)) => {
|
||||
// Standard frames have priority over extended frames if their Base IDs match.
|
||||
a.as_raw()
|
||||
.cmp(&b.standard_id().as_raw())
|
||||
.reverse()
|
||||
.then(Ordering::Greater)
|
||||
}
|
||||
(Id::Extended(a), Id::Standard(b)) => {
|
||||
a.standard_id().as_raw().cmp(&b.as_raw()).reverse().then(Ordering::Less)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for IdReg {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum RxFifo {
|
||||
Fifo0,
|
||||
Fifo1,
|
||||
}
|
52
embassy-stm32/src/can/common.rs
Normal file
52
embassy-stm32/src/can/common.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use embassy_sync::channel::{DynamicReceiver, DynamicSender};
|
||||
|
||||
use super::enums::*;
|
||||
use super::frame::*;
|
||||
|
||||
pub(crate) struct ClassicBufferedRxInner {
|
||||
pub rx_sender: DynamicSender<'static, Result<Envelope, BusError>>,
|
||||
}
|
||||
pub(crate) struct ClassicBufferedTxInner {
|
||||
pub tx_receiver: DynamicReceiver<'static, Frame>,
|
||||
}
|
||||
|
||||
#[cfg(any(can_fdcan_v1, can_fdcan_h7))]
|
||||
|
||||
pub(crate) struct FdBufferedRxInner {
|
||||
pub rx_sender: DynamicSender<'static, Result<FdEnvelope, BusError>>,
|
||||
}
|
||||
|
||||
#[cfg(any(can_fdcan_v1, can_fdcan_h7))]
|
||||
pub(crate) struct FdBufferedTxInner {
|
||||
pub tx_receiver: DynamicReceiver<'static, FdFrame>,
|
||||
}
|
||||
|
||||
/// Sender that can be used for sending CAN frames.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BufferedCanSender {
|
||||
pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, Frame>,
|
||||
pub(crate) waker: fn(),
|
||||
}
|
||||
|
||||
impl BufferedCanSender {
|
||||
/// Async write frame to TX buffer.
|
||||
pub fn try_write(&mut self, frame: Frame) -> Result<(), embassy_sync::channel::TrySendError<Frame>> {
|
||||
self.tx_buf.try_send(frame)?;
|
||||
(self.waker)();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: Frame) {
|
||||
self.tx_buf.send(frame).await;
|
||||
(self.waker)();
|
||||
}
|
||||
|
||||
/// Allows a poll_fn to poll until the channel is ready to write
|
||||
pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {
|
||||
self.tx_buf.poll_ready_to_send(cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub type BufferedCanReceiver = embassy_sync::channel::DynamicReceiver<'static, Result<Envelope, BusError>>;
|
@ -40,3 +40,13 @@ pub enum FrameCreateError {
|
||||
/// Invalid ID.
|
||||
InvalidCanId,
|
||||
}
|
||||
|
||||
/// Error returned by `try_read`
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TryReadError {
|
||||
/// Bus error
|
||||
BusError(BusError),
|
||||
/// Receive buffer is empty
|
||||
Empty,
|
||||
}
|
||||
|
@ -140,26 +140,6 @@ 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]);
|
||||
|
@ -325,17 +325,6 @@ impl Registers {
|
||||
*/
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
@ -408,66 +397,17 @@ impl Registers {
|
||||
|
||||
/// Moves out of ConfigMode and into specified mode
|
||||
#[inline]
|
||||
pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::FdcanOperatingMode) {
|
||||
pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::OperatingMode) {
|
||||
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),
|
||||
crate::can::OperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal),
|
||||
crate::can::OperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External),
|
||||
crate::can::OperatingMode::NormalOperationMode => self.set_normal_operations(true),
|
||||
crate::can::OperatingMode::RestrictedOperationMode => self.set_restricted_operations(true),
|
||||
crate::can::OperatingMode::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
|
||||
@ -565,6 +505,7 @@ impl Registers {
|
||||
|
||||
/// Configures and resets the timestamp counter
|
||||
#[inline]
|
||||
#[allow(unused)]
|
||||
pub fn set_timestamp_counter_source(&mut self, select: TimestampSource) {
|
||||
#[cfg(stm32h7)]
|
||||
let (tcp, tss) = match select {
|
||||
|
@ -5,24 +5,24 @@ use core::task::Poll;
|
||||
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::can::fd::peripheral::Registers;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::AFType;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{interrupt, peripherals, Peripheral};
|
||||
|
||||
pub mod enums;
|
||||
pub(crate) mod fd;
|
||||
pub mod frame;
|
||||
mod util;
|
||||
|
||||
use enums::*;
|
||||
use fd::config::*;
|
||||
use fd::filter::*;
|
||||
pub use fd::{config, filter};
|
||||
use frame::*;
|
||||
use self::fd::config::*;
|
||||
use self::fd::filter::*;
|
||||
pub use self::fd::{config, filter};
|
||||
pub use super::common::{BufferedCanReceiver, BufferedCanSender};
|
||||
use super::enums::*;
|
||||
use super::frame::*;
|
||||
use super::util;
|
||||
|
||||
/// Timestamp for incoming packets. Use Embassy time when enabled.
|
||||
#[cfg(feature = "time")]
|
||||
@ -53,8 +53,8 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
|
||||
}
|
||||
|
||||
match &T::state().tx_mode {
|
||||
sealed::TxMode::NonBuffered(waker) => waker.wake(),
|
||||
sealed::TxMode::ClassicBuffered(buf) => {
|
||||
TxMode::NonBuffered(waker) => waker.wake(),
|
||||
TxMode::ClassicBuffered(buf) => {
|
||||
if !T::registers().tx_queue_is_full() {
|
||||
match buf.tx_receiver.try_receive() {
|
||||
Ok(frame) => {
|
||||
@ -64,7 +64,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
|
||||
}
|
||||
}
|
||||
}
|
||||
sealed::TxMode::FdBuffered(buf) => {
|
||||
TxMode::FdBuffered(buf) => {
|
||||
if !T::registers().tx_queue_is_full() {
|
||||
match buf.tx_receiver.try_receive() {
|
||||
Ok(frame) => {
|
||||
@ -106,7 +106,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT1Interrupt> for IT1Interrup
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Different operating modes
|
||||
pub enum FdcanOperatingMode {
|
||||
pub enum OperatingMode {
|
||||
//PoweredDownMode,
|
||||
//ConfigMode,
|
||||
/// This mode can be used for a “Hot Selftest”, meaning the FDCAN can be tested without
|
||||
@ -144,7 +144,7 @@ pub enum FdcanOperatingMode {
|
||||
|
||||
/// FDCAN Configuration instance instance
|
||||
/// Create instance of this first
|
||||
pub struct FdcanConfigurator<'d, T: Instance> {
|
||||
pub struct CanConfigurator<'d, T: Instance> {
|
||||
config: crate::can::fd::config::FdCanConfig,
|
||||
/// Reference to internals.
|
||||
instance: FdcanInstance<'d, T>,
|
||||
@ -165,7 +165,7 @@ fn calc_ns_per_timer_tick<T: Instance>(mode: crate::can::fd::config::FrameTransm
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> FdcanConfigurator<'d, T> {
|
||||
impl<'d, T: Instance> CanConfigurator<'d, T> {
|
||||
/// Creates a new Fdcan instance, keeping the peripheral in sleep mode.
|
||||
/// You must call [Fdcan::enable_non_blocking] to use the peripheral.
|
||||
pub fn new(
|
||||
@ -175,7 +175,7 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
|
||||
_irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>>
|
||||
+ 'd,
|
||||
) -> FdcanConfigurator<'d, T> {
|
||||
) -> CanConfigurator<'d, T> {
|
||||
into_ref!(peri, rx, tx);
|
||||
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
@ -269,13 +269,13 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
|
||||
}
|
||||
|
||||
/// Start in mode.
|
||||
pub fn start(self, mode: FdcanOperatingMode) -> Fdcan<'d, T> {
|
||||
pub fn start(self, mode: OperatingMode) -> Can<'d, T> {
|
||||
let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(self.config.frame_transmit);
|
||||
critical_section::with(|_| unsafe {
|
||||
T::mut_state().ns_per_timer_tick = ns_per_timer_tick;
|
||||
});
|
||||
T::registers().into_mode(self.config, mode);
|
||||
let ret = Fdcan {
|
||||
let ret = Can {
|
||||
config: self.config,
|
||||
instance: self.instance,
|
||||
_mode: mode,
|
||||
@ -284,30 +284,30 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
|
||||
}
|
||||
|
||||
/// Start, entering mode. Does same as start(mode)
|
||||
pub fn into_normal_mode(self) -> Fdcan<'d, T> {
|
||||
self.start(FdcanOperatingMode::NormalOperationMode)
|
||||
pub fn into_normal_mode(self) -> Can<'d, T> {
|
||||
self.start(OperatingMode::NormalOperationMode)
|
||||
}
|
||||
|
||||
/// Start, entering mode. Does same as start(mode)
|
||||
pub fn into_internal_loopback_mode(self) -> Fdcan<'d, T> {
|
||||
self.start(FdcanOperatingMode::InternalLoopbackMode)
|
||||
pub fn into_internal_loopback_mode(self) -> Can<'d, T> {
|
||||
self.start(OperatingMode::InternalLoopbackMode)
|
||||
}
|
||||
|
||||
/// Start, entering mode. Does same as start(mode)
|
||||
pub fn into_external_loopback_mode(self) -> Fdcan<'d, T> {
|
||||
self.start(FdcanOperatingMode::ExternalLoopbackMode)
|
||||
pub fn into_external_loopback_mode(self) -> Can<'d, T> {
|
||||
self.start(OperatingMode::ExternalLoopbackMode)
|
||||
}
|
||||
}
|
||||
|
||||
/// FDCAN Instance
|
||||
pub struct Fdcan<'d, T: Instance> {
|
||||
pub struct Can<'d, T: Instance> {
|
||||
config: crate::can::fd::config::FdCanConfig,
|
||||
/// Reference to internals.
|
||||
instance: FdcanInstance<'d, T>,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
impl<'d, T: Instance> Can<'d, T> {
|
||||
/// Flush one of the TX mailboxes.
|
||||
pub async fn flush(&self, idx: usize) {
|
||||
poll_fn(|cx| {
|
||||
@ -330,12 +330,12 @@ impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
pub async fn write(&mut self, frame: &ClassicFrame) -> Option<ClassicFrame> {
|
||||
pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
|
||||
T::state().tx_mode.write::<T>(frame).await
|
||||
}
|
||||
|
||||
/// Returns the next received message frame
|
||||
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
T::state().rx_mode.read_classic::<T>().await
|
||||
}
|
||||
|
||||
@ -348,19 +348,19 @@ impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
}
|
||||
|
||||
/// Returns the next received message frame
|
||||
pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
|
||||
pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {
|
||||
T::state().rx_mode.read_fd::<T>().await
|
||||
}
|
||||
|
||||
/// Split instance into separate Tx(write) and Rx(read) portions
|
||||
pub fn split(self) -> (FdcanTx<'d, T>, FdcanRx<'d, T>) {
|
||||
pub fn split(self) -> (CanTx<'d, T>, CanRx<'d, T>) {
|
||||
(
|
||||
FdcanTx {
|
||||
CanTx {
|
||||
config: self.config,
|
||||
_instance: self.instance,
|
||||
_mode: self._mode,
|
||||
},
|
||||
FdcanRx {
|
||||
CanRx {
|
||||
_instance1: PhantomData::<T>,
|
||||
_instance2: T::regs(),
|
||||
_mode: self._mode,
|
||||
@ -369,8 +369,8 @@ impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
}
|
||||
|
||||
/// Join split rx and tx portions back together
|
||||
pub fn join(tx: FdcanTx<'d, T>, rx: FdcanRx<'d, T>) -> Self {
|
||||
Fdcan {
|
||||
pub fn join(tx: CanTx<'d, T>, rx: CanRx<'d, T>) -> Self {
|
||||
Can {
|
||||
config: tx.config,
|
||||
//_instance2: T::regs(),
|
||||
instance: tx._instance,
|
||||
@ -398,59 +398,27 @@ impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
}
|
||||
|
||||
/// User supplied buffer for RX Buffering
|
||||
pub type RxBuf<const BUF_SIZE: usize> =
|
||||
Channel<CriticalSectionRawMutex, Result<(ClassicFrame, Timestamp), BusError>, BUF_SIZE>;
|
||||
pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>;
|
||||
|
||||
/// User supplied buffer for TX buffering
|
||||
pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, ClassicFrame, BUF_SIZE>;
|
||||
pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>;
|
||||
|
||||
/// Buffered FDCAN Instance
|
||||
pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
tx_buf: &'static TxBuf<TX_BUF_SIZE>,
|
||||
rx_buf: &'static RxBuf<RX_BUF_SIZE>,
|
||||
}
|
||||
|
||||
/// Sender that can be used for sending CAN frames.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BufferedCanSender {
|
||||
tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>,
|
||||
waker: fn(),
|
||||
}
|
||||
|
||||
impl BufferedCanSender {
|
||||
/// Async write frame to TX buffer.
|
||||
pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError<ClassicFrame>> {
|
||||
self.tx_buf.try_send(frame)?;
|
||||
(self.waker)();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: ClassicFrame) {
|
||||
self.tx_buf.send(frame).await;
|
||||
(self.waker)();
|
||||
}
|
||||
|
||||
/// Allows a poll_fn to poll until the channel is ready to write
|
||||
pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {
|
||||
self.tx_buf.poll_ready_to_send(cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub type BufferedCanReceiver =
|
||||
embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>;
|
||||
|
||||
impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE>
|
||||
{
|
||||
fn new(
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
tx_buf: &'static TxBuf<TX_BUF_SIZE>,
|
||||
rx_buf: &'static RxBuf<RX_BUF_SIZE>,
|
||||
) -> Self {
|
||||
@ -467,26 +435,26 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
fn setup(self) -> Self {
|
||||
// We don't want interrupts being processed while we change modes.
|
||||
critical_section::with(|_| unsafe {
|
||||
let rx_inner = sealed::ClassicBufferedRxInner {
|
||||
let rx_inner = super::common::ClassicBufferedRxInner {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
let tx_inner = sealed::ClassicBufferedTxInner {
|
||||
let tx_inner = super::common::ClassicBufferedTxInner {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = sealed::RxMode::ClassicBuffered(rx_inner);
|
||||
T::mut_state().tx_mode = sealed::TxMode::ClassicBuffered(tx_inner);
|
||||
T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner);
|
||||
T::mut_state().tx_mode = TxMode::ClassicBuffered(tx_inner);
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: ClassicFrame) {
|
||||
pub async fn write(&mut self, frame: Frame) {
|
||||
self.tx_buf.send(frame).await;
|
||||
T::IT0Interrupt::pend(); // Wake for Tx
|
||||
}
|
||||
|
||||
/// Async read frame from RX buffer.
|
||||
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
self.rx_buf.receive().await
|
||||
}
|
||||
|
||||
@ -509,15 +477,14 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
T::mut_state().rx_mode = sealed::RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
T::mut_state().tx_mode = sealed::TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// User supplied buffer for RX Buffering
|
||||
pub type RxFdBuf<const BUF_SIZE: usize> =
|
||||
Channel<CriticalSectionRawMutex, Result<(FdFrame, Timestamp), BusError>, BUF_SIZE>;
|
||||
pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>;
|
||||
|
||||
/// User supplied buffer for TX buffering
|
||||
pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFrame, BUF_SIZE>;
|
||||
@ -526,7 +493,7 @@ pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFra
|
||||
pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,
|
||||
rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,
|
||||
}
|
||||
@ -534,7 +501,7 @@ pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF
|
||||
/// Sender that can be used for sending CAN frames.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BufferedFdCanSender {
|
||||
tx_buf: embassy_sync::channel::DynamicSender<'static, FdFrame>,
|
||||
tx_buf: DynamicSender<'static, FdFrame>,
|
||||
waker: fn(),
|
||||
}
|
||||
|
||||
@ -559,8 +526,7 @@ impl BufferedFdCanSender {
|
||||
}
|
||||
|
||||
/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub type BufferedFdCanReceiver =
|
||||
embassy_sync::channel::DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>;
|
||||
pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<FdEnvelope, BusError>>;
|
||||
|
||||
impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE>
|
||||
@ -568,7 +534,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
fn new(
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,
|
||||
rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,
|
||||
) -> Self {
|
||||
@ -585,14 +551,14 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
fn setup(self) -> Self {
|
||||
// We don't want interrupts being processed while we change modes.
|
||||
critical_section::with(|_| unsafe {
|
||||
let rx_inner = sealed::FdBufferedRxInner {
|
||||
let rx_inner = super::common::FdBufferedRxInner {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
let tx_inner = sealed::FdBufferedTxInner {
|
||||
let tx_inner = super::common::FdBufferedTxInner {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = sealed::RxMode::FdBuffered(rx_inner);
|
||||
T::mut_state().tx_mode = sealed::TxMode::FdBuffered(tx_inner);
|
||||
T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner);
|
||||
T::mut_state().tx_mode = TxMode::FdBuffered(tx_inner);
|
||||
});
|
||||
self
|
||||
}
|
||||
@ -604,7 +570,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
}
|
||||
|
||||
/// Async read frame from RX buffer.
|
||||
pub async fn read(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
|
||||
pub async fn read(&mut self) -> Result<FdEnvelope, BusError> {
|
||||
self.rx_buf.receive().await
|
||||
}
|
||||
|
||||
@ -627,32 +593,32 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
T::mut_state().rx_mode = sealed::RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
T::mut_state().tx_mode = sealed::TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// FDCAN Rx only Instance
|
||||
pub struct FdcanRx<'d, T: Instance> {
|
||||
pub struct CanRx<'d, T: Instance> {
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
}
|
||||
|
||||
/// FDCAN Tx only Instance
|
||||
pub struct FdcanTx<'d, T: Instance> {
|
||||
pub struct CanTx<'d, T: Instance> {
|
||||
config: crate::can::fd::config::FdCanConfig,
|
||||
_instance: FdcanInstance<'d, T>, //(PeripheralRef<'a, T>);
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
}
|
||||
|
||||
impl<'c, 'd, T: Instance> FdcanTx<'d, T> {
|
||||
impl<'c, 'd, T: Instance> CanTx<'d, T> {
|
||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
pub async fn write(&mut self, frame: &ClassicFrame) -> Option<ClassicFrame> {
|
||||
pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
|
||||
T::state().tx_mode.write::<T>(frame).await
|
||||
}
|
||||
|
||||
@ -665,204 +631,216 @@ impl<'c, 'd, T: Instance> FdcanTx<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'd, T: Instance> FdcanRx<'d, T> {
|
||||
impl<'c, 'd, T: Instance> CanRx<'d, T> {
|
||||
/// Returns the next received message frame
|
||||
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
T::state().rx_mode.read_classic::<T>().await
|
||||
}
|
||||
|
||||
/// Returns the next received message frame
|
||||
pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
|
||||
pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {
|
||||
T::state().rx_mode.read_fd::<T>().await
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
enum RxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(super::common::ClassicBufferedRxInner),
|
||||
FdBuffered(super::common::FdBufferedRxInner),
|
||||
}
|
||||
|
||||
use embassy_sync::channel::{DynamicReceiver, DynamicSender};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use super::CanHeader;
|
||||
use crate::can::_version::{BusError, Timestamp};
|
||||
use crate::can::frame::{ClassicFrame, FdFrame};
|
||||
|
||||
pub struct ClassicBufferedRxInner {
|
||||
pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>,
|
||||
}
|
||||
pub struct ClassicBufferedTxInner {
|
||||
pub tx_receiver: DynamicReceiver<'static, ClassicFrame>,
|
||||
}
|
||||
|
||||
pub struct FdBufferedRxInner {
|
||||
pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>,
|
||||
}
|
||||
pub struct FdBufferedTxInner {
|
||||
pub tx_receiver: DynamicReceiver<'static, FdFrame>,
|
||||
}
|
||||
|
||||
pub enum RxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(ClassicBufferedRxInner),
|
||||
FdBuffered(FdBufferedRxInner),
|
||||
}
|
||||
|
||||
impl RxMode {
|
||||
pub fn register(&self, arg: &core::task::Waker) {
|
||||
match self {
|
||||
RxMode::NonBuffered(waker) => waker.register(arg),
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_interrupt<T: Instance>(&self, fifonr: usize) {
|
||||
T::regs().ir().write(|w| w.set_rfn(fifonr, true));
|
||||
match self {
|
||||
RxMode::NonBuffered(waker) => {
|
||||
waker.wake();
|
||||
}
|
||||
RxMode::ClassicBuffered(buf) => {
|
||||
if let Some(result) = self.read::<T, _>() {
|
||||
let _ = buf.rx_sender.try_send(result);
|
||||
}
|
||||
}
|
||||
RxMode::FdBuffered(buf) => {
|
||||
if let Some(result) = self.read::<T, _>() {
|
||||
let _ = buf.rx_sender.try_send(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read<T: Instance, F: CanHeader>(&self) -> Option<Result<(F, Timestamp), BusError>> {
|
||||
if let Some((msg, ts)) = T::registers().read(0) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok((msg, ts)))
|
||||
} else if let Some((msg, ts)) = T::registers().read(1) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok((msg, ts)))
|
||||
} else if let Some(err) = T::registers().curr_error() {
|
||||
// TODO: this is probably wrong
|
||||
Some(Err(err))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_async<T: Instance, F: CanHeader>(&self) -> Result<(F, Timestamp), BusError> {
|
||||
poll_fn(|cx| {
|
||||
T::state().err_waker.register(cx.waker());
|
||||
self.register(cx.waker());
|
||||
match self.read::<T, _>() {
|
||||
Some(result) => Poll::Ready(result),
|
||||
None => Poll::Pending,
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read_classic<T: Instance>(&self) -> Result<(ClassicFrame, Timestamp), BusError> {
|
||||
self.read_async::<T, _>().await
|
||||
}
|
||||
|
||||
pub async fn read_fd<T: Instance>(&self) -> Result<(FdFrame, Timestamp), BusError> {
|
||||
self.read_async::<T, _>().await
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(ClassicBufferedTxInner),
|
||||
FdBuffered(FdBufferedTxInner),
|
||||
}
|
||||
|
||||
impl TxMode {
|
||||
pub fn register(&self, arg: &core::task::Waker) {
|
||||
match self {
|
||||
TxMode::NonBuffered(waker) => {
|
||||
waker.register(arg);
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
async fn write_generic<T: Instance, F: embedded_can::Frame + CanHeader>(&self, frame: &F) -> Option<F> {
|
||||
poll_fn(|cx| {
|
||||
self.register(cx.waker());
|
||||
|
||||
if let Ok(dropped) = T::registers().write(frame) {
|
||||
return Poll::Ready(dropped);
|
||||
}
|
||||
|
||||
// Couldn't replace any lower priority frames. Need to wait for some mailboxes
|
||||
// to clear.
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
pub async fn write<T: Instance>(&self, frame: &ClassicFrame) -> Option<ClassicFrame> {
|
||||
self.write_generic::<T, _>(frame).await
|
||||
}
|
||||
|
||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
pub async fn write_fd<T: Instance>(&self, frame: &FdFrame) -> Option<FdFrame> {
|
||||
self.write_generic::<T, _>(frame).await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub rx_mode: RxMode,
|
||||
pub tx_mode: TxMode,
|
||||
pub ns_per_timer_tick: u64,
|
||||
|
||||
pub err_waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
|
||||
tx_mode: TxMode::NonBuffered(AtomicWaker::new()),
|
||||
ns_per_timer_tick: 0,
|
||||
err_waker: AtomicWaker::new(),
|
||||
impl RxMode {
|
||||
fn register(&self, arg: &core::task::Waker) {
|
||||
match self {
|
||||
RxMode::NonBuffered(waker) => waker.register(arg),
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
const MSG_RAM_OFFSET: usize;
|
||||
fn on_interrupt<T: Instance>(&self, fifonr: usize) {
|
||||
T::regs().ir().write(|w| w.set_rfn(fifonr, true));
|
||||
match self {
|
||||
RxMode::NonBuffered(waker) => {
|
||||
waker.wake();
|
||||
}
|
||||
RxMode::ClassicBuffered(buf) => {
|
||||
if let Some(result) = self.try_read::<T>() {
|
||||
let _ = buf.rx_sender.try_send(result);
|
||||
}
|
||||
}
|
||||
RxMode::FdBuffered(buf) => {
|
||||
if let Some(result) = self.try_read_fd::<T>() {
|
||||
let _ = buf.rx_sender.try_send(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn regs() -> &'static crate::pac::can::Fdcan;
|
||||
fn registers() -> crate::can::fd::peripheral::Registers;
|
||||
fn ram() -> &'static crate::pac::fdcanram::Fdcanram;
|
||||
fn state() -> &'static State;
|
||||
unsafe fn mut_state() -> &'static mut State;
|
||||
fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp;
|
||||
//async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
|
||||
fn try_read<T: Instance>(&self) -> Option<Result<Envelope, BusError>> {
|
||||
if let Some((frame, ts)) = T::registers().read(0) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok(Envelope { ts, frame }))
|
||||
} else if let Some((frame, ts)) = T::registers().read(1) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok(Envelope { ts, frame }))
|
||||
} else if let Some(err) = T::registers().curr_error() {
|
||||
// TODO: this is probably wrong
|
||||
Some(Err(err))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
//async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
|
||||
fn try_read_fd<T: Instance>(&self) -> Option<Result<FdEnvelope, BusError>> {
|
||||
if let Some((frame, ts)) = T::registers().read(0) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok(FdEnvelope { ts, frame }))
|
||||
} else if let Some((frame, ts)) = T::registers().read(1) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok(FdEnvelope { ts, frame }))
|
||||
} else if let Some(err) = T::registers().curr_error() {
|
||||
// TODO: this is probably wrong
|
||||
Some(Err(err))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn read<T: Instance, F: CanHeader>(&self) -> Option<Result<(F, Timestamp), BusError>> {
|
||||
if let Some((msg, ts)) = T::registers().read(0) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok((msg, ts)))
|
||||
} else if let Some((msg, ts)) = T::registers().read(1) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok((msg, ts)))
|
||||
} else if let Some(err) = T::registers().curr_error() {
|
||||
// TODO: this is probably wrong
|
||||
Some(Err(err))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_async<T: Instance, F: CanHeader>(&self) -> Result<(F, Timestamp), BusError> {
|
||||
poll_fn(|cx| {
|
||||
T::state().err_waker.register(cx.waker());
|
||||
self.register(cx.waker());
|
||||
match self.read::<T, _>() {
|
||||
Some(result) => Poll::Ready(result),
|
||||
None => Poll::Pending,
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
|
||||
match self.read_async::<T, _>().await {
|
||||
Ok((frame, ts)) => Ok(Envelope { ts, frame }),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_fd<T: Instance>(&self) -> Result<FdEnvelope, BusError> {
|
||||
match self.read_async::<T, _>().await {
|
||||
Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(super::common::ClassicBufferedTxInner),
|
||||
FdBuffered(super::common::FdBufferedTxInner),
|
||||
}
|
||||
|
||||
impl TxMode {
|
||||
fn register(&self, arg: &core::task::Waker) {
|
||||
match self {
|
||||
TxMode::NonBuffered(waker) => {
|
||||
waker.register(arg);
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
async fn write_generic<T: Instance, F: embedded_can::Frame + CanHeader>(&self, frame: &F) -> Option<F> {
|
||||
poll_fn(|cx| {
|
||||
self.register(cx.waker());
|
||||
|
||||
if let Ok(dropped) = T::registers().write(frame) {
|
||||
return Poll::Ready(dropped);
|
||||
}
|
||||
|
||||
// Couldn't replace any lower priority frames. Need to wait for some mailboxes
|
||||
// to clear.
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
async fn write<T: Instance>(&self, frame: &Frame) -> Option<Frame> {
|
||||
self.write_generic::<T, _>(frame).await
|
||||
}
|
||||
|
||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
async fn write_fd<T: Instance>(&self, frame: &FdFrame) -> Option<FdFrame> {
|
||||
self.write_generic::<T, _>(frame).await
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
pub rx_mode: RxMode,
|
||||
pub tx_mode: TxMode,
|
||||
pub ns_per_timer_tick: u64,
|
||||
|
||||
pub err_waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl State {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
|
||||
tx_mode: TxMode::NonBuffered(AtomicWaker::new()),
|
||||
ns_per_timer_tick: 0,
|
||||
err_waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait SealedInstance {
|
||||
const MSG_RAM_OFFSET: usize;
|
||||
|
||||
fn regs() -> &'static crate::pac::can::Fdcan;
|
||||
fn registers() -> crate::can::fd::peripheral::Registers;
|
||||
fn state() -> &'static State;
|
||||
unsafe fn mut_state() -> &'static mut State;
|
||||
fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp;
|
||||
}
|
||||
|
||||
/// Instance trait
|
||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + RccPeripheral + 'static {
|
||||
/// Interrupt 0
|
||||
type IT0Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
/// Interrupt 0
|
||||
/// Interrupt 1
|
||||
type IT1Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
@ -871,7 +849,7 @@ pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>);
|
||||
|
||||
macro_rules! impl_fdcan {
|
||||
($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
impl SealedInstance for peripherals::$inst {
|
||||
const MSG_RAM_OFFSET: usize = $msg_ram_offset;
|
||||
|
||||
fn regs() -> &'static crate::pac::can::Fdcan {
|
||||
@ -880,14 +858,11 @@ macro_rules! impl_fdcan {
|
||||
fn registers() -> Registers {
|
||||
Registers{regs: &crate::pac::$inst, msgram: &crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET}
|
||||
}
|
||||
fn ram() -> &'static crate::pac::fdcanram::Fdcanram {
|
||||
&crate::pac::$msg_ram_inst
|
||||
unsafe fn mut_state() -> &'static mut State {
|
||||
static mut STATE: State = State::new();
|
||||
&mut *core::ptr::addr_of_mut!(STATE)
|
||||
}
|
||||
unsafe fn mut_state() -> & 'static mut sealed::State {
|
||||
static mut STATE: sealed::State = sealed::State::new();
|
||||
& mut STATE
|
||||
}
|
||||
fn state() -> &'static sealed::State {
|
||||
fn state() -> &'static State {
|
||||
unsafe { peripherals::$inst::mut_state() }
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,14 @@ use bit_field::BitField;
|
||||
|
||||
use crate::can::enums::FrameCreateError;
|
||||
|
||||
/// Calculate proper timestamp when available.
|
||||
#[cfg(feature = "time")]
|
||||
pub type Timestamp = embassy_time::Instant;
|
||||
|
||||
/// Raw register timestamp
|
||||
#[cfg(not(feature = "time"))]
|
||||
pub type Timestamp = u16;
|
||||
|
||||
/// CAN Header, without meta data
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Header {
|
||||
@ -136,19 +144,20 @@ impl ClassicData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Frame with up to 8 bytes of data payload as per Classic CAN
|
||||
/// Frame with up to 8 bytes of data payload as per Classic(non-FD) CAN
|
||||
/// For CAN-FD support use FdFrame
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ClassicFrame {
|
||||
pub struct Frame {
|
||||
can_header: Header,
|
||||
data: ClassicData,
|
||||
}
|
||||
|
||||
impl ClassicFrame {
|
||||
impl Frame {
|
||||
/// Create a new CAN classic Frame
|
||||
pub fn new(can_header: Header, raw_data: &[u8]) -> Result<Self, FrameCreateError> {
|
||||
let data = ClassicData::new(raw_data)?;
|
||||
Ok(ClassicFrame { can_header, data: data })
|
||||
Ok(Frame { can_header, data: data })
|
||||
}
|
||||
|
||||
/// Creates a new data frame.
|
||||
@ -206,9 +215,9 @@ impl ClassicFrame {
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_can::Frame for ClassicFrame {
|
||||
impl embedded_can::Frame for Frame {
|
||||
fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> {
|
||||
let frameopt = ClassicFrame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data);
|
||||
let frameopt = Frame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data);
|
||||
match frameopt {
|
||||
Ok(frame) => Some(frame),
|
||||
Err(_) => None,
|
||||
@ -216,7 +225,7 @@ impl embedded_can::Frame for ClassicFrame {
|
||||
}
|
||||
fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> {
|
||||
if len <= 8 {
|
||||
let frameopt = ClassicFrame::new(Header::new(id.into(), len as u8, true), &[0; 8]);
|
||||
let frameopt = Frame::new(Header::new(id.into(), len as u8, true), &[0; 8]);
|
||||
match frameopt {
|
||||
Ok(frame) => Some(frame),
|
||||
Err(_) => None,
|
||||
@ -245,7 +254,7 @@ impl embedded_can::Frame for ClassicFrame {
|
||||
}
|
||||
}
|
||||
|
||||
impl CanHeader for ClassicFrame {
|
||||
impl CanHeader for Frame {
|
||||
fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError> {
|
||||
Self::new(header, data)
|
||||
}
|
||||
@ -255,10 +264,31 @@ impl CanHeader for ClassicFrame {
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains CAN frame and additional metadata.
|
||||
///
|
||||
/// Timestamp is available if `time` feature is enabled.
|
||||
/// For CAN-FD support use FdEnvelope
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Envelope {
|
||||
/// Reception time.
|
||||
pub ts: Timestamp,
|
||||
/// The actual CAN frame.
|
||||
pub frame: Frame,
|
||||
}
|
||||
|
||||
impl Envelope {
|
||||
/// Convert into a tuple
|
||||
pub fn parts(self) -> (Frame, Timestamp) {
|
||||
(self.frame, self.ts)
|
||||
}
|
||||
}
|
||||
|
||||
/// Payload of a (FD)CAN data frame.
|
||||
///
|
||||
/// Contains 0 to 64 Bytes of data.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct FdData {
|
||||
pub(crate) bytes: [u8; 64],
|
||||
}
|
||||
@ -308,6 +338,7 @@ impl FdData {
|
||||
|
||||
/// Frame with up to 8 bytes of data payload as per Fd CAN
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct FdFrame {
|
||||
can_header: Header,
|
||||
data: FdData,
|
||||
@ -410,3 +441,23 @@ impl CanHeader for FdFrame {
|
||||
self.header()
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains CAN FD frame and additional metadata.
|
||||
///
|
||||
/// Timestamp is available if `time` feature is enabled.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct FdEnvelope {
|
||||
/// Reception time.
|
||||
pub ts: Timestamp,
|
||||
|
||||
/// The actual CAN frame.
|
||||
pub frame: FdFrame,
|
||||
}
|
||||
|
||||
impl FdEnvelope {
|
||||
/// Convert into a tuple
|
||||
pub fn parts(self) -> (FdFrame, Timestamp) {
|
||||
(self.frame, self.ts)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,14 @@
|
||||
//! Controller Area Network (CAN)
|
||||
#![macro_use]
|
||||
|
||||
#[cfg_attr(can_bxcan, path = "bxcan.rs")]
|
||||
#[cfg_attr(can_bxcan, path = "bxcan/mod.rs")]
|
||||
#[cfg_attr(any(can_fdcan_v1, can_fdcan_h7), path = "fdcan.rs")]
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
|
||||
mod common;
|
||||
pub mod enums;
|
||||
pub mod frame;
|
||||
pub mod util;
|
||||
|
||||
pub use frame::Frame;
|
||||
|
@ -2,7 +2,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
|
||||
use crate::pac::CRC as PAC_CRC;
|
||||
use crate::peripherals::CRC;
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::rcc::SealedRccPeripheral;
|
||||
use crate::Peripheral;
|
||||
|
||||
/// CRC driver.
|
||||
|
@ -3,7 +3,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use crate::pac::crc::vals;
|
||||
use crate::pac::CRC as PAC_CRC;
|
||||
use crate::peripherals::CRC;
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::rcc::SealedRccPeripheral;
|
||||
use crate::Peripheral;
|
||||
|
||||
/// CRC driver.
|
||||
|
@ -1885,16 +1885,13 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Instance {
|
||||
fn regs() -> pac::cryp::Cryp;
|
||||
}
|
||||
trait SealedInstance {
|
||||
fn regs() -> pac::cryp::Cryp;
|
||||
}
|
||||
|
||||
/// CRYP instance trait.
|
||||
pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
|
||||
/// Interrupt for this CRYP instance.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
@ -1905,7 +1902,7 @@ foreach_interrupt!(
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
impl SealedInstance for peripherals::$inst {
|
||||
fn regs() -> crate::pac::cryp::Cryp {
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
dma: impl Peripheral<P = DMA> + 'd,
|
||||
pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::sealed::Pin> + 'd,
|
||||
pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::Pin> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(dma, pin);
|
||||
pin.set_as_analog();
|
||||
@ -392,8 +392,8 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> {
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
dma_ch1: impl Peripheral<P = DMACh1> + 'd,
|
||||
dma_ch2: impl Peripheral<P = DMACh2> + 'd,
|
||||
pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::sealed::Pin> + 'd,
|
||||
pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::sealed::Pin> + 'd,
|
||||
pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::Pin> + 'd,
|
||||
pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::Pin> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2);
|
||||
pin_ch1.set_as_analog();
|
||||
@ -488,14 +488,13 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> &'static crate::pac::dac::Dac;
|
||||
}
|
||||
trait SealedInstance {
|
||||
fn regs() -> &'static crate::pac::dac::Dac;
|
||||
}
|
||||
|
||||
/// DAC instance.
|
||||
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + RccPeripheral + 'static {}
|
||||
dma_trait!(DacDma1, Instance);
|
||||
dma_trait!(DacDma2, Instance);
|
||||
|
||||
@ -504,7 +503,7 @@ pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(dac, $inst:ident) => {
|
||||
impl crate::dac::sealed::Instance for peripherals::$inst {
|
||||
impl crate::dac::SealedInstance for peripherals::$inst {
|
||||
fn regs() -> &'static crate::pac::dac::Dac {
|
||||
&crate::pac::$inst
|
||||
}
|
||||
|
@ -7,8 +7,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::dma::Transfer;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
use crate::gpio::{AFType, Speed};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
@ -431,14 +430,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Instance: crate::rcc::RccPeripheral {
|
||||
fn regs(&self) -> crate::pac::dcmi::Dcmi;
|
||||
}
|
||||
trait SealedInstance: crate::rcc::RccPeripheral {
|
||||
fn regs(&self) -> crate::pac::dcmi::Dcmi;
|
||||
}
|
||||
|
||||
/// DCMI instance.
|
||||
pub trait Instance: sealed::Instance + 'static {
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + 'static {
|
||||
/// Interrupt for this instance.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
@ -465,7 +463,7 @@ pin_trait!(PixClkPin, Instance);
|
||||
#[allow(unused)]
|
||||
macro_rules! impl_peripheral {
|
||||
($inst:ident, $irq:ident) => {
|
||||
impl sealed::Instance for crate::peripherals::$inst {
|
||||
impl SealedInstance for crate::peripherals::$inst {
|
||||
fn regs(&self) -> crate::pac::dcmi::Dcmi {
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{fence, AtomicUsize, Ordering};
|
||||
use core::task::{Context, Poll, Waker};
|
||||
@ -510,6 +510,31 @@ impl AnyChannel {
|
||||
DmaInfo::Bdma(r) => r.ch(info.num).ndtr().read().ndt(),
|
||||
}
|
||||
}
|
||||
|
||||
fn disable_circular_mode(&self) {
|
||||
let info = self.info();
|
||||
match self.info().dma {
|
||||
#[cfg(dma)]
|
||||
DmaInfo::Dma(regs) => regs.st(info.num).cr().modify(|w| {
|
||||
w.set_circ(false);
|
||||
}),
|
||||
#[cfg(bdma)]
|
||||
DmaInfo::Bdma(regs) => regs.ch(info.num).cr().modify(|w| {
|
||||
w.set_circ(false);
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_stop(&self) -> Poll<()> {
|
||||
use core::sync::atomic::compiler_fence;
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
if !self.is_running() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// DMA transfer.
|
||||
@ -829,6 +854,25 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
self.channel.is_running()
|
||||
}
|
||||
|
||||
/// Stop the DMA transfer and await until the buffer is full.
|
||||
///
|
||||
/// This disables the DMA transfer's circular mode so that the transfer
|
||||
/// stops when the buffer is full.
|
||||
///
|
||||
/// This is designed to be used with streaming input data such as the
|
||||
/// I2S/SAI or ADC.
|
||||
///
|
||||
/// When using the UART, you probably want `request_stop()`.
|
||||
pub async fn stop(&mut self) {
|
||||
self.channel.disable_circular_mode();
|
||||
//wait until cr.susp reads as true
|
||||
poll_fn(|cx| {
|
||||
self.set_waker(cx.waker());
|
||||
self.channel.poll_stop()
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {
|
||||
@ -940,6 +984,23 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
|
||||
pub fn is_running(&mut self) -> bool {
|
||||
self.channel.is_running()
|
||||
}
|
||||
|
||||
/// Stop the DMA transfer and await until the buffer is empty.
|
||||
///
|
||||
/// This disables the DMA transfer's circular mode so that the transfer
|
||||
/// stops when all available data has been written.
|
||||
///
|
||||
/// This is designed to be used with streaming output data such as the
|
||||
/// I2S/SAI or DAC.
|
||||
pub async fn stop(&mut self) {
|
||||
self.channel.disable_circular_mode();
|
||||
//wait until cr.susp reads as true
|
||||
poll_fn(|cx| {
|
||||
self.set_waker(cx.waker());
|
||||
self.channel.poll_stop()
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> {
|
||||
|
@ -19,9 +19,7 @@ pub(crate) fn configure_dmamux(info: &DmamuxInfo, request: u8) {
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) mod dmamux_sealed {
|
||||
pub trait MuxChannel {}
|
||||
}
|
||||
pub(crate) trait SealedMuxChannel {}
|
||||
|
||||
/// DMAMUX1 instance.
|
||||
pub struct DMAMUX1;
|
||||
@ -30,14 +28,15 @@ pub struct DMAMUX1;
|
||||
pub struct DMAMUX2;
|
||||
|
||||
/// DMAMUX channel trait.
|
||||
pub trait MuxChannel: dmamux_sealed::MuxChannel {
|
||||
#[allow(private_bounds)]
|
||||
pub trait MuxChannel: SealedMuxChannel {
|
||||
/// DMAMUX instance this channel is on.
|
||||
type Mux;
|
||||
}
|
||||
|
||||
macro_rules! dmamux_channel_impl {
|
||||
($channel_peri:ident, $dmamux:ident) => {
|
||||
impl crate::dma::dmamux_sealed::MuxChannel for crate::peripherals::$channel_peri {}
|
||||
impl crate::dma::SealedMuxChannel for crate::peripherals::$channel_peri {}
|
||||
impl crate::dma::MuxChannel for crate::peripherals::$channel_peri {
|
||||
type Mux = crate::dma::$dmamux;
|
||||
}
|
||||
|
@ -39,17 +39,18 @@ pub type Request = u8;
|
||||
#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))]
|
||||
pub type Request = ();
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Channel {
|
||||
fn id(&self) -> u8;
|
||||
}
|
||||
pub trait ChannelInterrupt {
|
||||
unsafe fn on_irq();
|
||||
}
|
||||
pub(crate) trait SealedChannel {
|
||||
fn id(&self) -> u8;
|
||||
}
|
||||
|
||||
pub(crate) trait ChannelInterrupt {
|
||||
#[cfg_attr(not(feature = "rt"), allow(unused))]
|
||||
unsafe fn on_irq();
|
||||
}
|
||||
|
||||
/// DMA channel.
|
||||
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Into<AnyChannel> + 'static {
|
||||
#[allow(private_bounds)]
|
||||
pub trait Channel: SealedChannel + Peripheral<P = Self> + Into<AnyChannel> + 'static {
|
||||
/// Type-erase (degrade) this pin into an `AnyChannel`.
|
||||
///
|
||||
/// This converts DMA channel singletons (`DMA1_CH3`, `DMA2_CH1`, ...), which
|
||||
@ -63,12 +64,12 @@ pub trait Channel: sealed::Channel + Peripheral<P = Self> + Into<AnyChannel> + '
|
||||
|
||||
macro_rules! dma_channel_impl {
|
||||
($channel_peri:ident, $index:expr) => {
|
||||
impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri {
|
||||
impl crate::dma::SealedChannel for crate::peripherals::$channel_peri {
|
||||
fn id(&self) -> u8 {
|
||||
$index
|
||||
}
|
||||
}
|
||||
impl crate::dma::sealed::ChannelInterrupt for crate::peripherals::$channel_peri {
|
||||
impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri {
|
||||
unsafe fn on_irq() {
|
||||
crate::dma::AnyChannel { id: $index }.on_irq();
|
||||
}
|
||||
@ -96,7 +97,7 @@ impl AnyChannel {
|
||||
}
|
||||
}
|
||||
|
||||
impl sealed::Channel for AnyChannel {
|
||||
impl SealedChannel for AnyChannel {
|
||||
fn id(&self) -> u8 {
|
||||
self.id
|
||||
}
|
||||
|
@ -20,14 +20,13 @@ impl WordSize {
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Word {}
|
||||
}
|
||||
trait SealedWord {}
|
||||
|
||||
/// DMA word trait.
|
||||
///
|
||||
/// This is implemented for u8, u16, u32, etc.
|
||||
pub trait Word: sealed::Word + Default + Copy + 'static {
|
||||
#[allow(private_bounds)]
|
||||
pub trait Word: SealedWord + Default + Copy + 'static {
|
||||
/// Word size
|
||||
fn size() -> WordSize;
|
||||
/// Amount of bits of this word size.
|
||||
@ -36,7 +35,7 @@ pub trait Word: sealed::Word + Default + Copy + 'static {
|
||||
|
||||
macro_rules! impl_word {
|
||||
(_, $T:ident, $bits:literal, $size:ident) => {
|
||||
impl sealed::Word for $T {}
|
||||
impl SealedWord for $T {}
|
||||
impl Word for $T {
|
||||
fn bits() -> usize {
|
||||
$bits
|
||||
|
@ -177,16 +177,15 @@ pub unsafe trait PHY {
|
||||
fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool;
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs() -> crate::pac::eth::Eth;
|
||||
}
|
||||
trait SealedInstance {
|
||||
fn regs() -> crate::pac::eth::Eth;
|
||||
}
|
||||
|
||||
/// Ethernet instance.
|
||||
pub trait Instance: sealed::Instance + RccPeripheral + Send + 'static {}
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + RccPeripheral + Send + 'static {}
|
||||
|
||||
impl sealed::Instance for crate::peripherals::ETH {
|
||||
impl SealedInstance for crate::peripherals::ETH {
|
||||
fn regs() -> crate::pac::eth::Eth {
|
||||
crate::pac::ETH
|
||||
}
|
||||
|
@ -12,15 +12,14 @@ use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress
|
||||
pub(crate) use self::rx_desc::{RDes, RDesRing};
|
||||
pub(crate) use self::tx_desc::{TDes, TDesRing};
|
||||
use super::*;
|
||||
use crate::gpio::sealed::{AFType, Pin as __GpioPin};
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::gpio::{AFType, AnyPin, SealedPin};
|
||||
use crate::interrupt::InterruptExt;
|
||||
#[cfg(eth_v1a)]
|
||||
use crate::pac::AFIO;
|
||||
#[cfg(any(eth_v1b, eth_v1c))]
|
||||
use crate::pac::SYSCFG;
|
||||
use crate::pac::{ETH, RCC};
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::rcc::SealedRccPeripheral;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
/// Interrupt handler.
|
||||
@ -149,8 +148,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
#[cfg(any(eth_v1b, eth_v1c))]
|
||||
config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
|
||||
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = ETH.ethernet_mac();
|
||||
let dma = T::regs().ethernet_dma();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
|
||||
// Reset and wait
|
||||
dma.dmabmr().modify(|w| w.set_sr(true));
|
||||
@ -192,7 +191,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
|
||||
// TODO MTU size setting not found for v1 ethernet, check if correct
|
||||
|
||||
let hclk = <T as RccPeripheral>::frequency();
|
||||
let hclk = <T as SealedRccPeripheral>::frequency();
|
||||
let hclk_mhz = hclk.0 / 1_000_000;
|
||||
|
||||
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
||||
@ -235,8 +234,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let mac = ETH.ethernet_mac();
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
let dma = T::regs().ethernet_dma();
|
||||
|
||||
mac.maccr().modify(|w| {
|
||||
w.set_re(true);
|
||||
@ -275,7 +274,7 @@ pub struct EthernetStationManagement<T: Instance> {
|
||||
|
||||
unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
|
||||
mac.macmiiar().modify(|w| {
|
||||
w.set_pa(phy_addr);
|
||||
@ -289,7 +288,7 @@ unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
}
|
||||
|
||||
fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
|
||||
mac.macmiidr().write(|w| w.set_md(val));
|
||||
mac.macmiiar().modify(|w| {
|
||||
@ -305,8 +304,8 @@ unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
|
||||
impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> {
|
||||
fn drop(&mut self) {
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = ETH.ethernet_mac();
|
||||
let dma = T::regs().ethernet_dma();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
|
||||
// Disable the TX DMA and wait for any previous transmissions to be completed
|
||||
dma.dmaomr().modify(|w| w.set_st(St::STOPPED));
|
||||
|
@ -7,11 +7,10 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
|
||||
pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing};
|
||||
use super::*;
|
||||
use crate::gpio::sealed::{AFType, Pin as _};
|
||||
use crate::gpio::{AnyPin, Speed};
|
||||
use crate::gpio::{AFType, AnyPin, SealedPin as _, Speed};
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::pac::ETH;
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::rcc::SealedRccPeripheral;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
/// Interrupt handler.
|
||||
@ -207,9 +206,9 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
phy: P,
|
||||
mac_addr: [u8; 6],
|
||||
) -> Self {
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mtl = ETH.ethernet_mtl();
|
||||
let dma = T::regs().ethernet_dma();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
let mtl = T::regs().ethernet_mtl();
|
||||
|
||||
// Reset and wait
|
||||
dma.dmamr().modify(|w| w.set_swr(true));
|
||||
@ -265,7 +264,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
w.set_rbsz(RX_BUFFER_SIZE as u16);
|
||||
});
|
||||
|
||||
let hclk = <T as RccPeripheral>::frequency();
|
||||
let hclk = <T as SealedRccPeripheral>::frequency();
|
||||
let hclk_mhz = hclk.0 / 1_000_000;
|
||||
|
||||
// Set the MDC clock frequency in the range 1MHz - 2.5MHz
|
||||
@ -296,9 +295,9 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mtl = ETH.ethernet_mtl();
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
let mtl = T::regs().ethernet_mtl();
|
||||
let dma = T::regs().ethernet_dma();
|
||||
|
||||
mac.maccr().modify(|w| {
|
||||
w.set_re(true);
|
||||
@ -334,7 +333,7 @@ pub struct EthernetStationManagement<T: Instance> {
|
||||
|
||||
unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 {
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
|
||||
mac.macmdioar().modify(|w| {
|
||||
w.set_pa(phy_addr);
|
||||
@ -348,7 +347,7 @@ unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
}
|
||||
|
||||
fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) {
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
|
||||
mac.macmdiodr().write(|w| w.set_md(val));
|
||||
mac.macmdioar().modify(|w| {
|
||||
@ -364,9 +363,9 @@ unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
|
||||
impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> {
|
||||
fn drop(&mut self) {
|
||||
let dma = ETH.ethernet_dma();
|
||||
let mac = ETH.ethernet_mac();
|
||||
let mtl = ETH.ethernet_mtl();
|
||||
let dma = T::regs().ethernet_dma();
|
||||
let mac = T::regs().ethernet_mac();
|
||||
let mtl = T::regs().ethernet_mtl();
|
||||
|
||||
// Disable the TX DMA and wait for any previous transmissions to be completed
|
||||
dma.dmactx_cr().modify(|w| w.set_st(false));
|
||||
|
@ -330,12 +330,11 @@ macro_rules! impl_irq {
|
||||
|
||||
foreach_exti_irq!(impl_irq);
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Channel {}
|
||||
}
|
||||
trait SealedChannel {}
|
||||
|
||||
/// EXTI channel trait.
|
||||
pub trait Channel: sealed::Channel + Sized {
|
||||
#[allow(private_bounds)]
|
||||
pub trait Channel: SealedChannel + Sized {
|
||||
/// Get the EXTI channel number.
|
||||
fn number(&self) -> u8;
|
||||
|
||||
@ -359,7 +358,7 @@ pub struct AnyChannel {
|
||||
}
|
||||
|
||||
impl_peripheral!(AnyChannel);
|
||||
impl sealed::Channel for AnyChannel {}
|
||||
impl SealedChannel for AnyChannel {}
|
||||
impl Channel for AnyChannel {
|
||||
fn number(&self) -> u8 {
|
||||
self.number
|
||||
@ -368,7 +367,7 @@ impl Channel for AnyChannel {
|
||||
|
||||
macro_rules! impl_exti {
|
||||
($type:ident, $number:expr) => {
|
||||
impl sealed::Channel for peripherals::$type {}
|
||||
impl SealedChannel for peripherals::$type {}
|
||||
impl Channel for peripherals::$type {
|
||||
fn number(&self) -> u8 {
|
||||
$number
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, AtomicBool, Ordering};
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use core::convert::TryInto;
|
||||
use core::ptr::write_volatile;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
|
@ -3,8 +3,7 @@ use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::{Pull, Speed};
|
||||
use crate::gpio::{AFType, Pull, Speed};
|
||||
use crate::Peripheral;
|
||||
|
||||
/// FMC driver
|
||||
@ -44,7 +43,7 @@ where
|
||||
|
||||
/// Get the kernel clock currently in use for this FMC instance.
|
||||
pub fn source_clock_hz(&self) -> u32 {
|
||||
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
|
||||
<T as crate::rcc::SealedRccPeripheral>::frequency().0
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +68,7 @@ where
|
||||
}
|
||||
|
||||
fn source_clock_hz(&self) -> u32 {
|
||||
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
|
||||
<T as crate::rcc::SealedRccPeripheral>::frequency().0
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,18 +200,17 @@ impl<'d, T: Instance> Fmc<'d, T> {
|
||||
));
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait Instance: crate::rcc::sealed::RccPeripheral {
|
||||
const REGS: crate::pac::fmc::Fmc;
|
||||
}
|
||||
trait SealedInstance: crate::rcc::SealedRccPeripheral {
|
||||
const REGS: crate::pac::fmc::Fmc;
|
||||
}
|
||||
|
||||
/// FMC instance trait.
|
||||
pub trait Instance: sealed::Instance + 'static {}
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + 'static {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(fmc, $inst:ident) => {
|
||||
impl crate::fmc::sealed::Instance for crate::peripherals::$inst {
|
||||
impl crate::fmc::SealedInstance for crate::peripherals::$inst {
|
||||
const REGS: crate::pac::fmc::Fmc = crate::pac::$inst;
|
||||
}
|
||||
impl crate::fmc::Instance for crate::peripherals::$inst {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user