This commit is contained in:
Gustav Toft 2024-04-04 15:52:44 +02:00
commit a373633d0d
222 changed files with 5557 additions and 4554 deletions

1
ci.sh
View File

@ -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 \

View File

@ -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> {

View File

@ -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.

View File

@ -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> {

View File

@ -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> {

View File

@ -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> {

View File

@ -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

View File

@ -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> {

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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"))]

View File

@ -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.
///

View File

@ -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> {

View File

@ -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;

View File

@ -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;

View File

@ -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> {

View File

@ -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> {

View File

@ -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;
}

View File

@ -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 {

View File

@ -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> {

View File

@ -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> {

View File

@ -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 })
}

View File

@ -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> {

View File

@ -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> {

View File

@ -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.

View File

@ -2,49 +2,40 @@
mod w5500;
pub use w5500::W5500;
mod w5100s;
use embedded_hal_async::spi::SpiDevice;
pub use w5100s::W5100S;
pub(crate) mod sealed {
use embedded_hal_async::spi::SpiDevice;
pub(crate) trait SealedChip {
type Address;
pub trait Chip {
type Address;
const COMMON_MODE: Self::Address;
const COMMON_MAC: Self::Address;
const COMMON_SOCKET_INTR: Self::Address;
const COMMON_PHY_CFG: Self::Address;
const SOCKET_MODE: Self::Address;
const SOCKET_COMMAND: Self::Address;
const SOCKET_RXBUF_SIZE: Self::Address;
const SOCKET_TXBUF_SIZE: Self::Address;
const SOCKET_TX_FREE_SIZE: Self::Address;
const SOCKET_TX_DATA_WRITE_PTR: Self::Address;
const SOCKET_RECVD_SIZE: Self::Address;
const SOCKET_RX_DATA_READ_PTR: Self::Address;
const SOCKET_INTR_MASK: Self::Address;
const SOCKET_INTR: Self::Address;
const COMMON_MODE: Self::Address;
const COMMON_MAC: Self::Address;
const COMMON_SOCKET_INTR: Self::Address;
const COMMON_PHY_CFG: Self::Address;
const SOCKET_MODE: Self::Address;
const SOCKET_COMMAND: Self::Address;
const SOCKET_RXBUF_SIZE: Self::Address;
const SOCKET_TXBUF_SIZE: Self::Address;
const SOCKET_TX_FREE_SIZE: Self::Address;
const SOCKET_TX_DATA_WRITE_PTR: Self::Address;
const SOCKET_RECVD_SIZE: Self::Address;
const SOCKET_RX_DATA_READ_PTR: Self::Address;
const SOCKET_INTR_MASK: Self::Address;
const SOCKET_INTR: Self::Address;
const SOCKET_MODE_VALUE: u8;
const SOCKET_MODE_VALUE: u8;
const BUF_SIZE: u16;
const AUTO_WRAP: bool;
const BUF_SIZE: u16;
const AUTO_WRAP: bool;
fn rx_addr(addr: u16) -> Self::Address;
fn tx_addr(addr: u16) -> Self::Address;
fn rx_addr(addr: u16) -> Self::Address;
fn tx_addr(addr: u16) -> Self::Address;
async fn bus_read<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &mut [u8],
) -> Result<(), SPI::Error>;
async fn bus_write<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &[u8],
) -> Result<(), SPI::Error>;
}
async fn bus_read<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &mut [u8])
-> Result<(), SPI::Error>;
async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error>;
}
/// Trait for Wiznet chips.
pub trait Chip: sealed::Chip {}
#[allow(private_bounds)]
pub trait Chip: SealedChip {}

View File

@ -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;

View File

@ -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);

View File

@ -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> {

View File

@ -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> {

View File

@ -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 {

View File

@ -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;

View File

@ -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();
}

View File

@ -21,8 +21,6 @@ pub(crate) mod sealed {
fn regs() -> &'static pac::timer0::RegisterBlock;
}
pub trait ExtendedInstance {}
pub trait TimerType {}
}
/// Basic Timer instance.

View File

@ -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()),
}
}

View File

@ -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 _));

View File

@ -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(),

View File

@ -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;

View File

@ -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
}

View File

@ -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> {

View File

@ -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> {

View File

@ -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?;
}
}
}

View File

@ -289,7 +289,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> {
let p = T::regs();
if buffer.len() == 0 {
if buffer.is_empty() {
return Err(Error::InvalidResponseBufferLength);
}
@ -318,15 +318,13 @@ impl<'d, T: Instance> I2cSlave<'d, T> {
}
Poll::Pending
} else if stat.rx_done() {
p.ic_clr_rx_done().read();
Poll::Ready(Ok(ReadStatus::Done))
} else if stat.rd_req() && stat.tx_empty() {
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
} else {
if stat.rx_done() {
p.ic_clr_rx_done().read();
Poll::Ready(Ok(ReadStatus::Done))
} else if stat.rd_req() && stat.tx_empty() {
Poll::Ready(Ok(ReadStatus::NeedMoreBytes))
} else {
Poll::Pending
}
Poll::Pending
}
},
|_me| {

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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
})
}
})
}
}

View File

@ -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.

View File

@ -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();
}
}

View File

