Merge branch 'main' of https://github.com/GustavToft/embassy
This commit is contained in:
ci.shrust-toolchain-nightly.tomlrust-toolchain.toml
cyw43/src
docs/modules/ROOT/pages
embassy-boot-nrf/src
embassy-boot-rp/src
embassy-boot-stm32/src
embassy-boot/src
embassy-embedded-hal/src/shared_bus
embassy-executor-macros/src
embassy-executor
embassy-futures/src
embassy-hal-internal/src
embassy-net-adin1110/src
embassy-net-driver-channel/src
embassy-net-enc28j60/src
embassy-net-esp-hosted/src
embassy-net-ppp/src
embassy-net-tuntap/src
embassy-net-wiznet/src/chip
embassy-net/src
embassy-nrf/src
embassy-rp/src
adc.rsclocks.rsdma.rsflash.rs
float
fmt.rsgpio.rsi2c.rsi2c_slave.rslib.rsmulticore.rspio
pwm.rsrelocate.rsrtc
uart
usb.rsembassy-stm32-wpan/src
embassy-stm32
Cargo.tomlbuild.rs
src
embassy-sync
embassy-time/src
embassy-usb-dfu/src
embassy-usb-logger/src
embassy-usb/src
examples
boot
nrf52840
src
rp
src
std
stm32f1
stm32f2
src
bin
stm32f3
src
stm32f4
stm32f7
src
stm32g0
src
stm32g4
src
stm32h5
src
stm32h7
stm32l1
src
stm32l4
stm32l5
stm32u5
src
tests
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,12 +2,10 @@
|
||||
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 trait Chip {
|
||||
pub(crate) trait SealedChip {
|
||||
type Address;
|
||||
|
||||
const COMMON_MODE: Self::Address;
|
||||
@ -33,18 +31,11 @@ pub(crate) mod sealed {
|
||||
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,8 +318,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
} else {
|
||||
if stat.rx_done() {
|
||||
} else if stat.rx_done() {
|
||||
p.ic_clr_rx_done().read();
|
||||
Poll::Ready(Ok(ReadStatus::Done))
|
||||
} else if stat.rd_req() && stat.tx_empty() {
|
||||
@ -327,7 +326,6 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
},
|
||||
|_me| {
|
||||
p.ic_intr_mask().write(|w| {
|
||||
|
@ -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 {
|
||||
#[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 {
|
||||
#[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;
|
||||
}
|
||||
|
||||
pub trait Instance: InterruptableInstance {
|
||||
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 AdcPin<T: Instance> {
|
||||
pub(crate) trait SealedAdcPin<T: Instance> {
|
||||
#[cfg(any(adc_v1, adc_l0, adc_v2))]
|
||||
fn set_as_analog(&mut self) {}
|
||||
|
||||
#[allow(unused)]
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InternalChannel<T> {
|
||||
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,32 +102,26 @@ 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 {
|
||||
type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
impl crate::adc::Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
||||
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,51 +631,26 @@ 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;
|
||||
|
||||
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 {
|
||||
enum RxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(ClassicBufferedRxInner),
|
||||
FdBuffered(FdBufferedRxInner),
|
||||
}
|
||||
ClassicBuffered(super::common::ClassicBufferedRxInner),
|
||||
FdBuffered(super::common::FdBufferedRxInner),
|
||||
}
|
||||
|
||||
impl RxMode {
|
||||
pub fn register(&self, arg: &core::task::Waker) {
|
||||
impl RxMode {
|
||||
fn register(&self, arg: &core::task::Waker) {
|
||||
match self {
|
||||
RxMode::NonBuffered(waker) => waker.register(arg),
|
||||
_ => {
|
||||
@ -718,25 +659,57 @@ pub(crate) mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_interrupt<T: Instance>(&self, fifonr: 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.read::<T, _>() {
|
||||
if let Some(result) = self.try_read::<T>() {
|
||||
let _ = buf.rx_sender.try_send(result);
|
||||
}
|
||||
}
|
||||
RxMode::FdBuffered(buf) => {
|
||||
if let Some(result) = self.read::<T, _>() {
|
||||
if let Some(result) = self.try_read_fd::<T>() {
|
||||
let _ = buf.rx_sender.try_send(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
@ -764,23 +737,29 @@ pub(crate) mod sealed {
|
||||
.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
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TxMode {
|
||||
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(ClassicBufferedTxInner),
|
||||
FdBuffered(FdBufferedTxInner),
|
||||
}
|
||||
ClassicBuffered(super::common::ClassicBufferedTxInner),
|
||||
FdBuffered(super::common::FdBufferedTxInner),
|
||||
}
|
||||
|
||||
impl TxMode {
|
||||
pub fn register(&self, arg: &core::task::Waker) {
|
||||
impl TxMode {
|
||||
fn register(&self, arg: &core::task::Waker) {
|
||||
match self {
|
||||
TxMode::NonBuffered(waker) => {
|
||||
waker.register(arg);
|
||||
@ -814,7 +793,7 @@ pub(crate) mod sealed {
|
||||
/// 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> {
|
||||
async fn write<T: Instance>(&self, frame: &Frame) -> Option<Frame> {
|
||||
self.write_generic::<T, _>(frame).await
|
||||
}
|
||||
|
||||
@ -822,21 +801,21 @@ pub(crate) mod sealed {
|
||||
/// 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> {
|
||||
async fn write_fd<T: Instance>(&self, frame: &FdFrame) -> Option<FdFrame> {
|
||||
self.write_generic::<T, _>(frame).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
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 {
|
||||
impl State {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
|
||||
tx_mode: TxMode::NonBuffered(AtomicWaker::new()),
|
||||
@ -844,25 +823,24 @@ pub(crate) mod sealed {
|
||||
err_waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
trait SealedInstance {
|
||||
const MSG_RAM_OFFSET: usize;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
pub(crate) trait SealedChannel {
|
||||
fn id(&self) -> u8;
|
||||
}
|
||||
pub trait ChannelInterrupt {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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
Reference in New Issue
Block a user