mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 06:42:32 +00:00
Merge branch 'main' of https://github.com/embassy-rs/embassy
This commit is contained in:
commit
4ffe35c840
@ -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]]
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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() }
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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() }
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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 {}
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {}
|
||||
};
|
||||
}
|
||||
|
@ -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 {}
|
||||
)*
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {}
|
||||
};
|
||||
}
|
||||
|
@ -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 {}
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"]
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
71
embassy-stm32/src/cordic/enums.rs
Normal file
71
embassy-stm32/src/cordic/enums.rs
Normal 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,
|
||||
}
|
144
embassy-stm32/src/cordic/errors.rs
Normal file
144
embassy-stm32/src/cordic/errors.rs
Normal 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,
|
||||
}
|
729
embassy-stm32/src/cordic/mod.rs
Normal file
729
embassy-stm32/src/cordic/mod.rs
Normal 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);
|
62
embassy-stm32/src/cordic/utils.rs
Normal file
62
embassy-stm32/src/cordic/utils.rs
Normal 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)
|
||||
);
|
@ -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))
|
||||
}))
|
||||
}
|
||||
|
@ -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 didn’t 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 didn’t have a check_and_clear call here, but blocking write did so
|
||||
// I’m 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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
386
embassy-stm32/src/ospi/enums.rs
Normal file
386
embassy-stm32/src/ospi/enums.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
1050
embassy-stm32/src/ospi/mod.rs
Normal file
1050
embassy-stm32/src/ospi/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -126,6 +126,11 @@ fn main() -> ! {
|
||||
// Initialize and create handle for devicer peripherals
|
||||
let _p = embassy_stm32::init(Default::default());
|
||||
|
||||
// STM32s don’t 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 aren’t used by the rest of your application.
|
||||
// In this case we’re using UART1 and UART2, but there’s 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);
|
||||
|
@ -127,6 +127,11 @@ fn main() -> ! {
|
||||
|
||||
let _p = embassy_stm32::init(Default::default());
|
||||
|
||||
// STM32s don’t 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 aren’t used by the rest of your application.
|
||||
// In this case we’re using UART4 and UART5, but there’s 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);
|
||||
|
@ -127,6 +127,11 @@ fn main() -> ! {
|
||||
|
||||
let _p = embassy_stm32::init(Default::default());
|
||||
|
||||
// STM32s don’t 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 aren’t used by the rest of your application.
|
||||
// In this case we’re using UART4 and UART5, but there’s 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);
|
||||
|
@ -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 you’re using doesn’t 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);
|
||||
|
||||
|
78
examples/stm32h5/src/bin/cordic.rs
Normal file
78
examples/stm32h5/src/bin/cordic.rs
Normal 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]);
|
||||
}
|
@ -127,6 +127,11 @@ fn main() -> ! {
|
||||
|
||||
let _p = embassy_stm32::init(Default::default());
|
||||
|
||||
// STM32s don’t 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 aren’t used by the rest of your application.
|
||||
// In this case we’re using UART4 and UART5, but there’s 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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
135
tests/stm32/src/bin/cordic.rs
Normal file
135
tests/stm32/src/bin/cordic.rs
Normal 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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user