This commit is contained in:
Gustav Toft 2024-04-10 13:26:59 +02:00
commit 4ffe35c840
65 changed files with 3951 additions and 1148 deletions

View File

@ -9,3 +9,11 @@ Here are known examples of real-world projects which make use of Embassy. Feel f
* The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL
** link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system]
*** Targets nRF52 and uses nrf-softdevice
* link:https://github.com/schmettow/ylab-edge-go[YLab Edge Go] and link:https://github.com/schmettow/ylab-edge-pro[YLab Edge Pro] projects develop
firmware (RP2040, STM32) for capturing physiological data in behavioural science research. Included so far are:
** biopotentials (analog ports)
** motion capture (6-axis accelerometers)
** air quality (CO2, Temp, Humidity)
** comes with an app for capturing and visualizing data [link:https://github.com/schmettow/ystudio-zero[Ystudio]]

View File

@ -20,8 +20,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
// Re-export SVD variants to allow user to directly set values
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
use crate::gpio::sealed::Pin;
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits, SealedPin};
use crate::interrupt::typelevel::Interrupt;
use crate::ppi::{
self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task,
@ -30,19 +29,15 @@ use crate::timer::{Instance as TimerInstance, Timer};
use crate::uarte::{configure, drop_tx_rx, Config, Instance as UarteInstance};
use crate::{interrupt, pac, Peripheral};
mod sealed {
use super::*;
pub(crate) struct State {
tx_buf: RingBuffer,
tx_count: AtomicUsize,
pub struct State {
pub tx_buf: RingBuffer,
pub tx_count: AtomicUsize,
pub rx_buf: RingBuffer,
pub rx_started: AtomicBool,
pub rx_started_count: AtomicU8,
pub rx_ended_count: AtomicU8,
pub rx_ppi_ch: AtomicU8,
}
rx_buf: RingBuffer,
rx_started: AtomicBool,
rx_started_count: AtomicU8,
rx_ended_count: AtomicU8,
rx_ppi_ch: AtomicU8,
}
/// UART error.
@ -53,8 +48,6 @@ pub enum Error {
// No errors for now
}
pub(crate) use sealed::State;
impl State {
pub(crate) const fn new() -> Self {
Self {

View File

@ -7,7 +7,6 @@ use core::hint::unreachable_unchecked;
use cfg_if::cfg_if;
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
use self::sealed::Pin as _;
#[cfg(feature = "nrf51")]
use crate::pac::gpio;
#[cfg(feature = "nrf51")]
@ -361,10 +360,7 @@ impl<'d> Drop for Flex<'d> {
}
}
pub(crate) mod sealed {
use super::*;
pub trait Pin {
pub(crate) trait SealedPin {
fn pin_port(&self) -> u8;
#[inline]
@ -410,10 +406,10 @@ pub(crate) mod sealed {
unsafe { self.block().outclr.write(|w| w.bits(1u32 << self._pin())) }
}
}
}
/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].
pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'static {
#[allow(private_bounds)]
pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + SealedPin + Sized + 'static {
/// Number of the pin within the port (0..31)
#[inline]
fn pin(&self) -> u8 {
@ -464,7 +460,7 @@ impl AnyPin {
impl_peripheral!(AnyPin);
impl Pin for AnyPin {}
impl sealed::Pin for AnyPin {
impl SealedPin for AnyPin {
#[inline]
fn pin_port(&self) -> u8 {
self.pin_port
@ -502,7 +498,7 @@ pub(crate) fn deconfigure_pin(psel_bits: u32) {
macro_rules! impl_pin {
($type:ident, $port_num:expr, $pin_num:expr) => {
impl crate::gpio::Pin for peripherals::$type {}
impl crate::gpio::sealed::Pin for peripherals::$type {
impl crate::gpio::SealedPin for peripherals::$type {
#[inline]
fn pin_port(&self) -> u8 {
$port_num * 32 + $pin_num

View File

@ -7,8 +7,7 @@ use core::task::{Context, Poll};
use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin};
use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin, SealedPin as _};
use crate::interrupt::InterruptExt;
use crate::ppi::{Event, Task};
use crate::{interrupt, pac, peripherals};
@ -446,14 +445,13 @@ impl<'d> Flex<'d> {
// =======================
mod sealed {
pub trait Channel {}
}
trait SealedChannel {}
/// GPIOTE channel trait.
///
/// Implemented by all GPIOTE channels.
pub trait Channel: sealed::Channel + Into<AnyChannel> + Sized + 'static {
#[allow(private_bounds)]
pub trait Channel: SealedChannel + Into<AnyChannel> + Sized + 'static {
/// Get the channel number.
fn number(&self) -> usize;
@ -478,7 +476,7 @@ pub struct AnyChannel {
number: u8,
}
impl_peripheral!(AnyChannel);
impl sealed::Channel for AnyChannel {}
impl SealedChannel for AnyChannel {}
impl Channel for AnyChannel {
fn number(&self) -> usize {
self.number as usize
@ -487,7 +485,7 @@ impl Channel for AnyChannel {
macro_rules! impl_channel {
($type:ident, $number:expr) => {
impl sealed::Channel for peripherals::$type {}
impl SealedChannel for peripherals::$type {}
impl Channel for peripherals::$type {
fn number(&self) -> usize {
$number as usize

View File

@ -6,11 +6,12 @@ use core::future::poll_fn;
use core::marker::PhantomData;
use core::mem::size_of;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{compiler_fence, Ordering};
use core::sync::atomic::{compiler_fence, AtomicBool, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::typelevel::Interrupt;
@ -1140,21 +1141,16 @@ impl<S: Sample, const NB: usize, const NS: usize> MultiBuffering<S, NB, NS> {
}
}
pub(crate) mod sealed {
use core::sync::atomic::AtomicBool;
use embassy_sync::waitqueue::AtomicWaker;
/// Peripheral static state
pub struct State {
pub started: AtomicBool,
pub rx_waker: AtomicWaker,
pub tx_waker: AtomicWaker,
pub stop_waker: AtomicWaker,
pub(crate) struct State {
started: AtomicBool,
rx_waker: AtomicWaker,
tx_waker: AtomicWaker,
stop_waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
started: AtomicBool::new(false),
rx_waker: AtomicWaker::new(),
@ -1164,26 +1160,26 @@ pub(crate) mod sealed {
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static crate::pac::i2s::RegisterBlock;
fn state() -> &'static State;
}
}
/// I2S peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_i2s {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::i2s::sealed::Instance for peripherals::$type {
impl crate::i2s::SealedInstance for peripherals::$type {
fn regs() -> &'static crate::pac::i2s::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::i2s::sealed::State {
static STATE: crate::i2s::sealed::State = crate::i2s::sealed::State::new();
fn state() -> &'static crate::i2s::State {
static STATE: crate::i2s::State = crate::i2s::State::new();
&STATE
}
}

View File

@ -9,11 +9,11 @@ use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use fixed::types::I7F1;
use crate::chip::EASY_DMA_SIZE;
use crate::gpio::sealed::Pin;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
@ -451,42 +451,39 @@ impl<'d, T: Instance> Drop for Pdm<'d, T> {
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
/// Peripheral static state
pub struct State {
pub waker: AtomicWaker,
pub(crate) struct State {
waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static crate::pac::pdm::RegisterBlock;
fn state() -> &'static State;
}
}
/// PDM peripheral instance
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_pdm {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::pdm::sealed::Instance for peripherals::$type {
impl crate::pdm::SealedInstance for peripherals::$type {
fn regs() -> &'static crate::pac::pdm::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::pdm::sealed::State {
static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new();
fn state() -> &'static crate::pdm::State {
static STATE: crate::pdm::State = crate::pdm::State::new();
&STATE
}
}

View File

@ -210,13 +210,12 @@ unsafe impl Send for Event<'_> {}
// ======================
// traits
pub(crate) mod sealed {
pub trait Channel {}
pub trait Group {}
}
pub(crate) trait SealedChannel {}
pub(crate) trait SealedGroup {}
/// Interface for PPI channels.
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized + 'static {
#[allow(private_bounds)]
pub trait Channel: SealedChannel + Peripheral<P = Self> + Sized + 'static {
/// Returns the number of the channel
fn number(&self) -> usize;
}
@ -234,7 +233,8 @@ pub trait StaticChannel: Channel + Into<AnyStaticChannel> {
}
/// Interface for a group of PPI channels.
pub trait Group: sealed::Group + Peripheral<P = Self> + Into<AnyGroup> + Sized + 'static {
#[allow(private_bounds)]
pub trait Group: SealedGroup + Peripheral<P = Self> + Into<AnyGroup> + Sized + 'static {
/// Returns the number of the group.
fn number(&self) -> usize;
/// Convert into a type erased group.
@ -254,7 +254,7 @@ pub struct AnyStaticChannel {
pub(crate) number: u8,
}
impl_peripheral!(AnyStaticChannel);
impl sealed::Channel for AnyStaticChannel {}
impl SealedChannel for AnyStaticChannel {}
impl Channel for AnyStaticChannel {
fn number(&self) -> usize {
self.number as usize
@ -272,7 +272,7 @@ pub struct AnyConfigurableChannel {
pub(crate) number: u8,
}
impl_peripheral!(AnyConfigurableChannel);
impl sealed::Channel for AnyConfigurableChannel {}
impl SealedChannel for AnyConfigurableChannel {}
impl Channel for AnyConfigurableChannel {
fn number(&self) -> usize {
self.number as usize
@ -287,7 +287,7 @@ impl ConfigurableChannel for AnyConfigurableChannel {
#[cfg(not(feature = "nrf51"))]
macro_rules! impl_ppi_channel {
($type:ident, $number:expr) => {
impl crate::ppi::sealed::Channel for peripherals::$type {}
impl crate::ppi::SealedChannel for peripherals::$type {}
impl crate::ppi::Channel for peripherals::$type {
fn number(&self) -> usize {
$number
@ -338,7 +338,7 @@ pub struct AnyGroup {
number: u8,
}
impl_peripheral!(AnyGroup);
impl sealed::Group for AnyGroup {}
impl SealedGroup for AnyGroup {}
impl Group for AnyGroup {
fn number(&self) -> usize {
self.number as usize
@ -347,7 +347,7 @@ impl Group for AnyGroup {
macro_rules! impl_group {
($type:ident, $number:expr) => {
impl sealed::Group for peripherals::$type {}
impl SealedGroup for peripherals::$type {}
impl Group for peripherals::$type {
fn number(&self) -> usize {
$number

View File

@ -6,8 +6,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
use embassy_hal_internal::{into_ref, PeripheralRef};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits, SealedPin as _};
use crate::ppi::{Event, Task};
use crate::util::slice_in_ram_or;
use crate::{interrupt, pac, Peripheral};
@ -847,23 +846,20 @@ impl<'a, T: Instance> Drop for SimplePwm<'a, T> {
}
}
pub(crate) mod sealed {
use super::*;
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static pac::pwm0::RegisterBlock;
}
}
/// PWM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_pwm {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::pwm::sealed::Instance for peripherals::$type {
impl crate::pwm::SealedInstance for peripherals::$type {
fn regs() -> &'static pac::pwm0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}

View File

@ -7,9 +7,9 @@ use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _};
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral};
@ -245,42 +245,39 @@ pub enum LedPolarity {
ActiveLow,
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
/// Peripheral static state
pub struct State {
pub waker: AtomicWaker,
pub(crate) struct State {
waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static crate::pac::qdec::RegisterBlock;
fn state() -> &'static State;
}
}
/// qdec peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_qdec {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::qdec::sealed::Instance for peripherals::$type {
impl crate::qdec::SealedInstance for peripherals::$type {
fn regs() -> &'static crate::pac::qdec::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::qdec::sealed::State {
static STATE: crate::qdec::sealed::State = crate::qdec::sealed::State::new();
fn state() -> &'static crate::qdec::State {
static STATE: crate::qdec::State = crate::qdec::State::new();
&STATE
}
}

View File

@ -9,6 +9,7 @@ use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
use crate::gpio::{self, Pin as GpioPin};
@ -652,42 +653,39 @@ mod _eh1 {
impl<'d, T: Instance> embedded_storage_async::nor_flash::MultiwriteNorFlash for Qspi<'d, T> {}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
/// Peripheral static state
pub struct State {
pub waker: AtomicWaker,
pub(crate) struct State {
waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static crate::pac::qspi::RegisterBlock;
fn state() -> &'static State;
}
}
/// QSPI peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_qspi {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::qspi::sealed::Instance for peripherals::$type {
impl crate::qspi::SealedInstance for peripherals::$type {
fn regs() -> &'static crate::pac::qspi::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::qspi::sealed::State {
static STATE: crate::qspi::sealed::State = crate::qspi::sealed::State::new();
fn state() -> &'static crate::qspi::State {
static STATE: crate::qspi::State = crate::qspi::State::new();
&STATE
}
}

View File

@ -19,6 +19,7 @@ pub mod ieee802154;
use core::marker::PhantomData;
use embassy_sync::waitqueue::AtomicWaker;
use pac::radio::state::STATE_A as RadioState;
pub use pac::radio::txpower::TXPOWER_A as TxPower;
@ -56,36 +57,32 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
pub struct State {
pub(crate) struct State {
/// end packet transmission or reception
pub event_waker: AtomicWaker,
event_waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
event_waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static crate::pac::radio::RegisterBlock;
fn state() -> &'static State;
}
}
macro_rules! impl_radio {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::radio::sealed::Instance for peripherals::$type {
impl crate::radio::SealedInstance for peripherals::$type {
fn regs() -> &'static pac::radio::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::radio::sealed::State {
static STATE: crate::radio::sealed::State = crate::radio::sealed::State::new();
fn state() -> &'static crate::radio::State {
static STATE: crate::radio::State = crate::radio::State::new();
&STATE
}
}
@ -96,7 +93,8 @@ macro_rules! impl_radio {
}
/// Radio peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}

View File

@ -2,13 +2,16 @@
#![macro_use]
use core::cell::{RefCell, RefMut};
use core::future::poll_fn;
use core::marker::PhantomData;
use core::ptr;
use core::task::Poll;
use critical_section::{CriticalSection, Mutex};
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::WakerRegistration;
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral};
@ -205,45 +208,33 @@ impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> {
impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {}
pub(crate) mod sealed {
use core::cell::{Ref, RefCell, RefMut};
use critical_section::{CriticalSection, Mutex};
use embassy_sync::waitqueue::WakerRegistration;
use super::*;
/// Peripheral static state
pub struct State {
pub(crate) struct State {
inner: Mutex<RefCell<InnerState>>,
}
pub struct InnerState {
pub ptr: *mut u8,
pub end: *mut u8,
pub waker: WakerRegistration,
struct InnerState {
ptr: *mut u8,
end: *mut u8,
waker: WakerRegistration,
}
unsafe impl Send for InnerState {}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
inner: Mutex::new(RefCell::new(InnerState::new())),
}
}
pub fn borrow<'cs>(&'cs self, cs: CriticalSection<'cs>) -> Ref<'cs, InnerState> {
self.inner.borrow(cs).borrow()
}
pub fn borrow_mut<'cs>(&'cs self, cs: CriticalSection<'cs>) -> RefMut<'cs, InnerState> {
fn borrow_mut<'cs>(&'cs self, cs: CriticalSection<'cs>) -> RefMut<'cs, InnerState> {
self.inner.borrow(cs).borrow_mut()
}
}
impl InnerState {
pub const fn new() -> Self {
const fn new() -> Self {
Self {
ptr: ptr::null_mut(),
end: ptr::null_mut(),
@ -252,26 +243,26 @@ pub(crate) mod sealed {
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static crate::pac::rng::RegisterBlock;
fn state() -> &'static State;
}
}
/// RNG peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_rng {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::rng::sealed::Instance for peripherals::$type {
impl crate::rng::SealedInstance for peripherals::$type {
fn regs() -> &'static crate::pac::rng::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::rng::sealed::State {
static STATE: crate::rng::sealed::State = crate::rng::sealed::State::new();
fn state() -> &'static crate::rng::State {
static STATE: crate::rng::State = crate::rng::State::new();
&STATE
}
}

View File

@ -16,7 +16,6 @@ pub(crate) use saadc::ch::pselp::PSELP_A as InputChannel;
use saadc::oversample::OVERSAMPLE_A;
use saadc::resolution::VAL_A;
use self::sealed::Input as _;
use crate::interrupt::InterruptExt;
use crate::ppi::{ConfigurableChannel, Event, Ppi, Task};
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
@ -662,16 +661,13 @@ pub enum Resolution {
_14BIT = 3,
}
pub(crate) mod sealed {
use super::*;
pub trait Input {
pub(crate) trait SealedInput {
fn channel(&self) -> InputChannel;
}
}
/// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal.
pub trait Input: sealed::Input + Into<AnyInput> + Peripheral<P = Self> + Sized + 'static {
#[allow(private_bounds)]
pub trait Input: SealedInput + Into<AnyInput> + Peripheral<P = Self> + Sized + 'static {
/// Convert this SAADC input to a type-erased `AnyInput`.
///
/// This allows using several inputs in situations that might require
@ -693,7 +689,7 @@ pub struct AnyInput {
impl_peripheral!(AnyInput);
impl sealed::Input for AnyInput {
impl SealedInput for AnyInput {
fn channel(&self) -> InputChannel {
self.channel
}
@ -706,7 +702,7 @@ macro_rules! impl_saadc_input {
impl_saadc_input!(@local, crate::peripherals::$pin, $ch);
};
(@local, $pin:ty, $ch:ident) => {
impl crate::saadc::sealed::Input for $pin {
impl crate::saadc::SealedInput for $pin {
fn channel(&self) -> crate::saadc::InputChannel {
crate::saadc::InputChannel::$ch
}

View File

@ -4,18 +4,20 @@
use core::future::poll_fn;
use core::marker::PhantomData;
#[cfg(feature = "_nrf52832_anomaly_109")]
use core::sync::atomic::AtomicU8;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
pub use pac::spim0::config::ORDER_A as BitOrder;
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits};
use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _};
use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut};
use crate::{interrupt, pac, Peripheral};
@ -487,24 +489,16 @@ impl<'d, T: Instance> Drop for Spim<'d, T> {
}
}
pub(crate) mod sealed {
pub(crate) struct State {
waker: AtomicWaker,
#[cfg(feature = "_nrf52832_anomaly_109")]
use core::sync::atomic::AtomicU8;
use embassy_sync::waitqueue::AtomicWaker;
use super::*;
pub struct State {
pub waker: AtomicWaker,
rx: AtomicU8,
#[cfg(feature = "_nrf52832_anomaly_109")]
pub rx: AtomicU8,
#[cfg(feature = "_nrf52832_anomaly_109")]
pub tx: AtomicU8,
tx: AtomicU8,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
#[cfg(feature = "_nrf52832_anomaly_109")]
@ -515,26 +509,26 @@ pub(crate) mod sealed {
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static pac::spim0::RegisterBlock;
fn state() -> &'static State;
}
}
/// SPIM peripheral instance
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_spim {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::spim::sealed::Instance for peripherals::$type {
impl crate::spim::SealedInstance for peripherals::$type {
fn regs() -> &'static pac::spim0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::spim::sealed::State {
static STATE: crate::spim::sealed::State = crate::spim::sealed::State::new();
fn state() -> &'static crate::spim::State {
static STATE: crate::spim::State = crate::spim::State::new();
&STATE
}
}

View File

@ -8,12 +8,12 @@ use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
pub use pac::spis0::config::ORDER_A as BitOrder;
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin};
use crate::gpio::{self, AnyPin, Pin as GpioPin, SealedPin as _};
use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
use crate::{interrupt, pac, Peripheral};
@ -456,43 +456,38 @@ impl<'d, T: Instance> Drop for Spis<'d, T> {
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
use super::*;
pub struct State {
pub waker: AtomicWaker,
pub(crate) struct State {
waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static pac::spis0::RegisterBlock;
fn state() -> &'static State;
}
}
/// SPIS peripheral instance
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_spis {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::spis::sealed::Instance for peripherals::$type {
impl crate::spis::SealedInstance for peripherals::$type {
fn regs() -> &'static pac::spis0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::spis::sealed::State {
static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new();
fn state() -> &'static crate::spis::State {
static STATE: crate::spis::State = crate::spis::State::new();
&STATE
}
}

View File

@ -11,30 +11,25 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
use crate::ppi::{Event, Task};
use crate::{pac, Peripheral};
pub(crate) mod sealed {
use super::*;
pub trait Instance {
pub(crate) trait SealedInstance {
/// The number of CC registers this instance has.
const CCS: usize;
fn regs() -> &'static pac::timer0::RegisterBlock;
}
pub trait ExtendedInstance {}
}
/// Basic Timer instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
/// Extended timer instance.
pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {}
pub trait ExtendedInstance: Instance {}
macro_rules! impl_timer {
($type:ident, $pac_type:ident, $irq:ident, $ccs:literal) => {
impl crate::timer::sealed::Instance for peripherals::$type {
impl crate::timer::SealedInstance for peripherals::$type {
const CCS: usize = $ccs;
fn regs() -> &'static pac::timer0::RegisterBlock {
unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) }
@ -49,7 +44,6 @@ macro_rules! impl_timer {
};
($type:ident, $pac_type:ident, $irq:ident, extended) => {
impl_timer!($type, $pac_type, $irq, 6);
impl crate::timer::sealed::ExtendedInstance for peripherals::$type {}
impl crate::timer::ExtendedInstance for peripherals::$type {}
};
}

View File

@ -727,41 +727,38 @@ impl<'a, T: Instance> Drop for Twim<'a, T> {
}
}
pub(crate) mod sealed {
use super::*;
pub struct State {
pub end_waker: AtomicWaker,
pub(crate) struct State {
end_waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
end_waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static pac::twim0::RegisterBlock;
fn state() -> &'static State;
}
}
/// TWIM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_twim {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::twim::sealed::Instance for peripherals::$type {
impl crate::twim::SealedInstance for peripherals::$type {
fn regs() -> &'static pac::twim0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::twim::sealed::State {
static STATE: crate::twim::sealed::State = crate::twim::sealed::State::new();
fn state() -> &'static crate::twim::State {
static STATE: crate::twim::State = crate::twim::State::new();
&STATE
}
}

View File

@ -754,41 +754,38 @@ impl<'a, T: Instance> Drop for Twis<'a, T> {
}
}
pub(crate) mod sealed {
use super::*;
pub struct State {
pub waker: AtomicWaker,
pub(crate) struct State {
waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static pac::twis0::RegisterBlock;
fn state() -> &'static State;
}
}
/// TWIS peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_twis {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::twis::sealed::Instance for peripherals::$type {
impl crate::twis::SealedInstance for peripherals::$type {
fn regs() -> &'static pac::twis0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::twis::sealed::State {
static STATE: crate::twis::sealed::State = crate::twis::sealed::State::new();
fn state() -> &'static crate::twis::State {
static STATE: crate::twis::State = crate::twis::State::new();
&STATE
}
}

View File

@ -15,18 +15,18 @@
use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::sync::atomic::{compiler_fence, AtomicU8, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use pac::uarte0::RegisterBlock;
// Re-export SVD variants to allow user to directly set values.
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits, SealedPin as _};
use crate::interrupt::typelevel::Interrupt;
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
@ -939,7 +939,7 @@ pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::Regist
}
}
pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &State) {
if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 {
// Finally we can disable, and we do so for the peripheral
// i.e. not just rx concerns.
@ -954,20 +954,13 @@ pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
}
}
pub(crate) mod sealed {
use core::sync::atomic::AtomicU8;
use embassy_sync::waitqueue::AtomicWaker;
use super::*;
pub struct State {
pub rx_waker: AtomicWaker,
pub tx_waker: AtomicWaker,
pub tx_rx_refcount: AtomicU8,
pub(crate) struct State {
pub(crate) rx_waker: AtomicWaker,
pub(crate) tx_waker: AtomicWaker,
pub(crate) tx_rx_refcount: AtomicU8,
}
impl State {
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
rx_waker: AtomicWaker::new(),
tx_waker: AtomicWaker::new(),
@ -976,27 +969,27 @@ pub(crate) mod sealed {
}
}
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static pac::uarte0::RegisterBlock;
fn state() -> &'static State;
fn buffered_state() -> &'static crate::buffered_uarte::State;
}
}
/// UARTE peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_uarte {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::uarte::sealed::Instance for peripherals::$type {
impl crate::uarte::SealedInstance for peripherals::$type {
fn regs() -> &'static pac::uarte0::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::uarte::sealed::State {
static STATE: crate::uarte::sealed::State = crate::uarte::sealed::State::new();
fn state() -> &'static crate::uarte::State {
static STATE: crate::uarte::State = crate::uarte::State::new();
&STATE
}
fn buffered_state() -> &'static crate::buffered_uarte::State {

View File

@ -793,23 +793,20 @@ impl Allocator {
}
}
pub(crate) mod sealed {
use super::*;
pub trait Instance {
pub(crate) trait SealedInstance {
fn regs() -> &'static pac::usbd::RegisterBlock;
}
}
/// USB peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
#[allow(private_bounds)]
pub trait Instance: Peripheral<P = Self> + SealedInstance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_usb {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::usb::sealed::Instance for peripherals::$type {
impl crate::usb::SealedInstance for peripherals::$type {
fn regs() -> &'static pac::usbd::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}

View File

@ -8,8 +8,7 @@ use core::task::Poll;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::sealed::Pin as GpioPin;
use crate::gpio::{self, AnyPin, Pull};
use crate::gpio::{self, AnyPin, Pull, SealedPin as GpioPin};
use crate::interrupt::typelevel::Binding;
use crate::interrupt::InterruptExt;
use crate::peripherals::{ADC, ADC_TEMP_SENSOR};
@ -334,29 +333,28 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for Inter
}
}
mod sealed {
pub trait AdcSample: crate::dma::Word {}
pub trait AdcChannel {}
}
trait SealedAdcSample: crate::dma::Word {}
trait SealedAdcChannel {}
/// ADC sample.
pub trait AdcSample: sealed::AdcSample {}
#[allow(private_bounds)]
pub trait AdcSample: SealedAdcSample {}
impl sealed::AdcSample for u16 {}
impl SealedAdcSample for u16 {}
impl AdcSample for u16 {}
impl sealed::AdcSample for u8 {}
impl SealedAdcSample for u8 {}
impl AdcSample for u8 {}
/// ADC channel.
pub trait AdcChannel: sealed::AdcChannel {}
#[allow(private_bounds)]
pub trait AdcChannel: SealedAdcChannel {}
/// ADC pin.
pub trait AdcPin: AdcChannel + gpio::Pin {}
macro_rules! impl_pin {
($pin:ident, $channel:expr) => {
impl sealed::AdcChannel for peripherals::$pin {}
impl SealedAdcChannel for peripherals::$pin {}
impl AdcChannel for peripherals::$pin {}
impl AdcPin for peripherals::$pin {}
};
@ -367,5 +365,5 @@ impl_pin!(PIN_27, 1);
impl_pin!(PIN_28, 2);
impl_pin!(PIN_29, 3);
impl sealed::AdcChannel for peripherals::ADC_TEMP_SENSOR {}
impl SealedAdcChannel for peripherals::ADC_TEMP_SENSOR {}
impl AdcChannel for peripherals::ADC_TEMP_SENSOR {}

View File

@ -6,8 +6,7 @@ use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
use embassy_hal_internal::{into_ref, PeripheralRef};
use pac::clocks::vals::*;
use crate::gpio::sealed::Pin;
use crate::gpio::AnyPin;
use crate::gpio::{AnyPin, SealedPin};
use crate::pac::common::{Reg, RW};
use crate::{pac, reset, Peripheral};
@ -788,14 +787,14 @@ impl_gpinpin!(PIN_20, 20, 0);
impl_gpinpin!(PIN_22, 22, 1);
/// General purpose clock input driver.
pub struct Gpin<'d, T: Pin> {
pub struct Gpin<'d, T: GpinPin> {
gpin: PeripheralRef<'d, AnyPin>,
_phantom: PhantomData<T>,
}
impl<'d, T: Pin> Gpin<'d, T> {
impl<'d, T: GpinPin> Gpin<'d, T> {
/// Create new gpin driver.
pub fn new<P: GpinPin>(gpin: impl Peripheral<P = P> + 'd) -> Gpin<'d, P> {
pub fn new(gpin: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(gpin);
gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08));
@ -811,7 +810,7 @@ impl<'d, T: Pin> Gpin<'d, T> {
// }
}
impl<'d, T: Pin> Drop for Gpin<'d, T> {
impl<'d, T: GpinPin> Drop for Gpin<'d, T> {
fn drop(&mut self) {
self.gpin
.gpio()

View File

@ -208,14 +208,12 @@ pub(crate) const CHANNEL_COUNT: usize = 12;
const NEW_AW: AtomicWaker = AtomicWaker::new();
static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT];
mod sealed {
pub trait Channel {}
pub trait Word {}
}
trait SealedChannel {}
trait SealedWord {}
/// DMA channel interface.
pub trait Channel: Peripheral<P = Self> + sealed::Channel + Into<AnyChannel> + Sized + 'static {
#[allow(private_bounds)]
pub trait Channel: Peripheral<P = Self> + SealedChannel + Into<AnyChannel> + Sized + 'static {
/// Channel number.
fn number(&self) -> u8;
@ -231,26 +229,27 @@ pub trait Channel: Peripheral<P = Self> + sealed::Channel + Into<AnyChannel> + S
}
/// DMA word.
pub trait Word: sealed::Word {
#[allow(private_bounds)]
pub trait Word: SealedWord {
/// Word size.
fn size() -> vals::DataSize;
}
impl sealed::Word for u8 {}
impl SealedWord for u8 {}
impl Word for u8 {
fn size() -> vals::DataSize {
vals::DataSize::SIZE_BYTE
}
}
impl sealed::Word for u16 {}
impl SealedWord for u16 {}
impl Word for u16 {
fn size() -> vals::DataSize {
vals::DataSize::SIZE_HALFWORD
}
}
impl sealed::Word for u32 {}
impl SealedWord for u32 {}
impl Word for u32 {
fn size() -> vals::DataSize {
vals::DataSize::SIZE_WORD
@ -264,7 +263,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
@ -273,7 +272,7 @@ impl Channel for AnyChannel {
macro_rules! channel {
($name:ident, $num:expr) => {
impl sealed::Channel for peripherals::$name {}
impl SealedChannel for peripherals::$name {}
impl Channel for peripherals::$name {
fn number(&self) -> u8 {
$num

View File

@ -903,22 +903,22 @@ pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
Ok(())
}
mod sealed {
pub trait Instance {}
pub trait Mode {}
}
trait SealedInstance {}
trait SealedMode {}
/// Flash instance.
pub trait Instance: sealed::Instance {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance {}
/// Flash mode.
pub trait Mode: sealed::Mode {}
#[allow(private_bounds)]
pub trait Mode: SealedMode {}
impl sealed::Instance for FLASH {}
impl SealedInstance for FLASH {}
impl Instance for FLASH {}
macro_rules! impl_mode {
($name:ident) => {
impl sealed::Mode for $name {}
impl SealedMode for $name {}
impl Mode for $name {}
};
}

View File

@ -8,7 +8,6 @@ use core::task::{Context, Poll};
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use self::sealed::Pin as _;
use crate::interrupt::InterruptExt;
use crate::pac::common::{Reg, RW};
use crate::pac::SIO;
@ -802,10 +801,7 @@ impl<'w> Drop for DormantWake<'w> {
}
}
pub(crate) mod sealed {
use super::*;
pub trait Pin: Sized {
pub(crate) trait SealedPin: Sized {
fn pin_bank(&self) -> u8;
#[inline]
@ -860,10 +856,10 @@ pub(crate) mod sealed {
self.io().int_proc(proc as _)
}
}
}
/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].
pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'static {
#[allow(private_bounds)]
pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + SealedPin + Sized + 'static {
/// Degrade to a generic pin struct
fn degrade(self) -> AnyPin {
AnyPin {
@ -903,7 +899,7 @@ impl AnyPin {
impl_peripheral!(AnyPin);
impl Pin for AnyPin {}
impl sealed::Pin for AnyPin {
impl SealedPin for AnyPin {
fn pin_bank(&self) -> u8 {
self.pin_bank
}
@ -914,7 +910,7 @@ impl sealed::Pin for AnyPin {
macro_rules! impl_pin {
($name:ident, $bank:expr, $pin_num:expr) => {
impl Pin for peripherals::$name {}
impl sealed::Pin for peripherals::$name {
impl SealedPin for peripherals::$name {
#[inline]
fn pin_bank(&self) -> u8 {
($bank as u8) * 32 + $pin_num

View File

@ -784,34 +784,24 @@ pub fn i2c_reserved_addr(addr: u16) -> bool {
((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0
}
mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt;
pub trait Instance {
pub(crate) trait SealedInstance {
const TX_DREQ: u8;
const RX_DREQ: u8;
type Interrupt: interrupt::typelevel::Interrupt;
fn regs() -> crate::pac::i2c::I2c;
fn reset() -> crate::pac::resets::regs::Peripherals;
fn waker() -> &'static AtomicWaker;
}
pub trait Mode {}
pub trait SdaPin<T: Instance> {}
pub trait SclPin<T: Instance> {}
}
trait SealedMode {}
/// Driver mode.
pub trait Mode: sealed::Mode {}
#[allow(private_bounds)]
pub trait Mode: SealedMode {}
macro_rules! impl_mode {
($name:ident) => {
impl sealed::Mode for $name {}
impl SealedMode for $name {}
impl Mode for $name {}
};
}
@ -825,16 +815,18 @@ impl_mode!(Blocking);
impl_mode!(Async);
/// I2C instance.
pub trait Instance: sealed::Instance {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_instance {
($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => {
impl sealed::Instance for peripherals::$type {
impl SealedInstance for peripherals::$type {
const TX_DREQ: u8 = $tx_dreq;
const RX_DREQ: u8 = $rx_dreq;
type Interrupt = crate::interrupt::typelevel::$irq;
#[inline]
fn regs() -> pac::i2c::I2c {
pac::$type
@ -854,7 +846,9 @@ macro_rules! impl_instance {
&WAKER
}
}
impl Instance for peripherals::$type {}
impl Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
@ -862,13 +856,12 @@ impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33);
impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35);
/// SDA pin.
pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {}
pub trait SdaPin<T: Instance>: crate::gpio::Pin {}
/// SCL pin.
pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {}
pub trait SclPin<T: Instance>: crate::gpio::Pin {}
macro_rules! impl_pin {
($pin:ident, $instance:ident, $function:ident) => {
impl sealed::$function<peripherals::$instance> for peripherals::$pin {}
impl $function<peripherals::$instance> for peripherals::$pin {}
};
}

View File

@ -15,8 +15,7 @@ use pac::pio::vals::SmExecctrlStatusSel;
use pio::{Program, SideSet, Wrap};
use crate::dma::{Channel, Transfer, Word};
use crate::gpio::sealed::Pin as SealedPin;
use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate};
use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};
use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
use crate::pac::dma::vals::TreqSel;
use crate::relocate::RelocatedProgram;
@ -695,6 +694,12 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
}
}
/// Set the clock divider for this state machine.
pub fn set_clock_divider(&mut self, clock_divider: FixedU32<U8>) {
let sm = Self::this_sm();
sm.clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8);
}
#[inline(always)]
fn this_sm() -> crate::pac::pio::StateMachine {
PIO::PIO.sm(SM)
@ -1148,16 +1153,10 @@ fn on_pio_drop<PIO: Instance>() {
}
}
mod sealed {
use super::*;
pub trait PioPin {}
pub trait Instance {
trait SealedInstance {
const PIO_NO: u8;
const PIO: &'static crate::pac::pio::Pio;
const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel;
type Interrupt: crate::interrupt::typelevel::Interrupt;
#[inline]
fn wakers() -> &'static Wakers {
@ -1177,20 +1176,24 @@ mod sealed {
&STATE
}
}
}
/// PIO instance.
pub trait Instance: sealed::Instance + Sized + Unpin {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + Sized + Unpin {
/// Interrupt for this peripheral.
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
macro_rules! impl_pio {
($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => {
impl sealed::Instance for peripherals::$name {
impl SealedInstance for peripherals::$name {
const PIO_NO: u8 = $pio;
const PIO: &'static pac::pio::Pio = &pac::$pac;
const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;
}
impl Instance for peripherals::$name {
type Interrupt = crate::interrupt::typelevel::$irq;
}
impl Instance for peripherals::$name {}
};
}
@ -1198,12 +1201,11 @@ impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
/// PIO pin.
pub trait PioPin: sealed::PioPin + gpio::Pin {}
pub trait PioPin: gpio::Pin {}
macro_rules! impl_pio_pin {
($( $pin:ident, )*) => {
$(
impl sealed::PioPin for peripherals::$pin {}
impl PioPin for peripherals::$pin {}
)*
};

View File

@ -6,8 +6,7 @@ use fixed::FixedU16;
use pac::pwm::regs::{ChDiv, Intr};
use pac::pwm::vals::Divmode;
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::gpio::{AnyPin, Pin as GpioPin, Pull, SealedPin as _};
use crate::{pac, peripherals, RegExt};
/// The configuration of a PWM slice.
@ -93,6 +92,7 @@ impl<'d, T: Slice> Pwm<'d, T> {
inner: impl Peripheral<P = T> + 'd,
a: Option<PeripheralRef<'d, AnyPin>>,
b: Option<PeripheralRef<'d, AnyPin>>,
b_pull: Pull,
config: Config,
divmode: Divmode,
) -> Self {
@ -111,6 +111,10 @@ impl<'d, T: Slice> Pwm<'d, T> {
}
if let Some(pin) = &b {
pin.gpio().ctrl().write(|w| w.set_funcsel(4));
pin.pad_ctrl().modify(|w| {
w.set_pue(b_pull == Pull::Up);
w.set_pde(b_pull == Pull::Down);
});
}
Self {
inner,
@ -122,7 +126,7 @@ impl<'d, T: Slice> Pwm<'d, T> {
/// Create PWM driver without any configured pins.
#[inline]
pub fn new_free(inner: impl Peripheral<P = T> + 'd, config: Config) -> Self {
Self::new_inner(inner, None, None, config, Divmode::DIV)
Self::new_inner(inner, None, None, Pull::None, config, Divmode::DIV)
}
/// Create PWM driver with a single 'a' as output.
@ -133,7 +137,7 @@ impl<'d, T: Slice> Pwm<'d, T> {
config: Config,
) -> Self {
into_ref!(a);
Self::new_inner(inner, Some(a.map_into()), None, config, Divmode::DIV)
Self::new_inner(inner, Some(a.map_into()), None, Pull::None, config, Divmode::DIV)
}
/// Create PWM driver with a single 'b' pin as output.
@ -144,7 +148,7 @@ impl<'d, T: Slice> Pwm<'d, T> {
config: Config,
) -> Self {
into_ref!(b);
Self::new_inner(inner, None, Some(b.map_into()), config, Divmode::DIV)
Self::new_inner(inner, None, Some(b.map_into()), Pull::None, config, Divmode::DIV)
}
/// Create PWM driver with a 'a' and 'b' pins as output.
@ -156,7 +160,14 @@ impl<'d, T: Slice> Pwm<'d, T> {
config: Config,
) -> Self {
into_ref!(a, b);
Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, Divmode::DIV)
Self::new_inner(
inner,
Some(a.map_into()),
Some(b.map_into()),
Pull::None,
config,
Divmode::DIV,
)
}
/// Create PWM driver with a single 'b' as input pin.
@ -164,11 +175,12 @@ impl<'d, T: Slice> Pwm<'d, T> {
pub fn new_input(
inner: impl Peripheral<P = T> + 'd,
b: impl Peripheral<P = impl ChannelBPin<T>> + 'd,
b_pull: Pull,
mode: InputMode,
config: Config,
) -> Self {
into_ref!(b);
Self::new_inner(inner, None, Some(b.map_into()), config, mode.into())
Self::new_inner(inner, None, Some(b.map_into()), b_pull, config, mode.into())
}
/// Create PWM driver with a 'a' and 'b' pins in the desired input mode.
@ -177,11 +189,19 @@ impl<'d, T: Slice> Pwm<'d, T> {
inner: impl Peripheral<P = T> + 'd,
a: impl Peripheral<P = impl ChannelAPin<T>> + 'd,
b: impl Peripheral<P = impl ChannelBPin<T>> + 'd,
b_pull: Pull,
mode: InputMode,
config: Config,
) -> Self {
into_ref!(a, b);
Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, mode.into())
Self::new_inner(
inner,
Some(a.map_into()),
Some(b.map_into()),
b_pull,
config,
mode.into(),
)
}
/// Set the PWM config.
@ -300,12 +320,11 @@ impl<'d, T: Slice> Drop for Pwm<'d, T> {
}
}
mod sealed {
pub trait Slice {}
}
trait SealedSlice {}
/// PWM Slice.
pub trait Slice: Peripheral<P = Self> + sealed::Slice + Sized + 'static {
#[allow(private_bounds)]
pub trait Slice: Peripheral<P = Self> + SealedSlice + Sized + 'static {
/// Slice number.
fn number(&self) -> u8;
@ -317,7 +336,7 @@ pub trait Slice: Peripheral<P = Self> + sealed::Slice + Sized + 'static {
macro_rules! slice {
($name:ident, $num:expr) => {
impl sealed::Slice for peripherals::$name {}
impl SealedSlice for peripherals::$name {}
impl Slice for peripherals::$name {
fn number(&self) -> u8 {
$num

View File

@ -188,16 +188,15 @@ pub enum RtcError {
NotRunning,
}
mod sealed {
pub trait Instance {
trait SealedInstance {
fn regs(&self) -> crate::pac::rtc::Rtc;
}
}
/// RTC peripheral instance.
pub trait Instance: sealed::Instance {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance {}
impl sealed::Instance for crate::peripherals::RTC {
impl SealedInstance for crate::peripherals::RTC {
fn regs(&self) -> crate::pac::rtc::Rtc {
crate::pac::RTC
}

View File

@ -7,8 +7,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Phase, Polarity};
use crate::dma::{AnyChannel, Channel};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _};
use crate::{pac, peripherals, Peripheral};
/// SPI errors.
@ -443,28 +442,26 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
}
}
mod sealed {
use super::*;
trait SealedMode {}
pub trait Mode {}
pub trait Instance {
trait SealedInstance {
const TX_DREQ: u8;
const RX_DREQ: u8;
fn regs(&self) -> pac::spi::Spi;
}
}
/// Mode.
pub trait Mode: sealed::Mode {}
#[allow(private_bounds)]
pub trait Mode: SealedMode {}
/// SPI instance trait.
pub trait Instance: sealed::Instance {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance {}
macro_rules! impl_instance {
($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
impl sealed::Instance for peripherals::$type {
impl SealedInstance for peripherals::$type {
const TX_DREQ: u8 = $tx_dreq;
const RX_DREQ: u8 = $rx_dreq;
@ -527,7 +524,7 @@ impl_pin!(PIN_29, SPI1, CsPin);
macro_rules! impl_mode {
($name:ident) => {
impl sealed::Mode for $name {}
impl SealedMode for $name {}
impl Mode for $name {}
};
}

View File

@ -12,8 +12,7 @@ use pac::uart::regs::Uartris;
use crate::clocks::clk_peri_freq;
use crate::dma::{AnyChannel, Channel};
use crate::gpio::sealed::Pin;
use crate::gpio::AnyPin;
use crate::gpio::{AnyPin, SealedPin};
use crate::interrupt::typelevel::{Binding, Interrupt};
use crate::pac::io::vals::{Inover, Outover};
use crate::{interrupt, pac, peripherals, Peripheral, RegExt};
@ -1107,35 +1106,26 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M>
}
}
mod sealed {
use super::*;
trait SealedMode {}
pub trait Mode {}
pub trait Instance {
trait SealedInstance {
const TX_DREQ: u8;
const RX_DREQ: u8;
type Interrupt: interrupt::typelevel::Interrupt;
fn regs() -> pac::uart::Uart;
fn buffered_state() -> &'static buffered::State;
fn dma_state() -> &'static DmaState;
}
pub trait TxPin<T: Instance> {}
pub trait RxPin<T: Instance> {}
pub trait CtsPin<T: Instance> {}
pub trait RtsPin<T: Instance> {}
}
/// UART mode.
pub trait Mode: sealed::Mode {}
#[allow(private_bounds)]
pub trait Mode: SealedMode {}
macro_rules! impl_mode {
($name:ident) => {
impl sealed::Mode for $name {}
impl SealedMode for $name {}
impl Mode for $name {}
};
}
@ -1149,16 +1139,18 @@ impl_mode!(Blocking);
impl_mode!(Async);
/// UART instance.
pub trait Instance: sealed::Instance {}
#[allow(private_bounds)]
pub trait Instance: SealedInstance {
/// Interrupt for this instance.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_instance {
($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
impl sealed::Instance for peripherals::$inst {
impl SealedInstance for peripherals::$inst {
const TX_DREQ: u8 = $tx_dreq;
const RX_DREQ: u8 = $rx_dreq;
type Interrupt = crate::interrupt::typelevel::$irq;
fn regs() -> pac::uart::Uart {
pac::$inst
}
@ -1176,7 +1168,9 @@ macro_rules! impl_instance {
&STATE
}
}
impl Instance for peripherals::$inst {}
impl Instance for peripherals::$inst {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
@ -1184,17 +1178,16 @@ impl_instance!(UART0, UART0_IRQ, 20, 21);
impl_instance!(UART1, UART1_IRQ, 22, 23);
/// Trait for TX pins.
pub trait TxPin<T: Instance>: sealed::TxPin<T> + crate::gpio::Pin {}
pub trait TxPin<T: Instance>: crate::gpio::Pin {}
/// Trait for RX pins.
pub trait RxPin<T: Instance>: sealed::RxPin<T> + crate::gpio::Pin {}
pub trait RxPin<T: Instance>: crate::gpio::Pin {}
/// Trait for Clear To Send (CTS) pins.
pub trait CtsPin<T: Instance>: sealed::CtsPin<T> + crate::gpio::Pin {}
pub trait CtsPin<T: Instance>: crate::gpio::Pin {}
/// Trait for Request To Send (RTS) pins.
pub trait RtsPin<T: Instance>: sealed::RtsPin<T> + crate::gpio::Pin {}
pub trait RtsPin<T: Instance>: crate::gpio::Pin {}
macro_rules! impl_pin {
($pin:ident, $instance:ident, $function:ident) => {
impl sealed::$function<peripherals::$instance> for peripherals::$pin {}
impl $function<peripherals::$instance> for peripherals::$pin {}
};
}

View File

@ -14,20 +14,19 @@ use embassy_usb_driver::{
use crate::interrupt::typelevel::{Binding, Interrupt};
use crate::{interrupt, pac, peripherals, Peripheral, RegExt};
pub(crate) mod sealed {
pub trait Instance {
trait SealedInstance {
fn regs() -> crate::pac::usb::Usb;
fn dpram() -> crate::pac::usb_dpram::UsbDpram;
}
}
/// USB peripheral instance.
pub trait Instance: sealed::Instance + 'static {
#[allow(private_bounds)]
pub trait Instance: SealedInstance + 'static {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
impl crate::usb::sealed::Instance for peripherals::USB {
impl crate::usb::SealedInstance for peripherals::USB {
fn regs() -> pac::usb::Usb {
pac::USBCTRL_REGS
}

View File

@ -70,7 +70,8 @@ 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-f84633553331c2d154ee72de779a40cbb10fd1bd" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9" }
vcell = "0.1.3"
nb = "1.0.0"
stm32-fmc = "0.3.0"
@ -93,9 +94,9 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies]
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-f84633553331c2d154ee72de779a40cbb10fd1bd", default-features = false, features = ["metadata"]}
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9", default-features = false, features = ["metadata"]}
[features]
default = ["rt"]

View File

@ -484,7 +484,7 @@ fn main() {
let expr = if let Some(mux) = self.chained_muxes.get(&v.name) {
self.gen_mux(mux)
} else {
self.gen_clock(&v.name)
self.gen_clock(v.name)
};
match_arms.extend(quote! {
crate::pac::rcc::vals::#enum_name::#variant_name => #expr,
@ -1006,6 +1006,18 @@ fn main() {
(("quadspi", "BK2_IO3"), quote!(crate::qspi::BK2D3Pin)),
(("quadspi", "BK2_NCS"), quote!(crate::qspi::BK2NSSPin)),
(("quadspi", "CLK"), quote!(crate::qspi::SckPin)),
(("octospi", "IO0"), quote!(crate::ospi::D0Pin)),
(("octospi", "IO1"), quote!(crate::ospi::D1Pin)),
(("octospi", "IO2"), quote!(crate::ospi::D2Pin)),
(("octospi", "IO3"), quote!(crate::ospi::D3Pin)),
(("octospi", "IO4"), quote!(crate::ospi::D4Pin)),
(("octospi", "IO5"), quote!(crate::ospi::D5Pin)),
(("octospi", "IO6"), quote!(crate::ospi::D6Pin)),
(("octospi", "IO7"), quote!(crate::ospi::D7Pin)),
(("octospi", "DQS"), quote!(crate::ospi::DQSPin)),
(("octospi", "NCS"), quote!(crate::ospi::NSSPin)),
(("octospi", "CLK"), quote!(crate::ospi::SckPin)),
(("octospi", "NCLK"), quote!(crate::ospi::NckPin)),
].into();
for p in METADATA.peripherals {
@ -1129,6 +1141,7 @@ fn main() {
// SDMMCv1 uses the same channel for both directions, so just implement for RX
(("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
(("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
(("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)),
(("dac", "CH1"), quote!(crate::dac::DacDma1)),
(("dac", "CH2"), quote!(crate::dac::DacDma2)),
(("timer", "UP"), quote!(crate::timer::UpDma)),
@ -1139,11 +1152,18 @@ fn main() {
(("timer", "CH2"), quote!(crate::timer::Ch2Dma)),
(("timer", "CH3"), quote!(crate::timer::Ch3Dma)),
(("timer", "CH4"), quote!(crate::timer::Ch4Dma)),
(("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver
(("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver
]
.into();
for p in METADATA.peripherals {
if let Some(regs) = &p.registers {
// FIXME: stm32u5a crash on Cordic driver
if chip_name.starts_with("stm32u5a") && regs.kind == "cordic" {
continue;
}
let mut dupe = HashSet::new();
for ch in p.dma_channels {
// Some chips have multiple request numbers for the same (peri, signal, channel) combos.

View File

@ -222,6 +222,13 @@ impl<'d, T: Instance> Adc<'d, T> {
// spin
}
// RM0492, RM0481, etc.
// "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected."
#[cfg(adc_h5)]
if pin.channel() == 0 {
T::regs().or().modify(|reg| reg.set_op0(true));
}
// Configure channel
Self::set_channel_sample_time(pin.channel(), self.sample_time);
@ -244,6 +251,13 @@ impl<'d, T: Instance> Adc<'d, T> {
T::regs().cr().modify(|reg| reg.set_addis(true));
// RM0492, RM0481, etc.
// "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected."
#[cfg(adc_h5)]
if pin.channel() == 0 {
T::regs().or().modify(|reg| reg.set_op0(false));
}
val
}

View File

@ -0,0 +1,71 @@
/// CORDIC function
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Function {
Cos = 0,
Sin,
Phase,
Modulus,
Arctan,
Cosh,
Sinh,
Arctanh,
Ln,
Sqrt,
}
/// CORDIC precision
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, Default)]
pub enum Precision {
Iters4 = 1,
Iters8,
Iters12,
Iters16,
Iters20,
#[default]
Iters24, // this value is recommended by Reference Manual
Iters28,
Iters32,
Iters36,
Iters40,
Iters44,
Iters48,
Iters52,
Iters56,
Iters60,
}
/// CORDIC scale
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, Default, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Scale {
#[default]
Arg1Res1 = 0,
Arg1o2Res2,
Arg1o4Res4,
Arg1o8Res8,
Arg1o16Res16,
Arg1o32Res32,
Arg1o64Res64,
Arg1o128Res128,
}
/// CORDIC argument/result register access count
#[allow(missing_docs)]
#[derive(Clone, Copy, Default)]
pub enum AccessCount {
#[default]
One,
Two,
}
/// CORDIC argument/result data width
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum Width {
Bits32,
Bits16,
}

View File

@ -0,0 +1,144 @@
use super::{Function, Scale};
/// Error for [Cordic](super::Cordic)
#[derive(Debug)]
pub enum CordicError {
/// Config error
ConfigError(ConfigError),
/// Argument length is incorrect
ArgumentLengthIncorrect,
/// Result buffer length error
ResultLengthNotEnough,
/// Input value is out of range for Q1.x format
NumberOutOfRange(NumberOutOfRange),
/// Argument error
ArgError(ArgError),
}
impl From<ConfigError> for CordicError {
fn from(value: ConfigError) -> Self {
Self::ConfigError(value)
}
}
impl From<NumberOutOfRange> for CordicError {
fn from(value: NumberOutOfRange) -> Self {
Self::NumberOutOfRange(value)
}
}
impl From<ArgError> for CordicError {
fn from(value: ArgError) -> Self {
Self::ArgError(value)
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for CordicError {
fn format(&self, fmt: defmt::Formatter) {
use CordicError::*;
match self {
ConfigError(e) => defmt::write!(fmt, "{}", e),
ResultLengthNotEnough => defmt::write!(fmt, "Output buffer length is not long enough"),
ArgumentLengthIncorrect => defmt::write!(fmt, "Argument length incorrect"),
NumberOutOfRange(e) => defmt::write!(fmt, "{}", e),
ArgError(e) => defmt::write!(fmt, "{}", e),
}
}
}
/// Error during parsing [Cordic::Config](super::Config)
#[allow(dead_code)]
#[derive(Debug)]
pub struct ConfigError {
pub(super) func: Function,
pub(super) scale_range: [u8; 2],
}
#[cfg(feature = "defmt")]
impl defmt::Format for ConfigError {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "For FUNCTION: {},", self.func);
if self.scale_range[0] == self.scale_range[1] {
defmt::write!(fmt, " SCALE value should be {}", self.scale_range[0])
} else {
defmt::write!(
fmt,
" SCALE value should be {} <= SCALE <= {}",
self.scale_range[0],
self.scale_range[1]
)
}
}
}
/// Input value is out of range for Q1.x format
#[allow(missing_docs)]
#[derive(Debug)]
pub enum NumberOutOfRange {
BelowLowerBound,
AboveUpperBound,
}
#[cfg(feature = "defmt")]
impl defmt::Format for NumberOutOfRange {
fn format(&self, fmt: defmt::Formatter) {
use NumberOutOfRange::*;
match self {
BelowLowerBound => defmt::write!(fmt, "input value should be equal or greater than -1"),
AboveUpperBound => defmt::write!(fmt, "input value should be equal or less than 1"),
}
}
}
/// Error on checking input arguments
#[allow(dead_code)]
#[derive(Debug)]
pub struct ArgError {
pub(super) func: Function,
pub(super) scale: Option<Scale>,
pub(super) arg_range: [f32; 2], // only for debug display, f32 is ok
pub(super) inclusive_upper_bound: bool,
pub(super) arg_type: ArgType,
}
#[cfg(feature = "defmt")]
impl defmt::Format for ArgError {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "For FUNCTION: {},", self.func);
if let Some(scale) = self.scale {
defmt::write!(fmt, " when SCALE is {},", scale);
}
defmt::write!(fmt, " {} should be", self.arg_type);
if self.inclusive_upper_bound {
defmt::write!(
fmt,
" {} <= {} <= {}",
self.arg_range[0],
self.arg_type,
self.arg_range[1]
)
} else {
defmt::write!(
fmt,
" {} <= {} < {}",
self.arg_range[0],
self.arg_type,
self.arg_range[1]
)
};
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(super) enum ArgType {
Arg1,
Arg2,
}

View File

@ -0,0 +1,729 @@
//! coordinate rotation digital computer (CORDIC)
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
use crate::pac::cordic::vals;
use crate::{dma, peripherals};
mod enums;
pub use enums::*;
mod errors;
pub use errors::*;
pub mod utils;
/// CORDIC driver
pub struct Cordic<'d, T: Instance> {
peri: PeripheralRef<'d, T>,
config: Config,
}
/// Cordic instance
trait SealedInstance {
/// Get access to CORDIC registers
fn regs() -> crate::pac::cordic::Cordic;
/// Set Function value
fn set_func(&self, func: Function) {
Self::regs()
.csr()
.modify(|v| v.set_func(vals::Func::from_bits(func as u8)));
}
/// Set Precision value
fn set_precision(&self, precision: Precision) {
Self::regs()
.csr()
.modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8)))
}
/// Set Scale value
fn set_scale(&self, scale: Scale) {
Self::regs()
.csr()
.modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8)))
}
/// Enable global interrupt
fn enable_irq(&self) {
Self::regs().csr().modify(|v| v.set_ien(true))
}
/// Disable global interrupt
fn disable_irq(&self) {
Self::regs().csr().modify(|v| v.set_ien(false))
}
/// Enable Read DMA
fn enable_read_dma(&self) {
Self::regs().csr().modify(|v| {
v.set_dmaren(true);
})
}
/// Disable Read DMA
fn disable_read_dma(&self) {
Self::regs().csr().modify(|v| {
v.set_dmaren(false);
})
}
/// Enable Write DMA
fn enable_write_dma(&self) {
Self::regs().csr().modify(|v| {
v.set_dmawen(true);
})
}
/// Disable Write DMA
fn disable_write_dma(&self) {
Self::regs().csr().modify(|v| {
v.set_dmawen(false);
})
}
/// Set NARGS value
fn set_argument_count(&self, n: AccessCount) {
Self::regs().csr().modify(|v| {
v.set_nargs(match n {
AccessCount::One => vals::Num::NUM1,
AccessCount::Two => vals::Num::NUM2,
})
})
}
/// Set NRES value
fn set_result_count(&self, n: AccessCount) {
Self::regs().csr().modify(|v| {
v.set_nres(match n {
AccessCount::One => vals::Num::NUM1,
AccessCount::Two => vals::Num::NUM2,
});
})
}
/// Set ARGSIZE and RESSIZE value
fn set_data_width(&self, arg: Width, res: Width) {
Self::regs().csr().modify(|v| {
v.set_argsize(match arg {
Width::Bits32 => vals::Size::BITS32,
Width::Bits16 => vals::Size::BITS16,
});
v.set_ressize(match res {
Width::Bits32 => vals::Size::BITS32,
Width::Bits16 => vals::Size::BITS16,
})
})
}
/// Read RRDY flag
fn ready_to_read(&self) -> bool {
Self::regs().csr().read().rrdy()
}
/// Write value to WDATA
fn write_argument(&self, arg: u32) {
Self::regs().wdata().write_value(arg)
}
/// Read value from RDATA
fn read_result(&self) -> u32 {
Self::regs().rdata().read()
}
}
/// CORDIC instance trait
#[allow(private_bounds)]
pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral {}
/// CORDIC configuration
#[derive(Debug)]
pub struct Config {
function: Function,
precision: Precision,
scale: Scale,
}
impl Config {
/// Create a config for Cordic driver
pub fn new(function: Function, precision: Precision, scale: Scale) -> Result<Self, CordicError> {
let config = Self {
function,
precision,
scale,
};
config.check_scale()?;
Ok(config)
}
fn check_scale(&self) -> Result<(), ConfigError> {
use Function::*;
let scale_raw = self.scale as u8;
let err_range = match self.function {
Cos | Sin | Phase | Modulus if !(0..=0).contains(&scale_raw) => Some([0, 0]),
Arctan if !(0..=7).contains(&scale_raw) => Some([0, 7]),
Cosh | Sinh | Arctanh if !(1..=1).contains(&scale_raw) => Some([1, 1]),
Ln if !(1..=4).contains(&scale_raw) => Some([1, 4]),
Sqrt if !(0..=2).contains(&scale_raw) => Some([0, 2]),
Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh | Ln | Sqrt => None,
};
if let Some(range) = err_range {
Err(ConfigError {
func: self.function,
scale_range: range,
})
} else {
Ok(())
}
}
}
// common method
impl<'d, T: Instance> Cordic<'d, T> {
/// Create a Cordic driver instance
///
/// Note:
/// If you need a peripheral -> CORDIC -> peripheral mode,
/// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguments with [Self::extra_config]
pub fn new(peri: impl Peripheral<P = T> + 'd, config: Config) -> Self {
T::enable_and_reset();
into_ref!(peri);
let mut instance = Self { peri, config };
instance.reconfigure();
instance
}
/// Set a new config for Cordic driver
pub fn set_config(&mut self, config: Config) {
self.config = config;
self.reconfigure();
}
/// Set extra config for data count and data width.
pub fn extra_config(&mut self, arg_cnt: AccessCount, arg_width: Width, res_width: Width) {
self.peri.set_argument_count(arg_cnt);
self.peri.set_data_width(arg_width, res_width);
}
fn clean_rrdy_flag(&mut self) {
while self.peri.ready_to_read() {
self.peri.read_result();
}
}
/// Disable IRQ and DMA, clean RRDY, and set ARG2 to +1 (0x7FFFFFFF)
pub fn reconfigure(&mut self) {
// reset ARG2 to +1
{
self.peri.disable_irq();
self.peri.disable_read_dma();
self.peri.disable_write_dma();
self.clean_rrdy_flag();
self.peri.set_func(Function::Cos);
self.peri.set_precision(Precision::Iters4);
self.peri.set_scale(Scale::Arg1Res1);
self.peri.set_argument_count(AccessCount::Two);
self.peri.set_data_width(Width::Bits32, Width::Bits32);
self.peri.write_argument(0x0u32);
self.peri.write_argument(0x7FFFFFFFu32);
self.clean_rrdy_flag();
}
self.peri.set_func(self.config.function);
self.peri.set_precision(self.config.precision);
self.peri.set_scale(self.config.scale);
// we don't set NRES in here, but to make sure NRES is set each time user call "calc"-ish functions,
// since each "calc"-ish functions can have different ARGSIZE and RESSIZE, thus NRES should be change accordingly.
}
}
impl<'d, T: Instance> Drop for Cordic<'d, T> {
fn drop(&mut self) {
T::disable();
}
}
// q1.31 related
impl<'d, T: Instance> Cordic<'d, T> {
/// Run a blocking CORDIC calculation in q1.31 format
///
/// Notice:
/// If you set `arg1_only` to `true`, please be sure ARG2 value has been set to desired value before.
/// This function won't set ARG2 to +1 before or after each round of calculation.
/// If you want to make sure ARG2 is set to +1, consider run [.reconfigure()](Self::reconfigure).
pub fn blocking_calc_32bit(
&mut self,
arg: &[u32],
res: &mut [u32],
arg1_only: bool,
res1_only: bool,
) -> Result<usize, CordicError> {
if arg.is_empty() {
return Ok(0);
}
let res_cnt = Self::check_arg_res_length_32bit(arg.len(), res.len(), arg1_only, res1_only)?;
self.peri
.set_argument_count(if arg1_only { AccessCount::One } else { AccessCount::Two });
self.peri
.set_result_count(if res1_only { AccessCount::One } else { AccessCount::Two });
self.peri.set_data_width(Width::Bits32, Width::Bits32);
let mut cnt = 0;
match arg1_only {
true => {
// To use cordic preload function, the first value is special.
// It is loaded to CORDIC WDATA register out side of loop
let first_value = arg[0];
// preload 1st value to CORDIC, to start the CORDIC calc
self.peri.write_argument(first_value);
for &arg1 in &arg[1..] {
// preload arg1 (for next calc)
self.peri.write_argument(arg1);
// then read current result out
res[cnt] = self.peri.read_result();
cnt += 1;
if !res1_only {
res[cnt] = self.peri.read_result();
cnt += 1;
}
}
// read the last result
res[cnt] = self.peri.read_result();
cnt += 1;
if !res1_only {
res[cnt] = self.peri.read_result();
// cnt += 1;
}
}
false => {
// To use cordic preload function, the first and last value is special.
// They are load to CORDIC WDATA register out side of loop
let first_value = arg[0];
let last_value = arg[arg.len() - 1];
let paired_args = &arg[1..arg.len() - 1];
// preload 1st value to CORDIC
self.peri.write_argument(first_value);
for args in paired_args.chunks(2) {
let arg2 = args[0];
let arg1 = args[1];
// load arg2 (for current calc) first, to start the CORDIC calc
self.peri.write_argument(arg2);
// preload arg1 (for next calc)
self.peri.write_argument(arg1);
// then read current result out
res[cnt] = self.peri.read_result();
cnt += 1;
if !res1_only {
res[cnt] = self.peri.read_result();
cnt += 1;
}
}
// load last value to CORDIC, and finish the calculation
self.peri.write_argument(last_value);
res[cnt] = self.peri.read_result();
cnt += 1;
if !res1_only {
res[cnt] = self.peri.read_result();
// cnt += 1;
}
}
}
// at this point cnt should be equal to res_cnt
Ok(res_cnt)
}
/// Run a async CORDIC calculation in q.1.31 format
///
/// Notice:
/// If you set `arg1_only` to `true`, please be sure ARG2 value has been set to desired value before.
/// This function won't set ARG2 to +1 before or after each round of calculation.
/// If you want to make sure ARG2 is set to +1, consider run [.reconfigure()](Self::reconfigure).
pub async fn async_calc_32bit(
&mut self,
write_dma: impl Peripheral<P = impl WriteDma<T>>,
read_dma: impl Peripheral<P = impl ReadDma<T>>,
arg: &[u32],
res: &mut [u32],
arg1_only: bool,
res1_only: bool,
) -> Result<usize, CordicError> {
if arg.is_empty() {
return Ok(0);
}
let res_cnt = Self::check_arg_res_length_32bit(arg.len(), res.len(), arg1_only, res1_only)?;
let active_res_buf = &mut res[..res_cnt];
into_ref!(write_dma, read_dma);
self.peri
.set_argument_count(if arg1_only { AccessCount::One } else { AccessCount::Two });
self.peri
.set_result_count(if res1_only { AccessCount::One } else { AccessCount::Two });
self.peri.set_data_width(Width::Bits32, Width::Bits32);
let write_req = write_dma.request();
let read_req = read_dma.request();
self.peri.enable_write_dma();
self.peri.enable_read_dma();
let _on_drop = OnDrop::new(|| {
self.peri.disable_write_dma();
self.peri.disable_read_dma();
});
unsafe {
let write_transfer = dma::Transfer::new_write(
&mut write_dma,
write_req,
arg,
T::regs().wdata().as_ptr() as *mut _,
Default::default(),
);
let read_transfer = dma::Transfer::new_read(
&mut read_dma,
read_req,
T::regs().rdata().as_ptr() as *mut _,
active_res_buf,
Default::default(),
);
embassy_futures::join::join(write_transfer, read_transfer).await;
}
Ok(res_cnt)
}
fn check_arg_res_length_32bit(
arg_len: usize,
res_len: usize,
arg1_only: bool,
res1_only: bool,
) -> Result<usize, CordicError> {
if !arg1_only && arg_len % 2 != 0 {
return Err(CordicError::ArgumentLengthIncorrect);
}
let mut minimal_res_length = arg_len;
if !res1_only {
minimal_res_length *= 2;
}
if !arg1_only {
minimal_res_length /= 2
}
if minimal_res_length > res_len {
return Err(CordicError::ResultLengthNotEnough);
}
Ok(minimal_res_length)
}
}
// q1.15 related
impl<'d, T: Instance> Cordic<'d, T> {
/// Run a blocking CORDIC calculation in q1.15 format
///
/// Notice::
/// User will take respond to merge two u16 arguments into one u32 data, and/or split one u32 data into two u16 results.
pub fn blocking_calc_16bit(&mut self, arg: &[u32], res: &mut [u32]) -> Result<usize, CordicError> {
if arg.is_empty() {
return Ok(0);
}
if arg.len() > res.len() {
return Err(CordicError::ResultLengthNotEnough);
}
let res_cnt = arg.len();
// In q1.15 mode, 1 write/read to access 2 arguments/results
self.peri.set_argument_count(AccessCount::One);
self.peri.set_result_count(AccessCount::One);
self.peri.set_data_width(Width::Bits16, Width::Bits16);
// To use cordic preload function, the first value is special.
// It is loaded to CORDIC WDATA register out side of loop
let first_value = arg[0];
// preload 1st value to CORDIC, to start the CORDIC calc
self.peri.write_argument(first_value);
let mut cnt = 0;
for &arg_val in &arg[1..] {
// preload arg_val (for next calc)
self.peri.write_argument(arg_val);
// then read current result out
res[cnt] = self.peri.read_result();
cnt += 1;
}
// read last result out
res[cnt] = self.peri.read_result();
// cnt += 1;
Ok(res_cnt)
}
/// Run a async CORDIC calculation in q1.15 format
///
/// Notice::
/// User will take respond to merge two u16 arguments into one u32 data, and/or split one u32 data into two u16 results.
pub async fn async_calc_16bit(
&mut self,
write_dma: impl Peripheral<P = impl WriteDma<T>>,
read_dma: impl Peripheral<P = impl ReadDma<T>>,
arg: &[u32],
res: &mut [u32],
) -> Result<usize, CordicError> {
if arg.is_empty() {
return Ok(0);
}
if arg.len() > res.len() {
return Err(CordicError::ResultLengthNotEnough);
}
let res_cnt = arg.len();
let active_res_buf = &mut res[..res_cnt];
into_ref!(write_dma, read_dma);
// In q1.15 mode, 1 write/read to access 2 arguments/results
self.peri.set_argument_count(AccessCount::One);
self.peri.set_result_count(AccessCount::One);
self.peri.set_data_width(Width::Bits16, Width::Bits16);
let write_req = write_dma.request();
let read_req = read_dma.request();
self.peri.enable_write_dma();
self.peri.enable_read_dma();
let _on_drop = OnDrop::new(|| {
self.peri.disable_write_dma();
self.peri.disable_read_dma();
});
unsafe {
let write_transfer = dma::Transfer::new_write(
&mut write_dma,
write_req,
arg,
T::regs().wdata().as_ptr() as *mut _,
Default::default(),
);
let read_transfer = dma::Transfer::new_read(
&mut read_dma,
read_req,
T::regs().rdata().as_ptr() as *mut _,
active_res_buf,
Default::default(),
);
embassy_futures::join::join(write_transfer, read_transfer).await;
}
Ok(res_cnt)
}
}
macro_rules! check_arg_value {
($func_arg1_name:ident, $func_arg2_name:ident, $float_type:ty) => {
impl<'d, T: Instance> Cordic<'d, T> {
/// check input value ARG1, SCALE and FUNCTION are compatible with each other
pub fn $func_arg1_name(&self, arg: $float_type) -> Result<(), ArgError> {
let config = &self.config;
use Function::*;
struct Arg1ErrInfo {
scale: Option<Scale>,
range: [f32; 2], // f32 is ok, it only used in error display
inclusive_upper_bound: bool,
}
let err_info = match config.function {
Cos | Sin | Phase | Modulus | Arctan if !(-1.0..=1.0).contains(arg) => Some(Arg1ErrInfo {
scale: None,
range: [-1.0, 1.0],
inclusive_upper_bound: true,
}),
Cosh | Sinh if !(-0.559..=0.559).contains(arg) => Some(Arg1ErrInfo {
scale: None,
range: [-0.559, 0.559],
inclusive_upper_bound: true,
}),
Arctanh if !(-0.403..=0.403).contains(arg) => Some(Arg1ErrInfo {
scale: None,
range: [-0.403, 0.403],
inclusive_upper_bound: true,
}),
Ln => match config.scale {
Scale::Arg1o2Res2 if !(0.0535..0.5).contains(arg) => Some(Arg1ErrInfo {
scale: Some(Scale::Arg1o2Res2),
range: [0.0535, 0.5],
inclusive_upper_bound: false,
}),
Scale::Arg1o4Res4 if !(0.25..0.75).contains(arg) => Some(Arg1ErrInfo {
scale: Some(Scale::Arg1o4Res4),
range: [0.25, 0.75],
inclusive_upper_bound: false,
}),
Scale::Arg1o8Res8 if !(0.375..0.875).contains(arg) => Some(Arg1ErrInfo {
scale: Some(Scale::Arg1o8Res8),
range: [0.375, 0.875],
inclusive_upper_bound: false,
}),
Scale::Arg1o16Res16 if !(0.4375..0.584).contains(arg) => Some(Arg1ErrInfo {
scale: Some(Scale::Arg1o16Res16),
range: [0.4375, 0.584],
inclusive_upper_bound: false,
}),
Scale::Arg1o2Res2 | Scale::Arg1o4Res4 | Scale::Arg1o8Res8 | Scale::Arg1o16Res16 => None,
_ => unreachable!(),
},
Sqrt => match config.scale {
Scale::Arg1Res1 if !(0.027..0.75).contains(arg) => Some(Arg1ErrInfo {
scale: Some(Scale::Arg1Res1),
range: [0.027, 0.75],
inclusive_upper_bound: false,
}),
Scale::Arg1o2Res2 if !(0.375..0.875).contains(arg) => Some(Arg1ErrInfo {
scale: Some(Scale::Arg1o2Res2),
range: [0.375, 0.875],
inclusive_upper_bound: false,
}),
Scale::Arg1o4Res4 if !(0.4375..0.584).contains(arg) => Some(Arg1ErrInfo {
scale: Some(Scale::Arg1o4Res4),
range: [0.4375, 0.584],
inclusive_upper_bound: false,
}),
Scale::Arg1Res1 | Scale::Arg1o2Res2 | Scale::Arg1o4Res4 => None,
_ => unreachable!(),
},
Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh => None,
};
if let Some(err) = err_info {
return Err(ArgError {
func: config.function,
scale: err.scale,
arg_range: err.range,
inclusive_upper_bound: err.inclusive_upper_bound,
arg_type: ArgType::Arg1,
});
}
Ok(())
}
/// check input value ARG2 and FUNCTION are compatible with each other
pub fn $func_arg2_name(&self, arg: $float_type) -> Result<(), ArgError> {
let config = &self.config;
use Function::*;
struct Arg2ErrInfo {
range: [f32; 2], // f32 is ok, it only used in error display
}
let err_info = match config.function {
Cos | Sin if !(0.0..=1.0).contains(arg) => Some(Arg2ErrInfo { range: [0.0, 1.0] }),
Phase | Modulus if !(-1.0..=1.0).contains(arg) => Some(Arg2ErrInfo { range: [-1.0, 1.0] }),
Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh | Ln | Sqrt => None,
};
if let Some(err) = err_info {
return Err(ArgError {
func: config.function,
scale: None,
arg_range: err.range,
inclusive_upper_bound: true,
arg_type: ArgType::Arg2,
});
}
Ok(())
}
}
};
}
check_arg_value!(check_f64_arg1, check_f64_arg2, &f64);
check_arg_value!(check_f32_arg1, check_f32_arg2, &f32);
foreach_interrupt!(
($inst:ident, cordic, $block:ident, GLOBAL, $irq:ident) => {
impl Instance for peripherals::$inst {
}
impl SealedInstance for peripherals::$inst {
fn regs() -> crate::pac::cordic::Cordic {
crate::pac::$inst
}
}
};
);
dma_trait!(WriteDma, Instance);
dma_trait!(ReadDma, Instance);

View File

@ -0,0 +1,62 @@
//! Common math utils
use super::errors::NumberOutOfRange;
macro_rules! floating_fixed_convert {
($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => {
/// convert float point to fixed point format
pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> {
const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) };
if value < -1.0 {
return Err(NumberOutOfRange::BelowLowerBound)
}
if value > 1.0 {
return Err(NumberOutOfRange::AboveUpperBound)
}
let value = if 1.0 - MIN_POSITIVE < value && value <= 1.0 {
// make a exception for value between (1.0^{-x} , 1.0] float point,
// convert it to max representable value of q1.x format
(1.0 as $float_ty) - MIN_POSITIVE
} else {
value
};
// It's necessary to cast the float value to signed integer, before convert it to a unsigned value.
// Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as a unsigned value for register writing.
// see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast
Ok((value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $signed_bin_typ as $unsigned_bin_typ)
}
#[inline(always)]
/// convert fixed point to float point format
pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty {
// It's necessary to cast the unsigned integer to signed integer, before convert it to a float value.
// Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as a signed value.
// see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast
(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty)
}
};
}
floating_fixed_convert!(
f64_to_q1_31,
q1_31_to_f64,
u32,
i32,
f64,
31,
0x3E00_0000_0000_0000u64 // binary form of 1f64^(-31)
);
floating_fixed_convert!(
f32_to_q1_15,
q1_15_to_f32,
u16,
i16,
f32,
15,
0x3800_0000u32 // binary form of 1f32^(-15)
);

View File

@ -6,6 +6,7 @@
mod _version;
use core::future::Future;
use core::iter;
use core::marker::PhantomData;
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
@ -332,8 +333,142 @@ impl<'d, T: Instance, TXDMA: TxDma<T>, RXDMA: RxDma<T>> embedded_hal_async::i2c:
address: u8,
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
let _ = address;
let _ = operations;
todo!()
self.transaction(address, operations).await
}
}
/// Frame type in I2C transaction.
///
/// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST
/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an
/// ACK or NACK after the last byte received.
///
/// For write operations, the following options are identical because they differ only in the (N)ACK
/// treatment relevant for read operations:
///
/// - `FirstFrame` and `FirstAndNextFrame`
/// - `NextFrame` and `LastFrameNoStop`
///
/// Abbreviations used below:
///
/// - `ST` = start condition
/// - `SR` = repeated start condition
/// - `SP` = stop condition
/// - `ACK`/`NACK` = last byte in read operation
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum FrameOptions {
/// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in transaction and also last frame overall.
FirstAndLastFrame,
/// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but
/// not the last frame overall.
FirstFrame,
/// `[ST/SR]+[ACK]` First frame of this type in transaction, neither last frame overall nor last
/// frame in a read operation.
FirstAndNextFrame,
/// `[ACK]` Middle frame in a read operation (neither first nor last).
NextFrame,
/// `[NACK]+[SP]` Last frame overall in this transaction but not the first frame.
LastFrame,
/// `[NACK]` Last frame in a read operation but not last frame overall in this transaction.
LastFrameNoStop,
}
#[allow(dead_code)]
impl FrameOptions {
/// Sends start or repeated start condition before transfer.
fn send_start(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true,
Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false,
}
}
/// Sends stop condition after transfer.
fn send_stop(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::LastFrame => true,
Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false,
}
}
/// Sends NACK after last byte received, indicating end of read operation.
fn send_nack(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true,
Self::FirstAndNextFrame | Self::NextFrame => false,
}
}
}
/// Iterates over operations in transaction.
///
/// Returns necessary frame options for each operation to uphold the [transaction contract] and have
/// the right start/stop/(N)ACK conditions on the wire.
///
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
#[allow(dead_code)]
fn operation_frames<'a, 'b: 'a>(
operations: &'a mut [embedded_hal_1::i2c::Operation<'b>],
) -> Result<impl IntoIterator<Item = (&'a mut embedded_hal_1::i2c::Operation<'b>, FrameOptions)>, Error> {
use embedded_hal_1::i2c::Operation::{Read, Write};
// Check empty read buffer before starting transaction. Otherwise, we would risk halting with an
// error in the middle of the transaction.
//
// In principle, we could allow empty read frames within consecutive read operations, as long as
// at least one byte remains in the final (merged) read operation, but that makes the logic more
// complicated and error-prone.
if operations.iter().any(|op| match op {
Read(read) => read.is_empty(),
Write(_) => false,
}) {
return Err(Error::Overrun);
}
let mut operations = operations.iter_mut().peekable();
let mut next_first_frame = true;
Ok(iter::from_fn(move || {
let Some(op) = operations.next() else {
return None;
};
// Is `op` first frame of its type?
let first_frame = next_first_frame;
let next_op = operations.peek();
// Get appropriate frame options as combination of the following properties:
//
// - For each first operation of its type, generate a (repeated) start condition.
// - For the last operation overall in the entire transaction, generate a stop condition.
// - For read operations, check the next operation: if it is also a read operation, we merge
// these and send ACK for all bytes in the current operation; send NACK only for the final
// read operation's last byte (before write or end of entire transaction) to indicate last
// byte read and release the bus for transmission of the bus master's next byte (or stop).
//
// We check the third property unconditionally, i.e. even for write opeartions. This is okay
// because the resulting frame options are identical for write operations.
let frame = match (first_frame, next_op) {
(true, None) => FrameOptions::FirstAndLastFrame,
(true, Some(Read(_))) => FrameOptions::FirstAndNextFrame,
(true, Some(Write(_))) => FrameOptions::FirstFrame,
//
(false, None) => FrameOptions::LastFrame,
(false, Some(Read(_))) => FrameOptions::NextFrame,
(false, Some(Write(_))) => FrameOptions::LastFrameNoStop,
};
// Pre-calculate if `next_op` is the first operation of its type. We do this here and not at
// the beginning of the loop because we hand out `op` as iterator value and cannot access it
// anymore in the next iteration.
next_first_frame = match (&op, next_op) {
(_, None) => false,
(Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true,
(Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false,
};
Some((op, frame))
}))
}

View File

@ -41,68 +41,6 @@ pub unsafe fn on_interrupt<T: Instance>() {
});
}
/// Frame type in I2C transaction.
///
/// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST
/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an
/// ACK or NACK after the last byte received.
///
/// For write operations, the following options are identical because they differ only in the (N)ACK
/// treatment relevant for read operations:
///
/// - `FirstFrame` and `FirstAndNextFrame`
/// - `NextFrame` and `LastFrameNoStop`
///
/// Abbreviations used below:
///
/// - `ST` = start condition
/// - `SR` = repeated start condition
/// - `SP` = stop condition
#[derive(Copy, Clone)]
enum FrameOptions {
/// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in operation and last frame overall in this
/// transaction.
FirstAndLastFrame,
/// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but
/// not the last frame overall.
FirstFrame,
/// `[ST/SR]+[ACK]` First frame of this type in transaction, neither last frame overall nor last
/// frame in a read operation.
FirstAndNextFrame,
/// `[ACK]` Middle frame in a read operation (neither first nor last).
NextFrame,
/// `[NACK]+[SP]` Last frame overall in this transaction but not the first frame.
LastFrame,
/// `[NACK]` Last frame in a read operation but not last frame overall in this transaction.
LastFrameNoStop,
}
impl FrameOptions {
/// Sends start or repeated start condition before transfer.
fn send_start(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true,
Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false,
}
}
/// Sends stop condition after transfer.
fn send_stop(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::LastFrame => true,
Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false,
}
}
/// Sends NACK after last byte received, indicating end of read operation.
fn send_nack(self) -> bool {
match self {
Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true,
Self::FirstAndNextFrame | Self::NextFrame => false,
}
}
}
impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
T::regs().cr1().modify(|reg| {
@ -199,17 +137,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
timeout.check()?;
}
// Also wait until signalled we're master and everything is waiting for us
while {
Self::check_and_clear_error_flags()?;
let sr2 = T::regs().sr2().read();
!sr2.msl() && !sr2.busy()
} {
timeout.check()?;
// Check if we were the ones to generate START
if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() {
return Err(Error::Arbitration);
}
// Set up current address, we're trying to talk to
// Set up current address we're trying to talk to
T::regs().dr().write(|reg| reg.set_dr(addr << 1));
// Wait until address was sent
@ -231,10 +164,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
if frame.send_stop() {
// Send a STOP condition
T::regs().cr1().modify(|reg| reg.set_stop(true));
// Wait for STOP condition to transmit.
while T::regs().cr1().read().stop() {
timeout.check()?;
}
}
// Fallthrough is success
@ -301,15 +230,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
timeout.check()?;
}
// Also wait until signalled we're master and everything is waiting for us
while {
let sr2 = T::regs().sr2().read();
!sr2.msl() && !sr2.busy()
} {
timeout.check()?;
// Check if we were the ones to generate START
if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() {
return Err(Error::Arbitration);
}
// Set up current address, we're trying to talk to
// Set up current address we're trying to talk to
T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1));
// Wait until address was sent
@ -340,13 +266,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Receive last byte
*last = self.recv_byte(timeout)?;
if frame.send_stop() {
// Wait for the STOP to be sent.
while T::regs().cr1().read().stop() {
timeout.check()?;
}
}
// Fallthrough is success
Ok(())
}
@ -386,64 +305,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
///
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
// Check empty read buffer before starting transaction. Otherwise, we would not generate the
// stop condition below.
if operations.iter().any(|op| match op {
Operation::Read(read) => read.is_empty(),
Operation::Write(_) => false,
}) {
return Err(Error::Overrun);
}
let timeout = self.timeout();
let mut operations = operations.iter_mut();
let mut prev_op: Option<&mut Operation<'_>> = None;
let mut next_op = operations.next();
while let Some(op) = next_op {
next_op = operations.next();
// Check if this is the first frame of this type. This is the case for the first overall
// frame in the transaction and whenever the type of operation changes.
let first_frame =
match (prev_op.as_ref(), &op) {
(None, _) => true,
(Some(Operation::Read(_)), Operation::Write(_))
| (Some(Operation::Write(_)), Operation::Read(_)) => true,
(Some(Operation::Read(_)), Operation::Read(_))
| (Some(Operation::Write(_)), Operation::Write(_)) => false,
};
let frame = match (first_frame, next_op.as_ref()) {
// If this is the first frame of this type, we generate a (repeated) start condition
// but have to consider the next operation: if it is the last, we generate the final
// stop condition. Otherwise, we branch on the operation: with read operations, only
// the last byte overall (before a write operation or the end of the transaction) is
// to be NACK'd, i.e. if another read operation follows, we must ACK this last byte.
(true, None) => FrameOptions::FirstAndLastFrame,
// Make sure to keep sending ACK for last byte in read operation when it is followed
// by another consecutive read operation. If the current operation is write, this is
// identical to `FirstFrame`.
(true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame,
// Otherwise, send NACK for last byte (in read operation). (For write, this does not
// matter and could also be `FirstAndNextFrame`.)
(true, Some(Operation::Write(_))) => FrameOptions::FirstFrame,
// If this is not the first frame of its type, we do not generate a (repeated) start
// condition. Otherwise, we branch the same way as above.
(false, None) => FrameOptions::LastFrame,
(false, Some(Operation::Read(_))) => FrameOptions::NextFrame,
(false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop,
};
for (op, frame) in operation_frames(operations)? {
match op {
Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?,
Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?,
}
prev_op = Some(op);
}
Ok(())
@ -459,43 +327,39 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
});
}
async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error>
async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error>
where
TXDMA: crate::i2c::TxDma<T>,
{
let dma_transfer = unsafe {
let regs = T::regs();
regs.cr2().modify(|w| {
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
w.set_dmaen(true);
T::regs().cr2().modify(|w| {
// Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for
// reception.
w.set_itbufen(false);
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2
// register.
w.set_dmaen(true);
// Sending NACK is not necessary (nor possible) for write transfer.
w.set_last(false);
});
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
let dst = regs.dr().as_ptr() as *mut u8;
let ch = &mut self.tx_dma;
let request = ch.request();
Transfer::new_write(ch, request, write, dst, Default::default())
};
// Sentinel to disable transfer when an error occurs or future is canceled.
// TODO: Generate STOP condition on cancel?
let on_drop = OnDrop::new(|| {
let regs = T::regs();
regs.cr2().modify(|w| {
T::regs().cr2().modify(|w| {
w.set_dmaen(false);
w.set_iterren(false);
w.set_itevten(false);
})
});
Self::enable_interrupts();
let state = T::state();
if frame.send_start() {
// Send a START condition
T::regs().cr1().modify(|reg| {
reg.set_start(true);
});
let state = T::state();
// Wait until START condition was generated
poll_fn(|cx| {
state.waker.register(cx.waker());
@ -506,6 +370,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
if sr1.start() {
Poll::Ready(Ok(()))
} else {
// When pending, (re-)enable interrupts to wake us up.
Self::enable_interrupts();
Poll::Pending
}
}
@ -513,41 +379,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
})
.await?;
// Also wait until signalled we're master and everything is waiting for us
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
// Check if we were the ones to generate START
if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() {
return Err(Error::Arbitration);
}
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(_) => {
let sr2 = T::regs().sr2().read();
if !sr2.msl() && !sr2.busy() {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
}
})
.await?;
// Set up current address, we're trying to talk to
Self::enable_interrupts();
// Set up current address we're trying to talk to
T::regs().dr().write(|reg| reg.set_dr(address << 1));
// Wait for the address to be acknowledged
poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => {
if sr1.addr() {
// Clear the ADDR condition by reading SR2.
T::regs().sr2().read();
Poll::Ready(Ok(()))
} else {
// If we need to go around, then re-enable the interrupts, otherwise nothing
// can wake us up and we'll hang.
// When pending, (re-)enable interrupts to wake us up.
Self::enable_interrupts();
Poll::Pending
}
@ -555,15 +405,32 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
})
.await?;
Self::enable_interrupts();
// Clear condition by reading SR2
T::regs().sr2().read();
}
let dma_transfer = unsafe {
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to
// this address from the memory after each TxE event.
let dst = T::regs().dr().as_ptr() as *mut u8;
let ch = &mut self.tx_dma;
let request = ch.request();
Transfer::new_write(ch, request, write, dst, Default::default())
};
// Wait for bytes to be sent, or an error to occur.
let poll_error = poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
// Unclear why the Err turbofish is necessary here? The compiler didnt require it in the other
// identical poll_fn check_and_clear matches.
Err(e) => Poll::Ready(Err::<T, Error>(e)),
Ok(_) => Poll::Pending,
Err(e) => Poll::Ready(Err::<(), Error>(e)),
Ok(_) => {
// When pending, (re-)enable interrupts to wake us up.
Self::enable_interrupts();
Poll::Pending
}
}
});
@ -573,17 +440,15 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
_ => Ok(()),
}?;
// The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too.
// 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA
// requests then wait for a BTF event before programming the Stop condition.”
// TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it?
T::regs().cr2().modify(|w| {
w.set_dmaen(false);
});
Self::enable_interrupts();
if frame.send_stop() {
// The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too.
// 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA
// requests then wait for a BTF event before programming the Stop condition.”
poll_fn(|cx| {
state.waker.register(cx.waker());
@ -591,14 +456,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => {
if sr1.btf() {
if send_stop {
T::regs().cr1().modify(|w| {
w.set_stop(true);
});
}
Poll::Ready(Ok(()))
} else {
// When pending, (re-)enable interrupts to wake us up.
Self::enable_interrupts();
Poll::Pending
}
}
@ -606,6 +467,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
})
.await?;
T::regs().cr1().modify(|w| {
w.set_stop(true);
});
}
drop(on_drop);
// Fallthrough is success
@ -617,19 +483,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
where
TXDMA: crate::i2c::TxDma<T>,
{
self.write_with_stop(address, write, true).await?;
// Wait for STOP condition to transmit.
Self::enable_interrupts();
poll_fn(|cx| {
T::state().waker.register(cx.waker());
// TODO: error interrupts are enabled here, should we additional check for and return errors?
if T::regs().cr1().read().stop() {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
})
self.write_frame(address, write, FrameOptions::FirstAndLastFrame)
.await?;
Ok(())
@ -640,35 +494,49 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
where
RXDMA: crate::i2c::RxDma<T>,
{
let state = T::state();
let buffer_len = buffer.len();
self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame)
.await?;
let dma_transfer = unsafe {
let regs = T::regs();
regs.cr2().modify(|w| {
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register.
Ok(())
}
async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error>
where
RXDMA: crate::i2c::RxDma<T>,
{
if buffer.is_empty() {
return Err(Error::Overrun);
}
// Some branches below depend on whether the buffer contains only a single byte.
let single_byte = buffer.len() == 1;
T::regs().cr2().modify(|w| {
// Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for
// reception.
w.set_itbufen(false);
// DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2
// register.
w.set_dmaen(true);
// If, in the I2C_CR2 register, the LAST bit is set, I2C automatically sends a NACK
// after the next byte following EOT_1. The user can generate a Stop condition in
// the DMA Transfer Complete interrupt routine if enabled.
w.set_last(frame.send_nack() && !single_byte);
});
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event.
let src = regs.dr().as_ptr() as *mut u8;
let ch = &mut self.rx_dma;
let request = ch.request();
Transfer::new_read(ch, request, src, buffer, Default::default())
};
// Sentinel to disable transfer when an error occurs or future is canceled.
// TODO: Generate STOP condition on cancel?
let on_drop = OnDrop::new(|| {
let regs = T::regs();
regs.cr2().modify(|w| {
T::regs().cr2().modify(|w| {
w.set_dmaen(false);
w.set_iterren(false);
w.set_itevten(false);
})
});
Self::enable_interrupts();
let state = T::state();
if frame.send_start() {
// Send a START condition and set ACK bit
T::regs().cr1().modify(|reg| {
reg.set_start(true);
@ -685,40 +553,24 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
if sr1.start() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
}
})
.await?;
// Also wait until signalled we're master and everything is waiting for us
// When pending, (re-)enable interrupts to wake us up.
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
// blocking read didnt have a check_and_clear call here, but blocking write did so
// Im adding it here in case that was an oversight.
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err(e)),
Ok(_) => {
let sr2 = T::regs().sr2().read();
if !sr2.msl() && !sr2.busy() {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
}
})
.await?;
// Set up current address, we're trying to talk to
// Check if we were the ones to generate START
if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() {
return Err(Error::Arbitration);
}
// Set up current address we're trying to talk to
T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1));
// Wait for the address to be acknowledged
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
@ -726,15 +578,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Err(e) => Poll::Ready(Err(e)),
Ok(sr1) => {
if sr1.addr() {
// 18.3.8: When a single byte must be received: the NACK must be programmed during EV6
// event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag.
if buffer_len == 1 {
T::regs().cr1().modify(|w| {
w.set_ack(false);
});
}
Poll::Ready(Ok(()))
} else {
// When pending, (re-)enable interrupts to wake us up.
Self::enable_interrupts();
Poll::Pending
}
}
@ -742,33 +589,56 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
})
.await?;
// Clear ADDR condition by reading SR2
T::regs().sr2().read();
// 18.3.8: When a single byte must be received: the NACK must be programmed during EV6
// event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag.
if frame.send_nack() && single_byte {
T::regs().cr1().modify(|w| {
w.set_ack(false);
});
}
// 18.3.8: When a single byte must be received: [snip] Then the
// user can program the STOP condition either after clearing ADDR flag, or in the
// DMA Transfer Complete interrupt routine.
if buffer_len == 1 {
// Clear condition by reading SR2
T::regs().sr2().read();
} else {
// Before starting reception of single byte (but without START condition, i.e. in case
// of continued frame), program NACK to emit at end of this byte.
if frame.send_nack() && single_byte {
T::regs().cr1().modify(|w| {
w.set_ack(false);
});
}
}
// 18.3.8: When a single byte must be received: [snip] Then the user can program the STOP
// condition either after clearing ADDR flag, or in the DMA Transfer Complete interrupt
// routine.
if frame.send_stop() && single_byte {
T::regs().cr1().modify(|w| {
w.set_stop(true);
});
} else {
// If, in the I2C_CR2 register, the LAST bit is set, I2C
// automatically sends a NACK after the next byte following EOT_1. The user can
// generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled.
T::regs().cr2().modify(|w| {
w.set_last(true);
})
}
let dma_transfer = unsafe {
// Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved
// from this address from the memory after each RxE event.
let src = T::regs().dr().as_ptr() as *mut u8;
let ch = &mut self.rx_dma;
let request = ch.request();
Transfer::new_read(ch, request, src, buffer, Default::default())
};
// Wait for bytes to be received, or an error to occur.
Self::enable_interrupts();
let poll_error = poll_fn(|cx| {
state.waker.register(cx.waker());
match Self::check_and_clear_error_flags() {
Err(e) => Poll::Ready(Err::<T, Error>(e)),
_ => Poll::Pending,
Err(e) => Poll::Ready(Err::<(), Error>(e)),
_ => {
// When pending, (re-)enable interrupts to wake us up.
Self::enable_interrupts();
Poll::Pending
}
}
});
@ -777,18 +647,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
_ => Ok(()),
}?;
// Wait for the STOP to be sent (STOP bit cleared).
Self::enable_interrupts();
poll_fn(|cx| {
state.waker.register(cx.waker());
// TODO: error interrupts are enabled here, should we additional check for and return errors?
if T::regs().cr1().read().stop() {
Poll::Pending
} else {
Poll::Ready(Ok(()))
T::regs().cr2().modify(|w| {
w.set_dmaen(false);
});
if frame.send_stop() && !single_byte {
T::regs().cr1().modify(|w| {
w.set_stop(true);
});
}
})
.await?;
drop(on_drop);
// Fallthrough is success
@ -801,8 +669,34 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
RXDMA: crate::i2c::RxDma<T>,
TXDMA: crate::i2c::TxDma<T>,
{
self.write_with_stop(address, write, false).await?;
self.read(address, read).await
// Check empty read buffer before starting transaction. Otherwise, we would not generate the
// stop condition below.
if read.is_empty() {
return Err(Error::Overrun);
}
self.write_frame(address, write, FrameOptions::FirstFrame).await?;
self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await
}
/// Transaction with operations.
///
/// Consecutive operations of same type are merged. See [transaction contract] for details.
///
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error>
where
RXDMA: crate::i2c::RxDma<T>,
TXDMA: crate::i2c::TxDma<T>,
{
for (op, frame) in operation_frames(operations)? {
match op {
Operation::Read(read) => self.read_frame(addr, read, frame).await?,
Operation::Write(write) => self.write_frame(addr, write, frame).await?,
}
}
Ok(())
}
}

View File

@ -557,6 +557,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
Ok(())
}
/// Transaction with operations.
///
/// Consecutive operations of same type are merged. See [transaction contract] for details.
///
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error>
where
RXDMA: crate::i2c::RxDma<T>,
TXDMA: crate::i2c::TxDma<T>,
{
let _ = addr;
let _ = operations;
todo!()
}
// =========================
// Blocking public API

View File

@ -32,6 +32,9 @@ pub mod timer;
pub mod adc;
#[cfg(can)]
pub mod can;
// FIXME: Cordic driver cause stm32u5a5zj crash
#[cfg(all(cordic, not(any(stm32u5a5, stm32u5a9))))]
pub mod cordic;
#[cfg(crc)]
pub mod crc;
#[cfg(cryp)]
@ -61,6 +64,8 @@ pub mod ipcc;
pub mod low_power;
#[cfg(opamp)]
pub mod opamp;
#[cfg(octospi)]
pub mod ospi;
#[cfg(quadspi)]
pub mod qspi;
#[cfg(rng)]
@ -244,7 +249,7 @@ pub fn init(config: Config) -> Peripherals {
#[cfg(dbgmcu)]
crate::pac::DBGMCU.cr().modify(|cr| {
#[cfg(any(dbgmcu_h5))]
#[cfg(dbgmcu_h5)]
{
cr.set_stop(config.enable_debug_during_sleep);
cr.set_standby(config.enable_debug_during_sleep);

View File

@ -0,0 +1,386 @@
//! Enums used in Ospi configuration.
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub(crate) enum OspiMode {
IndirectWrite,
IndirectRead,
AutoPolling,
MemoryMapped,
}
impl Into<u8> for OspiMode {
fn into(self) -> u8 {
match self {
OspiMode::IndirectWrite => 0b00,
OspiMode::IndirectRead => 0b01,
OspiMode::AutoPolling => 0b10,
OspiMode::MemoryMapped => 0b11,
}
}
}
/// Ospi lane width
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum OspiWidth {
/// None
NONE,
/// Single lane
SING,
/// Dual lanes
DUAL,
/// Quad lanes
QUAD,
/// Eight lanes
OCTO,
}
impl Into<u8> for OspiWidth {
fn into(self) -> u8 {
match self {
OspiWidth::NONE => 0b00,
OspiWidth::SING => 0b01,
OspiWidth::DUAL => 0b10,
OspiWidth::QUAD => 0b11,
OspiWidth::OCTO => 0b100,
}
}
}
/// Flash bank selection
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum FlashSelection {
/// Bank 1
Flash1,
/// Bank 2
Flash2,
}
impl Into<bool> for FlashSelection {
fn into(self) -> bool {
match self {
FlashSelection::Flash1 => false,
FlashSelection::Flash2 => true,
}
}
}
/// Wrap Size
#[allow(dead_code)]
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum WrapSize {
None,
_16Bytes,
_32Bytes,
_64Bytes,
_128Bytes,
}
impl Into<u8> for WrapSize {
fn into(self) -> u8 {
match self {
WrapSize::None => 0x00,
WrapSize::_16Bytes => 0x02,
WrapSize::_32Bytes => 0x03,
WrapSize::_64Bytes => 0x04,
WrapSize::_128Bytes => 0x05,
}
}
}
/// Memory Type
#[allow(missing_docs)]
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum MemoryType {
Micron,
Macronix,
Standard,
MacronixRam,
HyperBusMemory,
HyperBusRegister,
}
impl Into<u8> for MemoryType {
fn into(self) -> u8 {
match self {
MemoryType::Micron => 0x00,
MemoryType::Macronix => 0x01,
MemoryType::Standard => 0x02,
MemoryType::MacronixRam => 0x03,
MemoryType::HyperBusMemory => 0x04,
MemoryType::HyperBusRegister => 0x04,
}
}
}
/// Ospi memory size.
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum MemorySize {
_1KiB,
_2KiB,
_4KiB,
_8KiB,
_16KiB,
_32KiB,
_64KiB,
_128KiB,
_256KiB,
_512KiB,
_1MiB,
_2MiB,
_4MiB,
_8MiB,
_16MiB,
_32MiB,
_64MiB,
_128MiB,
_256MiB,
_512MiB,
_1GiB,
_2GiB,
_4GiB,
Other(u8),
}
impl Into<u8> for MemorySize {
fn into(self) -> u8 {
match self {
MemorySize::_1KiB => 9,
MemorySize::_2KiB => 10,
MemorySize::_4KiB => 11,
MemorySize::_8KiB => 12,
MemorySize::_16KiB => 13,
MemorySize::_32KiB => 14,
MemorySize::_64KiB => 15,
MemorySize::_128KiB => 16,
MemorySize::_256KiB => 17,
MemorySize::_512KiB => 18,
MemorySize::_1MiB => 19,
MemorySize::_2MiB => 20,
MemorySize::_4MiB => 21,
MemorySize::_8MiB => 22,
MemorySize::_16MiB => 23,
MemorySize::_32MiB => 24,
MemorySize::_64MiB => 25,
MemorySize::_128MiB => 26,
MemorySize::_256MiB => 27,
MemorySize::_512MiB => 28,
MemorySize::_1GiB => 29,
MemorySize::_2GiB => 30,
MemorySize::_4GiB => 31,
MemorySize::Other(val) => val,
}
}
}
/// Ospi Address size
#[derive(Copy, Clone)]
pub enum AddressSize {
/// 8-bit address
_8Bit,
/// 16-bit address
_16Bit,
/// 24-bit address
_24bit,
/// 32-bit address
_32bit,
}
impl Into<u8> for AddressSize {
fn into(self) -> u8 {
match self {
AddressSize::_8Bit => 0b00,
AddressSize::_16Bit => 0b01,
AddressSize::_24bit => 0b10,
AddressSize::_32bit => 0b11,
}
}
}
/// Time the Chip Select line stays high.
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum ChipSelectHighTime {
_1Cycle,
_2Cycle,
_3Cycle,
_4Cycle,
_5Cycle,
_6Cycle,
_7Cycle,
_8Cycle,
}
impl Into<u8> for ChipSelectHighTime {
fn into(self) -> u8 {
match self {
ChipSelectHighTime::_1Cycle => 0,
ChipSelectHighTime::_2Cycle => 1,
ChipSelectHighTime::_3Cycle => 2,
ChipSelectHighTime::_4Cycle => 3,
ChipSelectHighTime::_5Cycle => 4,
ChipSelectHighTime::_6Cycle => 5,
ChipSelectHighTime::_7Cycle => 6,
ChipSelectHighTime::_8Cycle => 7,
}
}
}
/// FIFO threshold.
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum FIFOThresholdLevel {
_1Bytes,
_2Bytes,
_3Bytes,
_4Bytes,
_5Bytes,
_6Bytes,
_7Bytes,
_8Bytes,
_9Bytes,
_10Bytes,
_11Bytes,
_12Bytes,
_13Bytes,
_14Bytes,
_15Bytes,
_16Bytes,
_17Bytes,
_18Bytes,
_19Bytes,
_20Bytes,
_21Bytes,
_22Bytes,
_23Bytes,
_24Bytes,
_25Bytes,
_26Bytes,
_27Bytes,
_28Bytes,
_29Bytes,
_30Bytes,
_31Bytes,
_32Bytes,
}
impl Into<u8> for FIFOThresholdLevel {
fn into(self) -> u8 {
match self {
FIFOThresholdLevel::_1Bytes => 0,
FIFOThresholdLevel::_2Bytes => 1,
FIFOThresholdLevel::_3Bytes => 2,
FIFOThresholdLevel::_4Bytes => 3,
FIFOThresholdLevel::_5Bytes => 4,
FIFOThresholdLevel::_6Bytes => 5,
FIFOThresholdLevel::_7Bytes => 6,
FIFOThresholdLevel::_8Bytes => 7,
FIFOThresholdLevel::_9Bytes => 8,
FIFOThresholdLevel::_10Bytes => 9,
FIFOThresholdLevel::_11Bytes => 10,
FIFOThresholdLevel::_12Bytes => 11,
FIFOThresholdLevel::_13Bytes => 12,
FIFOThresholdLevel::_14Bytes => 13,
FIFOThresholdLevel::_15Bytes => 14,
FIFOThresholdLevel::_16Bytes => 15,
FIFOThresholdLevel::_17Bytes => 16,
FIFOThresholdLevel::_18Bytes => 17,
FIFOThresholdLevel::_19Bytes => 18,
FIFOThresholdLevel::_20Bytes => 19,
FIFOThresholdLevel::_21Bytes => 20,
FIFOThresholdLevel::_22Bytes => 21,
FIFOThresholdLevel::_23Bytes => 22,
FIFOThresholdLevel::_24Bytes => 23,
FIFOThresholdLevel::_25Bytes => 24,
FIFOThresholdLevel::_26Bytes => 25,
FIFOThresholdLevel::_27Bytes => 26,
FIFOThresholdLevel::_28Bytes => 27,
FIFOThresholdLevel::_29Bytes => 28,
FIFOThresholdLevel::_30Bytes => 29,
FIFOThresholdLevel::_31Bytes => 30,
FIFOThresholdLevel::_32Bytes => 31,
}
}
}
/// Dummy cycle count
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum DummyCycles {
_0,
_1,
_2,
_3,
_4,
_5,
_6,
_7,
_8,
_9,
_10,
_11,
_12,
_13,
_14,
_15,
_16,
_17,
_18,
_19,
_20,
_21,
_22,
_23,
_24,
_25,
_26,
_27,
_28,
_29,
_30,
_31,
}
impl Into<u8> for DummyCycles {
fn into(self) -> u8 {
match self {
DummyCycles::_0 => 0,
DummyCycles::_1 => 1,
DummyCycles::_2 => 2,
DummyCycles::_3 => 3,
DummyCycles::_4 => 4,
DummyCycles::_5 => 5,
DummyCycles::_6 => 6,
DummyCycles::_7 => 7,
DummyCycles::_8 => 8,
DummyCycles::_9 => 9,
DummyCycles::_10 => 10,
DummyCycles::_11 => 11,
DummyCycles::_12 => 12,
DummyCycles::_13 => 13,
DummyCycles::_14 => 14,
DummyCycles::_15 => 15,
DummyCycles::_16 => 16,
DummyCycles::_17 => 17,
DummyCycles::_18 => 18,
DummyCycles::_19 => 19,
DummyCycles::_20 => 20,
DummyCycles::_21 => 21,
DummyCycles::_22 => 22,
DummyCycles::_23 => 23,
DummyCycles::_24 => 24,
DummyCycles::_25 => 25,
DummyCycles::_26 => 26,
DummyCycles::_27 => 27,
DummyCycles::_28 => 28,
DummyCycles::_29 => 29,
DummyCycles::_30 => 30,
DummyCycles::_31 => 31,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -27,8 +27,6 @@ pub struct TransferConfig {
pub address: Option<u32>,
/// Number of dummy cycles (DCYC)
pub dummy: DummyCycles,
/// Length of data
pub data_len: Option<usize>,
}
impl Default for TransferConfig {
@ -40,7 +38,6 @@ impl Default for TransferConfig {
instruction: 0,
address: None,
dummy: DummyCycles::_0,
data_len: None,
}
}
}
@ -231,7 +228,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
pub fn command(&mut self, transaction: TransferConfig) {
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
self.setup_transaction(QspiMode::IndirectWrite, &transaction, None);
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
@ -241,9 +238,8 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
if let Some(len) = transaction.data_len {
let current_ar = T::REGS.ar().read().address();
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
@ -252,10 +248,9 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
v.set_address(current_ar);
});
for idx in 0..len {
for b in buf {
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
}
*b = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
}
while !T::REGS.sr().read().tcf() {}
@ -268,17 +263,15 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
if let Some(len) = transaction.data_len {
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
});
for idx in 0..len {
for &b in buf {
while !T::REGS.sr().read().ftf() {}
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
}
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(b) };
}
while !T::REGS.sr().read().tcf() {}
@ -290,7 +283,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
where
Dma: QuadDma<T>,
{
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
@ -323,7 +316,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
where
Dma: QuadDma<T>,
{
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
@ -347,7 +340,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
transfer.blocking_wait();
}
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option<usize>) {
T::REGS.fcr().modify(|v| {
v.set_csmf(true);
v.set_ctcf(true);
@ -357,7 +350,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
while T::REGS.sr().read().busy() {}
if let Some(len) = transaction.data_len {
if let Some(len) = data_len {
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
}

View File

@ -111,7 +111,7 @@ mod util {
}
}
/// Get the kernel clocok frequency of the peripheral `T`.
/// Get the kernel clock frequency of the peripheral `T`.
///
/// # Panics
///

View File

@ -15,9 +15,7 @@ use crate::pac::timer::vals;
use crate::rcc::SealedRccPeripheral;
#[cfg(feature = "low-power")]
use crate::rtc::Rtc;
#[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))]
use crate::timer::AdvancedInstance1Channel;
use crate::timer::CoreInstance;
use crate::timer::{CoreInstance, GeneralInstance1Channel};
use crate::{interrupt, peripherals};
// NOTE regarding ALARM_COUNT:
@ -69,7 +67,7 @@ type T = peripherals::TIM23;
type T = peripherals::TIM24;
foreach_interrupt! {
(TIM1, timer, $block:ident, UP, $irq:ident) => {
(TIM1, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim1)]
#[cfg(feature = "rt")]
#[interrupt]
@ -85,7 +83,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM2, timer, $block:ident, UP, $irq:ident) => {
(TIM2, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim2)]
#[cfg(feature = "rt")]
#[interrupt]
@ -93,7 +91,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM3, timer, $block:ident, UP, $irq:ident) => {
(TIM3, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim3)]
#[cfg(feature = "rt")]
#[interrupt]
@ -101,7 +99,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM4, timer, $block:ident, UP, $irq:ident) => {
(TIM4, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim4)]
#[cfg(feature = "rt")]
#[interrupt]
@ -109,7 +107,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM5, timer, $block:ident, UP, $irq:ident) => {
(TIM5, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim5)]
#[cfg(feature = "rt")]
#[interrupt]
@ -117,7 +115,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM8, timer, $block:ident, UP, $irq:ident) => {
(TIM8, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim8)]
#[cfg(feature = "rt")]
#[interrupt]
@ -133,7 +131,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM9, timer, $block:ident, UP, $irq:ident) => {
(TIM9, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim9)]
#[cfg(feature = "rt")]
#[interrupt]
@ -141,7 +139,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM12, timer, $block:ident, UP, $irq:ident) => {
(TIM12, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim12)]
#[cfg(feature = "rt")]
#[interrupt]
@ -149,7 +147,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM15, timer, $block:ident, UP, $irq:ident) => {
(TIM15, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim15)]
#[cfg(feature = "rt")]
#[interrupt]
@ -157,7 +155,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM20, timer, $block:ident, UP, $irq:ident) => {
(TIM20, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim20)]
#[cfg(feature = "rt")]
#[interrupt]
@ -173,7 +171,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM21, timer, $block:ident, UP, $irq:ident) => {
(TIM21, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim21)]
#[cfg(feature = "rt")]
#[interrupt]
@ -181,7 +179,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM22, timer, $block:ident, UP, $irq:ident) => {
(TIM22, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim22)]
#[cfg(feature = "rt")]
#[interrupt]
@ -189,7 +187,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM23, timer, $block:ident, UP, $irq:ident) => {
(TIM23, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim23)]
#[cfg(feature = "rt")]
#[interrupt]
@ -197,7 +195,7 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM24, timer, $block:ident, UP, $irq:ident) => {
(TIM24, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim24)]
#[cfg(feature = "rt")]
#[interrupt]
@ -263,6 +261,7 @@ pub(crate) struct RtcDriver {
rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>,
}
#[allow(clippy::declare_interior_mutable_const)]
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
@ -307,16 +306,8 @@ impl RtcDriver {
w.set_ccie(0, true);
});
<T as CoreInstance>::Interrupt::unpend();
unsafe { <T as CoreInstance>::Interrupt::enable() };
#[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))]
{
<T as AdvancedInstance1Channel>::CaptureCompareInterrupt::unpend();
unsafe {
<T as AdvancedInstance1Channel>::CaptureCompareInterrupt::enable();
}
}
<T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend();
unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() };
r.cr1().modify(|w| w.set_cen(true));
}

View File

@ -47,8 +47,8 @@ pub enum TimerBits {
/// Core timer instance.
pub trait CoreInstance: RccPeripheral + 'static {
/// Interrupt for this timer.
type Interrupt: interrupt::typelevel::Interrupt;
/// Update Interrupt for this timer.
type UpdateInterrupt: interrupt::typelevel::Interrupt;
/// Amount of bits this timer has.
const BITS: TimerBits;
@ -64,29 +64,46 @@ pub trait BasicNoCr2Instance: CoreInstance {}
pub trait BasicInstance: BasicNoCr2Instance {}
/// General-purpose 16-bit timer with 1 channel instance.
pub trait GeneralInstance1Channel: CoreInstance {}
pub trait GeneralInstance1Channel: CoreInstance {
/// Capture compare interrupt for this timer.
type CaptureCompareInterrupt: interrupt::typelevel::Interrupt;
}
/// General-purpose 16-bit timer with 2 channels instance.
pub trait GeneralInstance2Channel: GeneralInstance1Channel {}
pub trait GeneralInstance2Channel: GeneralInstance1Channel {
/// Trigger event interrupt for this timer.
type TriggerInterrupt: interrupt::typelevel::Interrupt;
}
/// General-purpose 16-bit timer with 4 channels instance.
pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel {
// This trait add *extra* methods to GeneralInstance4Channel,
// that GeneralInstance4Channel doesn't use, but the "AdvancedInstance"s need.
// And it's a private trait, so it's content won't leak to outer namespace.
//
// If you want to add a new method to it, please leave a detail comment to explain it.
trait General4ChBlankSealed {
// SimplePwm<'d, T> is implemented for T: GeneralInstance4Channel
// Advanced timers implement this trait, but the output needs to be
// enabled explicitly.
// To support general-purpose and advanced timers, this function is added
// here defaulting to noop and overwritten for advanced timers.
/// Enable timer outputs.
//
// Enable timer outputs.
fn enable_outputs(&self) {}
}
/// General-purpose 16-bit timer with 4 channels instance.
#[allow(private_bounds)]
pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel + General4ChBlankSealed {}
/// General-purpose 32-bit timer with 4 channels instance.
pub trait GeneralInstance32bit4Channel: GeneralInstance4Channel {}
/// Advanced 16-bit timer with 1 channel instance.
pub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel {
/// Capture compare interrupt for this timer.
type CaptureCompareInterrupt: interrupt::typelevel::Interrupt;
/// Communication interrupt for this timer.
type CommunicationInterrupt: interrupt::typelevel::Interrupt;
/// Break input interrupt for this timer.
type BreakInputInterrupt: interrupt::typelevel::Interrupt;
}
/// Advanced 16-bit timer with 2 channels instance.
@ -127,7 +144,7 @@ dma_trait!(Ch4Dma, GeneralInstance4Channel);
macro_rules! impl_core_timer {
($inst:ident, $bits:expr) => {
impl CoreInstance for crate::peripherals::$inst {
type Interrupt = crate::_generated::peripheral_interrupts::$inst::UP;
type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP;
const BITS: TimerBits = $bits;
@ -138,6 +155,49 @@ macro_rules! impl_core_timer {
};
}
#[allow(unused)]
macro_rules! impl_general_1ch {
($inst:ident) => {
impl GeneralInstance1Channel for crate::peripherals::$inst {
type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC;
}
};
}
#[allow(unused)]
macro_rules! impl_general_2ch {
($inst:ident) => {
impl GeneralInstance2Channel for crate::peripherals::$inst {
type TriggerInterrupt = crate::_generated::peripheral_interrupts::$inst::TRG;
}
};
}
#[allow(unused)]
macro_rules! impl_advanced_1ch {
($inst:ident) => {
impl AdvancedInstance1Channel for crate::peripherals::$inst {
type CommunicationInterrupt = crate::_generated::peripheral_interrupts::$inst::COM;
type BreakInputInterrupt = crate::_generated::peripheral_interrupts::$inst::BRK;
}
};
}
// This macro only apply to "AdvancedInstance(s)",
// not "GeneralInstance4Channel" itself.
#[allow(unused)]
macro_rules! impl_general_4ch_blank_sealed {
($inst:ident) => {
impl General4ChBlankSealed for crate::peripherals::$inst {
fn enable_outputs(&self) {
unsafe { crate::pac::timer::Tim1chCmp::from_ptr(Self::regs()) }
.bdtr()
.modify(|w| w.set_moe(true));
}
}
};
}
foreach_interrupt! {
($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => {
impl_core_timer!($inst, TimerBits::Bits16);
@ -149,47 +209,52 @@ foreach_interrupt! {
impl_core_timer!($inst, TimerBits::Bits16);
impl BasicNoCr2Instance for crate::peripherals::$inst {}
impl BasicInstance for crate::peripherals::$inst {}
impl GeneralInstance1Channel for crate::peripherals::$inst {}
impl GeneralInstance2Channel for crate::peripherals::$inst {}
impl_general_1ch!($inst);
impl_general_2ch!($inst);
impl GeneralInstance4Channel for crate::peripherals::$inst {}
impl General4ChBlankSealed for crate::peripherals::$inst {}
};
($inst:ident, timer, TIM_2CH, UP, $irq:ident) => {
impl_core_timer!($inst, TimerBits::Bits16);
impl BasicNoCr2Instance for crate::peripherals::$inst {}
impl BasicInstance for crate::peripherals::$inst {}
impl GeneralInstance1Channel for crate::peripherals::$inst {}
impl GeneralInstance2Channel for crate::peripherals::$inst {}
impl_general_1ch!($inst);
impl_general_2ch!($inst);
impl GeneralInstance4Channel for crate::peripherals::$inst {}
impl General4ChBlankSealed for crate::peripherals::$inst {}
};
($inst:ident, timer, TIM_GP16, UP, $irq:ident) => {
impl_core_timer!($inst, TimerBits::Bits16);
impl BasicNoCr2Instance for crate::peripherals::$inst {}
impl BasicInstance for crate::peripherals::$inst {}
impl GeneralInstance1Channel for crate::peripherals::$inst {}
impl GeneralInstance2Channel for crate::peripherals::$inst {}
impl_general_1ch!($inst);
impl_general_2ch!($inst);
impl GeneralInstance4Channel for crate::peripherals::$inst {}
impl General4ChBlankSealed for crate::peripherals::$inst {}
};
($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {
impl_core_timer!($inst, TimerBits::Bits32);
impl BasicNoCr2Instance for crate::peripherals::$inst {}
impl BasicInstance for crate::peripherals::$inst {}
impl GeneralInstance1Channel for crate::peripherals::$inst {}
impl GeneralInstance2Channel for crate::peripherals::$inst {}
impl_general_1ch!($inst);
impl_general_2ch!($inst);
impl GeneralInstance4Channel for crate::peripherals::$inst {}
impl GeneralInstance32bit4Channel for crate::peripherals::$inst {}
impl General4ChBlankSealed for crate::peripherals::$inst {}
};
($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => {
impl_core_timer!($inst, TimerBits::Bits16);
impl BasicNoCr2Instance for crate::peripherals::$inst {}
impl BasicInstance for crate::peripherals::$inst {}
impl GeneralInstance1Channel for crate::peripherals::$inst {}
impl GeneralInstance2Channel for crate::peripherals::$inst {}
impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::<Self>() }}
impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; }
impl_general_1ch!($inst);
impl_general_2ch!($inst);
impl GeneralInstance4Channel for crate::peripherals::$inst {}
impl_general_4ch_blank_sealed!($inst);
impl_advanced_1ch!($inst);
impl AdvancedInstance2Channel for crate::peripherals::$inst {}
impl AdvancedInstance4Channel for crate::peripherals::$inst {}
};
@ -198,10 +263,11 @@ foreach_interrupt! {
impl_core_timer!($inst, TimerBits::Bits16);
impl BasicNoCr2Instance for crate::peripherals::$inst {}
impl BasicInstance for crate::peripherals::$inst {}
impl GeneralInstance1Channel for crate::peripherals::$inst {}
impl GeneralInstance2Channel for crate::peripherals::$inst {}
impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::<Self>() }}
impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; }
impl_general_1ch!($inst);
impl_general_2ch!($inst);
impl GeneralInstance4Channel for crate::peripherals::$inst {}
impl_general_4ch_blank_sealed!($inst);
impl_advanced_1ch!($inst);
impl AdvancedInstance2Channel for crate::peripherals::$inst {}
impl AdvancedInstance4Channel for crate::peripherals::$inst {}
};
@ -210,19 +276,12 @@ foreach_interrupt! {
impl_core_timer!($inst, TimerBits::Bits16);
impl BasicNoCr2Instance for crate::peripherals::$inst {}
impl BasicInstance for crate::peripherals::$inst {}
impl GeneralInstance1Channel for crate::peripherals::$inst {}
impl GeneralInstance2Channel for crate::peripherals::$inst {}
impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::<Self>() }}
impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; }
impl_general_1ch!($inst);
impl_general_2ch!($inst);
impl GeneralInstance4Channel for crate::peripherals::$inst {}
impl_general_4ch_blank_sealed!($inst);
impl_advanced_1ch!($inst);
impl AdvancedInstance2Channel for crate::peripherals::$inst {}
impl AdvancedInstance4Channel for crate::peripherals::$inst {}
};
}
#[cfg(not(stm32l0))]
#[allow(unused)]
fn set_moe<T: GeneralInstance4Channel>() {
unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) }
.bdtr()
.modify(|w| w.set_moe(true));
}

View File

@ -105,10 +105,7 @@ impl<T: BasicInstance> interrupt::typelevel::Handler<T::Interrupt> for Interrupt
}
}
pub(crate) use sealed::State;
pub(crate) mod sealed {
use super::*;
pub struct State {
pub(crate) struct State {
pub(crate) rx_waker: AtomicWaker,
pub(crate) rx_buf: RingBuffer,
pub(crate) tx_waker: AtomicWaker,
@ -118,7 +115,7 @@ pub(crate) mod sealed {
impl State {
/// Create new state
pub const fn new() -> Self {
pub(crate) const fn new() -> Self {
Self {
rx_buf: RingBuffer::new(),
tx_buf: RingBuffer::new(),
@ -128,7 +125,6 @@ pub(crate) mod sealed {
}
}
}
}
/// Bidirectional buffered UART
pub struct BufferedUart<'d, T: BasicInstance> {

View File

@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
- Add `len`, `is_empty` and `is_full` functions to `Channel`.
## 0.5.0 - 2023-12-04
- Add a PriorityChannel.
@ -35,7 +39,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Remove unnecessary uses of `atomic-polyfill`
- Add `#[must_use]` to all futures.
## 0.1.0 - 2022-08-26
- First release

View File

@ -449,6 +449,18 @@ impl<T, const N: usize> ChannelState<T, N> {
Poll::Pending
}
}
fn len(&self) -> usize {
self.queue.len()
}
fn is_empty(&self) -> bool {
self.queue.is_empty()
}
fn is_full(&self) -> bool {
self.queue.is_full()
}
}
/// A bounded channel for communicating between asynchronous tasks
@ -572,6 +584,21 @@ where
pub fn try_receive(&self) -> Result<T, TryReceiveError> {
self.lock(|c| c.try_receive())
}
/// Returns the number of elements currently in the channel.
pub fn len(&self) -> usize {
self.lock(|c| c.len())
}
/// Returns whether the channel is empty.
pub fn is_empty(&self) -> bool {
self.lock(|c| c.is_empty())
}
/// Returns whether the channel is full.
pub fn is_full(&self) -> bool {
self.lock(|c| c.is_full())
}
}
/// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the

View File

@ -38,11 +38,12 @@ pub struct Config<'a> {
/// Maximum packet size in bytes for the control endpoint 0.
///
/// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default
/// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in
/// which case using a larger packet size may be more efficient.
/// Valid values depend on the speed at which the bus is enumerated.
/// - low speed: 8
/// - full speed: 8, 16, 32, or 64
/// - high speed: 64
///
/// Default: 8 bytes
/// Default: 64 bytes
pub max_packet_size_0: u8,
/// Manufacturer name string descriptor.

View File

@ -69,7 +69,7 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
assert!(clock_divider >= 1, "clkdiv must be >= 1");
T::PIO.sm(SM).clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8);
self.sm.set_clock_divider(clock_divider);
self.sm.clkdiv_restart();
}

View File

@ -5,6 +5,7 @@
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::gpio::Pull;
use embassy_rp::pwm::{Config, InputMode, Pwm};
use embassy_time::{Duration, Ticker};
use {defmt_rtt as _, panic_probe as _};
@ -14,7 +15,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let cfg: Config = Default::default();
let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, InputMode::RisingEdge, cfg);
let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg);
let mut ticker = Ticker::every(Duration::from_secs(1));
loop {

View File

@ -126,6 +126,11 @@ fn main() -> ! {
// Initialize and create handle for devicer peripherals
let _p = embassy_stm32::init(Default::default());
// STM32s dont have any interrupts exclusively for software use, but they can all be triggered by software as well as
// by the peripheral, so we can just use any free interrupt vectors which arent used by the rest of your application.
// In this case were using UART1 and UART2, but theres nothing special about them. Any otherwise unused interrupt
// vector would work exactly the same.
// High-priority executor: USART1, priority level 6
interrupt::USART1.set_priority(Priority::P6);
let spawner = EXECUTOR_HIGH.start(interrupt::USART1);

View File

@ -127,6 +127,11 @@ fn main() -> ! {
let _p = embassy_stm32::init(Default::default());
// STM32s dont have any interrupts exclusively for software use, but they can all be triggered by software as well as
// by the peripheral, so we can just use any free interrupt vectors which arent used by the rest of your application.
// In this case were using UART4 and UART5, but theres nothing special about them. Any otherwise unused interrupt
// vector would work exactly the same.
// High-priority executor: UART4, priority level 6
interrupt::UART4.set_priority(Priority::P6);
let spawner = EXECUTOR_HIGH.start(interrupt::UART4);

View File

@ -127,6 +127,11 @@ fn main() -> ! {
let _p = embassy_stm32::init(Default::default());
// STM32s dont have any interrupts exclusively for software use, but they can all be triggered by software as well as
// by the peripheral, so we can just use any free interrupt vectors which arent used by the rest of your application.
// In this case were using UART4 and UART5, but theres nothing special about them. Any otherwise unused interrupt
// vector would work exactly the same.
// High-priority executor: UART4, priority level 6
interrupt::UART4.set_priority(Priority::P6);
let spawner = EXECUTOR_HIGH.start(interrupt::UART4);

View File

@ -49,6 +49,7 @@ async fn main(_spawner: Spawner) {
// Create the driver, from the HAL.
let mut ep_out_buffer = [0u8; 256];
let mut config = embassy_stm32::usb::Config::default();
// If the board youre using doesnt have the VBUS pin wired up correctly for detecting the USB bus voltage (e.g. on the f4 blackpill board), set this to false
config.vbus_detection = true;
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);

View File

@ -0,0 +1,78 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::cordic::{self, utils};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut dp = embassy_stm32::init(Default::default());
let mut cordic = cordic::Cordic::new(
&mut dp.CORDIC,
unwrap!(cordic::Config::new(
cordic::Function::Sin,
Default::default(),
Default::default(),
)),
);
// for output buf, the length is not that strict, larger than minimal required is ok.
let mut output_f64 = [0f64; 19];
let mut output_u32 = [0u32; 21];
// tips:
// CORDIC peripheral has some strict on input value, you can also use ".check_argX_fXX()" methods
// to make sure your input values are compatible with current CORDIC setup.
let arg1 = [-1.0, -0.5, 0.0, 0.5, 1.0]; // for trigonometric function, the ARG1 value [-pi, pi] should be map to [-1, 1]
let arg2 = [0.5]; // and for Sin function, ARG2 should be in [0, 1]
let mut input_buf = [0u32; 9];
// convert input from floating point to fixed point
input_buf[0] = unwrap!(utils::f64_to_q1_31(arg1[0]));
input_buf[1] = unwrap!(utils::f64_to_q1_31(arg2[0]));
// If input length is small, blocking mode can be used to minimize overhead.
let cnt0 = unwrap!(cordic.blocking_calc_32bit(
&input_buf[..2], // input length is strict, since driver use its length to detect calculation count
&mut output_u32,
false,
false
));
// convert result from fixed point into floating point
for (&u32_val, f64_val) in output_u32[..cnt0].iter().zip(output_f64.iter_mut()) {
*f64_val = utils::q1_31_to_f64(u32_val);
}
// convert input from floating point to fixed point
//
// first value from arg1 is used, so truncate to arg1[1..]
for (&f64_val, u32_val) in arg1[1..].iter().zip(input_buf.iter_mut()) {
*u32_val = unwrap!(utils::f64_to_q1_31(f64_val));
}
// If calculation is a little longer, async mode can make use of DMA, and let core do some other stuff.
let cnt1 = unwrap!(
cordic
.async_calc_32bit(
&mut dp.GPDMA1_CH0,
&mut dp.GPDMA1_CH1,
&input_buf[..arg1.len() - 1], // limit input buf to its actual length
&mut output_u32,
true,
false
)
.await
);
// convert result from fixed point into floating point
for (&u32_val, f64_val) in output_u32[..cnt1].iter().zip(output_f64[cnt0..cnt0 + cnt1].iter_mut()) {
*f64_val = utils::q1_31_to_f64(u32_val);
}
println!("result: {}", output_f64[..cnt0 + cnt1]);
}

View File

@ -127,6 +127,11 @@ fn main() -> ! {
let _p = embassy_stm32::init(Default::default());
// STM32s dont have any interrupts exclusively for software use, but they can all be triggered by software as well as
// by the peripheral, so we can just use any free interrupt vectors which arent used by the rest of your application.
// In this case were using UART4 and UART5, but theres nothing special about them. Any otherwise unused interrupt
// vector would work exactly the same.
// High-priority executor: UART4, priority level 6
interrupt::UART4.set_priority(Priority::P6);
let spawner = EXECUTOR_HIGH.start(interrupt::UART4);

View File

@ -94,7 +94,7 @@ async fn main(_spawner: Spawner) {
// Test level-gated
{
let mut pin2 = Output::new(&mut p11, Level::Low);
let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::Level, cfg.clone());
let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, Pull::None, InputMode::Level, cfg.clone());
assert_eq!(pwm.counter(), 0);
Timer::after_millis(5).await;
assert_eq!(pwm.counter(), 0);
@ -110,7 +110,13 @@ async fn main(_spawner: Spawner) {
// Test rising-gated
{
let mut pin2 = Output::new(&mut p11, Level::Low);
let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::RisingEdge, cfg.clone());
let pwm = Pwm::new_input(
&mut p.PWM_SLICE3,
&mut p7,
Pull::None,
InputMode::RisingEdge,
cfg.clone(),
);
assert_eq!(pwm.counter(), 0);
Timer::after_millis(5).await;
assert_eq!(pwm.counter(), 0);
@ -125,7 +131,13 @@ async fn main(_spawner: Spawner) {
// Test falling-gated
{
let mut pin2 = Output::new(&mut p11, Level::High);
let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::FallingEdge, cfg.clone());
let pwm = Pwm::new_input(
&mut p.PWM_SLICE3,
&mut p7,
Pull::None,
InputMode::FallingEdge,
cfg.clone(),
);
assert_eq!(pwm.counter(), 0);
Timer::after_millis(5).await;
assert_eq!(pwm.counter(), 0);
@ -137,6 +149,34 @@ async fn main(_spawner: Spawner) {
assert_eq!(pwm.counter(), 1);
}
// pull-down
{
let pin2 = Input::new(&mut p11, Pull::None);
Pwm::new_input(
&mut p.PWM_SLICE3,
&mut p7,
Pull::Down,
InputMode::FallingEdge,
cfg.clone(),
);
Timer::after_millis(1).await;
assert!(pin2.is_low());
}
// pull-up
{
let pin2 = Input::new(&mut p11, Pull::None);
Pwm::new_input(
&mut p.PWM_SLICE3,
&mut p7,
Pull::Up,
InputMode::FallingEdge,
cfg.clone(),
);
Timer::after_millis(1).await;
assert!(pin2.is_high());
}
info!("Test OK");
cortex_m::asm::bkpt();
}

View File

@ -14,8 +14,8 @@ stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not
stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"]
stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"]
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac", "ucpd"]
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"]
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash"]
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan", "cordic"]
stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash", "cordic"]
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"]
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"]
stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"]
@ -25,8 +25,8 @@ stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"]
stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"]
stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"]
stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"]
stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"]
stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"]
stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash", "cordic"]
stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash
stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"]
stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"]
stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"]
@ -48,6 +48,7 @@ embassy-stm32-wpan = []
not-gpdma = []
dac = []
ucpd = []
cordic = ["dep:num-traits"]
cm0 = ["portable-atomic/unsafe-assume-single-core"]
@ -83,6 +84,7 @@ chrono = { version = "^0.4", default-features = false, optional = true}
sha2 = { version = "0.10.8", default-features = false }
hmac = "0.12.1"
aes-gcm = {version = "0.10.3", default-features = false, features = ["aes", "heapless"] }
num-traits = {version="0.2", default-features = false,features = ["libm"], optional = true}
# BEGIN TESTS
# Generated by gen_test.py. DO NOT EDIT.
@ -91,6 +93,11 @@ name = "can"
path = "src/bin/can.rs"
required-features = [ "can",]
[[bin]]
name = "cordic"
path = "src/bin/cordic.rs"
required-features = [ "rng", "cordic",]
[[bin]]
name = "cryp"
path = "src/bin/cryp.rs"

View File

@ -14,7 +14,7 @@ for f in sorted(glob('./src/bin/*.rs')):
with open(f, 'r') as f:
for line in f:
if line.startswith('// required-features:'):
features = line.split(':', 2)[1].strip().split(',')
features = [feature.strip() for feature in line.split(':', 2)[1].strip().split(',')]
tests[name] = features

View File

@ -0,0 +1,135 @@
// required-features: rng, cordic
// Test Cordic driver, with Q1.31 format, Sin function, at 24 iterations (aka PRECISION = 6), using DMA transfer
#![no_std]
#![no_main]
#[path = "../common.rs"]
mod common;
use common::*;
use embassy_executor::Spawner;
use embassy_stm32::cordic::utils;
use embassy_stm32::{bind_interrupts, cordic, peripherals, rng};
use num_traits::Float;
use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
RNG => rng::InterruptHandler<peripherals::RNG>;
});
/* input value control, can be changed */
const INPUT_U32_COUNT: usize = 9;
const INPUT_U8_COUNT: usize = 4 * INPUT_U32_COUNT;
// Assume first calculation needs 2 arguments, the reset needs 1 argument.
// And all calculation generate 2 results.
const OUTPUT_LENGTH: usize = (INPUT_U32_COUNT - 1) * 2;
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let dp = embassy_stm32::init(config());
//
// use RNG generate random Q1.31 value
//
// we don't generate floating-point value, since not all binary value are valid floating-point value,
// and Q1.31 only accept a fixed range of value.
let mut rng = rng::Rng::new(dp.RNG, Irqs);
let mut input_buf_u8 = [0u8; INPUT_U8_COUNT];
defmt::unwrap!(rng.async_fill_bytes(&mut input_buf_u8).await);
// convert every [u8; 4] to a u32, for a Q1.31 value
let mut input_q1_31 = unsafe { core::mem::transmute::<[u8; INPUT_U8_COUNT], [u32; INPUT_U32_COUNT]>(input_buf_u8) };
// ARG2 for Sin function should be inside [0, 1], set MSB to 0 of a Q1.31 value, will make sure it's no less than 0.
input_q1_31[1] &= !(1u32 << 31);
//
// CORDIC calculation
//
let mut output_q1_31 = [0u32; OUTPUT_LENGTH];
// setup Cordic driver
let mut cordic = cordic::Cordic::new(
dp.CORDIC,
defmt::unwrap!(cordic::Config::new(
cordic::Function::Sin,
Default::default(),
Default::default(),
)),
);
#[cfg(feature = "stm32g491re")]
let (mut write_dma, mut read_dma) = (dp.DMA1_CH4, dp.DMA1_CH5);
#[cfg(any(feature = "stm32h563zi", feature = "stm32u585ai", feature = "stm32u5a5zj"))]
let (mut write_dma, mut read_dma) = (dp.GPDMA1_CH0, dp.GPDMA1_CH1);
// calculate first result using blocking mode
let cnt0 = defmt::unwrap!(cordic.blocking_calc_32bit(&input_q1_31[..2], &mut output_q1_31, false, false));
// calculate rest results using async mode
let cnt1 = defmt::unwrap!(
cordic
.async_calc_32bit(
&mut write_dma,
&mut read_dma,
&input_q1_31[2..],
&mut output_q1_31[cnt0..],
true,
false,
)
.await
);
// all output value length should be the same as our output buffer size
defmt::assert_eq!(cnt0 + cnt1, output_q1_31.len());
let mut cordic_result_f64 = [0.0f64; OUTPUT_LENGTH];
for (f64_val, u32_val) in cordic_result_f64.iter_mut().zip(output_q1_31) {
*f64_val = utils::q1_31_to_f64(u32_val);
}
//
// software calculation
//
let mut software_result_f64 = [0.0f64; OUTPUT_LENGTH];
let arg2 = utils::q1_31_to_f64(input_q1_31[1]);
for (&arg1, res) in input_q1_31
.iter()
.enumerate()
.filter_map(|(idx, val)| if idx != 1 { Some(val) } else { None })
.zip(software_result_f64.chunks_mut(2))
{
let arg1 = utils::q1_31_to_f64(arg1);
let (raw_res1, raw_res2) = (arg1 * core::f64::consts::PI).sin_cos();
(res[0], res[1]) = (raw_res1 * arg2, raw_res2 * arg2);
}
//
// check result are the same
//
for (cordic_res, software_res) in cordic_result_f64[..cnt0 + cnt1]
.chunks(2)
.zip(software_result_f64.chunks(2))
{
for (cord_res, soft_res) in cordic_res.iter().zip(software_res.iter()) {
// 2.0.powi(-19) is the max residual error for Sin function, in q1.31 format, with 24 iterations (aka PRECISION = 6)
defmt::assert!((cord_res - soft_res).abs() <= 2.0.powi(-19));
}
}
info!("Test OK");
cortex_m::asm::bkpt();
}