@ -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| {

View File

@ -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.

View File

@ -1,5 +1,3 @@
use core::convert::TryFrom;
use crate::evt::CsEvt;
use crate::PacketHeader;

View File

@ -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> {

View File

@ -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]

View File

@ -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();
)*
}
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -17,6 +17,8 @@ mod _version;
#[allow(unused)]
#[cfg(not(adc_f3_v2))]
pub use _version::*;
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(not(any(adc_f1, adc_f3_v2)))]
pub use crate::pac::adc::vals::Res as Resolution;
@ -31,63 +33,65 @@ pub struct Adc<'d, T: Instance> {
sample_time: SampleTime,
}
pub(crate) mod sealed {
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
pub struct State {
pub waker: AtomicWaker,
}
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
pub struct State {
pub waker: AtomicWaker,
}
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait InterruptableInstance {
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
trait SealedInstance {
#[allow(unused)]
fn regs() -> crate::pac::adc::Adc;
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon;
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
fn state() -> &'static State;
}
pub trait Instance: InterruptableInstance {
fn regs() -> crate::pac::adc::Adc;
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon;
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
fn state() -> &'static State;
}
pub(crate) trait SealedAdcPin<T: Instance> {
#[cfg(any(adc_v1, adc_l0, adc_v2))]
fn set_as_analog(&mut self) {}
pub trait AdcPin<T: Instance> {
#[cfg(any(adc_v1, adc_l0, adc_v2))]
fn set_as_analog(&mut self) {}
#[allow(unused)]
fn channel(&self) -> u8;
}
fn channel(&self) -> u8;
}
pub trait InternalChannel<T> {
fn channel(&self) -> u8;
}
trait SealedInternalChannel<T> {
#[allow(unused)]
fn channel(&self) -> u8;
}
/// ADC instance.
#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))]
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + crate::Peripheral<P = Self> {
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
/// ADC instance.
#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))]
pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
/// ADC pin.
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
#[allow(private_bounds)]
pub trait AdcPin<T: Instance>: SealedAdcPin<T> {}
/// ADC internal channel.
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
#[allow(private_bounds)]
pub trait InternalChannel<T>: SealedInternalChannel<T> {}
foreach_adc!(
($inst:ident, $common_inst:ident, $clock:ident) => {
impl crate::adc::sealed::Instance for peripherals::$inst {
impl crate::adc::SealedInstance for peripherals::$inst {
fn regs() -> crate::pac::adc::Adc {
crate::pac::$inst
}
@ -98,21 +102,15 @@ foreach_adc!(
}
#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))]
fn state() -> &'static sealed::State {
static STATE: sealed::State = sealed::State::new();
fn state() -> &'static State {
static STATE: State = State::new();
&STATE
}
}
foreach_interrupt!(
($inst,adc,ADC,GLOBAL,$irq:ident) => {
impl sealed::InterruptableInstance for peripherals::$inst {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
);
impl crate::adc::Instance for peripherals::$inst {}
impl crate::adc::Instance for peripherals::$inst {
type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL;
}
};
);
@ -120,10 +118,10 @@ macro_rules! impl_adc_pin {
($inst:ident, $pin:ident, $ch:expr) => {
impl crate::adc::AdcPin<peripherals::$inst> for crate::peripherals::$pin {}
impl crate::adc::sealed::AdcPin<peripherals::$inst> for crate::peripherals::$pin {
impl crate::adc::SealedAdcPin<peripherals::$inst> for crate::peripherals::$pin {
#[cfg(any(adc_v1, adc_l0, adc_v2))]
fn set_as_analog(&mut self) {
<Self as crate::gpio::sealed::Pin>::set_as_analog(self);
<Self as crate::gpio::SealedPin>::set_as_analog(self);
}
fn channel(&self) -> u8 {

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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();

View File

@ -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 frames 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
}
}

View File

@ -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,
}
}
}

View File

@ -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

View 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,
}
}
}

View 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 frames 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,
}

View 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>>;

View File

@ -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,
}

View File

@ -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]);

View File

@ -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 {

View File

@ -5,24 +5,24 @@ use core::task::Poll;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel;
use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender};
use embassy_sync::waitqueue::AtomicWaker;
use crate::can::fd::peripheral::Registers;
use crate::gpio::sealed::AFType;
use crate::gpio::AFType;
use crate::interrupt::typelevel::Interrupt;
use crate::rcc::RccPeripheral;
use crate::{interrupt, peripherals, Peripheral};
pub mod enums;
pub(crate) mod fd;
pub mod frame;
mod util;
use enums::*;
use fd::config::*;
use fd::filter::*;
pub use fd::{config, filter};
use frame::*;
use self::fd::config::*;
use self::fd::filter::*;
pub use self::fd::{config, filter};
pub use super::common::{BufferedCanReceiver, BufferedCanSender};
use super::enums::*;
use super::frame::*;
use super::util;
/// Timestamp for incoming packets. Use Embassy time when enabled.
#[cfg(feature = "time")]
@ -53,8 +53,8 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
}
match &T::state().tx_mode {
sealed::TxMode::NonBuffered(waker) => waker.wake(),
sealed::TxMode::ClassicBuffered(buf) => {
TxMode::NonBuffered(waker) => waker.wake(),
TxMode::ClassicBuffered(buf) => {
if !T::registers().tx_queue_is_full() {
match buf.tx_receiver.try_receive() {
Ok(frame) => {
@ -64,7 +64,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
}
}
}
sealed::TxMode::FdBuffered(buf) => {
TxMode::FdBuffered(buf) => {
if !T::registers().tx_queue_is_full() {
match buf.tx_receiver.try_receive() {
Ok(frame) => {
@ -106,7 +106,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT1Interrupt> for IT1Interrup
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Different operating modes
pub enum FdcanOperatingMode {
pub enum OperatingMode {
//PoweredDownMode,
//ConfigMode,
/// This mode can be used for a “Hot Selftest”, meaning the FDCAN can be tested without
@ -144,7 +144,7 @@ pub enum FdcanOperatingMode {
/// FDCAN Configuration instance instance
/// Create instance of this first
pub struct FdcanConfigurator<'d, T: Instance> {
pub struct CanConfigurator<'d, T: Instance> {
config: crate::can::fd::config::FdCanConfig,
/// Reference to internals.
instance: FdcanInstance<'d, T>,
@ -165,7 +165,7 @@ fn calc_ns_per_timer_tick<T: Instance>(mode: crate::can::fd::config::FrameTransm
}
}
impl<'d, T: Instance> FdcanConfigurator<'d, T> {
impl<'d, T: Instance> CanConfigurator<'d, T> {
/// Creates a new Fdcan instance, keeping the peripheral in sleep mode.
/// You must call [Fdcan::enable_non_blocking] to use the peripheral.
pub fn new(
@ -175,7 +175,7 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
_irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>>
+ interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>>
+ 'd,
) -> FdcanConfigurator<'d, T> {
) -> CanConfigurator<'d, T> {
into_ref!(peri, rx, tx);
rx.set_as_af(rx.af_num(), AFType::Input);
@ -269,13 +269,13 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
}
/// Start in mode.
pub fn start(self, mode: FdcanOperatingMode) -> Fdcan<'d, T> {
pub fn start(self, mode: OperatingMode) -> Can<'d, T> {
let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(self.config.frame_transmit);
critical_section::with(|_| unsafe {
T::mut_state().ns_per_timer_tick = ns_per_timer_tick;
});
T::registers().into_mode(self.config, mode);
let ret = Fdcan {
let ret = Can {
config: self.config,
instance: self.instance,
_mode: mode,
@ -284,30 +284,30 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
}
/// Start, entering mode. Does same as start(mode)
pub fn into_normal_mode(self) -> Fdcan<'d, T> {
self.start(FdcanOperatingMode::NormalOperationMode)
pub fn into_normal_mode(self) -> Can<'d, T> {
self.start(OperatingMode::NormalOperationMode)
}
/// Start, entering mode. Does same as start(mode)
pub fn into_internal_loopback_mode(self) -> Fdcan<'d, T> {
self.start(FdcanOperatingMode::InternalLoopbackMode)
pub fn into_internal_loopback_mode(self) -> Can<'d, T> {
self.start(OperatingMode::InternalLoopbackMode)
}
/// Start, entering mode. Does same as start(mode)
pub fn into_external_loopback_mode(self) -> Fdcan<'d, T> {
self.start(FdcanOperatingMode::ExternalLoopbackMode)
pub fn into_external_loopback_mode(self) -> Can<'d, T> {
self.start(OperatingMode::ExternalLoopbackMode)
}
}
/// FDCAN Instance
pub struct Fdcan<'d, T: Instance> {
pub struct Can<'d, T: Instance> {
config: crate::can::fd::config::FdCanConfig,
/// Reference to internals.
instance: FdcanInstance<'d, T>,
_mode: FdcanOperatingMode,
_mode: OperatingMode,
}
impl<'d, T: Instance> Fdcan<'d, T> {
impl<'d, T: Instance> Can<'d, T> {
/// Flush one of the TX mailboxes.
pub async fn flush(&self, idx: usize) {
poll_fn(|cx| {
@ -330,12 +330,12 @@ impl<'d, T: Instance> Fdcan<'d, T> {
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
pub async fn write(&mut self, frame: &ClassicFrame) -> Option<ClassicFrame> {
pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
T::state().tx_mode.write::<T>(frame).await
}
/// Returns the next received message frame
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
pub async fn read(&mut self) -> Result<Envelope, BusError> {
T::state().rx_mode.read_classic::<T>().await
}
@ -348,19 +348,19 @@ impl<'d, T: Instance> Fdcan<'d, T> {
}
/// Returns the next received message frame
pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {
T::state().rx_mode.read_fd::<T>().await
}
/// Split instance into separate Tx(write) and Rx(read) portions
pub fn split(self) -> (FdcanTx<'d, T>, FdcanRx<'d, T>) {
pub fn split(self) -> (CanTx<'d, T>, CanRx<'d, T>) {
(
FdcanTx {
CanTx {
config: self.config,
_instance: self.instance,
_mode: self._mode,
},
FdcanRx {
CanRx {
_instance1: PhantomData::<T>,
_instance2: T::regs(),
_mode: self._mode,
@ -369,8 +369,8 @@ impl<'d, T: Instance> Fdcan<'d, T> {
}
/// Join split rx and tx portions back together
pub fn join(tx: FdcanTx<'d, T>, rx: FdcanRx<'d, T>) -> Self {
Fdcan {
pub fn join(tx: CanTx<'d, T>, rx: CanRx<'d, T>) -> Self {
Can {
config: tx.config,
//_instance2: T::regs(),
instance: tx._instance,
@ -398,59 +398,27 @@ impl<'d, T: Instance> Fdcan<'d, T> {
}
/// User supplied buffer for RX Buffering
pub type RxBuf<const BUF_SIZE: usize> =
Channel<CriticalSectionRawMutex, Result<(ClassicFrame, Timestamp), BusError>, BUF_SIZE>;
pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>;
/// User supplied buffer for TX buffering
pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, ClassicFrame, BUF_SIZE>;
pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>;
/// Buffered FDCAN Instance
pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
_instance1: PhantomData<T>,
_instance2: &'d crate::pac::can::Fdcan,
_mode: FdcanOperatingMode,
_mode: OperatingMode,
tx_buf: &'static TxBuf<TX_BUF_SIZE>,
rx_buf: &'static RxBuf<RX_BUF_SIZE>,
}
/// Sender that can be used for sending CAN frames.
#[derive(Copy, Clone)]
pub struct BufferedCanSender {
tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>,
waker: fn(),
}
impl BufferedCanSender {
/// Async write frame to TX buffer.
pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError<ClassicFrame>> {
self.tx_buf.try_send(frame)?;
(self.waker)();
Ok(())
}
/// Async write frame to TX buffer.
pub async fn write(&mut self, frame: ClassicFrame) {
self.tx_buf.send(frame).await;
(self.waker)();
}
/// Allows a poll_fn to poll until the channel is ready to write
pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {
self.tx_buf.poll_ready_to_send(cx)
}
}
/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
pub type BufferedCanReceiver =
embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>;
impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE>
{
fn new(
_instance1: PhantomData<T>,
_instance2: &'d crate::pac::can::Fdcan,
_mode: FdcanOperatingMode,
_mode: OperatingMode,
tx_buf: &'static TxBuf<TX_BUF_SIZE>,
rx_buf: &'static RxBuf<RX_BUF_SIZE>,
) -> Self {
@ -467,26 +435,26 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
fn setup(self) -> Self {
// We don't want interrupts being processed while we change modes.
critical_section::with(|_| unsafe {
let rx_inner = sealed::ClassicBufferedRxInner {
let rx_inner = super::common::ClassicBufferedRxInner {
rx_sender: self.rx_buf.sender().into(),
};
let tx_inner = sealed::ClassicBufferedTxInner {
let tx_inner = super::common::ClassicBufferedTxInner {
tx_receiver: self.tx_buf.receiver().into(),
};
T::mut_state().rx_mode = sealed::RxMode::ClassicBuffered(rx_inner);
T::mut_state().tx_mode = sealed::TxMode::ClassicBuffered(tx_inner);
T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner);
T::mut_state().tx_mode = TxMode::ClassicBuffered(tx_inner);
});
self
}
/// Async write frame to TX buffer.
pub async fn write(&mut self, frame: ClassicFrame) {
pub async fn write(&mut self, frame: Frame) {
self.tx_buf.send(frame).await;
T::IT0Interrupt::pend(); // Wake for Tx
}
/// Async read frame from RX buffer.
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
pub async fn read(&mut self) -> Result<Envelope, BusError> {
self.rx_buf.receive().await
}
@ -509,15 +477,14 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr
{
fn drop(&mut self) {
critical_section::with(|_| unsafe {
T::mut_state().rx_mode = sealed::RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
T::mut_state().tx_mode = sealed::TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
});
}
}
/// User supplied buffer for RX Buffering
pub type RxFdBuf<const BUF_SIZE: usize> =
Channel<CriticalSectionRawMutex, Result<(FdFrame, Timestamp), BusError>, BUF_SIZE>;
pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>;
/// User supplied buffer for TX buffering
pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFrame, BUF_SIZE>;
@ -526,7 +493,7 @@ pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFra
pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
_instance1: PhantomData<T>,
_instance2: &'d crate::pac::can::Fdcan,
_mode: FdcanOperatingMode,
_mode: OperatingMode,
tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,
rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,
}
@ -534,7 +501,7 @@ pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF
/// Sender that can be used for sending CAN frames.
#[derive(Copy, Clone)]
pub struct BufferedFdCanSender {
tx_buf: embassy_sync::channel::DynamicSender<'static, FdFrame>,
tx_buf: DynamicSender<'static, FdFrame>,
waker: fn(),
}
@ -559,8 +526,7 @@ impl BufferedFdCanSender {
}
/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
pub type BufferedFdCanReceiver =
embassy_sync::channel::DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>;
pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<FdEnvelope, BusError>>;
impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE>
@ -568,7 +534,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
fn new(
_instance1: PhantomData<T>,
_instance2: &'d crate::pac::can::Fdcan,
_mode: FdcanOperatingMode,
_mode: OperatingMode,
tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,
rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,
) -> Self {
@ -585,14 +551,14 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
fn setup(self) -> Self {
// We don't want interrupts being processed while we change modes.
critical_section::with(|_| unsafe {
let rx_inner = sealed::FdBufferedRxInner {
let rx_inner = super::common::FdBufferedRxInner {
rx_sender: self.rx_buf.sender().into(),
};
let tx_inner = sealed::FdBufferedTxInner {
let tx_inner = super::common::FdBufferedTxInner {
tx_receiver: self.tx_buf.receiver().into(),
};
T::mut_state().rx_mode = sealed::RxMode::FdBuffered(rx_inner);
T::mut_state().tx_mode = sealed::TxMode::FdBuffered(tx_inner);
T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner);
T::mut_state().tx_mode = TxMode::FdBuffered(tx_inner);
});
self
}
@ -604,7 +570,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
}
/// Async read frame from RX buffer.
pub async fn read(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
pub async fn read(&mut self) -> Result<FdEnvelope, BusError> {
self.rx_buf.receive().await
}
@ -627,32 +593,32 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr
{
fn drop(&mut self) {
critical_section::with(|_| unsafe {
T::mut_state().rx_mode = sealed::RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
T::mut_state().tx_mode = sealed::TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
});
}
}
/// FDCAN Rx only Instance
pub struct FdcanRx<'d, T: Instance> {
pub struct CanRx<'d, T: Instance> {
_instance1: PhantomData<T>,
_instance2: &'d crate::pac::can::Fdcan,
_mode: FdcanOperatingMode,
_mode: OperatingMode,
}
/// FDCAN Tx only Instance
pub struct FdcanTx<'d, T: Instance> {
pub struct CanTx<'d, T: Instance> {
config: crate::can::fd::config::FdCanConfig,
_instance: FdcanInstance<'d, T>, //(PeripheralRef<'a, T>);
_mode: FdcanOperatingMode,
_mode: OperatingMode,
}
impl<'c, 'd, T: Instance> FdcanTx<'d, T> {
impl<'c, 'd, T: Instance> CanTx<'d, T> {
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
pub async fn write(&mut self, frame: &ClassicFrame) -> Option<ClassicFrame> {
pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
T::state().tx_mode.write::<T>(frame).await
}
@ -665,204 +631,216 @@ impl<'c, 'd, T: Instance> FdcanTx<'d, T> {
}
}
impl<'c, 'd, T: Instance> FdcanRx<'d, T> {
impl<'c, 'd, T: Instance> CanRx<'d, T> {
/// Returns the next received message frame
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
pub async fn read(&mut self) -> Result<Envelope, BusError> {
T::state().rx_mode.read_classic::<T>().await
}
/// Returns the next received message frame
pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {
T::state().rx_mode.read_fd::<T>().await
}
}
pub(crate) mod sealed {
use core::future::poll_fn;
use core::task::Poll;
enum RxMode {
NonBuffered(AtomicWaker),
ClassicBuffered(super::common::ClassicBufferedRxInner),
FdBuffered(super::common::FdBufferedRxInner),
}
use embassy_sync::channel::{DynamicReceiver, DynamicSender};
use embassy_sync::waitqueue::AtomicWaker;
use super::CanHeader;
use crate::can::_version::{BusError, Timestamp};
use crate::can::frame::{ClassicFrame, FdFrame};
pub struct ClassicBufferedRxInner {
pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>,
}
pub struct ClassicBufferedTxInner {
pub tx_receiver: DynamicReceiver<'static, ClassicFrame>,
}
pub struct FdBufferedRxInner {
pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>,
}
pub struct FdBufferedTxInner {
pub tx_receiver: DynamicReceiver<'static, FdFrame>,
}
pub enum RxMode {
NonBuffered(AtomicWaker),
ClassicBuffered(ClassicBufferedRxInner),
FdBuffered(FdBufferedRxInner),
}
impl RxMode {
pub fn register(&self, arg: &core::task::Waker) {
match self {
RxMode::NonBuffered(waker) => waker.register(arg),
_ => {
panic!("Bad Mode")
}
}
}
pub fn on_interrupt<T: Instance>(&self, fifonr: usize) {
T::regs().ir().write(|w| w.set_rfn(fifonr, true));
match self {
RxMode::NonBuffered(waker) => {
waker.wake();
}
RxMode::ClassicBuffered(buf) => {
if let Some(result) = self.read::<T, _>() {
let _ = buf.rx_sender.try_send(result);
}
}
RxMode::FdBuffered(buf) => {
if let Some(result) = self.read::<T, _>() {
let _ = buf.rx_sender.try_send(result);
}
}
}
}
fn read<T: Instance, F: CanHeader>(&self) -> Option<Result<(F, Timestamp), BusError>> {
if let Some((msg, ts)) = T::registers().read(0) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
Some(Ok((msg, ts)))
} else if let Some((msg, ts)) = T::registers().read(1) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
Some(Ok((msg, ts)))
} else if let Some(err) = T::registers().curr_error() {
// TODO: this is probably wrong
Some(Err(err))
} else {
None
}
}
async fn read_async<T: Instance, F: CanHeader>(&self) -> Result<(F, Timestamp), BusError> {
poll_fn(|cx| {
T::state().err_waker.register(cx.waker());
self.register(cx.waker());
match self.read::<T, _>() {
Some(result) => Poll::Ready(result),
None => Poll::Pending,
}
})
.await
}
pub async fn read_classic<T: Instance>(&self) -> Result<(ClassicFrame, Timestamp), BusError> {
self.read_async::<T, _>().await
}
pub async fn read_fd<T: Instance>(&self) -> Result<(FdFrame, Timestamp), BusError> {
self.read_async::<T, _>().await
}
}
pub enum TxMode {
NonBuffered(AtomicWaker),
ClassicBuffered(ClassicBufferedTxInner),
FdBuffered(FdBufferedTxInner),
}
impl TxMode {
pub fn register(&self, arg: &core::task::Waker) {
match self {
TxMode::NonBuffered(waker) => {
waker.register(arg);
}
_ => {
panic!("Bad mode");
}
}
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
async fn write_generic<T: Instance, F: embedded_can::Frame + CanHeader>(&self, frame: &F) -> Option<F> {
poll_fn(|cx| {
self.register(cx.waker());
if let Ok(dropped) = T::registers().write(frame) {
return Poll::Ready(dropped);
}
// Couldn't replace any lower priority frames. Need to wait for some mailboxes
// to clear.
Poll::Pending
})
.await
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
pub async fn write<T: Instance>(&self, frame: &ClassicFrame) -> Option<ClassicFrame> {
self.write_generic::<T, _>(frame).await
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
pub async fn write_fd<T: Instance>(&self, frame: &FdFrame) -> Option<FdFrame> {
self.write_generic::<T, _>(frame).await
}
}
pub struct State {
pub rx_mode: RxMode,
pub tx_mode: TxMode,
pub ns_per_timer_tick: u64,
pub err_waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
tx_mode: TxMode::NonBuffered(AtomicWaker::new()),
ns_per_timer_tick: 0,
err_waker: AtomicWaker::new(),
impl RxMode {
fn register(&self, arg: &core::task::Waker) {
match self {
RxMode::NonBuffered(waker) => waker.register(arg),
_ => {
panic!("Bad Mode")
}
}
}
pub trait Instance {
const MSG_RAM_OFFSET: usize;
fn on_interrupt<T: Instance>(&self, fifonr: usize) {
T::regs().ir().write(|w| w.set_rfn(fifonr, true));
match self {
RxMode::NonBuffered(waker) => {
waker.wake();
}
RxMode::ClassicBuffered(buf) => {
if let Some(result) = self.try_read::<T>() {
let _ = buf.rx_sender.try_send(result);
}
}
RxMode::FdBuffered(buf) => {
if let Some(result) = self.try_read_fd::<T>() {
let _ = buf.rx_sender.try_send(result);
}
}
}
}
fn regs() -> &'static crate::pac::can::Fdcan;
fn registers() -> crate::can::fd::peripheral::Registers;
fn ram() -> &'static crate::pac::fdcanram::Fdcanram;
fn state() -> &'static State;
unsafe fn mut_state() -> &'static mut State;
fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp;
//async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
fn try_read<T: Instance>(&self) -> Option<Result<Envelope, BusError>> {
if let Some((frame, ts)) = T::registers().read(0) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
Some(Ok(Envelope { ts, frame }))
} else if let Some((frame, ts)) = T::registers().read(1) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
Some(Ok(Envelope { ts, frame }))
} else if let Some(err) = T::registers().curr_error() {
// TODO: this is probably wrong
Some(Err(err))
} else {
None
}
}
//async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
fn try_read_fd<T: Instance>(&self) -> Option<Result<FdEnvelope, BusError>> {
if let Some((frame, ts)) = T::registers().read(0) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
Some(Ok(FdEnvelope { ts, frame }))
} else if let Some((frame, ts)) = T::registers().read(1) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
Some(Ok(FdEnvelope { ts, frame }))
} else if let Some(err) = T::registers().curr_error() {
// TODO: this is probably wrong
Some(Err(err))
} else {
None
}
}
fn read<T: Instance, F: CanHeader>(&self) -> Option<Result<(F, Timestamp), BusError>> {
if let Some((msg, ts)) = T::registers().read(0) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
Some(Ok((msg, ts)))
} else if let Some((msg, ts)) = T::registers().read(1) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
Some(Ok((msg, ts)))
} else if let Some(err) = T::registers().curr_error() {
// TODO: this is probably wrong
Some(Err(err))
} else {
None
}
}
async fn read_async<T: Instance, F: CanHeader>(&self) -> Result<(F, Timestamp), BusError> {
poll_fn(|cx| {
T::state().err_waker.register(cx.waker());
self.register(cx.waker());
match self.read::<T, _>() {
Some(result) => Poll::Ready(result),
None => Poll::Pending,
}
})
.await
}
async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
match self.read_async::<T, _>().await {
Ok((frame, ts)) => Ok(Envelope { ts, frame }),
Err(e) => Err(e),
}
}
async fn read_fd<T: Instance>(&self) -> Result<FdEnvelope, BusError> {
match self.read_async::<T, _>().await {
Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }),
Err(e) => Err(e),
}
}
}
enum TxMode {
NonBuffered(AtomicWaker),
ClassicBuffered(super::common::ClassicBufferedTxInner),
FdBuffered(super::common::FdBufferedTxInner),
}
impl TxMode {
fn register(&self, arg: &core::task::Waker) {
match self {
TxMode::NonBuffered(waker) => {
waker.register(arg);
}
_ => {
panic!("Bad mode");
}
}
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
async fn write_generic<T: Instance, F: embedded_can::Frame + CanHeader>(&self, frame: &F) -> Option<F> {
poll_fn(|cx| {
self.register(cx.waker());
if let Ok(dropped) = T::registers().write(frame) {
return Poll::Ready(dropped);
}
// Couldn't replace any lower priority frames. Need to wait for some mailboxes
// to clear.
Poll::Pending
})
.await
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
async fn write<T: Instance>(&self, frame: &Frame) -> Option<Frame> {
self.write_generic::<T, _>(frame).await
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
async fn write_fd<T: Instance>(&self, frame: &FdFrame) -> Option<FdFrame> {
self.write_generic::<T, _>(frame).await
}
}
struct State {
pub rx_mode: RxMode,
pub tx_mode: TxMode,
pub ns_per_timer_tick: u64,
pub err_waker: AtomicWaker,
}
impl State {
const fn new() -> Self {
Self {
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
tx_mode: TxMode::NonBuffered(AtomicWaker::new()),
ns_per_timer_tick: 0,
err_waker: AtomicWaker::new(),
}
}
}
trait SealedInstance {
const MSG_RAM_OFFSET: usize;
fn regs() -> &'static crate::pac::can::Fdcan;
fn registers() -> crate::can::fd::peripheral::Registers;
fn state() -> &'static State;
unsafe fn mut_state() -> &'static mut State;
fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp;
}
/// Instance trait
pub trait Instance: sealed::Instance + RccPeripheral + 'static {
#[allow(private_bounds)]
pub trait Instance: SealedInstance + RccPeripheral + 'static {
/// Interrupt 0
type IT0Interrupt: crate::interrupt::typelevel::Interrupt;
/// Interrupt 0
/// Interrupt 1
type IT1Interrupt: crate::interrupt::typelevel::Interrupt;
}
@ -871,7 +849,7 @@ pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>);
macro_rules! impl_fdcan {
($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => {
impl sealed::Instance for peripherals::$inst {
impl SealedInstance for peripherals::$inst {
const MSG_RAM_OFFSET: usize = $msg_ram_offset;
fn regs() -> &'static crate::pac::can::Fdcan {
@ -880,14 +858,11 @@ macro_rules! impl_fdcan {
fn registers() -> Registers {
Registers{regs: &crate::pac::$inst, msgram: &crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET}
}
fn ram() -> &'static crate::pac::fdcanram::Fdcanram {
&crate::pac::$msg_ram_inst
unsafe fn mut_state() -> &'static mut State {
static mut STATE: State = State::new();
&mut *core::ptr::addr_of_mut!(STATE)
}
unsafe fn mut_state() -> & 'static mut sealed::State {
static mut STATE: sealed::State = sealed::State::new();
& mut STATE
}
fn state() -> &'static sealed::State {
fn state() -> &'static State {
unsafe { peripherals::$inst::mut_state() }
}

View File

@ -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)
}
}

View File

@ -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;

View File

@ -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.

View File

@ -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.

View File

@ -1885,16 +1885,13 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> {
}
}
pub(crate) mod sealed {
use super::*;
pub trait Instance {
fn regs() -> pac::cryp::Cryp;
}
trait SealedInstance {
fn regs() -> pac::cryp::Cryp;
}
/// CRYP instance trait.
pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
/// Interrupt for this CRYP instance.
type Interrupt: interrupt::typelevel::Interrupt;
}
@ -1905,7 +1902,7 @@ foreach_interrupt!(
type Interrupt = crate::interrupt::typelevel::$irq;
}
impl sealed::Instance for peripherals::$inst {
impl SealedInstance for peripherals::$inst {
fn regs() -> crate::pac::cryp::Cryp {
crate::pac::$inst
}

View File

@ -127,7 +127,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> {
pub fn new(
_peri: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = DMA> + 'd,
pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::sealed::Pin> + 'd,
pin: impl Peripheral<P = impl DacPin<T, N> + crate::gpio::Pin> + 'd,
) -> Self {
into_ref!(dma, pin);
pin.set_as_analog();
@ -392,8 +392,8 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> {
_peri: impl Peripheral<P = T> + 'd,
dma_ch1: impl Peripheral<P = DMACh1> + 'd,
dma_ch2: impl Peripheral<P = DMACh2> + 'd,
pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::sealed::Pin> + 'd,
pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::sealed::Pin> + 'd,
pin_ch1: impl Peripheral<P = impl DacPin<T, 1> + crate::gpio::Pin> + 'd,
pin_ch2: impl Peripheral<P = impl DacPin<T, 2> + crate::gpio::Pin> + 'd,
) -> Self {
into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2);
pin_ch1.set_as_analog();
@ -488,14 +488,13 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> {
}
}
pub(crate) mod sealed {
pub trait Instance {
fn regs() -> &'static crate::pac::dac::Dac;
}
trait SealedInstance {
fn regs() -> &'static crate::pac::dac::Dac;
}
/// DAC instance.
pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + RccPeripheral + 'static {}
dma_trait!(DacDma1, Instance);
dma_trait!(DacDma2, Instance);
@ -504,7 +503,7 @@ pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
foreach_peripheral!(
(dac, $inst:ident) => {
impl crate::dac::sealed::Instance for peripherals::$inst {
impl crate::dac::SealedInstance for peripherals::$inst {
fn regs() -> &'static crate::pac::dac::Dac {
&crate::pac::$inst
}

View File

@ -7,8 +7,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::dma::Transfer;
use crate::gpio::sealed::AFType;
use crate::gpio::Speed;
use crate::gpio::{AFType, Speed};
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral};
@ -431,14 +430,13 @@ where
}
}
mod sealed {
pub trait Instance: crate::rcc::RccPeripheral {
fn regs(&self) -> crate::pac::dcmi::Dcmi;
}
trait SealedInstance: crate::rcc::RccPeripheral {
fn regs(&self) -> crate::pac::dcmi::Dcmi;
}
/// DCMI instance.
pub trait Instance: sealed::Instance + 'static {
#[allow(private_bounds)]
pub trait Instance: SealedInstance + 'static {
/// Interrupt for this instance.
type Interrupt: interrupt::typelevel::Interrupt;
}
@ -465,7 +463,7 @@ pin_trait!(PixClkPin, Instance);
#[allow(unused)]
macro_rules! impl_peripheral {
($inst:ident, $irq:ident) => {
impl sealed::Instance for crate::peripherals::$inst {
impl SealedInstance for crate::peripherals::$inst {
fn regs(&self) -> crate::pac::dcmi::Dcmi {
crate::pac::$inst
}

View File

@ -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> {

View File

@ -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;
}

View File

@ -39,17 +39,18 @@ pub type Request = u8;
#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))]
pub type Request = ();
pub(crate) mod sealed {
pub trait Channel {
fn id(&self) -> u8;
}
pub trait ChannelInterrupt {
unsafe fn on_irq();
}
pub(crate) trait SealedChannel {
fn id(&self) -> u8;
}
pub(crate) trait ChannelInterrupt {
#[cfg_attr(not(feature = "rt"), allow(unused))]
unsafe fn on_irq();
}
/// DMA channel.
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Into<AnyChannel> + 'static {
#[allow(private_bounds)]
pub trait Channel: SealedChannel + Peripheral<P = Self> + Into<AnyChannel> + 'static {
/// Type-erase (degrade) this pin into an `AnyChannel`.
///
/// This converts DMA channel singletons (`DMA1_CH3`, `DMA2_CH1`, ...), which
@ -63,12 +64,12 @@ pub trait Channel: sealed::Channel + Peripheral<P = Self> + Into<AnyChannel> + '
macro_rules! dma_channel_impl {
($channel_peri:ident, $index:expr) => {
impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri {
impl crate::dma::SealedChannel for crate::peripherals::$channel_peri {
fn id(&self) -> u8 {
$index
}
}
impl crate::dma::sealed::ChannelInterrupt for crate::peripherals::$channel_peri {
impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri {
unsafe fn on_irq() {
crate::dma::AnyChannel { id: $index }.on_irq();
}
@ -96,7 +97,7 @@ impl AnyChannel {
}
}
impl sealed::Channel for AnyChannel {
impl SealedChannel for AnyChannel {
fn id(&self) -> u8 {
self.id
}

View File

@ -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

View File

@ -177,16 +177,15 @@ pub unsafe trait PHY {
fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool;
}
pub(crate) mod sealed {
pub trait Instance {
fn regs() -> crate::pac::eth::Eth;
}
trait SealedInstance {
fn regs() -> crate::pac::eth::Eth;
}
/// Ethernet instance.
pub trait Instance: sealed::Instance + RccPeripheral + Send + 'static {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + RccPeripheral + Send + 'static {}
impl sealed::Instance for crate::peripherals::ETH {
impl SealedInstance for crate::peripherals::ETH {
fn regs() -> crate::pac::eth::Eth {
crate::pac::ETH
}

View File

@ -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));

View File

@ -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));

View File

@ -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

View File

@ -1,4 +1,3 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering};

View File

@ -1,4 +1,3 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering};

View File

@ -1,4 +1,3 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use core::sync::atomic::{fence, AtomicBool, Ordering};

View File

@ -1,4 +1,3 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering};

View File

@ -1,4 +1,3 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering};

View File

@ -1,4 +1,3 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering};

View File

@ -1,4 +1,3 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering};

View File

@ -3,8 +3,7 @@ use core::marker::PhantomData;
use embassy_hal_internal::into_ref;
use crate::gpio::sealed::AFType;
use crate::gpio::{Pull, Speed};
use crate::gpio::{AFType, Pull, Speed};
use crate::Peripheral;
/// FMC driver
@ -44,7 +43,7 @@ where
/// Get the kernel clock currently in use for this FMC instance.
pub fn source_clock_hz(&self) -> u32 {
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
<T as crate::rcc::SealedRccPeripheral>::frequency().0
}
}
@ -69,7 +68,7 @@ where
}
fn source_clock_hz(&self) -> u32 {
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
<T as crate::rcc::SealedRccPeripheral>::frequency().0
}
}
@ -201,18 +200,17 @@ impl<'d, T: Instance> Fmc<'d, T> {
));
}
pub(crate) mod sealed {
pub trait Instance: crate::rcc::sealed::RccPeripheral {
const REGS: crate::pac::fmc::Fmc;
}
trait SealedInstance: crate::rcc::SealedRccPeripheral {
const REGS: crate::pac::fmc::Fmc;
}
/// FMC instance trait.
pub trait Instance: sealed::Instance + 'static {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + 'static {}
foreach_peripheral!(
(fmc, $inst:ident) => {
impl crate::fmc::sealed::Instance for crate::peripherals::$inst {
impl crate::fmc::SealedInstance for crate::peripherals::$inst {
const REGS: crate::pac::fmc::Fmc = crate::pac::$inst;
}
impl crate::fmc::Instance for crate::peripherals::$inst {}

View File

@ -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