From e98674e8fa637530c7b1c25bad55ff46df02b18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 22 Nov 2024 16:59:31 +0100 Subject: [PATCH 01/27] [DMA 6/8] More helper types & working `split` (#2532) * Add helper traits to simplify DMA channel trait bounds * Document changes * Update doc example * Remove clutter from MG * Move DmaEligible down to place helper types closer * Rename * Include split in the MG --- esp-hal/CHANGELOG.md | 3 +- esp-hal/MIGRATING-0.22.md | 74 ++++++++++++++- esp-hal/src/aes/mod.rs | 14 +-- esp-hal/src/dma/gdma.rs | 12 +++ esp-hal/src/dma/mod.rs | 154 ++++++++++++++++++++++++------- esp-hal/src/i2s/master.rs | 24 ++--- esp-hal/src/i2s/parallel.rs | 9 +- esp-hal/src/lcd_cam/cam.rs | 6 +- esp-hal/src/lcd_cam/lcd/dpi.rs | 6 +- esp-hal/src/lcd_cam/lcd/i8080.rs | 6 +- esp-hal/src/parl_io.rs | 27 +++--- esp-hal/src/spi/master.rs | 10 +- esp-hal/src/spi/slave.rs | 17 ++-- hil-test/tests/lcd_cam.rs | 6 +- 14 files changed, 270 insertions(+), 98 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 0348757aaf5..b058fffe6a4 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESP32-S3: Added SDMMC signals (#2556) - Added `set_priority` to the `DmaChannel` trait on GDMA devices (#2403, #2526) - Added `into_async` and `into_blocking` functions for `ParlIoTxOnly`, `ParlIoRxOnly` (#2526) -- ESP32-C6, H2, S3: Added `split` function to the `DmaChannel` trait. (#2526) +- ESP32-C6, H2, S3: Added `split` function to the `DmaChannel` trait. (#2526, #2532) +- DMA: `PeripheralDmaChannel` type aliasses and `DmaChannelFor` traits to improve usability. (#2532) ### Changed diff --git a/esp-hal/MIGRATING-0.22.md b/esp-hal/MIGRATING-0.22.md index 92e53418c13..1e930faaee2 100644 --- a/esp-hal/MIGRATING-0.22.md +++ b/esp-hal/MIGRATING-0.22.md @@ -1,6 +1,8 @@ # Migration Guide from 0.22.x to v1.0.0-beta.0 -## DMA configuration changes +## DMA changes + +### Configuration changes - `configure_for_async` and `configure` have been removed - PDMA devices (ESP32, ESP32-S2) provide no configurability @@ -27,6 +29,76 @@ +.with_dma(dma_channel); ``` +### Usability changes affecting applications + +Individual channels are no longer wrapped in `Channel`, but they implement the `DmaChannel` trait. +This means that if you want to split them into an `rx` and a `tx` half (which is only supported on +the H2, C6 and S3 currently), you can't move out of the channel but instead you need to call +the `split` method. + +```diff +-let tx = channel.tx; ++use esp_hal::dma::DmaChannel; ++let (rx, tx) = channel.split(); +``` + +The `Channel` types remain available for use in peripheral drivers. + +It is now simpler to work with DMA channels in generic contexts. esp-hal now provides convenience +traits and type aliasses to specify peripheral compatibility. The `ChannelCreator` types have been +removed, further simplifying use. + +For example, previously you may have needed to write something like this to accept a DMA channel +in a generic function: + +```rust +fn new_foo<'d, T>( + dma_channel: ChannelCreator<2>, // It wasn't possible to accept a generic ChannelCreator. + peripheral: impl Peripheral

+ 'd, +) +where + T: SomePeripheralInstance, + ChannelCreator<2>: DmaChannelConvert<::Dma>, +{ + let dma_channel = dma_channel.configure_for_async(false, DmaPriority::Priority0); + + let driver = PeripheralDriver::new(peripheral, config).with_dma(dma_channel); + + // ... +} +``` + +From now on a similar, but more flexible implementation may look like: + +```rust +fn new_foo<'d, T, CH>( + dma_channel: impl Peripheral

+ 'd, + peripheral: impl Peripheral

+ 'd, +) +where + T: SomePeripheralInstance, + CH: DmaChannelFor, +{ + // Optionally: dma_channel.set_priority(DmaPriority::Priority2); + + let driver = PeripheralDriver::new(peripheral, config).with_dma(dma_channel); + + // ... +} +``` + +### Usability changes affecting third party peripheral drivers + +If you are writing a driver and need to store a channel in a structure, you can use one of the +`ChannelFor` type aliasses. + +```diff + struct Aes<'d> { +- channel: ChannelTx<'d, Blocking, ::Dma>, ++ channel: ChannelTx<'d, Blocking, PeripheralTxChannel>, + } +``` + ## Timer changes The low level timers, `SystemTimer` and `TimerGroup` are now "dumb". They contain no logic for operating modes or trait implementations (except the low level `Timer` trait). diff --git a/esp-hal/src/aes/mod.rs b/esp-hal/src/aes/mod.rs index f2a32f245c2..e80f0d96a34 100644 --- a/esp-hal/src/aes/mod.rs +++ b/esp-hal/src/aes/mod.rs @@ -241,16 +241,16 @@ pub mod dma { ChannelRx, ChannelTx, DescriptorChain, - DmaChannelConvert, DmaChannelFor, DmaDescriptor, DmaPeripheral, DmaTransferRxTx, + PeripheralDmaChannel, + PeripheralRxChannel, + PeripheralTxChannel, ReadBuffer, Rx, - RxChannelFor, Tx, - TxChannelFor, WriteBuffer, }, peripheral::Peripheral, @@ -281,7 +281,7 @@ pub mod dma { /// The underlying [`Aes`](super::Aes) driver pub aes: super::Aes<'d>, - channel: Channel<'d, Blocking, DmaChannelFor>, + channel: Channel<'d, Blocking, PeripheralDmaChannel>, rx_chain: DescriptorChain, tx_chain: DescriptorChain, } @@ -295,7 +295,7 @@ pub mod dma { tx_descriptors: &'static mut [DmaDescriptor], ) -> AesDma<'d> where - CH: DmaChannelConvert>, + CH: DmaChannelFor, { let channel = Channel::new(channel.map(|ch| ch.degrade())); channel.runtime_ensure_compatible(&self.aes); @@ -331,7 +331,7 @@ pub mod dma { } impl<'d> DmaSupportTx for AesDma<'d> { - type TX = ChannelTx<'d, Blocking, TxChannelFor>; + type TX = ChannelTx<'d, Blocking, PeripheralTxChannel>; fn tx(&mut self) -> &mut Self::TX { &mut self.channel.tx @@ -343,7 +343,7 @@ pub mod dma { } impl<'d> DmaSupportRx for AesDma<'d> { - type RX = ChannelRx<'d, Blocking, RxChannelFor>; + type RX = ChannelRx<'d, Blocking, PeripheralRxChannel>; fn rx(&mut self) -> &mut Self::RX { &mut self.channel.rx diff --git a/esp-hal/src/dma/gdma.rs b/esp-hal/src/dma/gdma.rs index 3d7fd7fa395..3d762572558 100644 --- a/esp-hal/src/dma/gdma.rs +++ b/esp-hal/src/dma/gdma.rs @@ -60,6 +60,12 @@ impl Peripheral for AnyGdmaRxChannel { } } +impl DmaChannelConvert for AnyGdmaRxChannel { + fn degrade(self) -> AnyGdmaRxChannel { + self + } +} + /// An arbitrary GDMA TX channel pub struct AnyGdmaTxChannel(u8); @@ -71,6 +77,12 @@ impl Peripheral for AnyGdmaTxChannel { } } +impl DmaChannelConvert for AnyGdmaTxChannel { + fn degrade(self) -> AnyGdmaTxChannel { + self + } +} + use embassy_sync::waitqueue::AtomicWaker; static TX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index cbe9a281317..bdcfb8a63e5 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -944,38 +944,6 @@ pub trait DmaEligible { fn dma_peripheral(&self) -> DmaPeripheral; } -/// Helper type to get the DMA (Rx and Tx) channel for a peripheral. -pub type DmaChannelFor = ::Dma; -/// Helper type to get the DMA Rx channel for a peripheral. -pub type RxChannelFor = as DmaChannel>::Rx; -/// Helper type to get the DMA Tx channel for a peripheral. -pub type TxChannelFor = as DmaChannel>::Tx; - -#[doc(hidden)] -#[macro_export] -macro_rules! impl_dma_eligible { - ([$dma_ch:ident] $name:ident => $dma:ident) => { - impl $crate::dma::DmaEligible for $crate::peripherals::$name { - type Dma = $dma_ch; - - fn dma_peripheral(&self) -> $crate::dma::DmaPeripheral { - $crate::dma::DmaPeripheral::$dma - } - } - }; - - ( - $dma_ch:ident { - $($(#[$cfg:meta])? $name:ident => $dma:ident,)* - } - ) => { - $( - $(#[$cfg])? - $crate::impl_dma_eligible!([$dma_ch] $name => $dma); - )* - }; -} - #[doc(hidden)] #[derive(Debug)] pub struct DescriptorChain { @@ -1593,6 +1561,38 @@ impl RxCircularState { } } +#[doc(hidden)] +#[macro_export] +macro_rules! impl_dma_eligible { + ([$dma_ch:ident] $name:ident => $dma:ident) => { + impl $crate::dma::DmaEligible for $crate::peripherals::$name { + type Dma = $dma_ch; + + fn dma_peripheral(&self) -> $crate::dma::DmaPeripheral { + $crate::dma::DmaPeripheral::$dma + } + } + }; + + ( + $dma_ch:ident { + $($(#[$cfg:meta])? $name:ident => $dma:ident,)* + } + ) => { + $( + $(#[$cfg])? + $crate::impl_dma_eligible!([$dma_ch] $name => $dma); + )* + }; +} + +/// Helper type to get the DMA (Rx and Tx) channel for a peripheral. +pub type PeripheralDmaChannel = ::Dma; +/// Helper type to get the DMA Rx channel for a peripheral. +pub type PeripheralRxChannel = as DmaChannel>::Rx; +/// Helper type to get the DMA Tx channel for a peripheral. +pub type PeripheralTxChannel = as DmaChannel>::Tx; + #[doc(hidden)] pub trait DmaRxChannel: RxRegisterAccess + InterruptAccess + Peripheral

@@ -1647,7 +1647,7 @@ pub trait DmaChannelExt: DmaChannel { note = "Not all channels are useable with all peripherals" )] #[doc(hidden)] -pub trait DmaChannelConvert: DmaChannel { +pub trait DmaChannelConvert { fn degrade(self) -> DEG; } @@ -1657,6 +1657,94 @@ impl DmaChannelConvert for DEG { } } +/// Trait implemented for DMA channels that are compatible with a particular +/// peripheral. +/// +/// You can use this in places where a peripheral driver would expect a +/// `DmaChannel` implementation. +#[cfg_attr(pdma, doc = "")] +#[cfg_attr( + pdma, + doc = "Note that using mismatching channels (e.g. trying to use `spi2channel` with SPI3) may compile, but will panic in runtime." +)] +#[cfg_attr(pdma, doc = "")] +/// ## Example +/// +/// The following example demonstrates how this trait can be used to only accept +/// types compatible with a specific peripheral. +/// +/// ```rust,no_run +#[doc = crate::before_snippet!()] +/// use esp_hal::spi::master::{Spi, SpiDma, Config, Instance as SpiInstance}; +/// use esp_hal::dma::DmaChannelFor; +/// use esp_hal::peripheral::Peripheral; +/// use esp_hal::Blocking; +/// use esp_hal::dma::Dma; +/// +/// fn configures_spi_dma<'d, S, CH>( +/// spi: Spi<'d, Blocking, S>, +/// channel: impl Peripheral

+ 'd, +/// ) -> SpiDma<'d, Blocking, S> +/// where +/// S: SpiInstance, +/// CH: DmaChannelFor + 'd, +/// { +/// spi.with_dma(channel) +/// } +/// +/// let dma = Dma::new(peripherals.DMA); +#[cfg_attr(pdma, doc = "let dma_channel = dma.spi2channel;")] +#[cfg_attr(gdma, doc = "let dma_channel = dma.channel0;")] +#[doc = ""] +/// let spi = Spi::new_with_config( +/// peripherals.SPI2, +/// Config::default(), +/// ); +/// +/// let spi_dma = configures_spi_dma(spi, dma_channel); +/// # } +/// ``` +pub trait DmaChannelFor: + DmaChannel + DmaChannelConvert> +{ +} +impl DmaChannelFor

for CH +where + P: DmaEligible, + CH: DmaChannel + DmaChannelConvert>, +{ +} + +/// Trait implemented for the RX half of split DMA channels that are compatible +/// with a particular peripheral. Accepts complete DMA channels or split halves. +/// +/// This trait is similar in use to [`DmaChannelFor`]. +/// +/// You can use this in places where a peripheral driver would expect a +/// `DmaRxChannel` implementation. +pub trait RxChannelFor: DmaChannelConvert> {} +impl RxChannelFor

for RX +where + P: DmaEligible, + RX: DmaChannelConvert>, +{ +} + +/// Trait implemented for the TX half of split DMA channels that are compatible +/// with a particular peripheral. Accepts complete DMA channels or split halves. +/// +/// This trait is similar in use to [`DmaChannelFor`]. +/// +/// You can use this in places where a peripheral driver would expect a +/// `DmaTxChannel` implementation. +pub trait TxChannelFor: DmaChannelConvert> {} +impl TxChannelFor

for TX +where + P: DmaEligible, + TX: DmaChannelConvert>, +{ +} + /// The functions here are not meant to be used outside the HAL #[doc(hidden)] pub trait Rx: crate::private::Sealed { diff --git a/esp-hal/src/i2s/master.rs b/esp-hal/src/i2s/master.rs index cdaaaee27e1..7fb77f84879 100644 --- a/esp-hal/src/i2s/master.rs +++ b/esp-hal/src/i2s/master.rs @@ -81,7 +81,6 @@ use crate::{ ChannelRx, ChannelTx, DescriptorChain, - DmaChannelConvert, DmaChannelFor, DmaDescriptor, DmaEligible, @@ -90,11 +89,12 @@ use crate::{ DmaTransferRxCircular, DmaTransferTx, DmaTransferTxCircular, + PeripheralDmaChannel, + PeripheralRxChannel, + PeripheralTxChannel, ReadBuffer, Rx, - RxChannelFor, Tx, - TxChannelFor, WriteBuffer, }, gpio::interconnect::PeripheralOutput, @@ -271,7 +271,7 @@ where standard: Standard, data_format: DataFormat, sample_rate: impl Into, - channel: PeripheralRef<'d, DmaChannelFor>, + channel: PeripheralRef<'d, PeripheralDmaChannel>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], ) -> Self { @@ -377,7 +377,7 @@ impl<'d> I2s<'d, Blocking> { tx_descriptors: &'static mut [DmaDescriptor], ) -> Self where - CH: DmaChannelConvert>, + CH: DmaChannelFor, { Self::new_typed( i2s.map_into(), @@ -408,7 +408,7 @@ where tx_descriptors: &'static mut [DmaDescriptor], ) -> Self where - CH: DmaChannelConvert>, + CH: DmaChannelFor, { crate::into_ref!(i2s); Self::new_internal( @@ -463,7 +463,7 @@ where DmaMode: Mode, { i2s: PeripheralRef<'d, T>, - tx_channel: ChannelTx<'d, DmaMode, TxChannelFor>, + tx_channel: ChannelTx<'d, DmaMode, PeripheralTxChannel>, tx_chain: DescriptorChain, _guard: PeripheralGuard, } @@ -497,7 +497,7 @@ where T: RegisterAccess, DmaMode: Mode, { - type TX = ChannelTx<'d, DmaMode, TxChannelFor>; + type TX = ChannelTx<'d, DmaMode, PeripheralTxChannel>; fn tx(&mut self) -> &mut Self::TX { &mut self.tx_channel @@ -596,7 +596,7 @@ where DmaMode: Mode, { i2s: PeripheralRef<'d, T>, - rx_channel: ChannelRx<'d, DmaMode, RxChannelFor>, + rx_channel: ChannelRx<'d, DmaMode, PeripheralRxChannel>, rx_chain: DescriptorChain, _guard: PeripheralGuard, } @@ -630,7 +630,7 @@ where T: RegisterAccess, DmaMode: Mode, { - type RX = ChannelRx<'d, DmaMode, RxChannelFor>; + type RX = ChannelRx<'d, DmaMode, PeripheralRxChannel>; fn rx(&mut self) -> &mut Self::RX { &mut self.rx_channel @@ -766,7 +766,7 @@ mod private { M: Mode, { pub i2s: PeripheralRef<'d, T>, - pub tx_channel: ChannelTx<'d, M, TxChannelFor>, + pub tx_channel: ChannelTx<'d, M, PeripheralTxChannel>, pub descriptors: &'static mut [DmaDescriptor], pub(crate) guard: PeripheralGuard, } @@ -826,7 +826,7 @@ mod private { M: Mode, { pub i2s: PeripheralRef<'d, T>, - pub rx_channel: ChannelRx<'d, M, RxChannelFor>, + pub rx_channel: ChannelRx<'d, M, PeripheralRxChannel>, pub descriptors: &'static mut [DmaDescriptor], pub(crate) guard: PeripheralGuard, } diff --git a/esp-hal/src/i2s/parallel.rs b/esp-hal/src/i2s/parallel.rs index e46f49bc8c5..d05a945c9aa 100644 --- a/esp-hal/src/i2s/parallel.rs +++ b/esp-hal/src/i2s/parallel.rs @@ -46,14 +46,13 @@ use crate::{ asynch::DmaTxFuture, Channel, ChannelTx, - DmaChannelConvert, DmaChannelFor, DmaEligible, DmaError, DmaPeripheral, DmaTxBuffer, + PeripheralTxChannel, Tx, - TxChannelFor, }, gpio::{ interconnect::{OutputConnection, PeripheralOutput}, @@ -179,7 +178,7 @@ where I: Instance, { instance: PeripheralRef<'d, I>, - tx_channel: ChannelTx<'d, DM, TxChannelFor>, + tx_channel: ChannelTx<'d, DM, PeripheralTxChannel>, _guard: PeripheralGuard, } @@ -193,7 +192,7 @@ impl<'d> I2sParallel<'d, Blocking> { clock_pin: impl Peripheral

+ 'd, ) -> Self where - CH: DmaChannelConvert>, + CH: DmaChannelFor, { Self::new_typed(i2s.map_into(), channel, frequency, pins, clock_pin) } @@ -212,7 +211,7 @@ where clock_pin: impl Peripheral

+ 'd, ) -> Self where - CH: DmaChannelConvert>, + CH: DmaChannelFor, { crate::into_ref!(i2s); crate::into_mapped_ref!(clock_pin); diff --git a/esp-hal/src/lcd_cam/cam.rs b/esp-hal/src/lcd_cam/cam.rs index 7748259d461..e5998faef0a 100644 --- a/esp-hal/src/lcd_cam/cam.rs +++ b/esp-hal/src/lcd_cam/cam.rs @@ -67,7 +67,7 @@ use fugit::HertzU32; use crate::{ clock::Clocks, - dma::{ChannelRx, DmaChannelConvert, DmaError, DmaPeripheral, DmaRxBuffer, Rx, RxChannelFor}, + dma::{ChannelRx, DmaError, DmaPeripheral, DmaRxBuffer, PeripheralRxChannel, Rx, RxChannelFor}, gpio::{ interconnect::{PeripheralInput, PeripheralOutput}, InputSignal, @@ -123,7 +123,7 @@ pub struct Cam<'d> { /// Represents the camera interface with DMA support. pub struct Camera<'d> { lcd_cam: PeripheralRef<'d, LCD_CAM>, - rx_channel: ChannelRx<'d, Blocking, RxChannelFor>, + rx_channel: ChannelRx<'d, Blocking, PeripheralRxChannel>, _guard: GenericPeripheralGuard<{ system::Peripheral::LcdCam as u8 }>, } @@ -136,7 +136,7 @@ impl<'d> Camera<'d> { frequency: HertzU32, ) -> Self where - CH: DmaChannelConvert>, + CH: RxChannelFor, P: RxPins, { let rx_channel = ChannelRx::new(channel.map(|ch| ch.degrade())); diff --git a/esp-hal/src/lcd_cam/lcd/dpi.rs b/esp-hal/src/lcd_cam/lcd/dpi.rs index 77b30012b2b..42cef5ed496 100644 --- a/esp-hal/src/lcd_cam/lcd/dpi.rs +++ b/esp-hal/src/lcd_cam/lcd/dpi.rs @@ -106,7 +106,7 @@ use fugit::HertzU32; use crate::{ clock::Clocks, - dma::{ChannelTx, DmaChannelConvert, DmaError, DmaPeripheral, DmaTxBuffer, Tx, TxChannelFor}, + dma::{ChannelTx, DmaError, DmaPeripheral, DmaTxBuffer, PeripheralTxChannel, Tx, TxChannelFor}, gpio::{interconnect::PeripheralOutput, Level, OutputSignal}, lcd_cam::{ calculate_clkm, @@ -123,7 +123,7 @@ use crate::{ /// Represents the RGB LCD interface. pub struct Dpi<'d, DM: Mode> { lcd_cam: PeripheralRef<'d, LCD_CAM>, - tx_channel: ChannelTx<'d, Blocking, TxChannelFor>, + tx_channel: ChannelTx<'d, Blocking, PeripheralTxChannel>, _mode: PhantomData, } @@ -139,7 +139,7 @@ where config: Config, ) -> Self where - CH: DmaChannelConvert>, + CH: TxChannelFor, { let tx_channel = ChannelTx::new(channel.map(|ch| ch.degrade())); let lcd_cam = lcd.lcd_cam; diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 12d9e5856b6..0255c9e6527 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -62,7 +62,7 @@ use fugit::HertzU32; use crate::{ clock::Clocks, - dma::{ChannelTx, DmaChannelConvert, DmaError, DmaPeripheral, DmaTxBuffer, Tx, TxChannelFor}, + dma::{ChannelTx, DmaError, DmaPeripheral, DmaTxBuffer, PeripheralTxChannel, Tx, TxChannelFor}, gpio::{ interconnect::{OutputConnection, PeripheralOutput}, OutputSignal, @@ -85,7 +85,7 @@ use crate::{ /// Represents the I8080 LCD interface. pub struct I8080<'d, DM: Mode> { lcd_cam: PeripheralRef<'d, LCD_CAM>, - tx_channel: ChannelTx<'d, Blocking, TxChannelFor>, + tx_channel: ChannelTx<'d, Blocking, PeripheralTxChannel>, _mode: PhantomData, } @@ -102,7 +102,7 @@ where config: Config, ) -> Self where - CH: DmaChannelConvert>, + CH: TxChannelFor, P: TxPins, { let tx_channel = ChannelTx::new(channel.map(|ch| ch.degrade())); diff --git a/esp-hal/src/parl_io.rs b/esp-hal/src/parl_io.rs index d356f8ffa4b..14b7d0bf9fc 100644 --- a/esp-hal/src/parl_io.rs +++ b/esp-hal/src/parl_io.rs @@ -35,13 +35,14 @@ use crate::{ ChannelRx, ChannelTx, DescriptorChain, - DmaChannelConvert, DmaChannelFor, DmaDescriptor, DmaError, DmaPeripheral, DmaTransferRx, DmaTransferTx, + PeripheralRxChannel, + PeripheralTxChannel, ReadBuffer, Rx, RxChannelFor, @@ -811,7 +812,7 @@ pub struct ParlIoTx<'d, DM> where DM: Mode, { - tx_channel: ChannelTx<'d, DM, TxChannelFor>, + tx_channel: ChannelTx<'d, DM, PeripheralTxChannel>, tx_chain: DescriptorChain, _guard: GenericPeripheralGuard<{ crate::system::Peripheral::ParlIo as u8 }>, } @@ -892,7 +893,7 @@ pub struct ParlIoRx<'d, DM> where DM: Mode, { - rx_channel: ChannelRx<'d, DM, RxChannelFor>, + rx_channel: ChannelRx<'d, DM, PeripheralRxChannel>, rx_chain: DescriptorChain, _guard: GenericPeripheralGuard<{ crate::system::Peripheral::ParlIo as u8 }>, } @@ -1013,7 +1014,7 @@ impl<'d> ParlIoFullDuplex<'d, Blocking> { frequency: HertzU32, ) -> Result where - CH: DmaChannelConvert>, + CH: DmaChannelFor, { let tx_guard = GenericPeripheralGuard::new(); let rx_guard = GenericPeripheralGuard::new(); @@ -1135,7 +1136,7 @@ impl<'d> ParlIoTxOnly<'d, Blocking> { frequency: HertzU32, ) -> Result where - CH: DmaChannelConvert>, + CH: TxChannelFor, { let guard = GenericPeripheralGuard::new(); let tx_channel = ChannelTx::new(dma_channel.map(|ch| ch.degrade())); @@ -1241,7 +1242,7 @@ impl<'d> ParlIoRxOnly<'d, Blocking> { frequency: HertzU32, ) -> Result where - CH: DmaChannelConvert>, + CH: RxChannelFor, { let guard = GenericPeripheralGuard::new(); let rx_channel = ChannelRx::new(dma_channel.map(|ch| ch.degrade())); @@ -1433,7 +1434,7 @@ impl<'d, DM> DmaSupportTx for ParlIoTx<'d, DM> where DM: Mode, { - type TX = ChannelTx<'d, DM, TxChannelFor>; + type TX = ChannelTx<'d, DM, PeripheralTxChannel>; fn tx(&mut self) -> &mut Self::TX { &mut self.tx_channel @@ -1475,7 +1476,7 @@ where } fn start_receive_bytes_dma( - rx_channel: &mut ChannelRx<'d, DM, RxChannelFor>, + rx_channel: &mut ChannelRx<'d, DM, PeripheralRxChannel>, rx_chain: &mut DescriptorChain, ptr: *mut u8, len: usize, @@ -1529,7 +1530,7 @@ impl<'d, DM> DmaSupportRx for ParlIoRx<'d, DM> where DM: Mode, { - type RX = ChannelRx<'d, DM, RxChannelFor>; + type RX = ChannelRx<'d, DM, PeripheralRxChannel>; fn rx(&mut self) -> &mut Self::RX { &mut self.rx_channel @@ -1545,7 +1546,7 @@ pub struct TxCreator<'d, DM> where DM: Mode, { - tx_channel: ChannelTx<'d, DM, TxChannelFor>, + tx_channel: ChannelTx<'d, DM, PeripheralTxChannel>, descriptors: &'static mut [DmaDescriptor], _guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>, } @@ -1555,7 +1556,7 @@ pub struct RxCreator<'d, DM> where DM: Mode, { - rx_channel: ChannelRx<'d, DM, RxChannelFor>, + rx_channel: ChannelRx<'d, DM, PeripheralRxChannel>, descriptors: &'static mut [DmaDescriptor], _guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>, } @@ -1565,7 +1566,7 @@ pub struct TxCreatorFullDuplex<'d, DM> where DM: Mode, { - tx_channel: ChannelTx<'d, DM, TxChannelFor>, + tx_channel: ChannelTx<'d, DM, PeripheralTxChannel>, descriptors: &'static mut [DmaDescriptor], _guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>, } @@ -1575,7 +1576,7 @@ pub struct RxCreatorFullDuplex<'d, DM> where DM: Mode, { - rx_channel: ChannelRx<'d, DM, RxChannelFor>, + rx_channel: ChannelRx<'d, DM, PeripheralRxChannel>, descriptors: &'static mut [DmaDescriptor], _guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>, } diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index a07fac57355..50446ba6bca 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -78,7 +78,7 @@ use procmacros::ram; use super::{DmaError, Error, SpiBitOrder, SpiDataMode, SpiMode}; use crate::{ clock::Clocks, - dma::{DmaChannelConvert, DmaChannelFor, DmaEligible, DmaRxBuffer, DmaTxBuffer, Rx, Tx}, + dma::{DmaChannelFor, DmaEligible, DmaRxBuffer, DmaTxBuffer, Rx, Tx}, gpio::{interconnect::PeripheralOutput, InputSignal, NoPin, OutputSignal}, interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, @@ -540,7 +540,7 @@ where /// operations. pub fn with_dma(self, channel: impl Peripheral

+ 'd) -> SpiDma<'d, Blocking, T> where - CH: DmaChannelConvert>, + CH: DmaChannelFor, { SpiDma::new(self.spi, channel.map(|ch| ch.degrade()).into_ref()) } @@ -856,12 +856,12 @@ mod dma { dma::{ asynch::{DmaRxFuture, DmaTxFuture}, Channel, - DmaChannelFor, DmaRxBuf, DmaRxBuffer, DmaTxBuf, DmaTxBuffer, EmptyBuf, + PeripheralDmaChannel, Rx, Tx, }, @@ -883,7 +883,7 @@ mod dma { M: Mode, { pub(crate) spi: PeripheralRef<'d, T>, - pub(crate) channel: Channel<'d, M, DmaChannelFor>, + pub(crate) channel: Channel<'d, M, PeripheralDmaChannel>, tx_transfer_in_progress: bool, rx_transfer_in_progress: bool, #[cfg(all(esp32, spi_address_workaround))] @@ -997,7 +997,7 @@ mod dma { { pub(super) fn new( spi: PeripheralRef<'d, T>, - channel: PeripheralRef<'d, DmaChannelFor>, + channel: PeripheralRef<'d, PeripheralDmaChannel>, ) -> Self { let channel = Channel::new(channel); channel.runtime_ensure_compatible(&spi); diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index cf48c07fe9b..4bfe0bc4572 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -73,7 +73,7 @@ use core::marker::PhantomData; use super::{Error, SpiMode}; use crate::{ - dma::{DmaChannelConvert, DmaEligible}, + dma::DmaEligible, gpio::{ interconnect::{PeripheralInput, PeripheralOutput}, InputSignal, @@ -182,11 +182,12 @@ pub mod dma { DmaTransferRx, DmaTransferRxTx, DmaTransferTx, + PeripheralDmaChannel, + PeripheralRxChannel, + PeripheralTxChannel, ReadBuffer, Rx, - RxChannelFor, Tx, - TxChannelFor, WriteBuffer, }, Mode, @@ -206,7 +207,7 @@ pub mod dma { tx_descriptors: &'static mut [DmaDescriptor], ) -> SpiDma<'d, Blocking, T> where - CH: DmaChannelConvert>, + CH: DmaChannelFor, { self.spi.info().set_data_mode(self.data_mode, true); SpiDma::new( @@ -225,7 +226,7 @@ pub mod dma { M: Mode, { pub(crate) spi: PeripheralRef<'d, T>, - pub(crate) channel: Channel<'d, M, DmaChannelFor>, + pub(crate) channel: Channel<'d, M, PeripheralDmaChannel>, rx_chain: DescriptorChain, tx_chain: DescriptorChain, _guard: PeripheralGuard, @@ -265,7 +266,7 @@ pub mod dma { T: InstanceDma, DmaMode: Mode, { - type TX = ChannelTx<'d, DmaMode, TxChannelFor>; + type TX = ChannelTx<'d, DmaMode, PeripheralTxChannel>; fn tx(&mut self) -> &mut Self::TX { &mut self.channel.tx @@ -281,7 +282,7 @@ pub mod dma { T: InstanceDma, DmaMode: Mode, { - type RX = ChannelRx<'d, DmaMode, RxChannelFor>; + type RX = ChannelRx<'d, DmaMode, PeripheralRxChannel>; fn rx(&mut self) -> &mut Self::RX { &mut self.channel.rx @@ -298,7 +299,7 @@ pub mod dma { { fn new( spi: PeripheralRef<'d, T>, - channel: PeripheralRef<'d, DmaChannelFor>, + channel: PeripheralRef<'d, PeripheralDmaChannel>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], ) -> Self { diff --git a/hil-test/tests/lcd_cam.rs b/hil-test/tests/lcd_cam.rs index 1dcae2c785a..c1002e7fbe0 100644 --- a/hil-test/tests/lcd_cam.rs +++ b/hil-test/tests/lcd_cam.rs @@ -6,7 +6,7 @@ #![no_main] use esp_hal::{ - dma::{Dma, DmaRxBuf, DmaTxBuf}, + dma::{Dma, DmaChannel, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::Level, lcd_cam::{ @@ -58,9 +58,7 @@ mod tests { let dma = Dma::new(peripherals.DMA); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); - // TODO: use split channels once supported - let tx_channel = dma.channel2; - let rx_channel = dma.channel3; + let (rx_channel, tx_channel) = dma.channel2.split(); let (vsync_in, vsync_out) = peripherals.GPIO6.split(); let (hsync_in, hsync_out) = peripherals.GPIO7.split(); From 79ca9d07aac812fe7de680147b058450b9362d65 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Fri, 22 Nov 2024 16:14:14 +0000 Subject: [PATCH 02/27] [2/3] Timer refactor: TIMG (#2581) * system timer simplfication * Removes _all_ type params on Alarm * Systimer no longer implements peripheral ref, the peripheral ref pattern is instead intended to be used on the higher level timer drivers * Removed `Unit` as a type, in favour of an enum * Alarms are back in the main `SystemTimer` "driver" * Made all `Unit` modification methods unsafe, it's not possible to modify the `Unit`'s safely whilst timers and or the `time::now` API is in use * fix examples and tests (by removing them :D) * changelog and migration * Review feedback * changelog and migration * /unit_count/unit_value/g * changelog and migration * /unit_count/unit_value/g * system timer simplfication * Removes _all_ type params on Alarm * Systimer no longer implements peripheral ref, the peripheral ref pattern is instead intended to be used on the higher level timer drivers * Removed `Unit` as a type, in favour of an enum * Alarms are back in the main `SystemTimer` "driver" * Made all `Unit` modification methods unsafe, it's not possible to modify the `Unit`'s safely whilst timers and or the `time::now` API is in use * Make TimerGroup Timer's dumb and untyped * changelog * review * review --- esp-hal-embassy/src/lib.rs | 7 +- esp-hal/CHANGELOG.md | 8 +- esp-hal/MIGRATING-0.22.md | 9 + esp-hal/src/prelude.rs | 5 +- esp-hal/src/timer/mod.rs | 47 +- esp-hal/src/timer/timg.rs | 636 ++++++---------------------- esp-wifi/src/lib.rs | 14 +- examples/src/bin/etm_timer.rs | 84 ---- examples/src/bin/timer_interrupt.rs | 59 --- 9 files changed, 144 insertions(+), 725 deletions(-) delete mode 100644 examples/src/bin/etm_timer.rs delete mode 100644 examples/src/bin/timer_interrupt.rs diff --git a/esp-hal-embassy/src/lib.rs b/esp-hal-embassy/src/lib.rs index 5efbd02fb5f..e2de84fcc9d 100644 --- a/esp-hal-embassy/src/lib.rs +++ b/esp-hal-embassy/src/lib.rs @@ -75,12 +75,7 @@ trait IntoAnyTimer: Into {} impl IntoAnyTimer for AnyTimer {} -impl IntoAnyTimer for TimgTimer -where - DM: esp_hal::Mode, - Self: Into, -{ -} +impl IntoAnyTimer for TimgTimer where Self: Into {} #[cfg(not(feature = "esp32"))] impl IntoAnyTimer for Alarm where Self: Into {} diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index b058fffe6a4..3353620d894 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -21,13 +21,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - DMA channel objects are no longer wrapped in `Channel`. The `Channel` drivers are now managed by DMA enabled peripheral drivers. (#2526) - The `Dpi` driver and `DpiTransfer` now have a `Mode` type parameter. The driver's asyncness is determined by the asyncness of the `Lcd` used to create it. (#2526) - `dma::{Channel, ChannelRx, ChannelTx}::set_priority` for GDMA devices (#2403) -- `SystemTimer::set_unit_count` & `SystemTimer::configure_unit` (#2576) - `SystemTimer::set_unit_value` & `SystemTimer::configure_unit` (#2576) - -### Changed - - `SystemTimer` no longer uses peripheral ref (#2576) +- `TIMGX` no longer uses peripheral ref (#2581) - `SystemTimer::now` has been renamed `SystemTimer::unit_value(Unit)` (#2576) +- `dma::{Channel, ChannelRx, ChannelTx}::set_priority` for GDMA devices (#2403) +- `SystemTimer`s `Alarm`s are now type erased (#2576) +- `TimerGroup` `Timer`s are now type erased (#2581) ### Fixed diff --git a/esp-hal/MIGRATING-0.22.md b/esp-hal/MIGRATING-0.22.md index 1e930faaee2..8c18db3f46b 100644 --- a/esp-hal/MIGRATING-0.22.md +++ b/esp-hal/MIGRATING-0.22.md @@ -116,3 +116,12 @@ let systimer = SystemTimer::new(peripherals.SYSTIMER); + let mut timer = PeriodicTimer::new(alarm0); + timer.start(1u64.secs()); ``` + +### TIMG + +Timer group timers have been type erased. + +```diff +- timg::Timer, Blocking> ++ timg::Timer +``` diff --git a/esp-hal/src/prelude.rs b/esp-hal/src/prelude.rs index 00dc7acb935..9824f700b07 100644 --- a/esp-hal/src/prelude.rs +++ b/esp-hal/src/prelude.rs @@ -33,10 +33,7 @@ mod imp { }, }; #[cfg(any(timg0, timg1))] - pub use crate::timer::timg::{ - Instance as _esp_hal_timer_timg_Instance, - TimerGroupInstance as _esp_hal_timer_timg_TimerGroupInstance, - }; + pub use crate::timer::timg::TimerGroupInstance as _esp_hal_timer_timg_TimerGroupInstance; #[cfg(any(systimer, timg0, timg1))] pub use crate::timer::Timer as _esp_hal_timer_Timer; pub use crate::{clock::CpuClock, entry, macros::*, InterruptConfigurable}; diff --git a/esp-hal/src/timer/mod.rs b/esp-hal/src/timer/mod.rs index e9c02ac10e0..a05138a1355 100644 --- a/esp-hal/src/timer/mod.rs +++ b/esp-hal/src/timer/mod.rs @@ -45,7 +45,6 @@ use fugit::{ExtU64, Instant, MicrosDurationU64}; use crate::{ interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, - Blocking, InterruptConfigurable, }; @@ -360,16 +359,7 @@ impl embedded_hal_02::timer::Periodic for PeriodicTimer<'_, T> where T: Timer /// An enum of all timer types enum AnyTimerInner { /// Timer 0 of the TIMG0 peripheral in blocking mode. - Timg0Timer0(timg::Timer, Blocking>), - /// Timer 1 of the TIMG0 peripheral in blocking mode. - #[cfg(timg_timer1)] - Timg0Timer1(timg::Timer, Blocking>), - /// Timer 0 of the TIMG1 peripheral in blocking mode. - #[cfg(timg1)] - Timg1Timer0(timg::Timer, Blocking>), - /// Timer 1 of the TIMG1 peripheral in blocking mode. - #[cfg(all(timg1, timg_timer1))] - Timg1Timer1(timg::Timer, Blocking>), + TimgTimer(timg::Timer), /// Systimer Alarm #[cfg(systimer)] SystimerAlarm(systimer::Alarm), @@ -382,30 +372,9 @@ pub struct AnyTimer(AnyTimerInner); impl crate::private::Sealed for AnyTimer {} -impl From, Blocking>> for AnyTimer { - fn from(value: timg::Timer, Blocking>) -> Self { - Self(AnyTimerInner::Timg0Timer0(value)) - } -} - -#[cfg(timg_timer1)] -impl From, Blocking>> for AnyTimer { - fn from(value: timg::Timer, Blocking>) -> Self { - Self(AnyTimerInner::Timg0Timer1(value)) - } -} - -#[cfg(timg1)] -impl From, Blocking>> for AnyTimer { - fn from(value: timg::Timer, Blocking>) -> Self { - Self(AnyTimerInner::Timg1Timer0(value)) - } -} - -#[cfg(all(timg1, timg_timer1))] -impl From, Blocking>> for AnyTimer { - fn from(value: timg::Timer, Blocking>) -> Self { - Self(AnyTimerInner::Timg1Timer1(value)) +impl From for AnyTimer { + fn from(value: timg::Timer) -> Self { + Self(AnyTimerInner::TimgTimer(value)) } } @@ -419,13 +388,7 @@ impl From for AnyTimer { impl Timer for AnyTimer { delegate::delegate! { to match &self.0 { - AnyTimerInner::Timg0Timer0(inner) => inner, - #[cfg(timg_timer1)] - AnyTimerInner::Timg0Timer1(inner) => inner, - #[cfg(timg1)] - AnyTimerInner::Timg1Timer0(inner) => inner, - #[cfg(all(timg1,timg_timer1))] - AnyTimerInner::Timg1Timer1(inner) => inner, + AnyTimerInner::TimgTimer(inner) => inner, #[cfg(systimer)] AnyTimerInner::SystimerAlarm(inner) => inner, } { diff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs index bcbfc619391..ab20836afb0 100644 --- a/esp-hal/src/timer/timg.rs +++ b/esp-hal/src/timer/timg.rs @@ -64,10 +64,7 @@ //! # } //! ``` -use core::{ - marker::PhantomData, - ops::{Deref, DerefMut}, -}; +use core::marker::PhantomData; use fugit::{HertzU32, Instant, MicrosDurationU64}; @@ -77,15 +74,12 @@ use crate::soc::constants::TIMG_DEFAULT_CLK_SRC; use crate::{ clock::Clocks, interrupt::{self, InterruptHandler}, - peripheral::{Peripheral, PeripheralRef}, + peripheral::Peripheral, peripherals::{timg0::RegisterBlock, Interrupt, TIMG0}, private::Sealed, sync::{lock, Lock}, system::PeripheralClockControl, - Async, - Blocking, InterruptConfigurable, - Mode, }; const NUM_TIMG: usize = 1 + cfg!(timg1) as usize; @@ -96,17 +90,16 @@ static INT_ENA_LOCK: [Lock; NUM_TIMG] = [const { Lock::new() }; NUM_TIMG]; #[cfg_attr(not(timg_timer1), doc = "a general purpose timer")] #[cfg_attr(timg_timer1, doc = "2 timers")] /// and a watchdog timer. -pub struct TimerGroup<'d, T, DM> +pub struct TimerGroup where T: TimerGroupInstance, - DM: Mode, { - _timer_group: PeripheralRef<'d, T>, + _timer_group: PhantomData, /// Timer 0 - pub timer0: Timer, DM>, + pub timer0: Timer, /// Timer 1 #[cfg(timg_timer1)] - pub timer1: Timer, DM>, + pub timer1: Timer, /// Watchdog timer pub wdt: Wdt, } @@ -240,283 +233,69 @@ impl TimerGroupInstance for crate::peripherals::TIMG1 { } } -impl<'d, T, DM> TimerGroup<'d, T, DM> +impl TimerGroup where T: TimerGroupInstance, - DM: Mode, { /// Construct a new instance of [`TimerGroup`] in blocking mode - pub fn new_inner(_timer_group: impl Peripheral

+ 'd) -> Self { - crate::into_ref!(_timer_group); - + pub fn new(_timer_group: T) -> Self { T::reset_peripheral(); T::enable_peripheral(); T::configure_src_clk(); - let clocks = Clocks::get(); - cfg_if::cfg_if! { - if #[cfg(esp32h2)] { - // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK - let apb_clk_freq = clocks.pll_48m_clock; - } else { - let apb_clk_freq = clocks.apb_clock; - } - } - - let timer0 = Timer::new( - Timer0 { - phantom: PhantomData, - }, - apb_clk_freq, - ); - - #[cfg(timg_timer1)] - let timer1 = Timer::new( - Timer1 { - phantom: PhantomData, - }, - apb_clk_freq, - ); - Self { - _timer_group, - timer0, + _timer_group: PhantomData, + timer0: Timer { + timer: 0, + tg: T::id(), + register_block: T::register_block(), + }, #[cfg(timg_timer1)] - timer1, + timer1: Timer { + timer: 1, + tg: T::id(), + register_block: T::register_block(), + }, wdt: Wdt::new(), } } } -impl<'d, T> TimerGroup<'d, T, Blocking> -where - T: TimerGroupInstance, -{ - /// Construct a new instance of [`TimerGroup`] in blocking mode - pub fn new(_timer_group: impl Peripheral

+ 'd) -> Self { - Self::new_inner(_timer_group) - } -} - -impl<'d, T> TimerGroup<'d, T, Async> -where - T: TimerGroupInstance, -{ - /// Construct a new instance of [`TimerGroup`] in asynchronous mode - pub fn new_async(_timer_group: impl Peripheral

+ 'd) -> Self { - match T::id() { - 0 => { - use crate::timer::timg::asynch::timg0_timer0_handler; - unsafe { - interrupt::bind_interrupt( - Interrupt::TG0_T0_LEVEL, - timg0_timer0_handler.handler(), - ); - interrupt::enable(Interrupt::TG0_T0_LEVEL, timg0_timer0_handler.priority()) - .unwrap(); - - #[cfg(timg_timer1)] - { - use crate::timer::timg::asynch::timg0_timer1_handler; - - interrupt::bind_interrupt( - Interrupt::TG0_T1_LEVEL, - timg0_timer1_handler.handler(), - ); - interrupt::enable(Interrupt::TG0_T1_LEVEL, timg0_timer1_handler.priority()) - .unwrap(); - } - } - } - #[cfg(timg1)] - 1 => { - use crate::timer::timg::asynch::timg1_timer0_handler; - unsafe { - { - interrupt::bind_interrupt( - Interrupt::TG1_T0_LEVEL, - timg1_timer0_handler.handler(), - ); - interrupt::enable(Interrupt::TG1_T0_LEVEL, timg1_timer0_handler.priority()) - .unwrap(); - } - #[cfg(timg_timer1)] - { - use crate::timer::timg::asynch::timg1_timer1_handler; - interrupt::bind_interrupt( - Interrupt::TG1_T1_LEVEL, - timg1_timer1_handler.handler(), - ); - interrupt::enable(Interrupt::TG1_T1_LEVEL, timg1_timer1_handler.priority()) - .unwrap(); - } - } - } - _ => unreachable!(), - } - - Self::new_inner(_timer_group) - } -} - -/// General-purpose timer. -pub struct Timer -where - DM: Mode, -{ - timg: T, - apb_clk_freq: HertzU32, - phantom: PhantomData, -} - -impl Timer -where - T: Instance, - DM: Mode, -{ - /// Construct a new instance of [`Timer`] - pub fn new(timg: T, apb_clk_freq: HertzU32) -> Self { - timg.set_counter_active(true); - - Self { - timg, - apb_clk_freq, - phantom: PhantomData, - } - } - - /// Check if the timer has elapsed - pub fn has_elapsed(&mut self) -> bool { - if !self.timg.is_counter_active() { - panic!("Called wait on an inactive timer!") - } - - if self.timg.is_interrupt_set() { - self.timg.clear_interrupt(); - self.timg.set_alarm_active(true); - - true - } else { - false - } - } - - /// Block until the timer has elapsed. - pub fn wait(&mut self) { - while !self.has_elapsed() {} - } -} - -impl Deref for Timer -where - T: Instance, - DM: Mode, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.timg - } -} - -impl DerefMut for Timer -where - T: Instance, - DM: Mode, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.timg - } -} - -impl Sealed for Timer -where - T: Instance, - DM: Mode, -{ -} - -impl super::Timer for Timer -where - T: Instance, - DM: Mode, -{ +impl super::Timer for Timer { fn start(&self) { - self.timg.set_counter_active(false); - self.timg.set_alarm_active(false); + self.set_counter_active(false); + self.set_alarm_active(false); - self.timg.reset_counter(); - self.timg.set_counter_decrementing(false); + self.reset_counter(); + self.set_counter_decrementing(false); - self.timg.set_counter_active(true); - self.timg.set_alarm_active(true); + self.set_counter_active(true); + self.set_alarm_active(true); } fn stop(&self) { - self.timg.set_counter_active(false); + self.set_counter_active(false); } fn reset(&self) { - let t = self.register_block().t(self.timer_number().into()); - - t.loadlo().write(|w| unsafe { w.load_lo().bits(0) }); - t.loadhi().write(|w| unsafe { w.load_hi().bits(0) }); - - t.load().write(|w| unsafe { w.load().bits(1) }); + self.reset_counter() } fn is_running(&self) -> bool { - self.register_block() - .t(self.timer_number().into()) - .config() - .read() - .en() - .bit_is_set() + self.is_counter_active() } fn now(&self) -> Instant { - let t = self.register_block().t(self.timer_number().into()); - - t.update().write(|w| w.update().set_bit()); - while t.update().read().update().bit_is_set() { - // Wait for the update to complete - } - - let value_lo = t.lo().read().bits() as u64; - let value_hi = t.hi().read().bits() as u64; - - let ticks = (value_hi << 32) | value_lo; - let micros = ticks_to_timeout(ticks, self.apb_clk_freq, self.timg.divider()); - - Instant::::from_ticks(micros) + self.now() } fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> { - let ticks = timeout_to_ticks(value, self.apb_clk_freq, self.timg.divider()); - - // The counter is 54-bits wide, so we must ensure that the provided - // value is not too wide: - if (ticks & !0x3F_FFFF_FFFF_FFFF) != 0 { - return Err(Error::InvalidTimeout); - } - - let high = (ticks >> 32) as u32; - let low = (ticks & 0xFFFF_FFFF) as u32; - - let t = self.register_block().t(self.timer_number().into()); - - t.alarmlo().write(|w| unsafe { w.alarm_lo().bits(low) }); - t.alarmhi().write(|w| unsafe { w.alarm_hi().bits(high) }); - - Ok(()) + self.load_value(value) } fn enable_auto_reload(&self, auto_reload: bool) { - self.register_block() - .t(self.timer_number().into()) - .config() - .modify(|_, w| w.autoreload().bit(auto_reload)); + self.set_auto_reload(auto_reload) } fn enable_interrupt(&self, state: bool) { @@ -535,9 +314,7 @@ where } fn clear_interrupt(&self) { - self.register_block() - .int_clr() - .write(|w| w.t(self.timer_number()).clear_bit_by_one()); + self.clear_interrupt() } fn set_interrupt_handler(&self, handler: InterruptHandler) { @@ -560,35 +337,15 @@ where } fn is_interrupt_set(&self) -> bool { - self.register_block() - .int_raw() - .read() - .t(self.timer_number()) - .bit_is_set() + self.is_interrupt_set() } fn set_alarm_active(&self, state: bool) { - self.register_block() - .t(self.timer_number().into()) - .config() - .modify(|_, w| w.alarm_en().bit(state)); + self.set_alarm_active(state) } } -impl InterruptConfigurable for Timer -where - T: Instance, -{ - fn set_interrupt_handler(&mut self, handler: interrupt::InterruptHandler) { - ::set_interrupt_handler(self, handler); - } -} - -impl Peripheral for Timer -where - T: Instance, - DM: Mode, -{ +impl Peripheral for Timer { type P = Self; #[inline] @@ -597,92 +354,41 @@ where } } -#[doc(hidden)] -pub trait Instance: Sealed { - fn register_block(&self) -> &RegisterBlock; - - fn timer_group(&self) -> u8; - - fn timer_number(&self) -> u8; - - fn reset_counter(&self); - - fn set_counter_active(&self, state: bool); - - fn is_counter_active(&self) -> bool; - - fn set_counter_decrementing(&self, decrementing: bool); - - fn set_auto_reload(&self, auto_reload: bool); - - fn set_alarm_active(&self, state: bool); - - fn is_alarm_active(&self) -> bool; - - fn load_alarm_value(&self, value: u64); - - fn listen(&self); - - fn unlisten(&self); - - fn clear_interrupt(&self); - - fn now(&self) -> u64; - - fn divider(&self) -> u32; - - fn set_divider(&self, divider: u16); - - fn is_interrupt_set(&self) -> bool; -} - /// A timer within a Timer Group. -pub struct TimerX { - phantom: PhantomData, -} +pub struct Timer { + /// Pointer to the register block for this TimerGroup instance. + pub register_block: *const RegisterBlock, -impl Sealed for TimerX {} + /// The timer number inside the TimerGroup + pub timer: u8, -impl TimerX -where - TG: TimerGroupInstance, -{ - /// Unsafely create an instance of this peripheral out of thin air. - /// - /// # Safety - /// - /// You must ensure that you're only using one instance of this type at a - /// time. - pub unsafe fn steal() -> Self { - Self { - phantom: PhantomData, - } - } - - unsafe fn t() -> &'static crate::peripherals::timg0::T { - (*TG::register_block()).t(T as usize) - } + /// The TimerGroup number + pub tg: u8, } +impl Sealed for Timer {} +unsafe impl Send for Timer {} + /// Timer peripheral instance -impl Instance for TimerX -where - TG: TimerGroupInstance, -{ +impl Timer { fn register_block(&self) -> &RegisterBlock { - unsafe { &*TG::register_block() } + unsafe { &*self.register_block } } fn timer_group(&self) -> u8 { - TG::id() + self.tg } fn timer_number(&self) -> u8 { - T + self.timer + } + + fn t(&self) -> &crate::peripherals::timg0::T { + self.register_block().t(self.timer_number().into()) } fn reset_counter(&self) { - let t = unsafe { Self::t() }; + let t = self.t(); t.loadlo().write(|w| unsafe { w.load_lo().bits(0) }); t.loadhi().write(|w| unsafe { w.load_hi().bits(0) }); @@ -691,91 +397,74 @@ where } fn set_counter_active(&self, state: bool) { - unsafe { Self::t() } - .config() - .modify(|_, w| w.en().bit(state)); + self.t().config().modify(|_, w| w.en().bit(state)); } fn is_counter_active(&self) -> bool { - unsafe { Self::t() }.config().read().en().bit_is_set() + self.t().config().read().en().bit_is_set() } fn set_counter_decrementing(&self, decrementing: bool) { - unsafe { Self::t() } + self.t() .config() .modify(|_, w| w.increase().bit(!decrementing)); } fn set_auto_reload(&self, auto_reload: bool) { - unsafe { Self::t() } + self.t() .config() .modify(|_, w| w.autoreload().bit(auto_reload)); } fn set_alarm_active(&self, state: bool) { - unsafe { Self::t() } - .config() - .modify(|_, w| w.alarm_en().bit(state)); + self.t().config().modify(|_, w| w.alarm_en().bit(state)); } - fn is_alarm_active(&self) -> bool { - unsafe { Self::t() }.config().read().alarm_en().bit_is_set() - } + fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> { + let ticks = timeout_to_ticks(value, Clocks::get().apb_clock, self.divider()); - fn load_alarm_value(&self, value: u64) { - let value = value & 0x3F_FFFF_FFFF_FFFF; - let high = (value >> 32) as u32; - let low = (value & 0xFFFF_FFFF) as u32; + // The counter is 54-bits wide, so we must ensure that the provided + // value is not too wide: + if (ticks & !0x3F_FFFF_FFFF_FFFF) != 0 { + return Err(Error::InvalidTimeout); + } + + let high = (ticks >> 32) as u32; + let low = (ticks & 0xFFFF_FFFF) as u32; - let t = unsafe { Self::t() }; + let t = self.t(); t.alarmlo().write(|w| unsafe { w.alarm_lo().bits(low) }); - t.alarmhi().write(|w| unsafe { w.alarm_hi().bits(high) }); - } - fn listen(&self) { - // always use level interrupt - #[cfg(any(esp32, esp32s2))] - unsafe { Self::t() } - .config() - .modify(|_, w| w.level_int_en().set_bit()); - - lock(&INT_ENA_LOCK[self.timer_group() as usize], || { - self.register_block() - .int_ena() - .modify(|_, w| w.t(T).set_bit()); - }); - } - - fn unlisten(&self) { - lock(&INT_ENA_LOCK[self.timer_group() as usize], || { - self.register_block() - .int_ena() - .modify(|_, w| w.t(T).clear_bit()); - }); + Ok(()) } fn clear_interrupt(&self) { self.register_block() .int_clr() - .write(|w| w.t(T).clear_bit_by_one()); + .write(|w| w.t(self.timer).clear_bit_by_one()); } - fn now(&self) -> u64 { - let t = unsafe { Self::t() }; + fn now(&self) -> Instant { + let t = self.t(); t.update().write(|w| w.update().set_bit()); - while t.update().read().update().bit_is_set() {} + while t.update().read().update().bit_is_set() { + // Wait for the update to complete + } let value_lo = t.lo().read().bits() as u64; - let value_hi = (t.hi().read().bits() as u64) << 32; + let value_hi = t.hi().read().bits() as u64; - value_lo | value_hi + let ticks = (value_hi << 32) | value_lo; + let micros = ticks_to_timeout(ticks, Clocks::get().apb_clock, self.divider()); + + Instant::::from_ticks(micros) } fn divider(&self) -> u32 { - let t = unsafe { Self::t() }; + let t = self.t(); // From the ESP32 TRM, "11.2.1 16­-bit Prescaler and Clock Selection": // @@ -791,23 +480,14 @@ where } fn is_interrupt_set(&self) -> bool { - self.register_block().int_raw().read().t(T).bit_is_set() - } - - fn set_divider(&self, divider: u16) { - unsafe { Self::t() } - .config() - .modify(|_, w| unsafe { w.divider().bits(divider) }); + self.register_block() + .int_raw() + .read() + .t(self.timer) + .bit_is_set() } } -/// Timer 0 in the Timer Group. -pub type Timer0 = TimerX; - -/// Timer 1 in the Timer Group. -#[cfg(timg_timer1)] -pub type Timer1 = TimerX; - fn ticks_to_timeout(ticks: u64, clock: F, divider: u32) -> u64 where F: Into, @@ -836,57 +516,6 @@ where (1_000_000 * micros / period as u64) as u64 } -impl embedded_hal_02::timer::CountDown for Timer -where - T: Instance + super::Timer, - DM: Mode, -{ - type Time = MicrosDurationU64; - - fn start

+ 'd) -> Dma<'d> { - crate::into_ref!(dma); - - if PeripheralClockControl::enable(system::Peripheral::Gdma) { - PeripheralClockControl::reset(system::Peripheral::Gdma); - } - dma.misc_conf().modify(|_, w| w.ahbm_rst_inter().set_bit()); - dma.misc_conf() - .modify(|_, w| w.ahbm_rst_inter().clear_bit()); - dma.misc_conf().modify(|_, w| w.clk_en().set_bit()); - - unsafe { - Dma { - _inner: dma, - channel0: DmaChannel0::steal(), - #[cfg(not(esp32c2))] - channel1: DmaChannel1::steal(), - #[cfg(not(esp32c2))] - channel2: DmaChannel2::steal(), - #[cfg(esp32s3)] - channel3: DmaChannel3::steal(), - #[cfg(esp32s3)] - channel4: DmaChannel4::steal(), - } - } - } +pub(super) fn init_dma(_cs: CriticalSection<'_>) { + let dma = unsafe { crate::soc::peripherals::DMA::steal() }; + dma.misc_conf().modify(|_, w| w.ahbm_rst_inter().set_bit()); + dma.misc_conf() + .modify(|_, w| w.ahbm_rst_inter().clear_bit()); + dma.misc_conf().modify(|_, w| w.clk_en().set_bit()); } diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index c667e8f427d..f6e520f71b8 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -19,10 +19,8 @@ #![doc = crate::before_snippet!()] //! # use esp_hal::dma_buffers; //! # use esp_hal::spi::{master::{Config, Spi}, SpiMode}; -//! # use esp_hal::dma::Dma; -//! let dma = Dma::new(peripherals.DMA); -#![cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = dma.spi2channel;")] -#![cfg_attr(not(any(esp32, esp32s2)), doc = "let dma_channel = dma.channel0;")] +#![cfg_attr(pdma, doc = "let dma_channel = peripherals.DMA_SPI2;")] +#![cfg_attr(gdma, doc = "let dma_channel = peripherals.DMA_CH0;")] //! let sclk = peripherals.GPIO0; //! let miso = peripherals.GPIO2; //! let mosi = peripherals.GPIO4; @@ -70,6 +68,7 @@ use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::Interrupt, soc::is_slice_in_dram, + system, Async, Blocking, Cpu, @@ -1665,7 +1664,7 @@ impl DmaChannelConvert for DEG { #[cfg_attr(pdma, doc = "")] #[cfg_attr( pdma, - doc = "Note that using mismatching channels (e.g. trying to use `spi2channel` with SPI3) may compile, but will panic in runtime." + doc = "Note that using mismatching channels (e.g. trying to use `DMA_SPI2` with SPI3) may compile, but will panic in runtime." )] #[cfg_attr(pdma, doc = "")] /// ## Example @@ -1679,7 +1678,6 @@ impl DmaChannelConvert for DEG { /// use esp_hal::dma::DmaChannelFor; /// use esp_hal::peripheral::Peripheral; /// use esp_hal::Blocking; -/// use esp_hal::dma::Dma; /// /// fn configures_spi_dma<'d, S, CH>( /// spi: Spi<'d, Blocking, S>, @@ -1691,10 +1689,8 @@ impl DmaChannelConvert for DEG { /// { /// spi.with_dma(channel) /// } -/// -/// let dma = Dma::new(peripherals.DMA); -#[cfg_attr(pdma, doc = "let dma_channel = dma.spi2channel;")] -#[cfg_attr(gdma, doc = "let dma_channel = dma.channel0;")] +#[cfg_attr(pdma, doc = "let dma_channel = peripherals.DMA_SPI2;")] +#[cfg_attr(gdma, doc = "let dma_channel = peripherals.DMA_CH0;")] #[doc = ""] /// let spi = Spi::new_with_config( /// peripherals.SPI2, @@ -1802,6 +1798,21 @@ pub trait Rx: crate::private::Sealed { fn waker(&self) -> &'static crate::asynch::AtomicWaker; } +// NOTE(p4): because the P4 has two different GDMAs, we won't be able to use +// `GenericPeripheralGuard`. +cfg_if::cfg_if! { + if #[cfg(pdma)] { + type PeripheralGuard = system::GenericPeripheralGuard<{ system::Peripheral::Dma as u8}>; + } else { + type PeripheralGuard = system::GenericPeripheralGuard<{ system::Peripheral::Gdma as u8}>; + } +} + +fn create_guard(_ch: &impl RegisterAccess) -> PeripheralGuard { + // NOTE(p4): this function will read the channel's DMA peripheral from `_ch` + system::GenericPeripheralGuard::new_with(init_dma) +} + // DMA receive channel #[non_exhaustive] #[doc(hidden)] @@ -1812,6 +1823,7 @@ where { pub(crate) rx_impl: PeripheralRef<'a, CH>, pub(crate) _phantom: PhantomData, + pub(crate) _guard: PeripheralGuard, } impl<'a, CH> ChannelRx<'a, Blocking, CH> @@ -1821,6 +1833,9 @@ where /// Creates a new RX channel half. pub fn new(rx_impl: impl Peripheral

+ 'a) -> Self { crate::into_ref!(rx_impl); + + let _guard = create_guard(&*rx_impl); + #[cfg(gdma)] // clear the mem2mem mode to avoid failed DMA if this // channel was previously used for a mem2mem transfer. @@ -1836,6 +1851,7 @@ where Self { rx_impl, _phantom: PhantomData, + _guard, } } @@ -1848,6 +1864,7 @@ where ChannelRx { rx_impl: self.rx_impl, _phantom: PhantomData, + _guard: self._guard, } } @@ -1878,6 +1895,7 @@ where ChannelRx { rx_impl: self.rx_impl, _phantom: PhantomData, + _guard: self._guard, } } } @@ -2095,6 +2113,7 @@ where { pub(crate) tx_impl: PeripheralRef<'a, CH>, pub(crate) _phantom: PhantomData, + pub(crate) _guard: PeripheralGuard, } impl<'a, CH> ChannelTx<'a, Blocking, CH> @@ -2104,6 +2123,9 @@ where /// Creates a new TX channel half. pub fn new(tx_impl: impl Peripheral

+ 'a) -> Self { crate::into_ref!(tx_impl); + + let _guard = create_guard(&*tx_impl); + if let Some(interrupt) = tx_impl.peripheral_interrupt() { for cpu in Cpu::all() { crate::interrupt::disable(cpu, interrupt); @@ -2113,6 +2135,7 @@ where Self { tx_impl, _phantom: PhantomData, + _guard, } } @@ -2125,6 +2148,7 @@ where ChannelTx { tx_impl: self.tx_impl, _phantom: PhantomData, + _guard: self._guard, } } @@ -2155,6 +2179,7 @@ where ChannelTx { tx_impl: self.tx_impl, _phantom: PhantomData, + _guard: self._guard, } } } diff --git a/esp-hal/src/dma/pdma.rs b/esp-hal/src/dma/pdma.rs index 2a41d2d6c69..8049f2b8ebb 100644 --- a/esp-hal/src/dma/pdma.rs +++ b/esp-hal/src/dma/pdma.rs @@ -11,6 +11,7 @@ //! [SPI]: ../spi/index.html //! [I2S]: ../i2s/index.html +use critical_section::CriticalSection; use portable_atomic::{AtomicBool, Ordering}; use crate::{ @@ -20,7 +21,6 @@ use crate::{ macros::handler, peripheral::{Peripheral, PeripheralRef}, peripherals::Interrupt, - system::{self, PeripheralClockControl}, }; type SpiRegisterBlock = crate::peripherals::spi2::RegisterBlock; @@ -876,52 +876,18 @@ crate::impl_dma_eligible!([I2s0DmaChannel] I2S0 => I2s0); #[cfg(i2s1)] crate::impl_dma_eligible!([I2s1DmaChannel] I2S1 => I2s1); -/// DMA Peripheral -/// -/// This offers the available DMA channels. -pub struct Dma<'d> { - _inner: PeripheralRef<'d, crate::peripherals::DMA>, - /// DMA channel for SPI2 - pub spi2channel: Spi2DmaChannel, - /// DMA channel for SPI3 - pub spi3channel: Spi3DmaChannel, - /// DMA channel for I2S0 - pub i2s0channel: I2s0DmaChannel, - /// DMA channel for I2S1 - #[cfg(i2s1)] - pub i2s1channel: I2s1DmaChannel, -} - -impl<'d> Dma<'d> { - /// Create a DMA instance. - pub fn new(dma: impl Peripheral

+ 'd) -> Dma<'d> { - if PeripheralClockControl::enable(system::Peripheral::Dma) { - PeripheralClockControl::reset(system::Peripheral::Dma); - } - - #[cfg(esp32)] - { - // (only) on ESP32 we need to configure DPORT for the SPI DMA channels - // This assignes the DMA channels to the SPI peripherals, which is more - // restrictive than necessary but we currently support the same - // number of SPI peripherals as SPI DMA channels so it's not a big - // deal. - let dport = unsafe { &*crate::peripherals::DPORT::PTR }; - dport.spi_dma_chan_sel().modify(|_, w| unsafe { - w.spi2_dma_chan_sel().bits(1).spi3_dma_chan_sel().bits(2) - }); - } - - unsafe { - Dma { - _inner: dma.into_ref(), - spi2channel: Spi2DmaChannel::steal(), - spi3channel: Spi3DmaChannel::steal(), - i2s0channel: I2s0DmaChannel::steal(), - #[cfg(i2s1)] - i2s1channel: I2s1DmaChannel::steal(), - } - } +pub(super) fn init_dma(_cs: CriticalSection<'_>) { + #[cfg(esp32)] + { + // (only) on ESP32 we need to configure DPORT for the SPI DMA channels + // This assignes the DMA channels to the SPI peripherals, which is more + // restrictive than necessary but we currently support the same + // number of SPI peripherals as SPI DMA channels so it's not a big + // deal. + let dport = unsafe { crate::peripherals::DPORT::steal() }; + dport + .spi_dma_chan_sel() + .modify(|_, w| unsafe { w.spi2_dma_chan_sel().bits(1).spi3_dma_chan_sel().bits(2) }); } } diff --git a/esp-hal/src/i2s/master.rs b/esp-hal/src/i2s/master.rs index 885aa9f1d1b..b63e81a2710 100644 --- a/esp-hal/src/i2s/master.rs +++ b/esp-hal/src/i2s/master.rs @@ -31,10 +31,11 @@ #![doc = crate::before_snippet!()] //! # use esp_hal::i2s::master::{I2s, Standard, DataFormat}; //! # use esp_hal::dma_buffers; -//! # use esp_hal::dma::Dma; -//! let dma = Dma::new(peripherals.DMA); -#![cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = dma.i2s0channel;")] -#![cfg_attr(not(any(esp32, esp32s2)), doc = "let dma_channel = dma.channel0;")] +#![cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = peripherals.DMA_I2S0;")] +#![cfg_attr( + not(any(esp32, esp32s2)), + doc = "let dma_channel = peripherals.DMA_CH0;" +)] //! let (mut rx_buffer, rx_descriptors, _, tx_descriptors) = //! dma_buffers!(0, 4 * 4092); //! diff --git a/esp-hal/src/i2s/parallel.rs b/esp-hal/src/i2s/parallel.rs index 7af20a17721..1bb0d8cc7a2 100644 --- a/esp-hal/src/i2s/parallel.rs +++ b/esp-hal/src/i2s/parallel.rs @@ -39,7 +39,7 @@ //! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::dma::{Dma, DmaTxBuf}; +//! # use esp_hal::dma::DmaTxBuf; //! # use esp_hal::dma_buffers; //! # use esp_hal::delay::Delay; //! # use esp_hal::i2s::parallel::{I2sParallel, TxEightBits}; @@ -48,8 +48,7 @@ //! const BUFFER_SIZE: usize = 256; //! //! let delay = Delay::new(); -//! let dma = Dma::new(peripherals.DMA); -//! let dma_channel = dma.i2s1channel; +//! let dma_channel = peripherals.DMA_I2S1; //! let i2s = peripherals.I2S1; //! let clock = peripherals.GPIO25; //! diff --git a/esp-hal/src/lcd_cam/cam.rs b/esp-hal/src/lcd_cam/cam.rs index e5998faef0a..ad772bf7084 100644 --- a/esp-hal/src/lcd_cam/cam.rs +++ b/esp-hal/src/lcd_cam/cam.rs @@ -19,10 +19,6 @@ //! # use esp_hal::lcd_cam::{cam::{Camera, RxEightBits}, LcdCam}; //! # use fugit::RateExtU32; //! # use esp_hal::dma_rx_stream_buffer; -//! # use esp_hal::dma::Dma; -//! -//! # let dma = Dma::new(peripherals.DMA); -//! # let channel = dma.channel0; //! //! # let dma_buf = dma_rx_stream_buffer!(20 * 1000, 1000); //! @@ -44,7 +40,7 @@ //! let lcd_cam = LcdCam::new(peripherals.LCD_CAM); //! let mut camera = Camera::new( //! lcd_cam.cam, -//! channel, +//! peripherals.DMA_CH0, //! data_pins, //! 20u32.MHz(), //! ) diff --git a/esp-hal/src/lcd_cam/lcd/dpi.rs b/esp-hal/src/lcd_cam/lcd/dpi.rs index 42cef5ed496..c7330a44826 100644 --- a/esp-hal/src/lcd_cam/lcd/dpi.rs +++ b/esp-hal/src/lcd_cam/lcd/dpi.rs @@ -24,11 +24,8 @@ //! # } //! # }; //! # use esp_hal::dma_loop_buffer; -//! # use esp_hal::dma::{Dma, DmaPriority}; -//! -//! # let dma = Dma::new(peripherals.DMA); -//! # let channel = dma.channel0; //! +//! # let channel = peripherals.DMA_CH0; //! # let mut dma_buf = dma_loop_buffer!(32); //! //! let lcd_cam = LcdCam::new(peripherals.LCD_CAM); diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 6b498b00f65..21350a1d92f 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -17,10 +17,7 @@ #![doc = crate::before_snippet!()] //! # use esp_hal::lcd_cam::{LcdCam, lcd::i8080::{Config, I8080, TxEightBits}}; //! # use esp_hal::dma_tx_buffer; -//! # use esp_hal::dma::{Dma, DmaTxBuf}; -//! -//! # let dma = Dma::new(peripherals.DMA); -//! # let channel = dma.channel0; +//! # use esp_hal::dma::DmaTxBuf; //! //! # let mut dma_buf = dma_tx_buffer!(32678).unwrap(); //! @@ -38,7 +35,7 @@ //! //! let mut i8080 = I8080::new( //! lcd_cam.lcd, -//! channel, +//! peripherals.DMA_CH0, //! tx_pins, //! 20.MHz(), //! Config::default(), diff --git a/esp-hal/src/parl_io.rs b/esp-hal/src/parl_io.rs index e23cf8e0459..64707e4622c 100644 --- a/esp-hal/src/parl_io.rs +++ b/esp-hal/src/parl_io.rs @@ -16,15 +16,13 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::delay::Delay; -//! # use esp_hal::dma::Dma; //! # use esp_hal::dma_buffers; //! # use esp_hal::gpio::NoPin; //! # use esp_hal::parl_io::{BitPackOrder, ParlIoRxOnly, RxFourBits}; //! //! // Initialize DMA buffer and descriptors for data reception //! let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0); -//! let dma = Dma::new(peripherals.DMA); -//! let dma_channel = dma.channel0; +//! let dma_channel = peripherals.DMA_CH0; //! //! // Configure the 4-bit input pins and clock pin //! let mut rx_pins = RxFourBits::new( @@ -74,14 +72,12 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::delay::Delay; -//! # use esp_hal::dma::Dma; //! # use esp_hal::dma_buffers; //! # use esp_hal::parl_io::{BitPackOrder, ParlIoTxOnly, TxFourBits, SampleEdge, ClkOutPin, TxPinConfigWithValidPin}; //! //! // Initialize DMA buffer and descriptors for data reception //! let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000); -//! let dma = Dma::new(peripherals.DMA); -//! let dma_channel = dma.channel0; +//! let dma_channel = peripherals.DMA_CH0; //! //! // Configure the 4-bit input pins and clock pin //! let tx_pins = TxFourBits::new( diff --git a/esp-hal/src/peripheral.rs b/esp-hal/src/peripheral.rs index 622470d859b..4a817111a84 100644 --- a/esp-hal/src/peripheral.rs +++ b/esp-hal/src/peripheral.rs @@ -246,10 +246,15 @@ mod peripheral_macros { peripherals: [ $( $name:ident <= $from_pac:tt $(($($interrupt:ident),*))? - ), *$(,)? + ),* $(,)? ], pins: [ $( ( $pin:literal, $($pin_tokens:tt)* ) )* + ], + dma_channels: [ + $( + $channel_name:ident : $channel_ty:path + ),* $(,)? ] ) => { @@ -280,6 +285,11 @@ mod peripheral_macros { #[doc = concat!("GPIO", stringify!($pin))] pub []: $crate::gpio::GpioPin<$pin>, )* + + $( + #[doc = concat!(stringify!($channel_name), " DMA channel.")] + pub $channel_name: $crate::dma::$channel_ty, + )* } impl Peripherals { @@ -313,6 +323,10 @@ mod peripheral_macros { $( []: $crate::gpio::GpioPin::<$pin>::steal(), )* + + $( + $channel_name: $crate::dma::$channel_ty::steal(), + )* } } } diff --git a/esp-hal/src/soc/esp32/peripherals.rs b/esp-hal/src/soc/esp32/peripherals.rs index 2bfa72fc05e..cc58dd71561 100644 --- a/esp-hal/src/soc/esp32/peripherals.rs +++ b/esp-hal/src/soc/esp32/peripherals.rs @@ -30,7 +30,6 @@ crate::peripherals! { CPU_CTRL <= virtual, DAC1 <= virtual, DAC2 <= virtual, - DMA <= virtual, EFUSE <= EFUSE, FLASH_ENCRYPTION <= FLASH_ENCRYPTION, FRC_TIMER <= FRC_TIMER, @@ -112,5 +111,11 @@ crate::peripherals! { (37, [Input, Analog, RtcIoInput]) (38, [Input, Analog, RtcIoInput]) (39, [Input, Analog, RtcIoInput]) + ], + dma_channels: [ + DMA_SPI2: Spi2DmaChannel, + DMA_SPI3: Spi3DmaChannel, + DMA_I2S0: I2s0DmaChannel, + DMA_I2S1: I2s1DmaChannel, ] } diff --git a/esp-hal/src/soc/esp32c2/peripherals.rs b/esp-hal/src/soc/esp32c2/peripherals.rs index 6748f4e6ce5..03db06e0a0e 100644 --- a/esp-hal/src/soc/esp32c2/peripherals.rs +++ b/esp-hal/src/soc/esp32c2/peripherals.rs @@ -25,7 +25,6 @@ crate::peripherals! { APB_CTRL <= APB_CTRL, ASSIST_DEBUG <= ASSIST_DEBUG, BT <= virtual, - DMA <= DMA (DMA_CH0), ECC <= ECC, EFUSE <= EFUSE, EXTMEM <= EXTMEM, @@ -72,5 +71,8 @@ crate::peripherals! { (18, [Input, Output]) (19, [Input, Output]) (20, [Input, Output] (0 => U0RXD) ()) + ], + dma_channels: [ + DMA_CH0: DmaChannel0, ] } diff --git a/esp-hal/src/soc/esp32c3/peripherals.rs b/esp-hal/src/soc/esp32c3/peripherals.rs index f4a52d4fc08..00e6548a294 100644 --- a/esp-hal/src/soc/esp32c3/peripherals.rs +++ b/esp-hal/src/soc/esp32c3/peripherals.rs @@ -27,7 +27,6 @@ crate::peripherals! { APB_CTRL <= APB_CTRL, ASSIST_DEBUG <= ASSIST_DEBUG, BT <= virtual, - DMA <= DMA (DMA_CH0,DMA_CH1,DMA_CH2), DS <= DS, EFUSE <= EFUSE, EXTMEM <= EXTMEM, @@ -85,5 +84,10 @@ crate::peripherals! { (19, [Input, Output]) (20, [Input, Output] (0 => U0RXD) ()) (21, [Input, Output] () (0 => U0TXD)) + ], + dma_channels: [ + DMA_CH0: DmaChannel0, + DMA_CH1: DmaChannel1, + DMA_CH2: DmaChannel2, ] } diff --git a/esp-hal/src/soc/esp32c6/peripherals.rs b/esp-hal/src/soc/esp32c6/peripherals.rs index b521161faf9..666e7a252a6 100644 --- a/esp-hal/src/soc/esp32c6/peripherals.rs +++ b/esp-hal/src/soc/esp32c6/peripherals.rs @@ -26,7 +26,6 @@ crate::peripherals! { ASSIST_DEBUG <= ASSIST_DEBUG, ATOMIC <= ATOMIC, BT <= virtual, - DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2), DS <= DS, ECC <= ECC, EFUSE <= EFUSE, @@ -130,5 +129,10 @@ crate::peripherals! { (28, [Input, Output] (0 => SPIHD) (0 => SPIHD)) (29, [Input, Output] () (0 => SPICLK_MUX)) (30, [Input, Output] (0 => SPID) (0 => SPID)) + ], + dma_channels: [ + DMA_CH0: DmaChannel0, + DMA_CH1: DmaChannel1, + DMA_CH2: DmaChannel2, ] } diff --git a/esp-hal/src/soc/esp32h2/peripherals.rs b/esp-hal/src/soc/esp32h2/peripherals.rs index 9f2c0cdb7df..b5743079885 100644 --- a/esp-hal/src/soc/esp32h2/peripherals.rs +++ b/esp-hal/src/soc/esp32h2/peripherals.rs @@ -25,7 +25,6 @@ crate::peripherals! { AES <= AES, ASSIST_DEBUG <= ASSIST_DEBUG, BT <= virtual, - DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2), DS <= DS, ECC <= ECC, EFUSE <= EFUSE, @@ -117,5 +116,10 @@ crate::peripherals! { (25, [Input, Output] () (2 => FSPICS3)) (26, [Input, Output] () (2 => FSPICS4)) (27, [Input, Output] () (2 => FSPICS5)) + ], + dma_channels: [ + DMA_CH0: DmaChannel0, + DMA_CH1: DmaChannel1, + DMA_CH2: DmaChannel2, ] } diff --git a/esp-hal/src/soc/esp32s2/peripherals.rs b/esp-hal/src/soc/esp32s2/peripherals.rs index fc0cf5917fd..868dd4c1ff5 100644 --- a/esp-hal/src/soc/esp32s2/peripherals.rs +++ b/esp-hal/src/soc/esp32s2/peripherals.rs @@ -26,7 +26,6 @@ crate::peripherals! { AES <= AES, DAC1 <= virtual, DAC2 <= virtual, - DMA <= virtual, DEDICATED_GPIO <= DEDICATED_GPIO, DS <= DS, EFUSE <= EFUSE, @@ -115,5 +114,10 @@ crate::peripherals! { (44, [Input, Output]) (45, [Input, Output]) (46, [Input, Output]) + ], + dma_channels: [ + DMA_SPI2: Spi2DmaChannel, + DMA_SPI3: Spi3DmaChannel, + DMA_I2S0: I2s0DmaChannel, ] } diff --git a/esp-hal/src/soc/esp32s3/peripherals.rs b/esp-hal/src/soc/esp32s3/peripherals.rs index c6ab886c9bf..c27239702c8 100644 --- a/esp-hal/src/soc/esp32s3/peripherals.rs +++ b/esp-hal/src/soc/esp32s3/peripherals.rs @@ -28,7 +28,6 @@ crate::peripherals! { ASSIST_DEBUG <= ASSIST_DEBUG, BT <= virtual, CPU_CTRL <= virtual, - DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_IN_CH3,DMA_IN_CH4,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2,DMA_OUT_CH3,DMA_OUT_CH4), DS <= DS, EFUSE <= EFUSE, EXTMEM <= EXTMEM, @@ -126,5 +125,12 @@ crate::peripherals! { (46, [Input, Output]) (47, [Input, Output]) (48, [Input, Output]) + ], + dma_channels: [ + DMA_CH0: DmaChannel0, + DMA_CH1: DmaChannel1, + DMA_CH2: DmaChannel2, + DMA_CH3: DmaChannel3, + DMA_CH4: DmaChannel4, ] } diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 5d7a4d31c43..62a97e4c4ba 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -18,10 +18,8 @@ //! # use esp_hal::dma_buffers; //! # use esp_hal::spi::SpiMode; //! # use esp_hal::spi::slave::Spi; -//! # use esp_hal::dma::Dma; -//! let dma = Dma::new(peripherals.DMA); -#![cfg_attr(pdma, doc = "let dma_channel = dma.spi2channel;")] -#![cfg_attr(gdma, doc = "let dma_channel = dma.channel0;")] +#![cfg_attr(pdma, doc = "let dma_channel = peripherals.DMA_SPI2;")] +#![cfg_attr(gdma, doc = "let dma_channel = peripherals.DMA_CH0;")] //! let sclk = peripherals.GPIO0; //! let miso = peripherals.GPIO1; //! let mosi = peripherals.GPIO2; diff --git a/esp-hal/src/system.rs b/esp-hal/src/system.rs index 40f0a69b31b..c69da05fd90 100755 --- a/esp-hal/src/system.rs +++ b/esp-hal/src/system.rs @@ -5,10 +5,9 @@ //! This `system` module defines the available radio peripherals and provides an //! interface to control and configure radio clocks. -use core::sync::atomic::Ordering; +use core::cell::RefCell; -use critical_section::CriticalSection; -use portable_atomic::AtomicUsize; +use critical_section::{CriticalSection, Mutex}; use strum::{EnumCount, EnumIter, IntoEnumIterator}; use crate::peripherals::SYSTEM; @@ -145,19 +144,22 @@ impl Peripheral { } } -static PERIPHERAL_REF_COUNT: [AtomicUsize; Peripheral::COUNT] = - [const { AtomicUsize::new(0) }; Peripheral::COUNT]; +static PERIPHERAL_REF_COUNT: Mutex> = + Mutex::new(RefCell::new([0; Peripheral::COUNT])); /// Disable all peripherals. /// /// Peripherals listed in [KEEP_ENABLED] are NOT disabled. pub(crate) fn disable_peripherals() { - for p in Peripheral::iter() { - if KEEP_ENABLED.contains(&p) { - continue; + // Take the critical section up front to avoid taking it multiple times. + critical_section::with(|cs| { + for p in Peripheral::iter() { + if KEEP_ENABLED.contains(&p) { + continue; + } + PeripheralClockControl::enable_forced_with_cs(p, false, true, cs); } - PeripheralClockControl::enable_forced(p, false, true); - } + }) } #[derive(Debug)] @@ -166,13 +168,18 @@ pub(crate) struct PeripheralGuard { } impl PeripheralGuard { - pub(crate) fn new(p: Peripheral) -> Self { + pub(crate) fn new_with(p: Peripheral, init: fn()) -> Self { if !KEEP_ENABLED.contains(&p) && PeripheralClockControl::enable(p) { PeripheralClockControl::reset(p); + init(); } Self { peripheral: p } } + + pub(crate) fn new(p: Peripheral) -> Self { + Self::new_with(p, || {}) + } } impl Drop for PeripheralGuard { @@ -187,14 +194,23 @@ impl Drop for PeripheralGuard { pub(crate) struct GenericPeripheralGuard {} impl GenericPeripheralGuard

{ - pub(crate) fn new() -> Self { + pub(crate) fn new_with(init: fn(CriticalSection<'_>)) -> Self { let peripheral = unwrap!(Peripheral::try_from(P)); - if !KEEP_ENABLED.contains(&peripheral) && PeripheralClockControl::enable(peripheral) { - PeripheralClockControl::reset(peripheral); - } + critical_section::with(|cs| { + if !KEEP_ENABLED.contains(&peripheral) + && PeripheralClockControl::enable_with_cs(peripheral, cs) + { + PeripheralClockControl::reset(peripheral); + init(cs); + } + }); Self {} } + + pub(crate) fn new() -> Self { + Self::new_with(|_| {}) + } } impl Drop for GenericPeripheralGuard

{ @@ -211,7 +227,7 @@ pub(crate) struct PeripheralClockControl; #[cfg(not(any(esp32c6, esp32h2)))] impl PeripheralClockControl { - fn enable_internal(peripheral: Peripheral, enable: bool, _cs: &CriticalSection<'_>) { + fn enable_internal(peripheral: Peripheral, enable: bool, _cs: CriticalSection<'_>) { debug!("Enable {:?} {}", peripheral, enable); let system = unsafe { &*SYSTEM::PTR }; @@ -595,7 +611,7 @@ impl PeripheralClockControl { #[cfg(any(esp32c6, esp32h2))] impl PeripheralClockControl { - fn enable_internal(peripheral: Peripheral, enable: bool, _cs: &CriticalSection<'_>) { + fn enable_internal(peripheral: Peripheral, enable: bool, _cs: CriticalSection<'_>) { debug!("Enable {:?} {}", peripheral, enable); let system = unsafe { &*SYSTEM::PTR }; @@ -971,6 +987,16 @@ impl PeripheralClockControl { Self::enable_forced(peripheral, true, false) } + /// Enables the given peripheral. + /// + /// This keeps track of enabling a peripheral - i.e. a peripheral + /// is only enabled with the first call attempt to enable it. + /// + /// Returns `true` if it actually enabled the peripheral. + pub(crate) fn enable_with_cs(peripheral: Peripheral, cs: CriticalSection<'_>) -> bool { + Self::enable_forced_with_cs(peripheral, true, false, cs) + } + /// Disables the given peripheral. /// /// This keeps track of disabling a peripheral - i.e. it only @@ -984,34 +1010,43 @@ impl PeripheralClockControl { } pub(crate) fn enable_forced(peripheral: Peripheral, enable: bool, force: bool) -> bool { - critical_section::with(|cs| { - if !force { - if enable { - let prev = - PERIPHERAL_REF_COUNT[peripheral as usize].fetch_add(1, Ordering::Relaxed); - if prev > 0 { - return false; - } - } else { - let prev = - PERIPHERAL_REF_COUNT[peripheral as usize].fetch_sub(1, Ordering::Relaxed); - assert!(prev != 0); - if prev > 1 { - return false; - } - }; - } else if !enable { - assert!(PERIPHERAL_REF_COUNT[peripheral as usize].swap(0, Ordering::Relaxed) == 0); - } + critical_section::with(|cs| Self::enable_forced_with_cs(peripheral, enable, force, cs)) + } - if !enable { - Self::reset(peripheral); - } + pub(crate) fn enable_forced_with_cs( + peripheral: Peripheral, + enable: bool, + force: bool, + cs: CriticalSection<'_>, + ) -> bool { + let mut ref_counts = PERIPHERAL_REF_COUNT.borrow_ref_mut(cs); + let ref_count = &mut ref_counts[peripheral as usize]; + if !force { + if enable { + let prev = *ref_count; + *ref_count += 1; + if prev > 0 { + return false; + } + } else { + let prev = *ref_count; + *ref_count -= 1; + if prev > 1 { + return false; + } + assert!(prev != 0); + }; + } else if !enable { + assert!(*ref_count == 0); + } + + if !enable { + Self::reset(peripheral); + } - Self::enable_internal(peripheral, enable, &cs); + Self::enable_internal(peripheral, enable, cs); - true - }) + true } } diff --git a/examples/src/bin/dma_extmem2mem.rs b/examples/src/bin/dma_extmem2mem.rs index bf249d9b149..55c075aec92 100644 --- a/examples/src/bin/dma_extmem2mem.rs +++ b/examples/src/bin/dma_extmem2mem.rs @@ -9,12 +9,7 @@ use aligned::{Aligned, A64}; use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - delay::Delay, - dma::{Dma, Mem2Mem}, - dma_descriptors_chunk_size, - prelude::*, -}; +use esp_hal::{delay::Delay, dma::Mem2Mem, dma_descriptors_chunk_size, prelude::*}; use log::{error, info}; extern crate alloc; @@ -67,11 +62,10 @@ fn main() -> ! { let mut intram_buffer = dma_buffer_aligned!(DATA_SIZE, A64); let (rx_descriptors, tx_descriptors) = dma_descriptors_chunk_size!(DATA_SIZE, CHUNK_SIZE); - let dma = Dma::new(peripherals.DMA); let dma_peripheral = peripherals.SPI2; let mut mem2mem = Mem2Mem::new_with_chunk_size( - dma.channel0, + peripherals.DMA_CH0, dma_peripheral, rx_descriptors, tx_descriptors, diff --git a/examples/src/bin/dma_mem2mem.rs b/examples/src/bin/dma_mem2mem.rs index 23dda706764..64304665d83 100644 --- a/examples/src/bin/dma_mem2mem.rs +++ b/examples/src/bin/dma_mem2mem.rs @@ -7,12 +7,7 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - delay::Delay, - dma::{Dma, Mem2Mem}, - dma_buffers, - prelude::*, -}; +use esp_hal::{delay::Delay, dma::Mem2Mem, dma_buffers, prelude::*}; use log::{error, info}; const DATA_SIZE: usize = 1024 * 10; @@ -27,14 +22,21 @@ fn main() -> ! { let (mut rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(DATA_SIZE); - let dma = Dma::new(peripherals.DMA); - #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] - let dma_peripheral = peripherals.SPI2; - #[cfg(not(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3")))] - let dma_peripheral = peripherals.MEM2MEM1; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32c2", feature = "esp32c3", feature = "esp32s3"))] { + let dma_peripheral = peripherals.SPI2; + } else { + let dma_peripheral = peripherals.MEM2MEM1; + } + } - let mut mem2mem = - Mem2Mem::new(dma.channel0, dma_peripheral, rx_descriptors, tx_descriptors).unwrap(); + let mut mem2mem = Mem2Mem::new( + peripherals.DMA_CH0, + dma_peripheral, + rx_descriptors, + tx_descriptors, + ) + .unwrap(); for i in 0..core::mem::size_of_val(tx_buffer) { tx_buffer[i] = (i % 256) as u8; diff --git a/examples/src/bin/embassy_spi.rs b/examples/src/bin/embassy_spi.rs index becc3e96134..1d7e8f9fd31 100644 --- a/examples/src/bin/embassy_spi.rs +++ b/examples/src/bin/embassy_spi.rs @@ -22,7 +22,7 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - dma::*, + dma::{DmaRxBuf, DmaTxBuf}, dma_buffers, prelude::*, spi::{ @@ -45,13 +45,11 @@ async fn main(_spawner: Spawner) { let mosi = peripherals.GPIO4; let cs = peripherals.GPIO5; - let dma = Dma::new(peripherals.DMA); - cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { - let dma_channel = dma.spi2channel; + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/examples/src/bin/spi_loopback_dma_psram.rs b/examples/src/bin/spi_loopback_dma_psram.rs index 5aeb74b8621..72787eb408c 100644 --- a/examples/src/bin/spi_loopback_dma_psram.rs +++ b/examples/src/bin/spi_loopback_dma_psram.rs @@ -25,7 +25,7 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - dma::{Dma, DmaBufBlkSize, DmaRxBuf, DmaTxBuf}, + dma::{DmaBufBlkSize, DmaRxBuf, DmaTxBuf}, peripheral::Peripheral, prelude::*, spi::{ @@ -67,9 +67,6 @@ fn main() -> ! { let miso = unsafe { mosi.clone_unchecked() }; let cs = peripherals.GPIO38; - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; - let (_, tx_descriptors) = esp_hal::dma_descriptors_chunk_size!(0, DMA_BUFFER_SIZE, DMA_CHUNK_SIZE); let tx_buffer = dma_alloc_buffer!(DMA_BUFFER_SIZE, DMA_ALIGNMENT as usize); @@ -103,7 +100,7 @@ fn main() -> ! { .with_miso(miso) .with_mosi(mosi) .with_cs(cs) - .with_dma(dma_channel); + .with_dma(peripherals.DMA_CH0); delay.delay_millis(100); // delay to let the above messages display diff --git a/examples/src/bin/spi_slave_dma.rs b/examples/src/bin/spi_slave_dma.rs index eaee969c415..5f92755e2eb 100644 --- a/examples/src/bin/spi_slave_dma.rs +++ b/examples/src/bin/spi_slave_dma.rs @@ -32,7 +32,6 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - dma::Dma, dma_buffers, gpio::{Input, Level, Output, Pull}, prelude::*, @@ -54,12 +53,11 @@ fn main() -> ! { let slave_mosi = peripherals.GPIO2; let slave_cs = peripherals.GPIO3; - let dma = Dma::new(peripherals.DMA); cfg_if::cfg_if! { if #[cfg(feature = "esp32s2")] { - let dma_channel = dma.spi2channel; + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/hil-test/tests/aes_dma.rs b/hil-test/tests/aes_dma.rs index e560e041dd3..be8eb3d804e 100644 --- a/hil-test/tests/aes_dma.rs +++ b/hil-test/tests/aes_dma.rs @@ -7,7 +7,6 @@ use esp_hal::{ aes::{dma::CipherMode, Aes, Mode}, - dma::Dma, dma_buffers, peripherals::Peripherals, }; @@ -27,8 +26,7 @@ mod tests { #[test] fn test_aes_128_dma_encryption(peripherals: Peripherals) { - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); @@ -66,8 +64,7 @@ mod tests { #[test] fn test_aes_128_dma_decryption(peripherals: Peripherals) { - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); @@ -104,8 +101,7 @@ mod tests { #[test] fn test_aes_256_dma_encryption(peripherals: Peripherals) { - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); @@ -143,8 +139,7 @@ mod tests { #[test] fn test_aes_256_dma_decryption(peripherals: Peripherals) { - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); diff --git a/hil-test/tests/dma_mem2mem.rs b/hil-test/tests/dma_mem2mem.rs index 20ac1cfe89b..09961d2517a 100644 --- a/hil-test/tests/dma_mem2mem.rs +++ b/hil-test/tests/dma_mem2mem.rs @@ -6,7 +6,7 @@ #![no_main] use esp_hal::{ - dma::{AnyGdmaChannel, Dma, DmaChannelConvert, DmaError, Mem2Mem}, + dma::{AnyGdmaChannel, DmaChannelConvert, DmaError, Mem2Mem}, dma_buffers, dma_buffers_chunk_size, dma_descriptors, @@ -37,8 +37,7 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; cfg_if::cfg_if! { if #[cfg(any(esp32c2, esp32c6, esp32h2))] { diff --git a/hil-test/tests/embassy_interrupt_spi_dma.rs b/hil-test/tests/embassy_interrupt_spi_dma.rs index cc9121bff83..82ddbfd825f 100644 --- a/hil-test/tests/embassy_interrupt_spi_dma.rs +++ b/hil-test/tests/embassy_interrupt_spi_dma.rs @@ -9,7 +9,7 @@ use embassy_time::{Duration, Instant, Ticker}; use esp_hal::{ - dma::{Dma, DmaRxBuf, DmaTxBuf}, + dma::{DmaRxBuf, DmaTxBuf}, dma_buffers, interrupt::{software::SoftwareInterruptControl, Priority}, peripheral::Peripheral, @@ -83,7 +83,6 @@ mod test { #[timeout(3)] async fn dma_does_not_lock_up_when_used_in_different_executors() { let peripherals = esp_hal::init(esp_hal::Config::default()); - let dma = Dma::new(peripherals.DMA); cfg_if::cfg_if! { if #[cfg(systimer)] { @@ -104,12 +103,12 @@ mod test { } cfg_if::cfg_if! { - if #[cfg(pdma)] { - let dma_channel1 = dma.spi2channel; - let dma_channel2 = dma.spi3channel; + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel1 = peripherals.DMA_SPI2; + let dma_channel2 = peripherals.DMA_SPI3; } else { - let dma_channel1 = dma.channel0; - let dma_channel2 = dma.channel1; + let dma_channel1 = peripherals.DMA_CH0; + let dma_channel2 = peripherals.DMA_CH1; } } @@ -253,7 +252,6 @@ mod test { } let peripherals = esp_hal::init(esp_hal::Config::default()); - let dma = Dma::new(peripherals.DMA); cfg_if::cfg_if! { if #[cfg(systimer)] { @@ -275,9 +273,9 @@ mod test { cfg_if::cfg_if! { if #[cfg(pdma)] { - let dma_channel = dma.spi2channel; + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/hil-test/tests/i2s.rs b/hil-test/tests/i2s.rs index e5191581085..77bb587e1c5 100644 --- a/hil-test/tests/i2s.rs +++ b/hil-test/tests/i2s.rs @@ -12,7 +12,6 @@ use esp_hal::{ delay::Delay, - dma::Dma, dma_buffers, gpio::{AnyPin, NoPin, Pin}, i2s::master::{DataFormat, I2s, I2sTx, Standard}, @@ -112,13 +111,11 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let dma = Dma::new(peripherals.DMA); - cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32s2))] { - let dma_channel = dma.i2s0channel; + if #[cfg(pdma)] { + let dma_channel = peripherals.DMA_I2S0; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/hil-test/tests/lcd_cam.rs b/hil-test/tests/lcd_cam.rs index c1002e7fbe0..c682ee25f73 100644 --- a/hil-test/tests/lcd_cam.rs +++ b/hil-test/tests/lcd_cam.rs @@ -6,7 +6,7 @@ #![no_main] use esp_hal::{ - dma::{Dma, DmaChannel, DmaRxBuf, DmaTxBuf}, + dma::{DmaChannel, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::Level, lcd_cam::{ @@ -55,10 +55,9 @@ mod tests { fn test_camera_can_receive_from_rgb(ctx: Context) { let peripherals = ctx.peripherals; - let dma = Dma::new(peripherals.DMA); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); - let (rx_channel, tx_channel) = dma.channel2.split(); + let (rx_channel, tx_channel) = peripherals.DMA_CH2.split(); let (vsync_in, vsync_out) = peripherals.GPIO6.split(); let (hsync_in, hsync_out) = peripherals.GPIO7.split(); diff --git a/hil-test/tests/lcd_cam_i8080.rs b/hil-test/tests/lcd_cam_i8080.rs index da5ad4df11f..0ce6e842cd6 100644 --- a/hil-test/tests/lcd_cam_i8080.rs +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -6,7 +6,7 @@ #![no_main] use esp_hal::{ - dma::{Dma, DmaTxBuf}, + dma::{DmaChannel0, DmaTxBuf}, dma_buffers, gpio::{GpioPin, NoPin}, lcd_cam::{ @@ -38,7 +38,7 @@ struct Context<'d> { lcd_cam: LcdCam<'d, Blocking>, pcnt: Pcnt<'d>, pins: Pins, - dma: Dma<'d>, + dma: DmaChannel0, dma_buf: DmaTxBuf, } @@ -50,7 +50,6 @@ mod tests { #[init] fn init() -> Context<'static> { let peripherals = esp_hal::init(esp_hal::Config::default()); - let dma = Dma::new(peripherals.DMA); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); let pcnt = Pcnt::new(peripherals.PCNT); @@ -59,7 +58,7 @@ mod tests { Context { lcd_cam, - dma, + dma: peripherals.DMA_CH0, pcnt, pins: Pins { GPIO8: peripherals.GPIO8, @@ -76,13 +75,7 @@ mod tests { fn test_i8080_8bit(ctx: Context<'static>) { let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin); - let i8080 = I8080::new( - ctx.lcd_cam.lcd, - ctx.dma.channel0, - pins, - 20.MHz(), - Config::default(), - ); + let i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, 20.MHz(), Config::default()); let xfer = i8080.send(Command::::None, 0, ctx.dma_buf).unwrap(); xfer.wait().0.unwrap(); @@ -139,15 +132,9 @@ mod tests { NoPin, ); - let mut i8080 = I8080::new( - ctx.lcd_cam.lcd, - ctx.dma.channel0, - pins, - 20.MHz(), - Config::default(), - ) - .with_cs(cs_signal) - .with_ctrl_pins(NoPin, NoPin); + let mut i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, 20.MHz(), Config::default()) + .with_cs(cs_signal) + .with_ctrl_pins(NoPin, NoPin); // This is to make the test values look more intuitive. i8080.set_bit_order(BitOrder::Inverted); @@ -238,7 +225,6 @@ mod tests { .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - let channel = ctx.dma.channel0; let pins = TxSixteenBits::new( NoPin, NoPin, @@ -258,7 +244,7 @@ mod tests { unit3_signal, ); - let mut i8080 = I8080::new(ctx.lcd_cam.lcd, channel, pins, 20.MHz(), Config::default()) + let mut i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, 20.MHz(), Config::default()) .with_cs(cs_signal) .with_ctrl_pins(NoPin, NoPin); diff --git a/hil-test/tests/lcd_cam_i8080_async.rs b/hil-test/tests/lcd_cam_i8080_async.rs index b2da394fb0c..f9c49f8f4f0 100644 --- a/hil-test/tests/lcd_cam_i8080_async.rs +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -7,7 +7,7 @@ #![no_main] use esp_hal::{ - dma::{Dma, DmaTxBuf}, + dma::{DmaChannel0, DmaTxBuf}, dma_buffers, gpio::NoPin, lcd_cam::{ @@ -23,7 +23,7 @@ const DATA_SIZE: usize = 1024 * 10; struct Context<'d> { lcd_cam: LcdCam<'d, Async>, - dma: Dma<'d>, + dma: DmaChannel0, dma_buf: DmaTxBuf, } @@ -36,14 +36,13 @@ mod tests { async fn init() -> Context<'static> { let peripherals = esp_hal::init(esp_hal::Config::default()); - let dma = Dma::new(peripherals.DMA); let lcd_cam = LcdCam::new(peripherals.LCD_CAM).into_async(); let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE); let dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); Context { lcd_cam, - dma, + dma: peripherals.DMA_CH0, dma_buf, } } @@ -52,13 +51,7 @@ mod tests { async fn test_i8080_8bit(ctx: Context<'static>) { let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin); - let i8080 = I8080::new( - ctx.lcd_cam.lcd, - ctx.dma.channel0, - pins, - 20.MHz(), - Config::default(), - ); + let i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, 20.MHz(), Config::default()); let mut transfer = i8080.send(Command::::None, 0, ctx.dma_buf).unwrap(); diff --git a/hil-test/tests/parl_io_tx.rs b/hil-test/tests/parl_io_tx.rs index 54fbe1f27ba..501d6a07775 100644 --- a/hil-test/tests/parl_io_tx.rs +++ b/hil-test/tests/parl_io_tx.rs @@ -7,7 +7,7 @@ #[cfg(esp32c6)] use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits}; use esp_hal::{ - dma::{Dma, DmaChannel0}, + dma::DmaChannel0, gpio::{ interconnect::{InputSignal, OutputSignal}, NoPin, @@ -57,8 +57,7 @@ mod tests { let (valid_loopback, valid) = valid.split(); let pcnt = Pcnt::new(peripherals.PCNT); let pcnt_unit = pcnt.unit0; - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; let parl_io = peripherals.PARL_IO; diff --git a/hil-test/tests/parl_io_tx_async.rs b/hil-test/tests/parl_io_tx_async.rs index ab7b202003b..8b9a6a8a04e 100644 --- a/hil-test/tests/parl_io_tx_async.rs +++ b/hil-test/tests/parl_io_tx_async.rs @@ -9,7 +9,7 @@ #[cfg(esp32c6)] use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits}; use esp_hal::{ - dma::{Dma, DmaChannel0}, + dma::DmaChannel0, gpio::{ interconnect::{InputSignal, OutputSignal}, NoPin, @@ -59,8 +59,7 @@ mod tests { let (valid_loopback, valid) = valid.split(); let pcnt = Pcnt::new(peripherals.PCNT); let pcnt_unit = pcnt.unit0; - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; let parl_io = peripherals.PARL_IO; diff --git a/hil-test/tests/qspi.rs b/hil-test/tests/qspi.rs index 8960f767dea..1c9f46fc371 100644 --- a/hil-test/tests/qspi.rs +++ b/hil-test/tests/qspi.rs @@ -8,7 +8,7 @@ #[cfg(pcnt)] use esp_hal::pcnt::{channel::EdgeMode, unit::Unit, Pcnt}; use esp_hal::{ - dma::{Dma, DmaRxBuf, DmaTxBuf}, + dma::{DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::{AnyPin, Input, Level, Output, Pull}, prelude::*, @@ -192,13 +192,11 @@ mod tests { let _ = Input::new(&mut pin_mirror, Pull::Down); let _ = Input::new(&mut unconnected_pin, Pull::Down); - let dma = Dma::new(peripherals.DMA); - cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32s2))] { - let dma_channel = dma.spi2channel; + if #[cfg(pdma)] { + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/hil-test/tests/spi_full_duplex.rs b/hil-test/tests/spi_full_duplex.rs index 35a9209acc0..756833d9cc4 100644 --- a/hil-test/tests/spi_full_duplex.rs +++ b/hil-test/tests/spi_full_duplex.rs @@ -12,7 +12,7 @@ use embedded_hal::spi::SpiBus; #[cfg(pcnt)] use embedded_hal_async::spi::SpiBus as SpiBusAsync; use esp_hal::{ - dma::{Dma, DmaDescriptor, DmaRxBuf, DmaTxBuf}, + dma::{DmaDescriptor, DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::{Level, NoPin}, peripheral::Peripheral, @@ -61,13 +61,11 @@ mod tests { let sclk = peripherals.GPIO0; let (_, mosi) = hil_test::common_test_pins!(peripherals); - let dma = Dma::new(peripherals.DMA); - cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32s2))] { - let dma_channel = dma.spi2channel; + if #[cfg(pdma)] { + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/hil-test/tests/spi_half_duplex_read.rs b/hil-test/tests/spi_half_duplex_read.rs index 12b77d1b065..c1c92da8be9 100644 --- a/hil-test/tests/spi_half_duplex_read.rs +++ b/hil-test/tests/spi_half_duplex_read.rs @@ -6,7 +6,7 @@ #![no_main] use esp_hal::{ - dma::{Dma, DmaRxBuf, DmaTxBuf}, + dma::{DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::{Level, Output}, prelude::*, @@ -38,13 +38,11 @@ mod tests { let miso_mirror = Output::new(miso_mirror, Level::High); - let dma = Dma::new(peripherals.DMA); - cfg_if::cfg_if! { - if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { - let dma_channel = dma.spi2channel; + if #[cfg(pdma)] { + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index f09a226ec53..fdd0cf43ba1 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -6,7 +6,7 @@ #![no_main] use esp_hal::{ - dma::{Dma, DmaRxBuf, DmaTxBuf}, + dma::{DmaRxBuf, DmaTxBuf}, dma_buffers, gpio::interconnect::InputSignal, pcnt::{channel::EdgeMode, unit::Unit, Pcnt}, @@ -39,13 +39,12 @@ mod tests { let (mosi, _) = hil_test::common_test_pins!(peripherals); let pcnt = Pcnt::new(peripherals.PCNT); - let dma = Dma::new(peripherals.DMA); cfg_if::cfg_if! { - if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { - let dma_channel = dma.spi2channel; + if #[cfg(pdma)] { + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/hil-test/tests/spi_half_duplex_write_psram.rs b/hil-test/tests/spi_half_duplex_write_psram.rs index e0eead9bde9..cb19aafd039 100644 --- a/hil-test/tests/spi_half_duplex_write_psram.rs +++ b/hil-test/tests/spi_half_duplex_write_psram.rs @@ -7,7 +7,7 @@ use defmt::error; use esp_alloc as _; use esp_hal::{ - dma::{Dma, DmaBufBlkSize, DmaRxBuf, DmaTxBuf}, + dma::{DmaBufBlkSize, DmaRxBuf, DmaTxBuf}, dma_buffers, dma_descriptors_chunk_size, gpio::interconnect::InputSignal, @@ -57,9 +57,8 @@ mod tests { let (mosi, _) = hil_test::common_test_pins!(peripherals); let pcnt = Pcnt::new(peripherals.PCNT); - let dma = Dma::new(peripherals.DMA); - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; let (mosi_loopback, mosi) = mosi.split(); diff --git a/hil-test/tests/spi_slave.rs b/hil-test/tests/spi_slave.rs index 299a592a2b4..5a0472619bc 100644 --- a/hil-test/tests/spi_slave.rs +++ b/hil-test/tests/spi_slave.rs @@ -9,7 +9,6 @@ #![no_main] use esp_hal::{ - dma::Dma, dma_buffers, gpio::{Input, Level, Output, Pull}, peripheral::Peripheral, @@ -107,13 +106,11 @@ mod tests { let (sclk_pin, _) = hil_test::common_test_pins!(peripherals); let cs_pin = hil_test::unconnected_pin!(peripherals); - let dma = Dma::new(peripherals.DMA); - cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32s2))] { - let dma_channel = dma.spi2channel; + if #[cfg(pdma)] { + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } diff --git a/qa-test/src/bin/embassy_i2s_read.rs b/qa-test/src/bin/embassy_i2s_read.rs index d6efb56455f..ad12db40d2d 100644 --- a/qa-test/src/bin/embassy_i2s_read.rs +++ b/qa-test/src/bin/embassy_i2s_read.rs @@ -20,7 +20,6 @@ use embassy_executor::Spawner; use esp_backtrace as _; use esp_hal::{ - dma::Dma, dma_buffers, i2s::master::{DataFormat, I2s, Standard}, prelude::*, @@ -36,11 +35,13 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let dma = Dma::new(peripherals.DMA); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.i2s0channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = peripherals.DMA_I2S0; + } else { + let dma_channel = peripherals.DMA_CH0; + } + } let (rx_buffer, rx_descriptors, _, tx_descriptors) = dma_buffers!(4092 * 4, 0); diff --git a/qa-test/src/bin/embassy_i2s_sound.rs b/qa-test/src/bin/embassy_i2s_sound.rs index 8b1b920c209..9678d976496 100644 --- a/qa-test/src/bin/embassy_i2s_sound.rs +++ b/qa-test/src/bin/embassy_i2s_sound.rs @@ -34,7 +34,6 @@ use embassy_executor::Spawner; use esp_backtrace as _; use esp_hal::{ - dma::Dma, dma_buffers, i2s::master::{DataFormat, I2s, Standard}, prelude::*, @@ -58,11 +57,13 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let dma = Dma::new(peripherals.DMA); - #[cfg(any(feature = "esp32", feature = "esp32s2"))] - let dma_channel = dma.i2s0channel; - #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] - let dma_channel = dma.channel0; + cfg_if::cfg_if! { + if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { + let dma_channel = peripherals.DMA_I2S0; + } else { + let dma_channel = peripherals.DMA_CH0; + } + } let (_, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000); diff --git a/qa-test/src/bin/lcd_cam_ov2640.rs b/qa-test/src/bin/lcd_cam_ov2640.rs index 77e2b47276b..14f8b690383 100644 --- a/qa-test/src/bin/lcd_cam_ov2640.rs +++ b/qa-test/src/bin/lcd_cam_ov2640.rs @@ -28,7 +28,6 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - dma::Dma, dma_rx_stream_buffer, i2c::{ self, @@ -47,8 +46,6 @@ use esp_println::{print, println}; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let dma = Dma::new(peripherals.DMA); - let dma_rx_buf = dma_rx_stream_buffer!(20 * 1000, 1000); let cam_siod = peripherals.GPIO4; @@ -69,7 +66,7 @@ fn main() -> ! { ); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); - let camera = Camera::new(lcd_cam.cam, dma.channel0, cam_data_pins, 20u32.MHz()) + let camera = Camera::new(lcd_cam.cam, peripherals.DMA_CH0, cam_data_pins, 20u32.MHz()) .with_master_clock(cam_xclk) .with_pixel_clock(cam_pclk) .with_ctrl_pins(cam_vsync, cam_href); diff --git a/qa-test/src/bin/lcd_dpi.rs b/qa-test/src/bin/lcd_dpi.rs index c70bf44368b..6785ff0b8d6 100644 --- a/qa-test/src/bin/lcd_dpi.rs +++ b/qa-test/src/bin/lcd_dpi.rs @@ -34,7 +34,6 @@ use core::iter::{empty, once}; use esp_backtrace as _; use esp_hal::{ delay::Delay, - dma::Dma, dma_loop_buffer, gpio::{Level, Output}, i2c, @@ -70,8 +69,7 @@ fn main() -> ! { .with_sda(peripherals.GPIO47) .with_scl(peripherals.GPIO48); - let dma = Dma::new(peripherals.DMA); - let tx_channel = dma.channel2; + let tx_channel = peripherals.DMA_CH2; let lcd_cam = LcdCam::new(peripherals.LCD_CAM); let mut expander = Tca9554::new(i2c); diff --git a/qa-test/src/bin/lcd_i8080.rs b/qa-test/src/bin/lcd_i8080.rs index e3288aeb553..88d8c96aab4 100644 --- a/qa-test/src/bin/lcd_i8080.rs +++ b/qa-test/src/bin/lcd_i8080.rs @@ -25,7 +25,7 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - dma::{Dma, DmaTxBuf}, + dma::DmaTxBuf, dma_tx_buffer, gpio::{Input, Level, Output, Pull}, lcd_cam::{ @@ -47,8 +47,6 @@ fn main() -> ! { let lcd_wr = peripherals.GPIO47; // Write clock let lcd_te = peripherals.GPIO48; // Frame sync - let dma = Dma::new(peripherals.DMA); - let dma_tx_buf = dma_tx_buffer!(4000).unwrap(); let delay = Delay::new(); @@ -71,7 +69,7 @@ fn main() -> ! { let lcd_cam = LcdCam::new(peripherals.LCD_CAM); let i8080 = I8080::new( lcd_cam.lcd, - dma.channel0, + peripherals.DMA_CH0, tx_pins, 20.MHz(), Config::default(), diff --git a/qa-test/src/bin/qspi_flash.rs b/qa-test/src/bin/qspi_flash.rs index 46552163fa9..861a3f4eb67 100644 --- a/qa-test/src/bin/qspi_flash.rs +++ b/qa-test/src/bin/qspi_flash.rs @@ -30,7 +30,7 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - dma::{Dma, DmaRxBuf, DmaTxBuf}, + dma::{DmaRxBuf, DmaTxBuf}, dma_buffers, prelude::*, spi::{ @@ -63,13 +63,11 @@ fn main() -> ! { } } - let dma = Dma::new(peripherals.DMA); - cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { - let dma_channel = dma.spi2channel; + let dma_channel = peripherals.DMA_SPI2; } else { - let dma_channel = dma.channel0; + let dma_channel = peripherals.DMA_CH0; } } From a00643f22d8382ac9751b971cd7776ae70b5c270 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov <62840029+playfulFence@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:13:10 +0100 Subject: [PATCH 10/27] Remove embedded-hal 0.2.x impls and deps from `esp-hal` (#2593) * Initial remove * update ssd1306 driver version * small docs cleanup * migration guide * changelog entry * update changelog entry (move under `Removed` section) * eh migration guide link * `Wait` trait linking --- esp-hal/CHANGELOG.md | 2 +- esp-hal/Cargo.toml | 1 - esp-hal/MIGRATING-0.22.md | 26 +++++ esp-hal/src/analog/adc/esp32.rs | 13 --- esp-hal/src/analog/adc/mod.rs | 21 ---- esp-hal/src/analog/adc/riscv.rs | 14 --- esp-hal/src/analog/adc/xtensa.rs | 14 --- esp-hal/src/delay.rs | 24 +---- esp-hal/src/gpio/mod.rs | 173 +------------------------------ esp-hal/src/gpio/placeholder.rs | 19 ---- esp-hal/src/i2c/master/mod.rs | 43 +------- esp-hal/src/lib.rs | 4 +- esp-hal/src/mcpwm/operator.rs | 33 ------ esp-hal/src/rng.rs | 23 +--- esp-hal/src/rtc_cntl/mod.rs | 30 ------ esp-hal/src/spi/master.rs | 70 +------------ esp-hal/src/timer/mod.rs | 51 --------- esp-hal/src/timer/timg.rs | 33 ------ esp-hal/src/twai/mod.rs | 133 +----------------------- esp-hal/src/uart.rs | 56 ---------- esp-hal/src/usb_serial_jtag.rs | 52 ---------- hil-test/Cargo.toml | 4 +- hil-test/tests/gpio.rs | 4 +- hil-test/tests/twai.rs | 2 +- hil-test/tests/uart.rs | 2 +- qa-test/Cargo.toml | 2 +- 26 files changed, 49 insertions(+), 800 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 84c9dd90e19..01f0286ecb2 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -41,13 +41,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - Remove more examples. Update doctests. (#2547) - - The `configure` and `configure_for_async` DMA channel functions has been removed (#2403) - The DMA channel objects no longer have `tx` and `rx` fields. (#2526) - `SysTimerAlarms` has been removed, alarms are now part of the `SystemTimer` struct (#2576) - `FrozenUnit`, `AnyUnit`, `SpecificUnit`, `SpecificComparator`, `AnyComparator` have been removed from `systimer` (#2576) - `esp_hal::psram::psram_range` (#2546) - The `Dma` structure has been removed. (#2545) +- Remove `embedded-hal 0.2.x` impls and deps from `esp-hal` (#2593) ## [0.22.0] - 2024-11-20 diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 90d4a6f2b52..a5a44892e7c 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -31,7 +31,6 @@ embassy-sync = "0.6.1" embassy-usb-driver = { version = "0.1.0", optional = true } embassy-usb-synopsys-otg = { version = "0.1.0", optional = true } embedded-can = "0.4.1" -embedded-hal-02 = { version = "0.2.7", features = ["unproven"], package = "embedded-hal" } embedded-hal = "1.0.0" embedded-hal-async = "1.0.0" embedded-hal-nb = "1.0.0" diff --git a/esp-hal/MIGRATING-0.22.md b/esp-hal/MIGRATING-0.22.md index 8ed0777b4b4..15cecfe3e9c 100644 --- a/esp-hal/MIGRATING-0.22.md +++ b/esp-hal/MIGRATING-0.22.md @@ -158,3 +158,29 @@ is enabled. To retrieve the address and size of the initialized external memory, ``` The usage of `esp_alloc::psram_allocator!` remains unchanged. + + +### embedded-hal 0.2.* is not supported anymore. + +As per https://github.com/rust-embedded/embedded-hal/pull/640, our driver no longer implements traits from `embedded-hal 0.2.x`. +Analogs of all traits from the above mentioned version are available in `embedded-hal 1.x.x` + +```diff +- use embedded_hal_02::can::Frame; ++ use embedded_can::Frame; +``` + +```diff +- use embedded_hal_02::digital::v2::OutputPin; +- use embedded_hal_02::digital::v2::ToggleableOutputPin; ++ use embedded_hal::digital::OutputPin; ++ use embedded_hal::digital::StatefulOutputPin; +``` + +```diff +- use embedded_hal_02::serial::{Read, Write}; ++ use embedded_hal_nb::serial::{Read, Write}; +``` + +You might also want to check the full official `embedded-hal` migration guide: +https://github.com/rust-embedded/embedded-hal/blob/master/docs/migrating-from-0.2-to-1.0.md diff --git a/esp-hal/src/analog/adc/esp32.rs b/esp-hal/src/analog/adc/esp32.rs index 4bacea253d0..d5d99358f10 100644 --- a/esp-hal/src/analog/adc/esp32.rs +++ b/esp-hal/src/analog/adc/esp32.rs @@ -343,19 +343,6 @@ impl<'d, ADC1> Adc<'d, ADC1> { } } -impl<'d, ADCI, PIN> embedded_hal_02::adc::OneShot> - for Adc<'d, ADCI> -where - PIN: embedded_hal_02::adc::Channel + super::AdcChannel, - ADCI: RegisterAccess, -{ - type Error = (); - - fn read(&mut self, pin: &mut super::AdcPin) -> nb::Result { - self.read_oneshot(pin) - } -} - mod adc_implementation { crate::analog::adc::impl_adc_interface! { ADC1 [ diff --git a/esp-hal/src/analog/adc/mod.rs b/esp-hal/src/analog/adc/mod.rs index d34b59c17dc..e247e82aadc 100644 --- a/esp-hal/src/analog/adc/mod.rs +++ b/esp-hal/src/analog/adc/mod.rs @@ -16,10 +16,6 @@ //! basic calibration, curve fitting or linear interpolation. The calibration //! schemes can be used to improve the accuracy of the ADC readings. //! -//! ## Usage -//! -//! The ADC driver implements the `embedded-hal@0.2.x` ADC traits. -//! //! ## Examples //! //! ### Read an analog signal from a pin @@ -108,17 +104,6 @@ pub struct AdcPin { _phantom: PhantomData, } -impl embedded_hal_02::adc::Channel for AdcPin -where - PIN: embedded_hal_02::adc::Channel, -{ - type ID = u8; - - fn channel() -> Self::ID { - PIN::channel() - } -} - /// Configuration for the ADC. pub struct AdcConfig { #[cfg_attr(not(esp32), allow(unused))] @@ -256,12 +241,6 @@ macro_rules! impl_adc_interface { impl $crate::analog::adc::AdcChannel for crate::gpio::GpioPin<$pin> { const CHANNEL: u8 = $channel; } - - impl embedded_hal_02::adc::Channel for crate::gpio::GpioPin<$pin> { - type ID = u8; - - fn channel() -> u8 { $channel } - } )+ } } diff --git a/esp-hal/src/analog/adc/riscv.rs b/esp-hal/src/analog/adc/riscv.rs index c228502e4e2..9cd593f43f7 100644 --- a/esp-hal/src/analog/adc/riscv.rs +++ b/esp-hal/src/analog/adc/riscv.rs @@ -531,20 +531,6 @@ impl super::AdcCalEfuse for crate::peripherals::ADC2 { } } -impl embedded_hal_02::adc::OneShot> - for Adc<'_, ADCI> -where - PIN: embedded_hal_02::adc::Channel + super::AdcChannel, - ADCI: RegisterAccess, - CS: super::AdcCalScheme, -{ - type Error = (); - - fn read(&mut self, pin: &mut super::AdcPin) -> nb::Result { - self.read_oneshot(pin) - } -} - #[cfg(esp32c2)] mod adc_implementation { crate::analog::adc::impl_adc_interface! { diff --git a/esp-hal/src/analog/adc/xtensa.rs b/esp-hal/src/analog/adc/xtensa.rs index c8f547dce5a..d7d01d895fd 100644 --- a/esp-hal/src/analog/adc/xtensa.rs +++ b/esp-hal/src/analog/adc/xtensa.rs @@ -590,20 +590,6 @@ impl super::AdcCalEfuse for crate::peripherals::ADC2 { } } -impl<'d, ADCI, PIN, CS> embedded_hal_02::adc::OneShot> - for Adc<'d, ADCI> -where - PIN: embedded_hal_02::adc::Channel + AdcChannel, - ADCI: RegisterAccess, - CS: AdcCalScheme, -{ - type Error = (); - - fn read(&mut self, pin: &mut AdcPin) -> nb::Result { - self.read_oneshot(pin) - } -} - mod adc_implementation { crate::analog::adc::impl_adc_interface! { ADC1 [ diff --git a/esp-hal/src/delay.rs b/esp-hal/src/delay.rs index d1c562b01d3..a361aabcc62 100644 --- a/esp-hal/src/delay.rs +++ b/esp-hal/src/delay.rs @@ -13,8 +13,7 @@ //! //! ## Usage //! -//! This module implements the blocking [DelayMs] and [DelayUs] traits from -//! [embedded-hal], both 0.2.x and 1.x.x. +//! This module implements the blocking [DelayNs] trait from [embedded-hal]. //! //! ## Examples //! ### Delay for 1 second @@ -28,8 +27,7 @@ //! # } //! ``` //! -//! [DelayMs]: embedded_hal_02::blocking::delay::DelayMs -//! [DelayUs]: embedded_hal_02::blocking::delay::DelayUs +//! [DelayNs]: https://docs.rs/embedded-hal/1.0.0/embedded_hal/delay/trait.DelayNs.html //! [embedded-hal]: https://docs.rs/embedded-hal/1.0.0/embedded_hal/delay/index.html //! [now]: crate::time::now @@ -43,24 +41,6 @@ pub use fugit::MicrosDurationU64; #[non_exhaustive] pub struct Delay; -impl embedded_hal_02::blocking::delay::DelayMs for Delay -where - T: Into, -{ - fn delay_ms(&mut self, ms: T) { - self.delay_millis(ms.into()); - } -} - -impl embedded_hal_02::blocking::delay::DelayUs for Delay -where - T: Into, -{ - fn delay_us(&mut self, us: T) { - self.delay_micros(us.into()); - } -} - impl embedded_hal::delay::DelayNs for Delay { fn delay_ns(&mut self, ns: u32) { self.delay_nanos(ns); diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index 6b01ef5d59f..de6ee6b7039 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -31,6 +31,9 @@ //! GPIO interrupts. For more information, see the //! [`Io::set_interrupt_handler`]. //! +//! This driver also implements pin-related traits from [embedded-hal] and +//! [Wait](embedded_hal_async::digital::Wait) trait from [embedded-hal-async]. +//! //! ## GPIO interconnect //! //! Sometimes you may want to connect peripherals together without using @@ -49,6 +52,7 @@ //! See the [Inverting TX and RX Pins] example of the UART documentation. //! //! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/ +//! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/index.html //! [Inverting TX and RX Pins]: crate::uart#inverting-rx-and-tx-pins use portable_atomic::{AtomicPtr, Ordering}; @@ -2297,175 +2301,6 @@ mod asynch { } } -mod embedded_hal_02_impls { - use embedded_hal_02::digital::v2 as digital; - - use super::*; - - impl

digital::InputPin for Input<'_, P> - where - P: InputPin, - { - type Error = core::convert::Infallible; - - fn is_high(&self) -> Result { - Ok(self.pin.is_high()) - } - fn is_low(&self) -> Result { - Ok(self.pin.is_low()) - } - } - - impl

digital::OutputPin for Output<'_, P> - where - P: OutputPin, - { - type Error = core::convert::Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.pin.set_high(); - Ok(()) - } - fn set_low(&mut self) -> Result<(), Self::Error> { - self.pin.set_low(); - Ok(()) - } - } - - impl

digital::StatefulOutputPin for Output<'_, P> - where - P: OutputPin, - { - fn is_set_high(&self) -> Result { - Ok(self.is_set_high()) - } - fn is_set_low(&self) -> Result { - Ok(self.is_set_low()) - } - } - - impl

digital::ToggleableOutputPin for Output<'_, P> - where - P: OutputPin, - { - type Error = core::convert::Infallible; - - fn toggle(&mut self) -> Result<(), Self::Error> { - self.toggle(); - Ok(()) - } - } - - impl

digital::InputPin for OutputOpenDrain<'_, P> - where - P: InputPin + OutputPin, - { - type Error = core::convert::Infallible; - - fn is_high(&self) -> Result { - Ok(self.pin.is_high()) - } - fn is_low(&self) -> Result { - Ok(self.pin.is_low()) - } - } - - impl

digital::OutputPin for OutputOpenDrain<'_, P> - where - P: InputPin + OutputPin, - { - type Error = core::convert::Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_high(); - Ok(()) - } - - fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_low(); - Ok(()) - } - } - - impl

digital::StatefulOutputPin for OutputOpenDrain<'_, P> - where - P: InputPin + OutputPin, - { - fn is_set_high(&self) -> Result { - Ok(self.is_set_high()) - } - fn is_set_low(&self) -> Result { - Ok(self.is_set_low()) - } - } - - impl

digital::ToggleableOutputPin for OutputOpenDrain<'_, P> - where - P: InputPin + OutputPin, - { - type Error = core::convert::Infallible; - - fn toggle(&mut self) -> Result<(), Self::Error> { - self.toggle(); - Ok(()) - } - } - - impl

digital::InputPin for Flex<'_, P> - where - P: InputPin + OutputPin, - { - type Error = core::convert::Infallible; - - fn is_high(&self) -> Result { - Ok(self.is_high()) - } - fn is_low(&self) -> Result { - Ok(self.is_low()) - } - } - - impl

digital::OutputPin for Flex<'_, P> - where - P: InputPin + OutputPin, - { - type Error = core::convert::Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(true, private::Internal); - Ok(()) - } - fn set_low(&mut self) -> Result<(), Self::Error> { - self.pin.set_output_high(false, private::Internal); - Ok(()) - } - } - - impl

digital::StatefulOutputPin for Flex<'_, P> - where - P: InputPin + OutputPin, - { - fn is_set_high(&self) -> Result { - Ok(self.is_set_high()) - } - fn is_set_low(&self) -> Result { - Ok(self.is_set_low()) - } - } - - impl

digital::ToggleableOutputPin for Flex<'_, P> - where - P: InputPin + OutputPin, - { - type Error = core::convert::Infallible; - - fn toggle(&mut self) -> Result<(), Self::Error> { - self.toggle(); - Ok(()) - } - } -} - mod embedded_hal_impls { use embedded_hal::digital; diff --git a/esp-hal/src/gpio/placeholder.rs b/esp-hal/src/gpio/placeholder.rs index bf269c4d262..8f4b12f2a15 100644 --- a/esp-hal/src/gpio/placeholder.rs +++ b/esp-hal/src/gpio/placeholder.rs @@ -85,25 +85,6 @@ impl crate::peripheral::Peripheral for NoPin { impl private::Sealed for NoPin {} -impl embedded_hal_02::digital::v2::OutputPin for NoPin { - type Error = core::convert::Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - fn set_low(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} -impl embedded_hal_02::digital::v2::StatefulOutputPin for NoPin { - fn is_set_high(&self) -> Result { - Ok(false) - } - fn is_set_low(&self) -> Result { - Ok(false) - } -} - impl embedded_hal::digital::ErrorType for NoPin { type Error = core::convert::Infallible; } diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs index 67596b699e6..0bed0dfd960 100644 --- a/esp-hal/src/i2c/master/mod.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -9,8 +9,7 @@ //! //! The I2C driver implements a number of third-party traits, with the //! intention of making the HAL inter-compatible with various device drivers -//! from the community. This includes the [`embedded-hal`] for both 0.2.x and -//! 1.0.x versions. +//! from the community, including the [`embedded-hal`]. //! //! ## Examples //! @@ -35,7 +34,7 @@ //! } //! # } //! ``` -//! [`embedded-hal`]: https://crates.io/crates/embedded-hal +//! [`embedded-hal`]: https://docs.rs/embedded-hal/latest/embedded_hal/index.html use core::marker::PhantomData; #[cfg(not(esp32))] @@ -285,44 +284,6 @@ impl SetConfig for I2c<'_, DM, T> { } } -impl embedded_hal_02::blocking::i2c::Read for I2c<'_, Blocking, T> -where - T: Instance, -{ - type Error = Error; - - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.read(address, buffer) - } -} - -impl embedded_hal_02::blocking::i2c::Write for I2c<'_, Blocking, T> -where - T: Instance, -{ - type Error = Error; - - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(addr, bytes) - } -} - -impl embedded_hal_02::blocking::i2c::WriteRead for I2c<'_, Blocking, T> -where - T: Instance, -{ - type Error = Error; - - fn write_read( - &mut self, - address: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - self.write_read(address, bytes, buffer) - } -} - impl embedded_hal::i2c::ErrorType for I2c<'_, DM, T> { type Error = Error; } diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 0d73e7dcfb4..553d2d6b549 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -127,8 +127,8 @@ //! //! [documentation]: https://docs.esp-rs.org/esp-hal //! [examples]: https://github.com/esp-rs/esp-hal/tree/main/examples -//! [embedded-hal]: https://github.com/rust-embedded/embedded-hal/tree/master/embedded-hal -//! [embedded-hal-async]: https://github.com/rust-embedded/embedded-hal/tree/master/embedded-hal-async +//! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/ +//! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/ //! [xtask]: https://github.com/matklad/cargo-xtask //! [esp-generate]: https://github.com/esp-rs/esp-generate //! [book]: https://docs.esp-rs.org/book/ diff --git a/esp-hal/src/mcpwm/operator.rs b/esp-hal/src/mcpwm/operator.rs index 98441048e85..6b814413ce6 100644 --- a/esp-hal/src/mcpwm/operator.rs +++ b/esp-hal/src/mcpwm/operator.rs @@ -421,39 +421,6 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, } } -impl embedded_hal_02::PwmPin - for PwmPin<'_, PWM, OP, IS_A> -{ - type Duty = u16; - - /// This only set the timestamp to 0, if you want to disable the PwmPin, - /// it must be done on the timer itself. - fn disable(&mut self) { - self.set_timestamp(0); - } - - /// This only set the timestamp to the maximum, if you want to disable the - /// PwmPin, it must be done on the timer itself. - fn enable(&mut self) { - self.set_timestamp(u16::MAX); - } - - /// Get the duty of the pin - fn get_duty(&self) -> Self::Duty { - self.timestamp() - } - - /// Get the max duty of the pin - fn get_max_duty(&self) -> Self::Duty { - self.period() - } - - /// Set the duty of the pin - fn set_duty(&mut self, duty: Self::Duty) { - self.set_timestamp(duty); - } -} - /// Implement no error type for the PwmPin because the method are infallible impl embedded_hal::pwm::ErrorType for PwmPin<'_, PWM, OP, IS_A> diff --git a/esp-hal/src/rng.rs b/esp-hal/src/rng.rs index 182257a4e32..3216744636e 100644 --- a/esp-hal/src/rng.rs +++ b/esp-hal/src/rng.rs @@ -32,10 +32,7 @@ //! method, which returns a 32-bit unsigned integer. //! //! ## Usage -//! This driver implements the [Read](embedded_hal_02::blocking::rng::Read) -//! trait from the `embedded_hal` crate, allowing you to generate random bytes -//! by calling the `read` method. The driver also implements the traits from the -//! [`rand_core`] crate. +//! The driver implements the traits from the [`rand_core`] crate. //! //! [`rand_core`]: https://crates.io/crates/rand_core //! @@ -139,15 +136,6 @@ impl Rng { } } -impl embedded_hal_02::blocking::rng::Read for Rng { - type Error = core::convert::Infallible; - - fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.read(buffer); - Ok(()) - } -} - impl rand_core::RngCore for Rng { fn next_u32(&mut self) -> u32 { self.random() @@ -229,15 +217,6 @@ impl Drop for Trng<'_> { } } -impl embedded_hal_02::blocking::rng::Read for Trng<'_> { - type Error = core::convert::Infallible; - /// Fills the provided buffer with random bytes. - fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.rng.read(buffer); - Ok(()) - } -} - /// Implementing RngCore trait from rand_core for `Trng` structure impl rand_core::RngCore for Trng<'_> { fn next_u32(&mut self) -> u32 { diff --git a/esp-hal/src/rtc_cntl/mod.rs b/esp-hal/src/rtc_cntl/mod.rs index 5aaf9370d43..be5724fc7a7 100644 --- a/esp-hal/src/rtc_cntl/mod.rs +++ b/esp-hal/src/rtc_cntl/mod.rs @@ -1095,29 +1095,6 @@ impl Rwdt { } } -impl embedded_hal_02::watchdog::WatchdogDisable for Rwdt { - fn disable(&mut self) { - self.disable(); - } -} - -impl embedded_hal_02::watchdog::WatchdogEnable for Rwdt { - type Time = MicrosDurationU64; - - fn start(&mut self, period: T) - where - T: Into, - { - self.set_timeout(RwdtStage::Stage0, period.into()); - } -} - -impl embedded_hal_02::watchdog::Watchdog for Rwdt { - fn feed(&mut self) { - self.feed(); - } -} - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] /// Super Watchdog pub struct Swd; @@ -1172,13 +1149,6 @@ impl Default for Swd { } } -#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] -impl embedded_hal_02::watchdog::WatchdogDisable for Swd { - fn disable(&mut self) { - self.disable(); - } -} - /// Return reset reason. pub fn reset_reason(cpu: Cpu) -> Option { let reason = crate::rom::rtc_get_reset_reason(cpu as u32); diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index 82bbb70d5cb..04894b9bd84 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -14,8 +14,6 @@ //! If all you want to do is to communicate to a single device, and you initiate //! transactions yourself, there are a number of ways to achieve this: //! -//! - Use the [`FullDuplex`](embedded_hal_02::spi::FullDuplex) trait to -//! read/write single bytes at a time, //! - Use the [`SpiBus`](embedded_hal::spi::SpiBus) trait and its associated //! functions to initiate transactions with simultaneous reads and writes, or //! - Use the `ExclusiveDevice` struct from [`embedded-hal-bus`] or `SpiDevice` @@ -30,8 +28,8 @@ //! //! ## Usage //! -//! The module implements several third-party traits from embedded-hal@0.2.x, -//! embedded-hal@1.x.x and embassy-embedded-hal +//! The module implements several third-party traits from embedded-hal@1.x.x +//! and embassy-embedded-hal. //! //! ## Examples //! @@ -806,44 +804,6 @@ where } } -impl embedded_hal_02::spi::FullDuplex for Spi<'_, M, T> -where - T: Instance, -{ - type Error = Error; - - fn read(&mut self) -> nb::Result { - self.read_byte() - } - - fn send(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.write_byte(word) - } -} - -impl embedded_hal_02::blocking::spi::Transfer for Spi<'_, M, T> -where - T: Instance, -{ - type Error = Error; - - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - self.transfer(words) - } -} - -impl embedded_hal_02::blocking::spi::Write for Spi<'_, M, T> -where - T: Instance, -{ - type Error = Error; - - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.write_bytes(words)?; - self.driver().flush() - } -} - mod dma { use core::{ cmp::min, @@ -1805,30 +1765,6 @@ mod dma { } } - impl embedded_hal_02::blocking::spi::Transfer for SpiDmaBus<'_, Blocking, T> - where - T: Instance, - { - type Error = Error; - - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - self.transfer_in_place(words)?; - Ok(words) - } - } - - impl embedded_hal_02::blocking::spi::Write for SpiDmaBus<'_, Blocking, T> - where - T: Instance, - { - type Error = Error; - - fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.write(words)?; - Ok(()) - } - } - /// Async functionality mod asynch { use core::{ @@ -2773,7 +2709,7 @@ impl Info { // Wait for all chunks to complete except the last one. // The function is allowed to return before the bus is idle. - // see [embedded-hal flushing](https://docs.rs/embedded-hal/1.0.0-alpha.8/embedded_hal/spi/blocking/index.html#flushing) + // see [embedded-hal flushing](https://docs.rs/embedded-hal/1.0.0/embedded_hal/spi/index.html#flushing) if i < num_chunks { self.flush()?; } diff --git a/esp-hal/src/timer/mod.rs b/esp-hal/src/timer/mod.rs index a05138a1355..47ab4221184 100644 --- a/esp-hal/src/timer/mod.rs +++ b/esp-hal/src/timer/mod.rs @@ -211,26 +211,6 @@ where } } -impl embedded_hal_02::blocking::delay::DelayMs for OneShotTimer<'_, T> -where - T: Timer, - UXX: Into, -{ - fn delay_ms(&mut self, ms: UXX) { - self.delay_millis(ms.into()); - } -} - -impl embedded_hal_02::blocking::delay::DelayUs for OneShotTimer<'_, T> -where - T: Timer, - UXX: Into, -{ - fn delay_us(&mut self, us: UXX) { - self.delay_micros(us.into()); - } -} - impl embedded_hal::delay::DelayNs for OneShotTimer<'_, T> where T: Timer, @@ -325,37 +305,6 @@ where } } -impl embedded_hal_02::timer::CountDown for PeriodicTimer<'_, T> -where - T: Timer, -{ - type Time = MicrosDurationU64; - - fn start

+ 'd) -> OneShotTimer<'d, Blocking> { + Self::new_typed(inner.map_into()) + } } -impl<'d, T> OneShotTimer<'d, T> +impl<'d, T> OneShotTimer<'d, Blocking, T> where T: Timer, { - /// Construct a new instance of [`OneShotTimer`]. - pub fn new(inner: impl Peripheral

+ 'd) -> Self { + /// Construct a typed instance of [`OneShotTimer`]. + pub fn new_typed(inner: impl Peripheral

+ 'd) -> Self { crate::into_ref!(inner); + Self { + inner, + _ph: PhantomData, + } + } + + /// Converts the driver to [`Async`] mode. + pub fn into_async(mut self) -> OneShotTimer<'d, Async, T> { + let handler = self.inner.async_interrupt_handler(); + self.inner.set_interrupt_handler(handler); + OneShotTimer { + inner: self.inner, + _ph: PhantomData, + } + } +} + +impl<'d, T> OneShotTimer<'d, Async, T> +where + T: Timer, +{ + /// Converts the driver to [`Blocking`] mode. + pub fn into_blocking(self) -> Self { + crate::interrupt::disable(Cpu::current(), self.inner.peripheral_interrupt()); + Self { + inner: self.inner, + _ph: PhantomData, + } + } +} + +impl OneShotTimer<'_, Async, T> +where + T: Timer, +{ + /// Delay for *at least* `ns` nanoseconds. + pub async fn delay_nanos_async(&mut self, ns: u32) { + self.delay_async(MicrosDurationU64::from_ticks(ns.div_ceil(1000) as u64)) + .await + } + + /// Delay for *at least* `ms` milliseconds. + pub async fn delay_millis_async(&mut self, ms: u32) { + self.delay_async((ms as u64).millis()).await; + } + + /// Delay for *at least* `us` microseconds. + pub async fn delay_micros_async(&mut self, us: u32) { + self.delay_async((us as u64).micros()).await; + } - Self { inner } + async fn delay_async(&mut self, us: MicrosDurationU64) { + unwrap!(self.schedule(us)); + self.inner.wait().await; + self.stop(); + self.clear_interrupt(); } +} - /// Pauses execution for *at least* `ms` milliseconds. - pub fn delay_millis(&self, ms: u32) { +impl<'d, M, T> OneShotTimer<'d, M, T> +where + M: Mode, + T: Timer, +{ + /// Delay for *at least* `ms` milliseconds. + pub fn delay_millis(&mut self, ms: u32) { self.delay((ms as u64).millis()); } - /// Pauses execution for *at least* `us` microseconds. - pub fn delay_micros(&self, us: u32) { + /// Delay for *at least* `us` microseconds. + pub fn delay_micros(&mut self, us: u32) { self.delay((us as u64).micros()); } - /// Pauses execution for *at least* `ns` nanoseconds. - pub fn delay_nanos(&self, ns: u32) { + /// Delay for *at least* `ns` nanoseconds. + pub fn delay_nanos(&mut self, ns: u32) { self.delay((ns.div_ceil(1000) as u64).micros()) } - fn delay(&self, us: MicrosDurationU64) { - if self.inner.is_running() { - self.inner.stop(); - } - - self.inner.clear_interrupt(); - self.inner.reset(); - - self.inner.enable_auto_reload(false); - self.inner.load_value(us).unwrap(); - self.inner.start(); + fn delay(&mut self, us: MicrosDurationU64) { + self.schedule(us).unwrap(); while !self.inner.is_interrupt_set() { // Wait } - self.inner.stop(); - self.inner.clear_interrupt(); + self.stop(); + self.clear_interrupt(); } /// Start counting until the given timeout and raise an interrupt @@ -195,14 +264,19 @@ where /// Clear the interrupt flag pub fn clear_interrupt(&mut self) { self.inner.clear_interrupt(); - self.inner.set_alarm_active(false); } } -impl crate::private::Sealed for OneShotTimer<'_, T> where T: Timer {} +impl crate::private::Sealed for OneShotTimer<'_, M, T> +where + T: Timer, + M: Mode, +{ +} -impl InterruptConfigurable for OneShotTimer<'_, T> +impl InterruptConfigurable for OneShotTimer<'_, M, T> where + M: Mode, T: Timer, { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { @@ -210,7 +284,7 @@ where } } -impl embedded_hal::delay::DelayNs for OneShotTimer<'_, T> +impl embedded_hal::delay::DelayNs for OneShotTimer<'_, Blocking, T> where T: Timer, { @@ -219,22 +293,47 @@ where } } +impl embedded_hal_async::delay::DelayNs for OneShotTimer<'_, Async, T> +where + T: Timer, +{ + async fn delay_ns(&mut self, ns: u32) { + self.delay_nanos_async(ns).await + } +} + /// A periodic timer. -pub struct PeriodicTimer<'d, T> { +pub struct PeriodicTimer<'d, M, T = AnyTimer> { inner: PeripheralRef<'d, T>, + _ph: PhantomData, } -impl<'d, T> PeriodicTimer<'d, T> +impl<'d> PeriodicTimer<'d, Blocking> { + /// Construct a new instance of [`PeriodicTimer`]. + pub fn new(inner: impl Peripheral

+ 'd) -> PeriodicTimer<'d, Blocking> { + Self::new_typed(inner.map_into()) + } +} + +impl<'d, T> PeriodicTimer<'d, Blocking, T> where T: Timer, { - /// Construct a new instance of [`PeriodicTimer`]. - pub fn new(inner: impl Peripheral

+ 'd) -> Self { + /// Construct a typed instance of [`PeriodicTimer`]. + pub fn new_typed(inner: impl Peripheral

+ 'd) -> Self { crate::into_ref!(inner); - - Self { inner } + Self { + inner, + _ph: PhantomData, + } } +} +impl<'d, M, T> PeriodicTimer<'d, M, T> +where + M: Mode, + T: Timer, +{ /// Start a new count down. pub fn start(&mut self, timeout: MicrosDurationU64) -> Result<(), Error> { if self.inner.is_running() { @@ -255,7 +354,6 @@ where pub fn wait(&mut self) -> nb::Result<(), void::Void> { if self.inner.is_interrupt_set() { self.inner.clear_interrupt(); - self.inner.set_alarm_active(true); Ok(()) } else { @@ -289,14 +387,14 @@ where /// Clear the interrupt flag pub fn clear_interrupt(&mut self) { self.inner.clear_interrupt(); - self.inner.set_alarm_active(true); } } -impl crate::private::Sealed for PeriodicTimer<'_, T> where T: Timer {} +impl crate::private::Sealed for PeriodicTimer<'_, M, T> where T: Timer {} -impl InterruptConfigurable for PeriodicTimer<'_, T> +impl InterruptConfigurable for PeriodicTimer<'_, M, T> where + M: Mode, T: Timer, { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { @@ -304,32 +402,12 @@ where } } -/// An enum of all timer types -enum AnyTimerInner { - /// Timer 0 of the TIMG0 peripheral in blocking mode. - TimgTimer(timg::Timer), - /// Systimer Alarm - #[cfg(systimer)] - SystimerAlarm(systimer::Alarm), -} - -/// A type-erased timer -/// -/// You can create an instance of this by just calling `.into()` on a timer. -pub struct AnyTimer(AnyTimerInner); - -impl crate::private::Sealed for AnyTimer {} - -impl From for AnyTimer { - fn from(value: timg::Timer) -> Self { - Self(AnyTimerInner::TimgTimer(value)) - } -} - -#[cfg(systimer)] -impl From for AnyTimer { - fn from(value: systimer::Alarm) -> Self { - Self(AnyTimerInner::SystimerAlarm(value)) +crate::any_peripheral! { + /// Any Timer peripheral. + pub peripheral AnyTimer { + TimgTimer(timg::Timer), + #[cfg(systimer)] + SystimerAlarm(systimer::Alarm), } } @@ -349,18 +427,22 @@ impl Timer for AnyTimer { fn enable_auto_reload(&self, auto_reload: bool); fn enable_interrupt(&self, state: bool); fn clear_interrupt(&self); - fn set_interrupt_handler(&self, handler: InterruptHandler); fn is_interrupt_set(&self) -> bool; - fn set_alarm_active(&self, state: bool); + async fn wait(&self); + fn async_interrupt_handler(&self) -> InterruptHandler; + fn peripheral_interrupt(&self) -> Interrupt; } } } -impl Peripheral for AnyTimer { - type P = Self; - - #[inline] - unsafe fn clone_unchecked(&self) -> Self::P { - core::ptr::read(self as *const _) +impl InterruptConfigurable for AnyTimer { + delegate::delegate! { + to match &mut self.0 { + AnyTimerInner::TimgTimer(inner) => inner, + #[cfg(systimer)] + AnyTimerInner::SystimerAlarm(inner) => inner, + } { + fn set_interrupt_handler(&mut self, handler: InterruptHandler); + } } } diff --git a/esp-hal/src/timer/systimer.rs b/esp-hal/src/timer/systimer.rs index 6efa78e582d..786eeba03da 100644 --- a/esp-hal/src/timer/systimer.rs +++ b/esp-hal/src/timer/systimer.rs @@ -23,7 +23,7 @@ use fugit::{Instant, MicrosDurationU64}; use super::{Error, Timer as _}; use crate::{ - interrupt::{self, InterruptHandler}, + interrupt::{self, InterruptConfigurable, InterruptHandler}, peripheral::Peripheral, peripherals::{Interrupt, SYSTIMER}, sync::{lock, Lock}, @@ -400,7 +400,7 @@ impl Alarm { } /// Set the interrupt handler for this comparator. - fn set_interrupt_handler(&self, handler: InterruptHandler) { + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { let interrupt = match self.channel() { 0 => Interrupt::SYSTIMER_TARGET0, 1 => Interrupt::SYSTIMER_TARGET1, @@ -457,6 +457,12 @@ impl Alarm { } } +impl InterruptConfigurable for Alarm { + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + self.set_interrupt_handler(handler) + } +} + /// The modes of a comparator. #[derive(Copy, Clone)] enum ComparatorMode { @@ -583,12 +589,26 @@ impl super::Timer for Alarm { .bit_is_set() } - fn set_alarm_active(&self, _active: bool) { - // Nothing to do + async fn wait(&self) { + asynch::AlarmFuture::new(self).await } - fn set_interrupt_handler(&self, handler: InterruptHandler) { - self.set_interrupt_handler(handler); + fn async_interrupt_handler(&self) -> InterruptHandler { + match self.channel() { + 0 => asynch::target0_handler, + 1 => asynch::target1_handler, + 2 => asynch::target2_handler, + _ => unreachable!(), + } + } + + fn peripheral_interrupt(&self) -> Interrupt { + match self.channel() { + 0 => Interrupt::SYSTIMER_TARGET0, + 1 => Interrupt::SYSTIMER_TARGET1, + 2 => Interrupt::SYSTIMER_TARGET2, + _ => unreachable!(), + } } } @@ -611,7 +631,6 @@ static INT_ENA_LOCK: Lock = Lock::new(); // Async functionality of the system timer. mod asynch { - #![allow(unused)] // FIXME (mabez) use core::{ pin::Pin, task::{Context, Poll}, @@ -633,21 +652,6 @@ mod asynch { impl<'a> AlarmFuture<'a> { pub(crate) fn new(alarm: &'a Alarm) -> Self { - alarm.clear_interrupt(); - - let (interrupt, handler) = match alarm.channel() { - 0 => (Interrupt::SYSTIMER_TARGET0, target0_handler), - 1 => (Interrupt::SYSTIMER_TARGET1, target1_handler), - _ => (Interrupt::SYSTIMER_TARGET2, target2_handler), - }; - - unsafe { - interrupt::bind_interrupt(interrupt, handler.handler()); - interrupt::enable(interrupt, handler.priority()).unwrap(); - } - - alarm.set_interrupt_handler(handler); - alarm.enable_interrupt(true); Self { alarm } @@ -723,33 +727,64 @@ pub mod etm { //! The system timer can generate the following ETM events: //! - SYSTIMER_EVT_CNT_CMPx: Indicates the alarm pulses generated by //! COMPx - // FIXME(mabez) + //! ## Example + //! ```rust, no_run + #![doc = crate::before_snippet!()] + //! # use esp_hal::timer::systimer::{etm::Event, SystemTimer}; + //! # use esp_hal::timer::PeriodicTimer; + //! # use esp_hal::etm::Etm; + //! # use esp_hal::gpio::{ + //! # etm::{Channels, OutputConfig}, + //! # Level, + //! # Pull, + //! # }; + //! # use fugit::ExtU32; + //! let syst = SystemTimer::new(peripherals.SYSTIMER); + //! let etm = Etm::new(peripherals.SOC_ETM); + //! let gpio_ext = Channels::new(peripherals.GPIO_SD); + //! let alarm0 = syst.alarm0; + //! let mut led = peripherals.GPIO1; + //! + //! let timer_event = Event::new(&alarm0); + //! let led_task = gpio_ext.channel0_task.toggle( + //! &mut led, + //! OutputConfig { + //! open_drain: false, + //! pull: Pull::None, + //! initial_state: Level::High, + //! }, + //! ); + //! + //! let _configured_etm_channel = etm.channel0.setup(&timer_event, + //! &led_task); + //! + //! let timer = PeriodicTimer::new(alarm0); + //! // configure the timer as usual + //! // when it fires it will toggle the GPIO + //! # } + //! ``` use super::*; /// An ETM controlled SYSTIMER event - pub struct Event<'a> { - alarm: &'a mut Alarm, + pub struct Event { + id: u8, } - impl<'a> Event<'a> { + impl Event { /// Creates an ETM event from the given [Alarm] - pub fn new(alarm: &'a mut Alarm) -> Self { - Self { alarm } - } - - /// Execute closure f with mutable access to the wrapped [Alarm]. - pub fn with(&self, f: impl FnOnce(&&'a mut Alarm) -> R) -> R { - let alarm = &self.alarm; - f(alarm) + pub fn new(alarm: &Alarm) -> Self { + Self { + id: 50 + alarm.channel(), + } } } - impl crate::private::Sealed for Event<'_> {} + impl crate::private::Sealed for Event {} - impl crate::etm::EtmEvent for Event<'_> { + impl crate::etm::EtmEvent for Event { fn id(&self) -> u8 { - 50 + self.alarm.channel() + self.id } } diff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs index 7d7bbafccb9..af005b8524f 100644 --- a/esp-hal/src/timer/timg.rs +++ b/esp-hal/src/timer/timg.rs @@ -221,7 +221,7 @@ impl TimerGroupInstance for crate::peripherals::TIMG1 { } else if #[cfg(any(esp32c6, esp32h2))] { unsafe { &*crate::peripherals::PCR::PTR } .timergroup1_wdt_clk_conf() - .modify(|_, w| unsafe { w.tg1_wdt_clk_sel().bits(1) }); + .modify(|_, w| unsafe { w.tg1_wdt_clk_sel().bits(TIMG_DEFAULT_CLK_SRC) }); } } } @@ -315,8 +315,29 @@ impl super::Timer for Timer { self.clear_interrupt() } - fn set_interrupt_handler(&self, handler: InterruptHandler) { - let interrupt = match (self.timer_group(), self.timer_number()) { + fn is_interrupt_set(&self) -> bool { + self.is_interrupt_set() + } + + async fn wait(&self) { + asynch::TimerFuture::new(self).await + } + + fn async_interrupt_handler(&self) -> InterruptHandler { + match (self.timer_group(), self.timer_number()) { + (0, 0) => asynch::timg0_timer0_handler, + #[cfg(timg_timer1)] + (0, 1) => asynch::timg0_timer1_handler, + #[cfg(timg1)] + (1, 0) => asynch::timg1_timer0_handler, + #[cfg(all(timg_timer1, timg1))] + (1, 1) => asynch::timg1_timer1_handler, + _ => unreachable!(), + } + } + + fn peripheral_interrupt(&self) -> Interrupt { + match (self.timer_group(), self.timer_number()) { (0, 0) => Interrupt::TG0_T0_LEVEL, #[cfg(timg_timer1)] (0, 1) => Interrupt::TG0_T1_LEVEL, @@ -325,21 +346,13 @@ impl super::Timer for Timer { #[cfg(all(timg_timer1, timg1))] (1, 1) => Interrupt::TG1_T1_LEVEL, _ => unreachable!(), - }; - - for core in crate::Cpu::other() { - crate::interrupt::disable(core, interrupt); } - unsafe { interrupt::bind_interrupt(interrupt, handler.handler()) }; - unwrap!(interrupt::enable(interrupt, handler.priority())); - } - - fn is_interrupt_set(&self) -> bool { - self.is_interrupt_set() } +} - fn set_alarm_active(&self, state: bool) { - self.set_alarm_active(state) +impl InterruptConfigurable for Timer { + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + self.set_interrupt_handler(handler) } } @@ -354,14 +367,9 @@ impl Peripheral for Timer { /// A timer within a Timer Group. pub struct Timer { - /// Pointer to the register block for this TimerGroup instance. - pub register_block: *const RegisterBlock, - - /// The timer number inside the TimerGroup - pub timer: u8, - - /// The TimerGroup number - pub tg: u8, + register_block: *const RegisterBlock, + timer: u8, + tg: u8, } impl Sealed for Timer {} @@ -369,6 +377,25 @@ unsafe impl Send for Timer {} /// Timer peripheral instance impl Timer { + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + let interrupt = match (self.timer_group(), self.timer_number()) { + (0, 0) => Interrupt::TG0_T0_LEVEL, + #[cfg(timg_timer1)] + (0, 1) => Interrupt::TG0_T1_LEVEL, + #[cfg(timg1)] + (1, 0) => Interrupt::TG1_T0_LEVEL, + #[cfg(all(timg_timer1, timg1))] + (1, 1) => Interrupt::TG1_T1_LEVEL, + _ => unreachable!(), + }; + + for core in crate::Cpu::other() { + crate::interrupt::disable(core, interrupt); + } + unsafe { interrupt::bind_interrupt(interrupt, handler.handler()) }; + unwrap!(interrupt::enable(interrupt, handler.priority())); + } + fn register_block(&self) -> &RegisterBlock { unsafe { &*self.register_block } } @@ -419,7 +446,15 @@ impl Timer { } fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> { - let ticks = timeout_to_ticks(value, Clocks::get().apb_clock, self.divider()); + cfg_if::cfg_if! { + if #[cfg(esp32h2)] { + // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK + let clk_src = Clocks::get().pll_48m_clock; + } else { + let clk_src = Clocks::get().apb_clock; + } + } + let ticks = timeout_to_ticks(value, clk_src, self.divider()); // The counter is 54-bits wide, so we must ensure that the provided // value is not too wide: @@ -442,6 +477,8 @@ impl Timer { self.register_block() .int_clr() .write(|w| w.t(self.timer).clear_bit_by_one()); + let periodic = self.t().config().read().autoreload().bit_is_set(); + self.set_alarm_active(periodic); } fn now(&self) -> Instant { @@ -456,7 +493,15 @@ impl Timer { let value_hi = t.hi().read().bits() as u64; let ticks = (value_hi << 32) | value_lo; - let micros = ticks_to_timeout(ticks, Clocks::get().apb_clock, self.divider()); + cfg_if::cfg_if! { + if #[cfg(esp32h2)] { + // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK + let clk_src = Clocks::get().pll_48m_clock; + } else { + let clk_src = Clocks::get().apb_clock; + } + } + let micros = ticks_to_timeout(ticks, clk_src, self.divider()); Instant::::from_ticks(micros) } @@ -747,7 +792,6 @@ where // Async functionality of the timer groups. mod asynch { - #![allow(unused)] // FIXME(mabez) use core::{ pin::Pin, task::{Context, Poll}, diff --git a/esp-hal/src/touch.rs b/esp-hal/src/touch.rs index 3ea39ebb14c..be26e24c52e 100644 --- a/esp-hal/src/touch.rs +++ b/esp-hal/src/touch.rs @@ -29,9 +29,9 @@ use core::marker::PhantomData; use crate::{ gpio::TouchPin, - interrupt::InterruptConfigurable, peripheral::{Peripheral, PeripheralRef}, peripherals::{RTC_CNTL, SENS, TOUCH}, + prelude::*, private::{Internal, Sealed}, rtc_cntl::Rtc, Async, diff --git a/esp-wifi/src/lib.rs b/esp-wifi/src/lib.rs index a7c89021064..924799eac92 100644 --- a/esp-wifi/src/lib.rs +++ b/esp-wifi/src/lib.rs @@ -99,6 +99,7 @@ use hal::{ rng::{Rng, Trng}, system::RadioClockController, timer::{timg::Timer as TimgTimer, AnyTimer, PeriodicTimer}, + Blocking, }; use portable_atomic::Ordering; @@ -216,7 +217,7 @@ const _: () = { core::assert!(CONFIG.rx_ba_win < (CONFIG.static_rx_buf_num * 2), "WiFi configuration check: rx_ba_win should not be larger than double of the static_rx_buf_num!"); }; -type TimeBase = PeriodicTimer<'static, AnyTimer>; +type TimeBase = PeriodicTimer<'static, Blocking, AnyTimer>; pub(crate) mod flags { use portable_atomic::{AtomicBool, AtomicUsize}; diff --git a/examples/src/bin/etm_timer.rs b/examples/src/bin/etm_timer.rs new file mode 100644 index 00000000000..a5dedabd581 --- /dev/null +++ b/examples/src/bin/etm_timer.rs @@ -0,0 +1,62 @@ +//! Control LED by the boot button via ETM without involving the CPU. + +//! The following wiring is assumed: +//! - LED => GPIO2 + +//% CHIPS: esp32c6 esp32h2 + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + etm::Etm, + gpio::{ + etm::{Channels, OutputConfig}, + Level, + Output, + Pull, + }, + prelude::*, + timer::{ + systimer::{etm::Event, SystemTimer}, + PeriodicTimer, + }, +}; + +#[entry] +fn main() -> ! { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let mut led = Output::new(peripherals.GPIO2, Level::Low); + led.set_high(); + + let syst = SystemTimer::new(peripherals.SYSTIMER); + let alarm = syst.alarm0; + + let timer_event = Event::new(&alarm); + + // setup ETM + let gpio_ext = Channels::new(peripherals.GPIO_SD); + let led_task = gpio_ext.channel0_task.toggle( + led, + OutputConfig { + open_drain: false, + pull: Pull::None, + initial_state: Level::Low, + }, + ); + + let etm = Etm::new(peripherals.SOC_ETM); + let channel0 = etm.channel0; + + // make sure the configured channel doesn't get dropped - dropping it will + // disable the channel + let _configured_channel = channel0.setup(&timer_event, &led_task); + + let mut timer = PeriodicTimer::new(alarm); + timer.start(1u64.secs()).unwrap(); + + // the LED is controlled by the button without involving the CPU + loop {} +} diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index a51aafa2322..e033ac3f353 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -31,6 +31,10 @@ harness = false name = "delay" harness = false +[[test]] +name = "delay_async" +harness = false + [[test]] name = "dma_macros" harness = false @@ -160,6 +164,11 @@ harness = false name = "uart_tx_rx_async" harness = false +[[test]] +name = "embassy_timers_executors" +harness = false +required-features = ["embassy"] + [[test]] name = "embassy_interrupt_executor" harness = false @@ -170,6 +179,10 @@ name = "embassy_interrupt_spi_dma" harness = false required-features = ["embassy"] +[[test]] +name = "systimer" +harness = false + [[test]] name = "twai" harness = false diff --git a/hil-test/tests/delay_async.rs b/hil-test/tests/delay_async.rs new file mode 100644 index 00000000000..130a679711a --- /dev/null +++ b/hil-test/tests/delay_async.rs @@ -0,0 +1,172 @@ +//! Async Delay Test +//! +//! Specifically tests the various implementations of the +//! `embedded_hal_async::delay::DelayNs` trait. + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use embedded_hal_async::delay::DelayNs; +#[cfg(systimer)] +use esp_hal::timer::systimer::SystemTimer; +use esp_hal::{ + peripherals::Peripherals, + timer::{timg::TimerGroup, OneShotTimer}, +}; +use hil_test as _; + +struct Context { + peripherals: Peripherals, +} + +async fn test_async_delay_ns(mut timer: impl DelayNs, duration: u32) { + for i in 1..5 { + let t1 = esp_hal::time::now(); + timer.delay_ns(duration).await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1); + assert!( + (t2 - t1).to_nanos() >= duration as u64, + "diff[{}]: {:?} >= {}", + i, + (t2 - t1).to_nanos(), + duration + ); + } +} + +async fn test_async_delay_us(mut timer: impl DelayNs, duration: u32) { + for _ in 1..5 { + let t1 = esp_hal::time::now(); + timer.delay_us(duration).await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1); + assert!( + (t2 - t1).to_nanos() >= duration as u64, + "diff: {:?}", + (t2 - t1).to_nanos() + ); + } +} + +async fn test_async_delay_ms(mut timer: impl DelayNs, duration: u32) { + for _ in 1..5 { + let t1 = esp_hal::time::now(); + timer.delay_ms(duration).await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1); + assert!( + (t2 - t1).to_nanos() >= duration as u64, + "diff: {:?}", + (t2 - t1).to_nanos() + ); + } +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + use super::*; + + #[init] + fn init() -> Context { + Context { + peripherals: esp_hal::init(esp_hal::Config::default()), + } + } + + #[cfg(systimer)] + #[test] + #[timeout(2)] + async fn test_systimer_async_delay_ns(ctx: Context) { + let alarms = SystemTimer::new(ctx.peripherals.SYSTIMER); + + test_async_delay_ns(OneShotTimer::new(alarms.alarm0).into_async(), 10_000_000).await; + } + + #[test] + #[timeout(2)] + async fn test_timg0_async_delay_ns(ctx: Context) { + let timg0 = TimerGroup::new(ctx.peripherals.TIMG0); + + test_async_delay_ns(OneShotTimer::new(timg0.timer0).into_async(), 10_000_000).await; + #[cfg(timg_timer1)] + test_async_delay_ns(OneShotTimer::new(timg0.timer1).into_async(), 10_000_000).await; + } + + #[cfg(timg1)] + #[test] + #[timeout(2)] + async fn test_timg1_async_delay_ns(ctx: Context) { + let timg1 = TimerGroup::new(ctx.peripherals.TIMG1); + + test_async_delay_ns(OneShotTimer::new(timg1.timer0).into_async(), 10_000_000).await; + #[cfg(timg_timer1)] + test_async_delay_ns(OneShotTimer::new(timg1.timer1).into_async(), 10_000_000).await; + } + + #[cfg(systimer)] + #[test] + #[timeout(2)] + async fn test_systimer_async_delay_us(ctx: Context) { + let alarms = SystemTimer::new(ctx.peripherals.SYSTIMER); + + test_async_delay_us(OneShotTimer::new(alarms.alarm0).into_async(), 10_000).await; + } + + #[test] + #[timeout(2)] + async fn test_timg0_async_delay_us(ctx: Context) { + let timg0 = TimerGroup::new(ctx.peripherals.TIMG0); + + test_async_delay_us(OneShotTimer::new(timg0.timer0).into_async(), 10_000).await; + #[cfg(timg_timer1)] + test_async_delay_us(OneShotTimer::new(timg0.timer1).into_async(), 10_000).await; + } + + #[cfg(timg1)] + #[test] + #[timeout(2)] + async fn test_timg1_async_delay_us(ctx: Context) { + let timg1 = TimerGroup::new(ctx.peripherals.TIMG1); + + test_async_delay_us(OneShotTimer::new(timg1.timer0).into_async(), 10_000).await; + #[cfg(timg_timer1)] + test_async_delay_us(OneShotTimer::new(timg1.timer1).into_async(), 10_000).await; + } + + #[cfg(systimer)] + #[test] + #[timeout(2)] + async fn test_systimer_async_delay_ms(ctx: Context) { + let alarms = SystemTimer::new(ctx.peripherals.SYSTIMER); + + test_async_delay_ms(OneShotTimer::new(alarms.alarm0).into_async(), 10).await; + } + + #[test] + #[timeout(2)] + async fn test_timg0_async_delay_ms(ctx: Context) { + let timg0 = TimerGroup::new(ctx.peripherals.TIMG0); + + test_async_delay_ms(OneShotTimer::new(timg0.timer0).into_async(), 10).await; + #[cfg(timg_timer1)] + test_async_delay_ms(OneShotTimer::new(timg0.timer1).into_async(), 10).await; + } + + #[cfg(timg1)] + #[test] + #[timeout(2)] + async fn test_timg1_async_delay_ms(ctx: Context) { + let timg1 = TimerGroup::new(ctx.peripherals.TIMG1); + + test_async_delay_ms(OneShotTimer::new(timg1.timer0).into_async(), 10).await; + #[cfg(timg_timer1)] + test_async_delay_ms(OneShotTimer::new(timg1.timer1).into_async(), 10).await; + } +} diff --git a/hil-test/tests/embassy_timers_executors.rs b/hil-test/tests/embassy_timers_executors.rs new file mode 100644 index 00000000000..a8327acbb55 --- /dev/null +++ b/hil-test/tests/embassy_timers_executors.rs @@ -0,0 +1,286 @@ +//! Embassy timer and executor Test + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: integrated-timers +//% FEATURES: generic-queue + +#![no_std] +#![no_main] + +use embassy_time::{Duration, Ticker, Timer}; +#[cfg(not(feature = "esp32"))] +use esp_hal::{ + interrupt::software::SoftwareInterruptControl, + interrupt::Priority, + timer::systimer::SystemTimer, + timer::AnyTimer, +}; +use esp_hal::{ + peripherals::Peripherals, + prelude::*, + timer::{timg::TimerGroup, OneShotTimer, PeriodicTimer}, +}; +#[cfg(not(feature = "esp32"))] +use esp_hal_embassy::InterruptExecutor; +use hil_test as _; + +#[cfg(not(feature = "esp32"))] +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} + +// List of the functions that are ACTUALLY TESTS but are called in the invokers +mod test_helpers { + use super::*; + + #[embassy_executor::task] + pub async fn e_task30ms() { + Timer::after_millis(30).await; + } +} + +mod test_cases { + use esp_hal::peripheral::Peripheral; + + use super::*; + + pub async fn run_test_one_shot_async() { + let t1 = esp_hal::time::now(); + Timer::after_millis(50).await; + Timer::after_millis(30).await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 80u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); + } + + pub fn run_test_periodic_timer(timer: impl Peripheral

) { + let mut periodic = PeriodicTimer::new_typed(timer); + + let t1 = esp_hal::time::now(); + periodic.start(100.millis()).unwrap(); + + nb::block!(periodic.wait()).unwrap(); + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 100u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); + } + + pub fn run_test_oneshot_timer(timer: impl Peripheral

) { + let mut timer = OneShotTimer::new_typed(timer); + + let t1 = esp_hal::time::now(); + timer.delay_millis(50); + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 50u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); + } + + pub async fn run_join_test() { + let t1 = esp_hal::time::now(); + embassy_futures::join::join(Timer::after_millis(50), Timer::after_millis(30)).await; + Timer::after_millis(50).await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_millis() >= 100u64, + "diff: {:?}", + (t2 - t1).to_millis() + ); + } +} + +fn set_up_embassy_with_timg0(peripherals: Peripherals) { + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); +} + +#[cfg(not(feature = "esp32"))] +fn set_up_embassy_with_systimer(peripherals: Peripherals) { + let systimer = SystemTimer::new(peripherals.SYSTIMER); + esp_hal_embassy::init(systimer.alarm0); +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod test { + use super::*; + use crate::test_cases::*; + #[cfg(not(feature = "esp32"))] + use crate::test_helpers::*; + + #[init] + fn init() -> Peripherals { + esp_hal::init(esp_hal::Config::default()) + } + + #[test] + #[timeout(3)] + async fn test_one_shot_timg(peripherals: Peripherals) { + set_up_embassy_with_timg0(peripherals); + + run_test_one_shot_async().await; + } + + #[test] + #[timeout(3)] + #[cfg(not(feature = "esp32"))] + async fn test_one_shot_systimer(peripherals: Peripherals) { + set_up_embassy_with_systimer(peripherals); + + run_test_one_shot_async().await; + } + + #[test] + #[timeout(3)] + fn test_periodic_timg(peripherals: Peripherals) { + let timg0 = TimerGroup::new(peripherals.TIMG0); + + run_test_periodic_timer(timg0.timer0); + } + + #[test] + #[timeout(3)] + #[cfg(not(feature = "esp32"))] + fn test_periodic_systimer(peripherals: Peripherals) { + let systimer = SystemTimer::new(peripherals.SYSTIMER); + + run_test_periodic_timer(systimer.alarm0); + } + + #[test] + #[timeout(3)] + fn test_periodic_oneshot_timg(peripherals: Peripherals) { + let mut timg0 = TimerGroup::new(peripherals.TIMG0); + run_test_periodic_timer(&mut timg0.timer0); + run_test_oneshot_timer(&mut timg0.timer0); + } + + #[test] + #[timeout(3)] + #[cfg(not(feature = "esp32"))] + fn test_periodic_oneshot_systimer(peripherals: Peripherals) { + let mut systimer = SystemTimer::new(peripherals.SYSTIMER); + run_test_periodic_timer(&mut systimer.alarm0); + run_test_oneshot_timer(&mut systimer.alarm0); + } + + #[test] + #[timeout(3)] + async fn test_join_timg(peripherals: Peripherals) { + set_up_embassy_with_timg0(peripherals); + + run_join_test().await; + } + + #[test] + #[timeout(3)] + #[cfg(not(feature = "esp32"))] + async fn test_join_systimer(peripherals: Peripherals) { + set_up_embassy_with_systimer(peripherals); + + run_join_test().await; + } + + /// Test that the ticker works in tasks ran by the interrupt executors. + #[test] + #[timeout(3)] + #[cfg(not(feature = "esp32"))] + async fn test_interrupt_executor(peripherals: Peripherals) { + let timg0 = TimerGroup::new(peripherals.TIMG0); + let timer0: AnyTimer = timg0.timer0.into(); + + let systimer = SystemTimer::new(peripherals.SYSTIMER); + let alarm0: AnyTimer = systimer.alarm0.into(); + + esp_hal_embassy::init([timer0, alarm0]); + + let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + + let executor = mk_static!( + InterruptExecutor<2>, + InterruptExecutor::new(sw_ints.software_interrupt2) + ); + + #[embassy_executor::task] + #[cfg(not(feature = "esp32"))] + async fn test_interrupt_executor_invoker() { + let outcome = async { + let mut ticker = Ticker::every(Duration::from_millis(30)); + + let t1 = esp_hal::time::now(); + ticker.next().await; + ticker.next().await; + ticker.next().await; + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + assert!( + (t2 - t1).to_micros() >= 85000u64, + "diff: {:?}", + (t2 - t1).to_micros() + ); + }; + + embedded_test::export::check_outcome(outcome.await); + } + + let spawner_int = executor.start(Priority::Priority3); + spawner_int.must_spawn(test_interrupt_executor_invoker()); + + let spawner = embassy_executor::Spawner::for_current_executor().await; + spawner.must_spawn(e_task30ms()); + + // The test ends once the interrupt executor's task has finished + loop {} + } + + /// Test that timg0 and systimer don't have vastly different tick rates. + #[test] + #[timeout(3)] + async fn tick_test_timer_tick_rates(peripherals: Peripherals) { + set_up_embassy_with_timg0(peripherals); + + // We are retrying 5 times because probe-rs polling RTT may introduce some + // jitter. + for _ in 0..5 { + let t1 = esp_hal::time::now(); + + let mut ticker = Ticker::every(Duration::from_hz(100_000)); + for _ in 0..2000 { + ticker.next().await; + } + let t2 = esp_hal::time::now(); + + assert!(t2 > t1, "t2: {:?}, t1: {:?}", t2, t1); + let duration = (t2 - t1).to_micros(); + + assert!(duration >= 19000, "diff: {:?}", (t2 - t1).to_micros()); + if duration <= 21000 { + return; + } + } + + assert!(false, "Test failed after 5 retries"); + } +} diff --git a/hil-test/tests/systimer.rs b/hil-test/tests/systimer.rs new file mode 100644 index 00000000000..112e4393c19 --- /dev/null +++ b/hil-test/tests/systimer.rs @@ -0,0 +1,165 @@ +//! System Timer Test + +// esp32 disabled as it does not have a systimer +//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use embedded_hal::delay::DelayNs; +use esp_hal::{ + delay::Delay, + prelude::*, + timer::{ + systimer::{Alarm, SystemTimer}, + OneShotTimer, + PeriodicTimer, + }, + Blocking, +}; +use hil_test as _; +use portable_atomic::{AtomicUsize, Ordering}; + +static ALARM_TARGET: Mutex>>> = + Mutex::new(RefCell::new(None)); +static ALARM_PERIODIC: Mutex>>> = + Mutex::new(RefCell::new(None)); + +struct Context { + alarm0: Alarm, + alarm1: Alarm, +} + +#[handler(priority = esp_hal::interrupt::Priority::min())] +fn pass_test_if_called() { + critical_section::with(|cs| { + ALARM_TARGET + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); + embedded_test::export::check_outcome(()); +} + +#[handler(priority = esp_hal::interrupt::Priority::min())] +fn handle_periodic_interrupt() { + critical_section::with(|cs| { + ALARM_PERIODIC + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); +} + +static COUNTER: AtomicUsize = AtomicUsize::new(0); + +#[handler(priority = esp_hal::interrupt::Priority::min())] +fn pass_test_if_called_twice() { + critical_section::with(|cs| { + ALARM_PERIODIC + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); + COUNTER.fetch_add(1, Ordering::Relaxed); + if COUNTER.load(Ordering::Relaxed) == 2 { + embedded_test::export::check_outcome(()); + } +} + +#[handler(priority = esp_hal::interrupt::Priority::min())] +fn target_fail_test_if_called_twice() { + critical_section::with(|cs| { + ALARM_TARGET + .borrow_ref_mut(cs) + .as_mut() + .unwrap() + .clear_interrupt() + }); + COUNTER.fetch_add(1, Ordering::Relaxed); + assert!(COUNTER.load(Ordering::Relaxed) != 2); +} + +#[cfg(test)] +#[embedded_test::tests] +mod tests { + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let systimer = SystemTimer::new(peripherals.SYSTIMER); + + Context { + alarm0: systimer.alarm0, + alarm1: systimer.alarm1, + } + } + + #[test] + #[timeout(3)] + fn target_interrupt_is_handled(ctx: Context) { + let mut alarm0 = OneShotTimer::new(ctx.alarm0); + + critical_section::with(|cs| { + alarm0.set_interrupt_handler(pass_test_if_called); + alarm0.enable_interrupt(true); + alarm0.schedule(10_u64.millis()).unwrap(); + + ALARM_TARGET.borrow_ref_mut(cs).replace(alarm0); + }); + + // We'll end the test in the interrupt handler. + loop {} + } + + #[test] + #[timeout(3)] + fn target_interrupt_is_handled_once(ctx: Context) { + let mut alarm0 = OneShotTimer::new(ctx.alarm0); + let mut alarm1 = PeriodicTimer::new(ctx.alarm1); + + COUNTER.store(0, Ordering::Relaxed); + + critical_section::with(|cs| { + alarm0.set_interrupt_handler(target_fail_test_if_called_twice); + alarm0.enable_interrupt(true); + alarm0.schedule(10_u64.millis()).unwrap(); + + alarm1.set_interrupt_handler(handle_periodic_interrupt); + alarm1.enable_interrupt(true); + alarm1.start(100u64.millis()).unwrap(); + + ALARM_TARGET.borrow_ref_mut(cs).replace(alarm0); + ALARM_PERIODIC.borrow_ref_mut(cs).replace(alarm1); + }); + + let mut delay = Delay::new(); + delay.delay_ms(300); + } + + #[test] + #[timeout(3)] + fn periodic_interrupt_is_handled(ctx: Context) { + let mut alarm1 = PeriodicTimer::new(ctx.alarm1); + + COUNTER.store(0, Ordering::Relaxed); + + critical_section::with(|cs| { + alarm1.set_interrupt_handler(pass_test_if_called_twice); + alarm1.enable_interrupt(true); + alarm1.start(100u64.millis()).unwrap(); + + ALARM_PERIODIC.borrow_ref_mut(cs).replace(alarm1); + }); + + // We'll end the test in the interrupt handler. + loop {} + } +} From cfb83b153dfeb7ca44b622a50be3131bbd213676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Wed, 27 Nov 2024 15:14:04 +0100 Subject: [PATCH 18/27] Fixed triggering a debug-assertion during scan (#2612) * Fixed triggering a debug-assertion during scan * CHANGELOGs * Change `debug_assert` into warning level log * Enable debug-asserts in hil-test, qa-test and examples * Change the way we detect and warn about debug-builds * Warn if opt-level is `0` or `1` --- esp-hal/Cargo.toml | 1 + esp-hal/build.rs | 16 +++++++--------- esp-hal/src/lib.rs | 9 +++++++++ esp-metadata/CHANGELOG.md | 1 + esp-metadata/devices/esp32c6.toml | 1 + esp-wifi/CHANGELOG.md | 1 + esp-wifi/src/wifi/event.rs | 29 +++++++++++++++++++++-------- examples/Cargo.toml | 2 +- hil-test/Cargo.toml | 2 +- qa-test/Cargo.toml | 1 + 10 files changed, 44 insertions(+), 19 deletions(-) diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index e1b3770fc2d..68ab0ade890 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -37,6 +37,7 @@ embedded-hal-nb = "1.0.0" embedded-io = "0.6.1" embedded-io-async = "0.6.1" enumset = "1.1.5" +esp-build = { version = "0.1.0", path = "../esp-build" } esp-synopsys-usb-otg = { version = "0.4.2", optional = true, features = ["fs", "esp32sx"] } fugit = "0.3.7" log = { version = "0.4.22", optional = true } diff --git a/esp-hal/build.rs b/esp-hal/build.rs index 9e0f49684c0..8e283f1c474 100644 --- a/esp-hal/build.rs +++ b/esp-hal/build.rs @@ -11,16 +11,14 @@ use esp_build::assert_unique_used_features; use esp_config::{generate_config, Value}; use esp_metadata::{Chip, Config}; -#[cfg(debug_assertions)] -esp_build::warning! {" -WARNING: use --release - We *strongly* recommend using release profile when building esp-hal. - The dev profile can potentially be one or more orders of magnitude - slower than release, and may cause issues with timing-senstive - peripherals and/or devices. -"} - fn main() -> Result<(), Box> { + println!("cargo:rustc-check-cfg=cfg(is_debug_build)"); + if let Ok(level) = std::env::var("OPT_LEVEL") { + if level == "0" || level == "1" { + println!("cargo:rustc-cfg=is_debug_build"); + } + } + // NOTE: update when adding new device support! // Ensure that exactly one chip has been specified: assert_unique_used_features!( diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 8dfb652c50d..aafe8b21ea8 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -261,6 +261,15 @@ pub mod trapframe { // be directly exposed. mod soc; +#[cfg(is_debug_build)] +esp_build::warning! {" +WARNING: use --release + We *strongly* recommend using release profile when building esp-hal. + The dev profile can potentially be one or more orders of magnitude + slower than release, and may cause issues with timing-senstive + peripherals and/or devices. +"} + /// A marker trait for initializing drivers in a specific mode. pub trait Mode: crate::private::Sealed {} diff --git a/esp-metadata/CHANGELOG.md b/esp-metadata/CHANGELOG.md index ea6930f68c0..d362ae1a250 100644 --- a/esp-metadata/CHANGELOG.md +++ b/esp-metadata/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Introduced the `wifi6` symbol (#2612) ### Fixed diff --git a/esp-metadata/devices/esp32c6.toml b/esp-metadata/devices/esp32c6.toml index 128e8a06c24..6b59d5e02ea 100644 --- a/esp-metadata/devices/esp32c6.toml +++ b/esp-metadata/devices/esp32c6.toml @@ -80,6 +80,7 @@ symbols = [ "phy", "bt", "wifi", + "wifi6", "ieee802154", "lp_core", diff --git a/esp-wifi/CHANGELOG.md b/esp-wifi/CHANGELOG.md index 0ffc9b157b0..5f9b7ef3457 100644 --- a/esp-wifi/CHANGELOG.md +++ b/esp-wifi/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed ### Fixed +- Fixed triggering a debug-assertion during scan (#2612) ### Removed diff --git a/esp-wifi/src/wifi/event.rs b/esp-wifi/src/wifi/event.rs index e4925d921b4..ac99767460c 100644 --- a/esp-wifi/src/wifi/event.rs +++ b/esp-wifi/src/wifi/event.rs @@ -104,7 +104,7 @@ macro_rules! impl_wifi_event { } impl_wifi_event!(WifiReady); -impl_wifi_event!(ScanDone); +impl_wifi_event!(ScanDone, wifi_event_sta_scan_done_t); impl_wifi_event!(StaStart); impl_wifi_event!(StaStop); impl_wifi_event!(StaConnected, wifi_event_sta_connected_t); @@ -131,13 +131,25 @@ impl_wifi_event!(ApWpsRgFailed, wifi_event_ap_wps_rg_fail_reason_t); impl_wifi_event!(ApWpsRgTimeout); impl_wifi_event!(ApWpsRgPin, wifi_event_ap_wps_rg_pin_t); impl_wifi_event!(ApWpsRgPbcOverlap); -impl_wifi_event!(ItwtSetup); -impl_wifi_event!(ItwtTeardown); -impl_wifi_event!(ItwtProbe); -impl_wifi_event!(ItwtSuspend); -impl_wifi_event!(TwtWakeup); -impl_wifi_event!(BtwtSetup); -impl_wifi_event!(BtwtTeardown); +cfg_if::cfg_if! { + if #[cfg(wifi6)] { + impl_wifi_event!(ItwtSetup, wifi_event_sta_itwt_setup_t); + impl_wifi_event!(ItwtTeardown, wifi_event_sta_itwt_teardown_t); + impl_wifi_event!(ItwtProbe, wifi_event_sta_itwt_probe_t); + impl_wifi_event!(ItwtSuspend, wifi_event_sta_itwt_suspend_t); + impl_wifi_event!(TwtWakeup); + impl_wifi_event!(BtwtSetup, wifi_event_sta_btwt_setup_t); + impl_wifi_event!(BtwtTeardown, wifi_event_sta_btwt_teardown_t); + } else { + impl_wifi_event!(ItwtSetup); + impl_wifi_event!(ItwtTeardown); + impl_wifi_event!(ItwtProbe); + impl_wifi_event!(ItwtSuspend); + impl_wifi_event!(TwtWakeup); + impl_wifi_event!(BtwtSetup); + impl_wifi_event!(BtwtTeardown); + } +} impl_wifi_event!(NanStarted); impl_wifi_event!(NanStopped); impl_wifi_event!(NanSvcMatch, wifi_event_nan_svc_match_t); @@ -173,6 +185,7 @@ pub(crate) unsafe fn handle_raw( core::mem::size_of::(), "wrong size event data" ); + handle::(unsafe { &Event::from_raw_event_data(event_data) }) } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 5972c6a3b8b..50faa946801 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -65,7 +65,7 @@ embassy-generic-timers = ["embassy-time/generic-queue-8"] [profile.release] codegen-units = 1 debug = 2 -debug-assertions = false +debug-assertions = true incremental = false opt-level = 3 lto = 'fat' diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index e033ac3f353..cc273714e98 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -311,7 +311,7 @@ overflow-checks = true [profile.release] codegen-units = 1 debug = 2 -debug-assertions = false +debug-assertions = true incremental = false opt-level = 3 lto = false # LTO (thin or fat) miscompiles some tests on RISC-V diff --git a/qa-test/Cargo.toml b/qa-test/Cargo.toml index ea0f64d239c..d7760ce52e9 100644 --- a/qa-test/Cargo.toml +++ b/qa-test/Cargo.toml @@ -32,5 +32,6 @@ embassy-generic-timers = ["embassy-time/generic-queue-8"] [profile.release] debug = 2 +debug-assertions = true lto = "fat" codegen-units = 1 From aeda6ac00afcdaf3c628c3002e827ff30520ba4c Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov <62840029+playfulFence@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:54:00 +0100 Subject: [PATCH 19/27] Remove `embedded-hal 0.2.x` impls and dependency from `esp-lp-hal` package (#2609) * removed eh02 dependencies, sh*tcode, not yet tested properly,copy-pasted * changelog entry * Don't implement eh1 traits --- esp-lp-hal/CHANGELOG.md | 1 + esp-lp-hal/Cargo.toml | 9 +++---- esp-lp-hal/README.md | 2 +- esp-lp-hal/examples/blinky.rs | 4 +-- esp-lp-hal/examples/i2c.rs | 2 -- esp-lp-hal/examples/uart.rs | 3 +-- esp-lp-hal/src/delay.rs | 24 ----------------- esp-lp-hal/src/gpio.rs | 42 ----------------------------- esp-lp-hal/src/i2c.rs | 51 +++++++---------------------------- esp-lp-hal/src/uart.rs | 22 --------------- 10 files changed, 16 insertions(+), 144 deletions(-) diff --git a/esp-lp-hal/CHANGELOG.md b/esp-lp-hal/CHANGELOG.md index 05ec2a29987..c1a0c6418c6 100644 --- a/esp-lp-hal/CHANGELOG.md +++ b/esp-lp-hal/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed ### Removed +- Remove embedded-hal 0.2.x impls and dependency from esp-lp-hal package (#2609) ## 0.1.0 - 2024-07-15 diff --git a/esp-lp-hal/Cargo.toml b/esp-lp-hal/Cargo.toml index b8397a792e5..9ea3c5e4908 100644 --- a/esp-lp-hal/Cargo.toml +++ b/esp-lp-hal/Cargo.toml @@ -28,7 +28,6 @@ test = false cfg-if = "1.0.0" document-features = "0.2.10" embedded-hal = { version = "1.0.0", optional = true } -embedded-hal-02 = { version = "0.2.7", optional = true, features = ["unproven"], package = "embedded-hal" } embedded-hal-nb = { version = "1.0.0", optional = true } embedded-io = { version = "0.6.1", optional = true } esp32c6-lp = { version = "0.3.0", features = ["critical-section"], optional = true } @@ -63,8 +62,6 @@ esp32s2 = ["dep:esp32s2-ulp", "procmacros/is-ulp-core"] esp32s3 = ["dep:esp32s3-ulp", "procmacros/is-ulp-core"] #! ### Trait Implementation Feature Flags -## Implement the traits defined in the `0.2.x` release of `embedded-hal`. -embedded-hal-02 = ["dep:embedded-hal-02"] ## Implement the traits defined in the `1.0.0` releases of `embedded-hal` and ## `embedded-hal-nb` for the relevant peripherals. embedded-hal = ["dep:embedded-hal", "dep:embedded-hal-nb"] @@ -73,15 +70,15 @@ embedded-io = ["dep:embedded-io"] [[example]] name = "blinky" -required-features = ["embedded-hal-02"] +required-features = [] [[example]] name = "i2c" -required-features = ["embedded-hal-02", "esp32c6"] +required-features = ["esp32c6"] [[example]] name = "uart" -required-features = ["embedded-hal-02", "esp32c6"] +required-features = ["esp32c6"] [lints.rust] unexpected_cfgs = "allow" diff --git a/esp-lp-hal/README.md b/esp-lp-hal/README.md index e3057e34d76..820be8aac9a 100644 --- a/esp-lp-hal/README.md +++ b/esp-lp-hal/README.md @@ -12,7 +12,7 @@ Implements a number of blocking and, where applicable, async traits from the var For help getting started with this HAL, please refer to [The Rust on ESP Book] and the [documentation]. -[embedded-hal]: https://github.com/rust-embedded/embedded-hal +[embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/ [the rust on esp book]: https://docs.esp-rs.org/book/ ## [Documentation] diff --git a/esp-lp-hal/examples/blinky.rs b/esp-lp-hal/examples/blinky.rs index e4334ed6fbd..dfd12d10947 100644 --- a/esp-lp-hal/examples/blinky.rs +++ b/esp-lp-hal/examples/blinky.rs @@ -7,12 +7,10 @@ //! //! Make sure the LP RAM is cleared before loading the code. -//% FEATURES: embedded-hal-02 - #![no_std] #![no_main] -use embedded_hal_02::{blocking::delay::DelayMs, digital::v2::OutputPin}; +use embedded_hal::{delay::DelayNs, digital::OutputPin}; use esp_lp_hal::{delay::Delay, gpio::Output, prelude::*}; use panic_halt as _; diff --git a/esp-lp-hal/examples/i2c.rs b/esp-lp-hal/examples/i2c.rs index 25cf1f8a80e..65e854d8bd5 100644 --- a/esp-lp-hal/examples/i2c.rs +++ b/esp-lp-hal/examples/i2c.rs @@ -8,12 +8,10 @@ //! - SCL => GPIO7 //% CHIPS: esp32c6 -//% FEATURES: embedded-hal-02 #![no_std] #![no_main] -use embedded_hal_02::blocking::i2c::WriteRead; use esp_lp_hal::{i2c::LpI2c, prelude::*}; use panic_halt as _; diff --git a/esp-lp-hal/examples/uart.rs b/esp-lp-hal/examples/uart.rs index 293250ef28a..bce4da8817b 100644 --- a/esp-lp-hal/examples/uart.rs +++ b/esp-lp-hal/examples/uart.rs @@ -6,14 +6,13 @@ //! logs from LP_UART. Make sure the LP RAM is cleared before loading the code. //% CHIPS: esp32c6 -//% FEATURES: embedded-hal-02 #![no_std] #![no_main] use core::fmt::Write; -use embedded_hal_02::blocking::delay::DelayMs; +use embedded_hal::delay::DelayNs; use esp_lp_hal::{delay::Delay, prelude::*, uart::LpUart}; use panic_halt as _; diff --git a/esp-lp-hal/src/delay.rs b/esp-lp-hal/src/delay.rs index cdfbe07c808..2a0e9460895 100644 --- a/esp-lp-hal/src/delay.rs +++ b/esp-lp-hal/src/delay.rs @@ -74,30 +74,6 @@ fn cycles() -> u64 { cycles as u64 } -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::delay::DelayUs for Delay { - #[inline(always)] - fn delay_us(&mut self, us: u64) { - self.delay_micros(us as u32); - } -} - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::delay::DelayUs for Delay { - #[inline(always)] - fn delay_us(&mut self, us: u32) { - self.delay_micros(us); - } -} - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::delay::DelayMs for Delay { - #[inline(always)] - fn delay_ms(&mut self, ms: u32) { - self.delay_millis(ms); - } -} - #[cfg(feature = "embedded-hal")] impl embedded_hal::delay::DelayNs for Delay { #[inline(always)] diff --git a/esp-lp-hal/src/gpio.rs b/esp-lp-hal/src/gpio.rs index e50c418199a..ca87a57c5ad 100644 --- a/esp-lp-hal/src/gpio.rs +++ b/esp-lp-hal/src/gpio.rs @@ -86,48 +86,6 @@ pub unsafe fn conjure_input() -> Option> { } } -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::digital::v2::InputPin for Input { - type Error = core::convert::Infallible; - - fn is_high(&self) -> Result { - Ok(self.input_state()) - } - - fn is_low(&self) -> Result { - Ok(!self.is_high()?) - } -} - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::digital::v2::OutputPin for Output { - type Error = core::convert::Infallible; - - fn set_low(&mut self) -> Result<(), Self::Error> { - self.set_output(false); - Ok(()) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.set_output(true); - Ok(()) - } -} - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::digital::v2::StatefulOutputPin for Output { - fn is_set_high(&self) -> Result { - Ok(self.output_state()) - } - - fn is_set_low(&self) -> Result { - Ok(!self.is_set_high()?) - } -} - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::digital::v2::toggleable::Default for Output {} - #[cfg(feature = "embedded-hal")] impl embedded_hal::digital::ErrorType for Input { type Error = core::convert::Infallible; diff --git a/esp-lp-hal/src/i2c.rs b/esp-lp-hal/src/i2c.rs index ec455bd2a0d..c4a6029dada 100644 --- a/esp-lp-hal/src/i2c.rs +++ b/esp-lp-hal/src/i2c.rs @@ -187,7 +187,8 @@ pub struct LpI2c { } impl LpI2c { - fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { + /// Writes bytes to slave with address `addr` + pub fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { let mut cmd_iterator = CommandRegister::COMD0; // If SCL is busy, reset the Master FSM @@ -272,7 +273,8 @@ impl LpI2c { Ok(()) } - fn master_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { + /// Reads enough bytes from slave with `addr` to fill `buffer` + pub fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> { // Check size constraints if buffer.len() > 254 { return Err(Error::ExceedingFifo); @@ -370,17 +372,14 @@ impl LpI2c { Ok(()) } - fn master_write_read( - &mut self, - addr: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Error> { + /// Writes bytes to slave with address `addr` and then reads enough bytes + /// to fill `buffer` *in a single transaction* + pub fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> { // It would be possible to combine the write and read in one transaction, but // filling the tx fifo with the current code is somewhat slow even in release // mode which can cause issues. - self.master_write(addr, bytes)?; - self.master_read(addr, buffer)?; + self.write(addr, bytes)?; + self.read(addr, buffer)?; Ok(()) } @@ -468,35 +467,3 @@ impl LpI2c { Ok(()) } } - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::Read for LpI2c { - type Error = Error; - - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.master_read(address, buffer) - } -} - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::Write for LpI2c { - type Error = Error; - - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.master_write(addr, bytes) - } -} - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::blocking::i2c::WriteRead for LpI2c { - type Error = Error; - - fn write_read( - &mut self, - address: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - self.master_write_read(address, bytes, buffer) - } -} diff --git a/esp-lp-hal/src/uart.rs b/esp-lp-hal/src/uart.rs index dc8664267fb..db09f0be2a7 100644 --- a/esp-lp-hal/src/uart.rs +++ b/esp-lp-hal/src/uart.rs @@ -231,28 +231,6 @@ impl core::fmt::Write for LpUart { } } -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::serial::Read for LpUart { - type Error = Error; - - fn read(&mut self) -> nb::Result { - self.read_byte() - } -} - -#[cfg(feature = "embedded-hal-02")] -impl embedded_hal_02::serial::Write for LpUart { - type Error = Error; - - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.write_byte(word) - } - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - self.flush_tx() - } -} - #[cfg(feature = "embedded-hal")] impl embedded_hal_nb::serial::ErrorType for LpUart { type Error = Error; From 1a2bee6f1f30b456e433dc74f4a86f371f7c29ad Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Wed, 27 Nov 2024 07:54:43 -0800 Subject: [PATCH 20/27] Add derive macro for `BuilderLite`, add `#[non_exhaustive]` to some enums and structs (#2614) * Add a derive procmacro to implement the Builder Lite pattern for a struct * Add `#[non_exhaustive]` and derive `BuilderLite` where necessary in I2C module * Add `#[non_exhaustive]` and derive `BuilderLite` where necessary in UART module * Add `#[non_exhaustive]` and derive `BuilderLite` where necessary in SPI module * Update `CHANGELOG.md` * Fix build errors in HIL tests * Fix generated doc comments * Return a `ParseError` rather than panicking * Add a method to set the value to `None` for `Option` types --- esp-hal-procmacros/CHANGELOG.md | 2 + esp-hal-procmacros/src/lib.rs | 145 +++++++++++++++++- esp-hal/CHANGELOG.md | 2 + esp-hal/src/dma/mod.rs | 6 +- esp-hal/src/i2c/master/mod.rs | 9 +- esp-hal/src/spi/master.rs | 10 +- esp-hal/src/spi/mod.rs | 1 + esp-hal/src/uart.rs | 46 +++--- examples/src/bin/embassy_spi.rs | 8 +- examples/src/bin/spi_loopback.rs | 8 +- examples/src/bin/spi_loopback_dma_psram.rs | 8 +- hil-test/tests/embassy_interrupt_spi_dma.rs | 24 ++- hil-test/tests/qspi.rs | 8 +- hil-test/tests/spi_full_duplex.rs | 32 ++-- hil-test/tests/spi_half_duplex_read.rs | 8 +- hil-test/tests/spi_half_duplex_write.rs | 8 +- hil-test/tests/spi_half_duplex_write_psram.rs | 8 +- hil-test/tests/uart.rs | 10 +- qa-test/src/bin/lcd_dpi.rs | 8 +- qa-test/src/bin/qspi_flash.rs | 8 +- .../spi_halfduplex_read_manufacturer_id.rs | 8 +- 21 files changed, 235 insertions(+), 132 deletions(-) diff --git a/esp-hal-procmacros/CHANGELOG.md b/esp-hal-procmacros/CHANGELOG.md index d56177b241a..d8793c08a0e 100644 --- a/esp-hal-procmacros/CHANGELOG.md +++ b/esp-hal-procmacros/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added the `BuilderLite` derive macro which implements the Builder Lite pattern for a struct (#2614) + ### Fixed ### Changed diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs index 88936f3cd47..f03571c1d6e 100644 --- a/esp-hal-procmacros/src/lib.rs +++ b/esp-hal-procmacros/src/lib.rs @@ -52,7 +52,22 @@ use proc_macro::{Span, TokenStream}; use proc_macro2::Ident; use proc_macro_crate::{crate_name, FoundCrate}; use proc_macro_error2::abort; -use syn::{parse, parse::Error as ParseError, spanned::Spanned, Item, ItemFn, ReturnType, Type}; +use quote::{format_ident, quote}; +use syn::{ + parse, + parse::Error as ParseError, + spanned::Spanned, + Data, + DataStruct, + GenericArgument, + Item, + ItemFn, + Path, + PathArguments, + PathSegment, + ReturnType, + Type, +}; use self::interrupt::{check_attr_whitelist, WhiteListCaller}; @@ -238,8 +253,8 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { /// esp_hal::interrupt::Priority::Priority2)]`. /// /// If no priority is given, `Priority::min()` is assumed -#[proc_macro_error2::proc_macro_error] #[proc_macro_attribute] +#[proc_macro_error2::proc_macro_error] pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream { #[derive(Debug, FromMeta)] struct MacroArgs { @@ -341,8 +356,8 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { /// Marks the entry function of a LP core / ULP program. #[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))] -#[proc_macro_error2::proc_macro_error] #[proc_macro_attribute] +#[proc_macro_error2::proc_macro_error] pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { lp_core::entry(args, input) } @@ -381,3 +396,127 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { run(&args.meta, f, main()).unwrap_or_else(|x| x).into() } + +/// Automatically implement the [Builder Lite] pattern for a struct. +/// +/// This will create an `impl` which contains methods for each field of a +/// struct, allowing users to easily set the values. The generated methods will +/// be the field name prefixed with `with_`, and calls to these methods can be +/// chained as needed. +/// +/// ## Example +/// +/// ```rust, no_run +/// #[derive(Default)] +/// enum MyEnum { +/// #[default] +/// A, +/// B, +/// } +/// +/// #[derive(Default, BuilderLite)] +/// #[non_exhaustive] +/// struct MyStruct { +/// enum_field: MyEnum, +/// bool_field: bool, +/// option_field: Option, +/// } +/// +/// MyStruct::default() +/// .with_enum_field(MyEnum::B) +/// .with_bool_field(true) +/// .with_option_field(-5); +/// ``` +/// +/// [Builder Lite]: https://matklad.github.io/2022/05/29/builder-lite.html +#[proc_macro_derive(BuilderLite)] +pub fn builder_lite_derive(item: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(item as syn::DeriveInput); + + let span = input.span(); + let ident = input.ident; + + let mut fns = Vec::new(); + if let Data::Struct(DataStruct { fields, .. }) = &input.data { + for field in fields { + let field_ident = field.ident.as_ref().unwrap(); + let field_type = &field.ty; + + let function_ident = format_ident!("with_{}", field_ident); + + let maybe_path_type = extract_type_path(field_type) + .and_then(|path| extract_option_segment(path)) + .and_then(|path_seg| match path_seg.arguments { + PathArguments::AngleBracketed(ref params) => params.args.first(), + _ => None, + }) + .and_then(|generic_arg| match *generic_arg { + GenericArgument::Type(ref ty) => Some(ty), + _ => None, + }); + + let (field_type, field_assigns) = if let Some(inner_type) = maybe_path_type { + (inner_type, quote! { Some(#field_ident) }) + } else { + (field_type, quote! { #field_ident }) + }; + + fns.push(quote! { + #[doc = concat!(" Assign the given value to the `", stringify!(#field_ident) ,"` field.")] + pub fn #function_ident(mut self, #field_ident: #field_type) -> Self { + self.#field_ident = #field_assigns; + self + } + }); + + if maybe_path_type.is_some() { + let function_ident = format_ident!("with_{}_none", field_ident); + fns.push(quote! { + #[doc = concat!(" Set the value of `", stringify!(#field_ident), "` to `None`.")] + pub fn #function_ident(mut self) -> Self { + self.#field_ident = None; + self + } + }); + } + } + } else { + return ParseError::new( + span, + "#[derive(Builder)] is only defined for structs, not for enums or unions!", + ) + .to_compile_error() + .into(); + } + + let implementation = quote! { + #[automatically_derived] + impl #ident { + #(#fns)* + } + }; + + implementation.into() +} + +// https://stackoverflow.com/a/56264023 +fn extract_type_path(ty: &Type) -> Option<&Path> { + match *ty { + Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path), + _ => None, + } +} + +// https://stackoverflow.com/a/56264023 +fn extract_option_segment(path: &Path) -> Option<&PathSegment> { + let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| { + acc.push_str(&v.ident.to_string()); + acc.push('|'); + acc + }); + + vec!["Option|", "std|option|Option|", "core|option|Option|"] + .into_iter() + .find(|s| idents_of_path == *s) + .and_then(|_| path.segments.last()) +} diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 360c1427d71..9f59253395e 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `esp_hal::psram::psram_raw_parts` (#2546) - The timer drivers `OneShotTimer` & `PeriodicTimer` have `into_async` and `new_typed` methods (#2586) - `timer::Timer` trait has three new methods, `wait`, `async_interrupt_handler` and `peripheral_interrupt` (#2586) +- Configuration structs in the I2C, SPI, and UART drivers now implement the Builder Lite pattern (#2614) ### Changed @@ -42,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The timer drivers `OneShotTimer` & `PeriodicTimer` now have a `Mode` parameter and type erase the underlying driver by default (#2586) - `timer::Timer` has new trait requirements of `Into`, `'static` and `InterruptConfigurable` (#2586) - `systimer::etm::Event` no longer borrows the alarm indefinitely (#2586) +- A number of public enums and structs in the I2C, SPI, and UART drivers have been marked with `#[non_exhaustive]` (#2614) ### Fixed diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index f6e520f71b8..29b54747069 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -28,11 +28,7 @@ //! //! let mut spi = Spi::new_with_config( //! peripherals.SPI2, -//! Config { -//! frequency: 100.kHz(), -//! mode: SpiMode::Mode0, -//! ..Config::default() -//! }, +//! Config::default().with_frequency(100.kHz()).with_mode(SpiMode::Mode0) //! ) //! .with_sck(sclk) //! .with_mosi(mosi) diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs index 613da6dbe0b..4a9daed950e 100644 --- a/esp-hal/src/i2c/master/mod.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -86,6 +86,7 @@ const MAX_ITERATIONS: u32 = 1_000_000; /// I2C-specific transmission errors #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum Error { /// The transmission exceeded the FIFO size. ExceedingFifo, @@ -106,14 +107,15 @@ pub enum Error { /// I2C-specific configuration errors #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum ConfigError {} -#[derive(PartialEq)] // This enum is used to keep track of the last/next operation that was/will be // performed in an embedded-hal(-async) I2c::transaction. It is used to // determine whether a START condition should be issued at the start of the // current operation and whether a read needs an ack or a nack for the final // byte. +#[derive(PartialEq)] enum OpKind { Write, Read, @@ -217,6 +219,7 @@ enum Ack { Ack = 0, Nack = 1, } + impl From for Ack { fn from(ack: u32) -> Self { match ack { @@ -226,6 +229,7 @@ impl From for Ack { } } } + impl From for u32 { fn from(ack: Ack) -> u32 { ack as u32 @@ -233,8 +237,9 @@ impl From for u32 { } /// I2C driver configuration -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub struct Config { /// The I2C clock frequency. pub frequency: HertzU32, diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index 41084201e42..4c5b943fc17 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -45,11 +45,7 @@ //! //! let mut spi = Spi::new_with_config( //! peripherals.SPI2, -//! Config { -//! frequency: 100.kHz(), -//! mode: SpiMode::Mode0, -//! ..Config::default() -//! }, +//! Config::default().with_frequency(100.kHz()).with_mode(SpiMode::Mode0) //! ) //! .with_sck(sclk) //! .with_mosi(mosi) @@ -93,6 +89,7 @@ use crate::{ #[cfg(gdma)] #[derive(Debug, EnumSetType)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum SpiInterrupt { /// Indicates that the SPI transaction has completed successfully. /// @@ -423,8 +420,9 @@ impl Address { } /// SPI peripheral configuration -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub struct Config { /// SPI clock frequency pub frequency: HertzU32, diff --git a/esp-hal/src/spi/mod.rs b/esp-hal/src/spi/mod.rs index 7ac8b313ae1..f3546f65c82 100644 --- a/esp-hal/src/spi/mod.rs +++ b/esp-hal/src/spi/mod.rs @@ -17,6 +17,7 @@ pub mod slave; /// SPI errors #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum Error { /// Error occurred due to a DMA-related issue. DmaError(DmaError), diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index bb0f3af4253..8ec7f4b7c0b 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -244,6 +244,7 @@ const UART_FIFO_SIZE: u16 = 128; /// UART Error #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum Error { /// An invalid configuration argument was provided. /// @@ -289,21 +290,23 @@ impl embedded_io::Error for Error { // (outside of `config` module in order not to "use" it an extra time) /// UART clock source -#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ClockSource { /// APB_CLK clock source (default for UART on all the chips except of /// esp32c6 and esp32h2) + #[cfg_attr(not(any(esp32c6, esp32h2, lp_uart)), default)] Apb, - #[cfg(not(any(esp32, esp32s2)))] /// RC_FAST_CLK clock source (17.5 MHz) - RcFast, #[cfg(not(any(esp32, esp32s2)))] + RcFast, /// XTAL_CLK clock source (default for UART on esp32c6 and esp32h2 and /// LP_UART) + #[cfg(not(any(esp32, esp32s2)))] + #[cfg_attr(any(esp32c6, esp32h2, lp_uart), default)] Xtal, - #[cfg(any(esp32, esp32s2))] /// REF_TICK clock source (derived from XTAL or RC_FAST, 1MHz) + #[cfg(any(esp32, esp32s2))] RefTick, } @@ -317,7 +320,7 @@ const UART_TOUT_THRESH_DEFAULT: u8 = 10; /// This enum represents the various configurations for the number of data /// bits used in UART communication. The number of data bits defines the /// length of each transmitted or received data frame. -#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DataBits { /// 5 data bits per frame. @@ -327,6 +330,7 @@ pub enum DataBits { /// 7 data bits per frame. DataBits7 = 2, /// 8 data bits per frame (most common). + #[default] DataBits8 = 3, } @@ -336,10 +340,11 @@ pub enum DataBits { /// ensure that the data has not been corrupted during transmission. The /// parity bit is added to the data bits to make the number of 1-bits /// either even or odd. -#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Parity { /// No parity bit is used (most common). + #[default] ParityNone, /// Even parity: the parity bit is set to make the total number of /// 1-bits even. @@ -354,10 +359,11 @@ pub enum Parity { /// The stop bit(s) signal the end of a data packet in UART communication. /// This enum defines the possible configurations for the number of stop /// bits. -#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum StopBits { /// 1 stop bit. + #[default] STOP1 = 1, /// 1.5 stop bits. STOP1P5 = 2, @@ -366,8 +372,9 @@ pub enum StopBits { } /// UART Configuration -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone, Copy, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub struct Config { /// The baud rate (speed) of the UART communication in bits per second /// (bps). @@ -467,18 +474,10 @@ impl Default for Config { fn default() -> Config { Config { baudrate: 115_200, - data_bits: DataBits::DataBits8, - parity: Parity::ParityNone, - stop_bits: StopBits::STOP1, - clock_source: { - cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32h2, lp_uart))] { - ClockSource::Xtal - } else { - ClockSource::Apb - } - } - }, + data_bits: Default::default(), + parity: Default::default(), + stop_bits: Default::default(), + clock_source: Default::default(), rx_fifo_full_threshold: UART_FULL_THRESH_DEFAULT, rx_timeout: Some(UART_TOUT_THRESH_DEFAULT), } @@ -1158,6 +1157,7 @@ where /// List of exposed UART events. #[derive(Debug, EnumSetType)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum UartInterrupt { /// Indicates that the received has detected the configured /// [`Uart::set_at_cmd`] character. @@ -1589,12 +1589,13 @@ where } } -#[derive(EnumSetType, Debug)] +#[derive(Debug, EnumSetType)] pub(crate) enum TxEvent { TxDone, TxFiFoEmpty, } -#[derive(EnumSetType, Debug)] + +#[derive(Debug, EnumSetType)] pub(crate) enum RxEvent { FifoFull, CmdCharDetected, @@ -1705,6 +1706,7 @@ struct UartTxFuture { state: &'static State, registered: bool, } + impl UartTxFuture { fn new(uart: impl Peripheral

, events: impl Into>) -> Self { crate::into_ref!(uart); diff --git a/examples/src/bin/embassy_spi.rs b/examples/src/bin/embassy_spi.rs index 1d7e8f9fd31..de95e0714f6 100644 --- a/examples/src/bin/embassy_spi.rs +++ b/examples/src/bin/embassy_spi.rs @@ -59,11 +59,9 @@ async fn main(_spawner: Spawner) { let mut spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_sck(sclk) .with_mosi(mosi) diff --git a/examples/src/bin/spi_loopback.rs b/examples/src/bin/spi_loopback.rs index 67ff6a9a7a1..191ebdee802 100644 --- a/examples/src/bin/spi_loopback.rs +++ b/examples/src/bin/spi_loopback.rs @@ -39,11 +39,9 @@ fn main() -> ! { let mut spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_sck(sclk) .with_miso(miso) // order matters diff --git a/examples/src/bin/spi_loopback_dma_psram.rs b/examples/src/bin/spi_loopback_dma_psram.rs index 72787eb408c..02d53a00274 100644 --- a/examples/src/bin/spi_loopback_dma_psram.rs +++ b/examples/src/bin/spi_loopback_dma_psram.rs @@ -90,11 +90,9 @@ fn main() -> ! { // output connection (because we are using the same pin to loop back) let mut spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_sck(sclk) .with_miso(miso) diff --git a/hil-test/tests/embassy_interrupt_spi_dma.rs b/hil-test/tests/embassy_interrupt_spi_dma.rs index 82ddbfd825f..c59f51e5431 100644 --- a/hil-test/tests/embassy_interrupt_spi_dma.rs +++ b/hil-test/tests/embassy_interrupt_spi_dma.rs @@ -120,11 +120,9 @@ mod test { let mut spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 10000.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(10000.kHz()) + .with_mode(SpiMode::Mode0), ) .with_miso(unsafe { mosi.clone_unchecked() }) .with_mosi(mosi) @@ -135,11 +133,9 @@ mod test { #[cfg(any(esp32, esp32s2, esp32s3))] let other_peripheral = Spi::new_with_config( peripherals.SPI3, - Config { - frequency: 10000.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(10000.kHz()) + .with_mode(SpiMode::Mode0), ) .with_dma(dma_channel2) .into_async(); @@ -231,11 +227,9 @@ mod test { let mut spi = Spi::new_with_config( peripherals.spi, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_dma(peripherals.dma_channel) .with_buffers(dma_rx_buf, dma_tx_buf) diff --git a/hil-test/tests/qspi.rs b/hil-test/tests/qspi.rs index 1c9f46fc371..95033daf631 100644 --- a/hil-test/tests/qspi.rs +++ b/hil-test/tests/qspi.rs @@ -202,11 +202,9 @@ mod tests { let spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ); Context { diff --git a/hil-test/tests/spi_full_duplex.rs b/hil-test/tests/spi_full_duplex.rs index 756833d9cc4..1a5517b8412 100644 --- a/hil-test/tests/spi_full_duplex.rs +++ b/hil-test/tests/spi_full_duplex.rs @@ -73,16 +73,11 @@ mod tests { let (mosi_loopback_pcnt, mosi) = mosi.split(); // Need to set miso first so that mosi can overwrite the // output connection (because we are using the same pin to loop back) - let spi = Spi::new_with_config( - peripherals.SPI2, - Config { - frequency: 10.MHz(), - ..Config::default() - }, - ) - .with_sck(sclk) - .with_miso(unsafe { mosi.clone_unchecked() }) - .with_mosi(mosi); + let spi = + Spi::new_with_config(peripherals.SPI2, Config::default().with_frequency(10.MHz())) + .with_sck(sclk) + .with_miso(unsafe { mosi.clone_unchecked() }) + .with_mosi(mosi); let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000); @@ -487,10 +482,7 @@ mod tests { // This means that without working cancellation, the test case should // fail. ctx.spi - .apply_config(&Config { - frequency: 80.kHz(), - ..Config::default() - }) + .apply_config(&Config::default().with_frequency(80.kHz())) .unwrap(); // Set up a large buffer that would trigger a timeout @@ -513,10 +505,7 @@ mod tests { fn can_transmit_after_cancel(mut ctx: Context) { // Slow down. At 80kHz, the transfer is supposed to take a bit over 3 seconds. ctx.spi - .apply_config(&Config { - frequency: 80.kHz(), - ..Config::default() - }) + .apply_config(&Config::default().with_frequency(80.kHz())) .unwrap(); // Set up a large buffer that would trigger a timeout @@ -533,11 +522,8 @@ mod tests { transfer.cancel(); (spi, (dma_rx_buf, dma_tx_buf)) = transfer.wait(); - spi.apply_config(&Config { - frequency: 10.MHz(), - ..Config::default() - }) - .unwrap(); + spi.apply_config(&Config::default().with_frequency(10.MHz())) + .unwrap(); let transfer = spi .transfer(dma_rx_buf, dma_tx_buf) diff --git a/hil-test/tests/spi_half_duplex_read.rs b/hil-test/tests/spi_half_duplex_read.rs index c1c92da8be9..27564c02a6a 100644 --- a/hil-test/tests/spi_half_duplex_read.rs +++ b/hil-test/tests/spi_half_duplex_read.rs @@ -48,11 +48,9 @@ mod tests { let spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_sck(sclk) .with_miso(miso) diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index fdd0cf43ba1..a58ea3c4977 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -52,11 +52,9 @@ mod tests { let spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_sck(sclk) .with_mosi(mosi) diff --git a/hil-test/tests/spi_half_duplex_write_psram.rs b/hil-test/tests/spi_half_duplex_write_psram.rs index cb19aafd039..52fe8f75ca4 100644 --- a/hil-test/tests/spi_half_duplex_write_psram.rs +++ b/hil-test/tests/spi_half_duplex_write_psram.rs @@ -64,11 +64,9 @@ mod tests { let spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_sck(sclk) .with_mosi(mosi) diff --git a/hil-test/tests/uart.rs b/hil-test/tests/uart.rs index 38e3b9db4f6..85c5ab90418 100644 --- a/hil-test/tests/uart.rs +++ b/hil-test/tests/uart.rs @@ -90,11 +90,11 @@ mod tests { let mut byte_to_write = 0xA5; for (baudrate, clock_source) in configs { ctx.uart - .apply_config(&uart::Config { - baudrate, - clock_source, - ..Default::default() - }) + .apply_config( + &uart::Config::default() + .with_baudrate(baudrate) + .with_clock_source(clock_source), + ) .unwrap(); ctx.uart.write(byte_to_write).ok(); let read = block!(ctx.uart.read()); diff --git a/qa-test/src/bin/lcd_dpi.rs b/qa-test/src/bin/lcd_dpi.rs index 6785ff0b8d6..7b3b0e04fd6 100644 --- a/qa-test/src/bin/lcd_dpi.rs +++ b/qa-test/src/bin/lcd_dpi.rs @@ -36,8 +36,7 @@ use esp_hal::{ delay::Delay, dma_loop_buffer, gpio::{Level, Output}, - i2c, - i2c::master::I2c, + i2c::{self, master::I2c}, lcd_cam::{ lcd::{ dpi::{Config, Dpi, Format, FrameTiming}, @@ -61,10 +60,7 @@ fn main() -> ! { let i2c = I2c::new( peripherals.I2C0, - i2c::master::Config { - frequency: 400.kHz(), - ..Default::default() - }, + i2c::master::Config::default().with_frequency(400.kHz()), ) .with_sda(peripherals.GPIO47) .with_scl(peripherals.GPIO48); diff --git a/qa-test/src/bin/qspi_flash.rs b/qa-test/src/bin/qspi_flash.rs index 861a3f4eb67..7b960f9fdd8 100644 --- a/qa-test/src/bin/qspi_flash.rs +++ b/qa-test/src/bin/qspi_flash.rs @@ -77,11 +77,9 @@ fn main() -> ! { let mut spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_sck(sclk) .with_mosi(mosi) diff --git a/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs b/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs index 6ea46778f86..3faae70cacc 100644 --- a/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs +++ b/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs @@ -63,11 +63,9 @@ fn main() -> ! { let mut spi = Spi::new_with_config( peripherals.SPI2, - Config { - frequency: 100.kHz(), - mode: SpiMode::Mode0, - ..Config::default() - }, + Config::default() + .with_frequency(100.kHz()) + .with_mode(SpiMode::Mode0), ) .with_sck(sclk) .with_mosi(mosi) From 92910bf1cb73af2f8afe1d6dfd8408bc430c9dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 28 Nov 2024 10:28:50 +0100 Subject: [PATCH 21/27] Constructor consistency update (#2610) * UART: only implement constructors with config, define ConfigError * UART: only implement interrupt functions for Blocking * I2C: fallible constructors * Lcd/Cam * SPI * Update tests and examples * Changelog * Add note about ConfigError * Fmt --- documentation/API-GUIDELINES.md | 7 +- esp-hal/CHANGELOG.md | 7 +- esp-hal/MIGRATING-0.22.md | 48 +++- esp-hal/src/dma/mod.rs | 8 +- esp-hal/src/i2c/master/mod.rs | 16 +- esp-hal/src/lcd_cam/cam.rs | 150 +++++++------ esp-hal/src/lcd_cam/lcd/dpi.rs | 72 ++++-- esp-hal/src/lcd_cam/lcd/i8080.rs | 71 ++++-- esp-hal/src/lcd_cam/lcd/mod.rs | 6 +- esp-hal/src/lcd_cam/mod.rs | 30 ++- esp-hal/src/spi/master.rs | 27 +-- esp-hal/src/uart.rs | 208 ++++++++---------- examples/src/bin/embassy_serial.rs | 2 +- examples/src/bin/embassy_spi.rs | 3 +- examples/src/bin/ieee802154_sniffer.rs | 14 +- examples/src/bin/spi_loopback.rs | 3 +- examples/src/bin/spi_loopback_dma_psram.rs | 3 +- hil-test/tests/embassy_interrupt_spi_dma.rs | 9 +- hil-test/tests/i2c.rs | 1 + hil-test/tests/lcd_cam.rs | 12 +- hil-test/tests/lcd_cam_i8080.rs | 29 ++- hil-test/tests/lcd_cam_i8080_async.rs | 7 +- hil-test/tests/qspi.rs | 5 +- hil-test/tests/spi_full_duplex.rs | 10 +- hil-test/tests/spi_half_duplex_read.rs | 3 +- hil-test/tests/spi_half_duplex_write.rs | 3 +- hil-test/tests/spi_half_duplex_write_psram.rs | 3 +- hil-test/tests/uart.rs | 2 +- hil-test/tests/uart_async.rs | 9 +- hil-test/tests/uart_regression.rs | 6 +- hil-test/tests/uart_tx_rx.rs | 6 +- hil-test/tests/uart_tx_rx_async.rs | 10 +- qa-test/src/bin/embassy_i2c.rs | 1 + .../embassy_i2c_bmp180_calibration_data.rs | 1 + .../src/bin/i2c_bmp180_calibration_data.rs | 1 + qa-test/src/bin/i2c_display.rs | 1 + qa-test/src/bin/lcd_cam_ov2640.rs | 9 +- qa-test/src/bin/lcd_dpi.rs | 5 +- qa-test/src/bin/lcd_i8080.rs | 13 +- qa-test/src/bin/qspi_flash.rs | 3 +- .../spi_halfduplex_read_manufacturer_id.rs | 3 +- 41 files changed, 495 insertions(+), 332 deletions(-) diff --git a/documentation/API-GUIDELINES.md b/documentation/API-GUIDELINES.md index d92521a5409..26de75d322c 100644 --- a/documentation/API-GUIDELINES.md +++ b/documentation/API-GUIDELINES.md @@ -30,12 +30,17 @@ In general, the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines - The peripheral instance type must default to a type that supports any of the peripheral instances. - The author must to use `crate::any_peripheral` to define the "any" peripheral instance type. - The driver must implement a `new` constructor that automatically converts the peripheral instance into the any type, and a `new_typed` that preserves the peripheral type. +- If a driver is configurable, configuration options should be implemented as a `Config` struct in the same module where the driver is located. + - The driver's constructor should take the config struct by value, and it should return `Result`. + - The `ConfigError` enum should be separate from other `Error` enums used by the driver. + - The driver should implement `fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError>`. + - In case the driver's configuration is infallible (all possible combinations of options are supported by the hardware), the `ConfigError` should be implemented as an empty `enum`. - If a driver only supports a single peripheral instance, no instance type parameter is necessary. - If a driver implements both blocking and async operations, or only implements blocking operations, but may support asynchronous ones in the future, the driver's type signature must include a `crate::Mode` type parameter. - By default, constructors must configure the driver for blocking mode. The driver must implement `into_async` (and a matching `into_blocking`) function that reconfigures the driver. - `into_async` must configure the driver and/or the associated DMA channels. This most often means enabling an interrupt handler. - `into_blocking` must undo the configuration done by `into_async`. -- The asynchronous driver implemntation must also expose the blocking methods (except for interrupt related functions). +- The asynchronous driver implementation must also expose the blocking methods (except for interrupt related functions). - Drivers must have a `Drop` implementation resetting the peripheral to idle state. There are some exceptions to this: - GPIO where common usage is to "set and drop" so they can't be changed - Where we don't want to disable the peripheral as it's used internally, for example SYSTIMER is used by `time::now()` API. See `KEEP_ENABLED` in src/system.rs diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 9f59253395e..17af6b314db 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The timer drivers `OneShotTimer` & `PeriodicTimer` have `into_async` and `new_typed` methods (#2586) - `timer::Timer` trait has three new methods, `wait`, `async_interrupt_handler` and `peripheral_interrupt` (#2586) - Configuration structs in the I2C, SPI, and UART drivers now implement the Builder Lite pattern (#2614) +- Added `I8080::apply_config`, `DPI::apply_config` and `Camera::apply_config` (#2610) ### Changed @@ -44,6 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `timer::Timer` has new trait requirements of `Into`, `'static` and `InterruptConfigurable` (#2586) - `systimer::etm::Event` no longer borrows the alarm indefinitely (#2586) - A number of public enums and structs in the I2C, SPI, and UART drivers have been marked with `#[non_exhaustive]` (#2614) +- Interrupt handling related functions are only provided for Blocking UART. (#2610) +- Changed how `Spi`, (split or unsplit) `Uart`, `LpUart`, `I8080`, `Camera`, `DPI` and `I2C` drivers are constructed (#2610) +- I8080, camera, DPI: The various standalone configuration options have been merged into `Config` (#2610) ### Fixed @@ -58,7 +62,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `FrozenUnit`, `AnyUnit`, `SpecificUnit`, `SpecificComparator`, `AnyComparator` have been removed from `systimer` (#2576) - `esp_hal::psram::psram_range` (#2546) - The `Dma` structure has been removed. (#2545) -- Remove `embedded-hal 0.2.x` impls and deps from `esp-hal` (#2593) +- Removed `embedded-hal 0.2.x` impls and deps from `esp-hal` (#2593) +- Removed `Camera::set_` functions (#2610) ## [0.22.0] - 2024-11-20 diff --git a/esp-hal/MIGRATING-0.22.md b/esp-hal/MIGRATING-0.22.md index 64ca5ea996a..bb941d96f82 100644 --- a/esp-hal/MIGRATING-0.22.md +++ b/esp-hal/MIGRATING-0.22.md @@ -188,7 +188,7 @@ is enabled. To retrieve the address and size of the initialized external memory, The usage of `esp_alloc::psram_allocator!` remains unchanged. -### embedded-hal 0.2.* is not supported anymore. +## embedded-hal 0.2.* is not supported anymore. As per https://github.com/rust-embedded/embedded-hal/pull/640, our driver no longer implements traits from `embedded-hal 0.2.x`. Analogs of all traits from the above mentioned version are available in `embedded-hal 1.x.x` @@ -221,3 +221,49 @@ https://github.com/rust-embedded/embedded-hal/blob/master/docs/migrating-from-0. + use esp_hal::interrupt::InterruptConfigurable; + use esp_hal::interrupt::DEFAULT_INTERRUPT_HANDLER; ``` + +## Driver constructors now take a configuration and are fallible + +The old `new_with_config` constructor have been removed, and `new` constructors now always take +a configuration structure. They have also been updated to return a `ConfigError` if the configuration +is not compatible with the hardware. + +```diff +-let mut spi = Spi::new_with_config( ++let mut spi = Spi::new( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, +-); ++) ++.unwrap(); +``` + +```diff + let mut spi = Spi::new( + peripherals.SPI2, ++ Config::default(), +-); ++) ++.unwrap(); +``` + +### LCD_CAM configuration changes + +- `cam` now has a `Config` strurct that contains frequency, bit/byte order, VSync filter options. +- DPI, I8080: `frequency` has been moved into `Config`. + +```diff ++let mut cam_config = cam::Config::default(); ++cam_config.frequency = 1u32.MHz(); + cam::Camera::new( + lcd_cam.cam, + dma_rx_channel, + pins, +- 1u32.MHz(), ++ cam_config, + ) +``` diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 29b54747069..419014720a5 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -26,10 +26,11 @@ //! let mosi = peripherals.GPIO4; //! let cs = peripherals.GPIO5; //! -//! let mut spi = Spi::new_with_config( +//! let mut spi = Spi::new( //! peripherals.SPI2, //! Config::default().with_frequency(100.kHz()).with_mode(SpiMode::Mode0) //! ) +//! .unwrap() //! .with_sck(sclk) //! .with_mosi(mosi) //! .with_miso(miso) @@ -1688,10 +1689,11 @@ impl DmaChannelConvert for DEG { #[cfg_attr(pdma, doc = "let dma_channel = peripherals.DMA_SPI2;")] #[cfg_attr(gdma, doc = "let dma_channel = peripherals.DMA_CH0;")] #[doc = ""] -/// let spi = Spi::new_with_config( +/// let spi = Spi::new( /// peripherals.SPI2, /// Config::default(), -/// ); +/// ) +/// .unwrap(); /// /// let spi_dma = configures_spi_dma(spi, dma_channel); /// # } diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs index 4a9daed950e..5024ca81ab8 100644 --- a/esp-hal/src/i2c/master/mod.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -25,6 +25,7 @@ //! peripherals.I2C0, //! Config::default(), //! ) +//! .unwrap() //! .with_sda(peripherals.GPIO1) //! .with_scl(peripherals.GPIO2); //! @@ -421,7 +422,10 @@ impl<'d> I2c<'d, Blocking> { /// Create a new I2C instance /// This will enable the peripheral but the peripheral won't get /// automatically disabled when this gets dropped. - pub fn new(i2c: impl Peripheral

+ 'd, config: Config) -> Self { + pub fn new( + i2c: impl Peripheral

+ 'd, + config: Config, + ) -> Result { Self::new_typed(i2c.map_into(), config) } } @@ -433,7 +437,10 @@ where /// Create a new I2C instance /// This will enable the peripheral but the peripheral won't get /// automatically disabled when this gets dropped. - pub fn new_typed(i2c: impl Peripheral

+ 'd, config: Config) -> Self { + pub fn new_typed( + i2c: impl Peripheral

+ 'd, + config: Config, + ) -> Result { crate::into_ref!(i2c); let guard = PeripheralGuard::new(i2c.info().peripheral); @@ -445,8 +452,9 @@ where guard, }; - unwrap!(i2c.driver().setup(&i2c.config)); - i2c + i2c.driver().setup(&i2c.config)?; + + Ok(i2c) } // TODO: missing interrupt APIs diff --git a/esp-hal/src/lcd_cam/cam.rs b/esp-hal/src/lcd_cam/cam.rs index ad772bf7084..abe7d8c7673 100644 --- a/esp-hal/src/lcd_cam/cam.rs +++ b/esp-hal/src/lcd_cam/cam.rs @@ -16,7 +16,7 @@ //! master mode. //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::lcd_cam::{cam::{Camera, RxEightBits}, LcdCam}; +//! # use esp_hal::lcd_cam::{cam::{Camera, Config, RxEightBits}, LcdCam}; //! # use fugit::RateExtU32; //! # use esp_hal::dma_rx_stream_buffer; //! @@ -37,15 +37,18 @@ //! peripherals.GPIO16, //! ); //! +//! let mut config = Config::default(); +//! config.frequency = 20.MHz(); +//! //! let lcd_cam = LcdCam::new(peripherals.LCD_CAM); //! let mut camera = Camera::new( //! lcd_cam.cam, //! peripherals.DMA_CH0, //! data_pins, -//! 20u32.MHz(), +//! config, //! ) -//! // Remove this for slave mode. -//! .with_master_clock(mclk_pin) +//! .unwrap() +//! .with_master_clock(mclk_pin) // Remove this for slave mode //! .with_pixel_clock(pclk_pin) //! .with_ctrl_pins(vsync_pin, href_pin); //! @@ -59,7 +62,7 @@ use core::{ ops::{Deref, DerefMut}, }; -use fugit::HertzU32; +use fugit::{HertzU32, RateExtU32}; use crate::{ clock::Clocks, @@ -70,7 +73,7 @@ use crate::{ OutputSignal, Pull, }, - lcd_cam::{calculate_clkm, BitOrder, ByteOrder}, + lcd_cam::{calculate_clkm, BitOrder, ByteOrder, ClockError}, peripheral::{Peripheral, PeripheralRef}, peripherals::LCD_CAM, system::{self, GenericPeripheralGuard}, @@ -109,6 +112,14 @@ pub enum VsyncFilterThreshold { Eight, } +/// Vsync Filter Threshold +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ConfigError { + /// The frequency is out of range. + Clock(ClockError), +} + /// Represents the camera interface. pub struct Cam<'d> { /// The LCD_CAM peripheral reference for managing the camera functionality. @@ -129,109 +140,86 @@ impl<'d> Camera<'d> { cam: Cam<'d>, channel: impl Peripheral

+ 'd, _pins: P, - frequency: HertzU32, - ) -> Self + config: Config, + ) -> Result where CH: RxChannelFor, P: RxPins, { let rx_channel = ChannelRx::new(channel.map(|ch| ch.degrade())); - let lcd_cam = cam.lcd_cam; + let mut this = Self { + lcd_cam: cam.lcd_cam, + rx_channel, + _guard: cam._guard, + }; + + this.lcd_cam + .cam_ctrl1() + .modify(|_, w| w.cam_2byte_en().bit(P::BUS_WIDTH == 2)); + + this.apply_config(&config)?; + + Ok(this) + } + + /// Applies the configuration to the camera interface. + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { let clocks = Clocks::get(); let (i, divider) = calculate_clkm( - frequency.to_Hz() as _, + config.frequency.to_Hz() as _, &[ clocks.xtal_clock.to_Hz() as _, clocks.cpu_clock.to_Hz() as _, clocks.crypto_pwm_clock.to_Hz() as _, ], - ); + ) + .map_err(ConfigError::Clock)?; - lcd_cam.cam_ctrl().write(|w| { + self.lcd_cam.cam_ctrl().write(|w| { // Force enable the clock for all configuration registers. unsafe { w.cam_clk_sel().bits((i + 1) as _); w.cam_clkm_div_num().bits(divider.div_num as _); w.cam_clkm_div_b().bits(divider.div_b as _); w.cam_clkm_div_a().bits(divider.div_a as _); - w.cam_vsync_filter_thres().bits(0); + if let Some(threshold) = config.vsync_filter_threshold { + w.cam_vsync_filter_thres().bits(threshold as _); + } + w.cam_byte_order() + .bit(config.byte_order != ByteOrder::default()); + w.cam_bit_order() + .bit(config.bit_order != BitOrder::default()); w.cam_vs_eof_en().set_bit(); w.cam_line_int_en().clear_bit(); w.cam_stop_en().clear_bit() } }); - lcd_cam.cam_ctrl1().write(|w| unsafe { + self.lcd_cam.cam_ctrl1().modify(|_, w| unsafe { w.cam_vh_de_mode_en().set_bit(); w.cam_rec_data_bytelen().bits(0); w.cam_line_int_num().bits(0); - w.cam_vsync_filter_en().clear_bit(); - w.cam_2byte_en().bit(P::BUS_WIDTH == 2); + w.cam_vsync_filter_en() + .bit(config.vsync_filter_threshold.is_some()); w.cam_clk_inv().clear_bit(); w.cam_de_inv().clear_bit(); w.cam_hsync_inv().clear_bit(); w.cam_vsync_inv().clear_bit() }); - lcd_cam + self.lcd_cam .cam_rgb_yuv() .write(|w| w.cam_conv_bypass().clear_bit()); - lcd_cam.cam_ctrl().modify(|_, w| w.cam_update().set_bit()); - - Self { - lcd_cam, - rx_channel, - _guard: cam._guard, - } - } -} - -impl<'d> Camera<'d> { - /// Configures the byte order for the camera data. - pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self { self.lcd_cam .cam_ctrl() - .modify(|_, w| w.cam_byte_order().bit(byte_order != ByteOrder::default())); - self - } - - /// Configures the bit order for the camera data. - pub fn set_bit_order(&mut self, bit_order: BitOrder) -> &mut Self { - self.lcd_cam - .cam_ctrl() - .modify(|_, w| w.cam_bit_order().bit(bit_order != BitOrder::default())); - self - } + .modify(|_, w| w.cam_update().set_bit()); - /// Configures the VSYNC filter threshold. - pub fn set_vsync_filter(&mut self, threshold: Option) -> &mut Self { - if let Some(threshold) = threshold { - let value = match threshold { - VsyncFilterThreshold::One => 0, - VsyncFilterThreshold::Two => 1, - VsyncFilterThreshold::Three => 2, - VsyncFilterThreshold::Four => 3, - VsyncFilterThreshold::Five => 4, - VsyncFilterThreshold::Six => 5, - VsyncFilterThreshold::Seven => 6, - VsyncFilterThreshold::Eight => 7, - }; - - self.lcd_cam - .cam_ctrl() - .modify(|_, w| unsafe { w.cam_vsync_filter_thres().bits(value) }); - self.lcd_cam - .cam_ctrl1() - .modify(|_, w| w.cam_vsync_filter_en().set_bit()); - } else { - self.lcd_cam - .cam_ctrl1() - .modify(|_, w| w.cam_vsync_filter_en().clear_bit()); - } - self + Ok(()) } +} +impl<'d> Camera<'d> { /// Configures the master clock (MCLK) pin for the camera interface. pub fn with_master_clock( self, @@ -605,3 +593,31 @@ impl RxPins for RxSixteenBits { pub trait RxPins { const BUS_WIDTH: usize; } + +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Configuration settings for the Camera interface. +pub struct Config { + /// The pixel clock frequency for the camera interface. + pub frequency: HertzU32, + + /// The byte order for the camera data. + pub byte_order: ByteOrder, + + /// The bit order for the camera data. + pub bit_order: BitOrder, + + /// The Vsync filter threshold. + pub vsync_filter_threshold: Option, +} + +impl Default for Config { + fn default() -> Self { + Self { + frequency: 20.MHz(), + byte_order: Default::default(), + bit_order: Default::default(), + vsync_filter_threshold: None, + } + } +} diff --git a/esp-hal/src/lcd_cam/lcd/dpi.rs b/esp-hal/src/lcd_cam/lcd/dpi.rs index c7330a44826..368ae1c8aed 100644 --- a/esp-hal/src/lcd_cam/lcd/dpi.rs +++ b/esp-hal/src/lcd_cam/lcd/dpi.rs @@ -31,6 +31,7 @@ //! let lcd_cam = LcdCam::new(peripherals.LCD_CAM); //! //! let mut config = dpi::Config::default(); +//! config.frequency = 1.MHz(); //! config.clock_mode = ClockMode { //! polarity: Polarity::IdleLow, //! phase: Phase::ShiftLow, @@ -58,7 +59,7 @@ //! config.de_idle_level = Level::Low; //! config.disable_black_region = false; //! -//! let mut dpi = Dpi::new(lcd_cam.lcd, channel, 1.MHz(), config) +//! let mut dpi = Dpi::new(lcd_cam.lcd, channel, config).unwrap() //! .with_vsync(peripherals.GPIO3) //! .with_hsync(peripherals.GPIO46) //! .with_de(peripherals.GPIO17) @@ -99,7 +100,7 @@ use core::{ ops::{Deref, DerefMut}, }; -use fugit::HertzU32; +use fugit::{HertzU32, RateExtU32}; use crate::{ clock::Clocks, @@ -110,6 +111,7 @@ use crate::{ lcd::{ClockMode, DelayMode, Lcd, Phase, Polarity}, BitOrder, ByteOrder, + ClockError, }, peripheral::{Peripheral, PeripheralRef}, peripherals::LCD_CAM, @@ -117,6 +119,14 @@ use crate::{ Mode, }; +/// Errors that can occur when configuring the DPI peripheral. +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ConfigError { + /// Clock configuration error. + Clock(ClockError), +} + /// Represents the RGB LCD interface. pub struct Dpi<'d, DM: Mode> { lcd_cam: PeripheralRef<'d, LCD_CAM>, @@ -132,29 +142,41 @@ where pub fn new( lcd: Lcd<'d, DM>, channel: impl Peripheral

+ 'd, - frequency: HertzU32, config: Config, - ) -> Self + ) -> Result where CH: TxChannelFor, { let tx_channel = ChannelTx::new(channel.map(|ch| ch.degrade())); - let lcd_cam = lcd.lcd_cam; + let mut this = Self { + lcd_cam: lcd.lcd_cam, + tx_channel, + _mode: PhantomData, + }; + + this.apply_config(&config)?; + + Ok(this) + } + + /// Applies the configuration to the peripheral. + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { let clocks = Clocks::get(); // Due to https://www.espressif.com/sites/default/files/documentation/esp32-s3_errata_en.pdf // the LCD_PCLK divider must be at least 2. To make up for this the user // provided frequency is doubled to match. let (i, divider) = calculate_clkm( - (frequency.to_Hz() * 2) as _, + (config.frequency.to_Hz() * 2) as _, &[ clocks.xtal_clock.to_Hz() as _, clocks.cpu_clock.to_Hz() as _, clocks.crypto_pwm_clock.to_Hz() as _, ], - ); + ) + .map_err(ConfigError::Clock)?; - lcd_cam.lcd_clock().write(|w| unsafe { + self.lcd_cam.lcd_clock().write(|w| unsafe { // Force enable the clock for all configuration registers. w.clk_en().set_bit(); w.lcd_clk_sel().bits((i + 1) as _); @@ -168,13 +190,15 @@ where w.lcd_ck_out_edge() .bit(config.clock_mode.phase == Phase::ShiftHigh) }); - lcd_cam.lcd_user().modify(|_, w| w.lcd_reset().set_bit()); + self.lcd_cam + .lcd_user() + .modify(|_, w| w.lcd_reset().set_bit()); - lcd_cam + self.lcd_cam .lcd_rgb_yuv() .write(|w| w.lcd_conv_bypass().clear_bit()); - lcd_cam.lcd_user().modify(|_, w| { + self.lcd_cam.lcd_user().modify(|_, w| { if config.format.enable_2byte_mode { w.lcd_8bits_order().bit(false); w.lcd_byte_order() @@ -197,7 +221,7 @@ where }); let timing = &config.timing; - lcd_cam.lcd_ctrl().modify(|_, w| unsafe { + self.lcd_cam.lcd_ctrl().modify(|_, w| unsafe { // Enable RGB mode, and input VSYNC, HSYNC, and DE signals. w.lcd_rgb_mode_en().set_bit(); @@ -208,7 +232,7 @@ where w.lcd_vt_height() .bits((timing.vertical_total_height as u16).saturating_sub(1)) }); - lcd_cam.lcd_ctrl1().modify(|_, w| unsafe { + self.lcd_cam.lcd_ctrl1().modify(|_, w| unsafe { w.lcd_vb_front() .bits((timing.vertical_blank_front_porch as u8).saturating_sub(1)); w.lcd_ha_width() @@ -216,7 +240,7 @@ where w.lcd_ht_width() .bits((timing.horizontal_total_width as u16).saturating_sub(1)) }); - lcd_cam.lcd_ctrl2().modify(|_, w| unsafe { + self.lcd_cam.lcd_ctrl2().modify(|_, w| unsafe { w.lcd_vsync_width() .bits((timing.vsync_width as u8).saturating_sub(1)); w.lcd_vsync_idle_pol().bit(config.vsync_idle_level.into()); @@ -228,7 +252,7 @@ where w.lcd_hsync_position().bits(timing.hsync_position as u8) }); - lcd_cam.lcd_misc().modify(|_, w| unsafe { + self.lcd_cam.lcd_misc().modify(|_, w| unsafe { // TODO: Find out what this field actually does. // Set the threshold for Async Tx FIFO full event. (5 bits) w.lcd_afifo_threshold_num().bits((1 << 5) - 1); @@ -244,13 +268,13 @@ where // Enable blank region when LCD sends data out. w.lcd_bk_en().bit(!config.disable_black_region) }); - lcd_cam.lcd_dly_mode().modify(|_, w| unsafe { + self.lcd_cam.lcd_dly_mode().modify(|_, w| unsafe { w.lcd_de_mode().bits(config.de_mode as u8); w.lcd_hsync_mode().bits(config.hsync_mode as u8); w.lcd_vsync_mode().bits(config.vsync_mode as u8); w }); - lcd_cam.lcd_data_dout_mode().modify(|_, w| unsafe { + self.lcd_cam.lcd_data_dout_mode().modify(|_, w| unsafe { w.dout0_mode().bits(config.output_bit_mode as u8); w.dout1_mode().bits(config.output_bit_mode as u8); w.dout2_mode().bits(config.output_bit_mode as u8); @@ -269,13 +293,11 @@ where w.dout15_mode().bits(config.output_bit_mode as u8) }); - lcd_cam.lcd_user().modify(|_, w| w.lcd_update().set_bit()); + self.lcd_cam + .lcd_user() + .modify(|_, w| w.lcd_update().set_bit()); - Self { - lcd_cam, - tx_channel, - _mode: PhantomData, - } + Ok(()) } /// Assign the VSYNC pin for the LCD_CAM. @@ -675,6 +697,9 @@ pub struct Config { /// Specifies the clock mode, including polarity and phase settings. pub clock_mode: ClockMode, + /// The frequency of the pixel clock. + pub frequency: HertzU32, + /// Format of the byte data sent out. pub format: Format, @@ -712,6 +737,7 @@ impl Default for Config { fn default() -> Self { Config { clock_mode: Default::default(), + frequency: 1.MHz(), format: Default::default(), timing: Default::default(), vsync_idle_level: Level::Low, diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 21350a1d92f..7cea535d765 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -33,13 +33,16 @@ //! ); //! let lcd_cam = LcdCam::new(peripherals.LCD_CAM); //! +//! let mut config = Config::default(); +//! config.frequency = 20.MHz(); +//! //! let mut i8080 = I8080::new( //! lcd_cam.lcd, //! peripherals.DMA_CH0, //! tx_pins, -//! 20.MHz(), -//! Config::default(), +//! config, //! ) +//! .unwrap() //! .with_ctrl_pins(peripherals.GPIO0, peripherals.GPIO47); //! //! dma_buf.fill(&[0x55]); @@ -55,7 +58,7 @@ use core::{ ops::{Deref, DerefMut}, }; -use fugit::HertzU32; +use fugit::{HertzU32, RateExtU32}; use crate::{ clock::Clocks, @@ -69,6 +72,7 @@ use crate::{ lcd::{ClockMode, DelayMode, Phase, Polarity}, BitOrder, ByteOrder, + ClockError, Instance, Lcd, LCD_DONE_WAKER, @@ -79,6 +83,14 @@ use crate::{ Mode, }; +/// A configuration error. +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ConfigError { + /// Clock configuration error. + Clock(ClockError), +} + /// Represents the I8080 LCD interface. pub struct I8080<'d, DM: Mode> { lcd_cam: PeripheralRef<'d, LCD_CAM>, @@ -95,30 +107,43 @@ where lcd: Lcd<'d, DM>, channel: impl Peripheral

+ 'd, mut pins: P, - frequency: HertzU32, config: Config, - ) -> Self + ) -> Result where CH: TxChannelFor, P: TxPins, { let tx_channel = ChannelTx::new(channel.map(|ch| ch.degrade())); - let lcd_cam = lcd.lcd_cam; + let mut this = Self { + lcd_cam: lcd.lcd_cam, + tx_channel, + _mode: PhantomData, + }; + + this.apply_config(&config)?; + pins.configure(); + + Ok(this) + } + + /// Applies configuration. + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { let clocks = Clocks::get(); // Due to https://www.espressif.com/sites/default/files/documentation/esp32-s3_errata_en.pdf // the LCD_PCLK divider must be at least 2. To make up for this the user // provided frequency is doubled to match. let (i, divider) = calculate_clkm( - (frequency.to_Hz() * 2) as _, + (config.frequency.to_Hz() * 2) as _, &[ clocks.xtal_clock.to_Hz() as _, clocks.cpu_clock.to_Hz() as _, clocks.crypto_pwm_clock.to_Hz() as _, ], - ); + ) + .map_err(ConfigError::Clock)?; - lcd_cam.lcd_clock().write(|w| unsafe { + self.lcd_cam.lcd_clock().write(|w| unsafe { // Force enable the clock for all configuration registers. w.clk_en().set_bit(); w.lcd_clk_sel().bits((i + 1) as _); @@ -133,20 +158,20 @@ where .bit(config.clock_mode.phase == Phase::ShiftHigh) }); - lcd_cam + self.lcd_cam .lcd_ctrl() .write(|w| w.lcd_rgb_mode_en().clear_bit()); - lcd_cam + self.lcd_cam .lcd_rgb_yuv() .write(|w| w.lcd_conv_bypass().clear_bit()); - lcd_cam.lcd_user().modify(|_, w| { + self.lcd_cam.lcd_user().modify(|_, w| { w.lcd_8bits_order().bit(false); w.lcd_bit_order().bit(false); w.lcd_byte_order().bit(false); w.lcd_2byte_en().bit(false) }); - lcd_cam.lcd_misc().write(|w| unsafe { + self.lcd_cam.lcd_misc().write(|w| unsafe { // Set the threshold for Async Tx FIFO full event. (5 bits) w.lcd_afifo_threshold_num().bits(0); // Configure the setup cycles in LCD non-RGB mode. Setup cycles @@ -177,10 +202,10 @@ where // The default value of LCD_CD w.lcd_cd_idle_edge().bit(config.cd_idle_edge) }); - lcd_cam + self.lcd_cam .lcd_dly_mode() .write(|w| unsafe { w.lcd_cd_mode().bits(config.cd_mode as u8) }); - lcd_cam.lcd_data_dout_mode().write(|w| unsafe { + self.lcd_cam.lcd_data_dout_mode().write(|w| unsafe { w.dout0_mode().bits(config.output_bit_mode as u8); w.dout1_mode().bits(config.output_bit_mode as u8); w.dout2_mode().bits(config.output_bit_mode as u8); @@ -199,15 +224,11 @@ where w.dout15_mode().bits(config.output_bit_mode as u8) }); - lcd_cam.lcd_user().modify(|_, w| w.lcd_update().set_bit()); - - pins.configure(); + self.lcd_cam + .lcd_user() + .modify(|_, w| w.lcd_update().set_bit()); - Self { - lcd_cam, - tx_channel, - _mode: PhantomData, - } + Ok(()) } /// Configures the byte order for data transmission in 16-bit mode. @@ -523,6 +544,9 @@ pub struct Config { /// Specifies the clock mode, including polarity and phase settings. pub clock_mode: ClockMode, + /// The frequency of the pixel clock. + pub frequency: HertzU32, + /// Setup cycles expected, must be at least 1. (6 bits) pub setup_cycles: usize, @@ -548,6 +572,7 @@ impl Default for Config { fn default() -> Self { Self { clock_mode: Default::default(), + frequency: 20.MHz(), setup_cycles: 1, hold_cycles: 1, cd_idle_edge: false, diff --git a/esp-hal/src/lcd_cam/lcd/mod.rs b/esp-hal/src/lcd_cam/lcd/mod.rs index 66bd82a65c8..53ec894bcd2 100644 --- a/esp-hal/src/lcd_cam/lcd/mod.rs +++ b/esp-hal/src/lcd_cam/lcd/mod.rs @@ -11,11 +11,7 @@ //! - RGB is not supported yet use super::GenericPeripheralGuard; -use crate::{ - peripheral::PeripheralRef, - peripherals::LCD_CAM, - system::{self}, -}; +use crate::{peripheral::PeripheralRef, peripherals::LCD_CAM, system}; pub mod dpi; pub mod i8080; diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index 88065446319..b6f9320dcbd 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -173,10 +173,18 @@ pub(crate) struct ClockDivider { pub div_a: usize, } +/// Clock configuration errors. +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockError { + /// Desired frequency was too low for the dividers to divide to + FrequencyTooLow, +} + pub(crate) fn calculate_clkm( desired_frequency: usize, source_frequencies: &[usize], -) -> (usize, ClockDivider) { +) -> Result<(usize, ClockDivider), ClockError> { let mut result_freq = 0; let mut result = None; @@ -191,7 +199,7 @@ pub(crate) fn calculate_clkm( } } - result.expect("Desired frequency was too low for the dividers to divide to") + result.ok_or(ClockError::FrequencyTooLow) } fn calculate_output_frequency(source_frequency: usize, divider: &ClockDivider) -> usize { @@ -257,15 +265,15 @@ fn calculate_closest_divider( } } else { let target = div_fraction; - let closest = farey_sequence(63) - .find(|curr| { - // https://en.wikipedia.org/wiki/Fraction#Adding_unlike_quantities - - let new_curr_num = curr.numerator * target.denominator; - let new_target_num = target.numerator * curr.denominator; - new_curr_num >= new_target_num - }) - .expect("The fraction must be between 0 and 1"); + let closest = farey_sequence(63).find(|curr| { + // https://en.wikipedia.org/wiki/Fraction#Adding_unlike_quantities + + let new_curr_num = curr.numerator * target.denominator; + let new_target_num = target.numerator * curr.denominator; + new_curr_num >= new_target_num + }); + + let closest = unwrap!(closest, "The fraction must be between 0 and 1"); ClockDivider { div_num, diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index 4c5b943fc17..8f4538ba86b 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -43,10 +43,11 @@ //! let mosi = peripherals.GPIO1; //! let cs = peripherals.GPIO5; //! -//! let mut spi = Spi::new_with_config( +//! let mut spi = Spi::new( //! peripherals.SPI2, //! Config::default().with_frequency(100.kHz()).with_mode(SpiMode::Mode0) //! ) +//! .unwrap() //! .with_sck(sclk) //! .with_mosi(mosi) //! .with_miso(miso) @@ -503,16 +504,11 @@ where impl<'d> Spi<'d, Blocking> { /// Constructs an SPI instance in 8bit dataframe mode. - pub fn new(spi: impl Peripheral

+ 'd) -> Spi<'d, Blocking> { - Self::new_with_config(spi, Config::default()) - } - - /// Constructs an SPI instance in 8bit dataframe mode. - pub fn new_with_config( + pub fn new( spi: impl Peripheral

+ 'd, config: Config, - ) -> Spi<'d, Blocking> { - Self::new_typed_with_config(spi.map_into(), config) + ) -> Result { + Self::new_typed(spi.map_into(), config) } /// Converts the SPI instance into async mode. @@ -558,15 +554,10 @@ where T: Instance, { /// Constructs an SPI instance in 8bit dataframe mode. - pub fn new_typed(spi: impl Peripheral

+ 'd) -> Spi<'d, M, T> { - Self::new_typed_with_config(spi, Config::default()) - } - - /// Constructs an SPI instance in 8bit dataframe mode. - pub fn new_typed_with_config( + pub fn new_typed( spi: impl Peripheral

+ 'd, config: Config, - ) -> Spi<'d, M, T> { + ) -> Result { crate::into_ref!(spi); let guard = PeripheralGuard::new(spi.info().peripheral); @@ -578,7 +569,7 @@ where }; this.driver().init(); - unwrap!(this.apply_config(&config)); // FIXME: update based on the resolution of https://github.com/esp-rs/esp-hal/issues/2416 + this.apply_config(&config)?; let this = this .with_mosi(NoPin) @@ -594,7 +585,7 @@ where unwrap!(this.driver().sio3_output).connect_to(NoPin); } - this + Ok(this) } /// Assign the MOSI (Master Out Slave In) pin for the SPI instance. diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 8ec7f4b7c0b..9e5dc7ce61a 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -22,10 +22,11 @@ //! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::Uart; +//! # use esp_hal::uart::{Config, Uart}; //! //! let mut uart1 = Uart::new( //! peripherals.UART1, +//! Config::default(), //! peripherals.GPIO1, //! peripherals.GPIO2, //! ).unwrap(); @@ -53,7 +54,7 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::uart::{Config, Uart}; -//! # let mut uart1 = Uart::new_with_config( +//! # let mut uart1 = Uart::new( //! # peripherals.UART1, //! # Config::default(), //! # peripherals.GPIO1, @@ -68,7 +69,7 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::uart::{Config, Uart}; -//! # let mut uart1 = Uart::new_with_config( +//! # let mut uart1 = Uart::new( //! # peripherals.UART1, //! # Config::default(), //! # peripherals.GPIO1, @@ -86,12 +87,13 @@ //! ### Inverting RX and TX Pins //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::Uart; +//! # use esp_hal::uart::{Config, Uart}; //! //! let (rx, _) = peripherals.GPIO2.split(); //! let (_, tx) = peripherals.GPIO1.split(); //! let mut uart1 = Uart::new( //! peripherals.UART1, +//! Config::default(), //! rx.inverted(), //! tx.inverted(), //! ).unwrap(); @@ -101,10 +103,18 @@ //! ### Constructing RX and TX Components //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{UartTx, UartRx}; +//! # use esp_hal::uart::{Config, UartTx, UartRx}; //! -//! let tx = UartTx::new(peripherals.UART0, peripherals.GPIO1).unwrap(); -//! let rx = UartRx::new(peripherals.UART1, peripherals.GPIO2).unwrap(); +//! let tx = UartTx::new( +//! peripherals.UART0, +//! Config::default(), +//! peripherals.GPIO1, +//! ).unwrap(); +//! let rx = UartRx::new( +//! peripherals.UART1, +//! Config::default(), +//! peripherals.GPIO2, +//! ).unwrap(); //! # } //! ``` //! @@ -145,7 +155,7 @@ )] //! let config = Config::default().rx_fifo_full_threshold(30); //! -//! let mut uart0 = Uart::new_with_config( +//! let mut uart0 = Uart::new( //! peripherals.UART0, //! config, //! tx_pin, @@ -560,7 +570,7 @@ where self } - fn init(self, config: Config) -> Result, Error> { + fn init(self, config: Config) -> Result, ConfigError> { let rx_guard = PeripheralGuard::new(self.uart.parts().0.peripheral); let tx_guard = PeripheralGuard::new(self.uart.parts().0.peripheral); @@ -602,13 +612,23 @@ pub struct UartRx<'d, M, T = AnyUart> { guard: PeripheralGuard, } +/// A configuration error. +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ConfigError { + /// The requested timeout is not supported. + UnsupportedTimeout, + /// The requested fifo threshold is not supported. + UnsupportedFifoThreshold, +} + impl SetConfig for Uart<'_, M, T> where T: Instance, M: Mode, { type Config = Config; - type ConfigError = Error; + type ConfigError = ConfigError; fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { self.apply_config(config) @@ -621,7 +641,7 @@ where M: Mode, { type Config = Config; - type ConfigError = Error; + type ConfigError = ConfigError; fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { self.apply_config(config) @@ -634,7 +654,7 @@ where M: Mode, { type Config = Config; - type ConfigError = Error; + type ConfigError = ConfigError; fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { self.apply_config(config) @@ -658,9 +678,8 @@ where /// Change the configuration. /// /// Note that this also changes the configuration of the RX half. - pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { - self.uart.info().apply_config(config)?; - Ok(()) + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.uart.info().apply_config(config) } /// Writes bytes @@ -748,20 +767,11 @@ where impl<'d> UartTx<'d, Blocking> { /// Create a new UART TX instance in [`Blocking`] mode. pub fn new( - uart: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_typed(uart.map_into(), tx) - } - - /// Create a new UART TX instance with configuration options in - /// [`Blocking`] mode. - pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, tx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_with_config_typed(uart.map_into(), config, tx) + ) -> Result { + Self::new_typed(uart.map_into(), config, tx) } } @@ -771,19 +781,10 @@ where { /// Create a new UART TX instance in [`Blocking`] mode. pub fn new_typed( - uart: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_with_config_typed(uart, Config::default(), tx) - } - - /// Create a new UART TX instance with configuration options in - /// [`Blocking`] mode. - pub fn new_with_config_typed( uart: impl Peripheral

+ 'd, config: Config, tx: impl Peripheral

+ 'd, - ) -> Result { + ) -> Result { let (_, uart_tx) = UartBuilder::<'d, Blocking, T>::new(uart) .with_tx(tx) .init(config)? @@ -868,9 +869,8 @@ where /// Change the configuration. /// /// Note that this also changes the configuration of the TX half. - pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { - self.uart.info().apply_config(config)?; - Ok(()) + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.uart.info().apply_config(config) } /// Fill a buffer with received bytes @@ -1006,20 +1006,11 @@ where impl<'d> UartRx<'d, Blocking> { /// Create a new UART RX instance in [`Blocking`] mode. pub fn new( - uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - ) -> Result { - UartRx::new_typed(uart.map_into(), rx) - } - - /// Create a new UART RX instance with configuration options in - /// [`Blocking`] mode. - pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, rx: impl Peripheral

+ 'd, - ) -> Result { - UartRx::new_with_config_typed(uart.map_into(), config, rx) + ) -> Result { + UartRx::new_typed(uart.map_into(), config, rx) } } @@ -1029,19 +1020,10 @@ where { /// Create a new UART RX instance in [`Blocking`] mode. pub fn new_typed( - uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_with_config_typed(uart, Config::default(), rx) - } - - /// Create a new UART RX instance with configuration options in - /// [`Blocking`] mode. - pub fn new_with_config_typed( uart: impl Peripheral

+ 'd, config: Config, rx: impl Peripheral

+ 'd, - ) -> Result { + ) -> Result { let (uart_rx, _) = UartBuilder::new(uart).with_rx(rx).init(config)?.split(); Ok(uart_rx) @@ -1089,22 +1071,12 @@ where impl<'d> Uart<'d, Blocking> { /// Create a new UART instance in [`Blocking`] mode. pub fn new( - uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_typed(uart.map_into(), rx, tx) - } - - /// Create a new UART instance with configuration options in - /// [`Blocking`] mode. - pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, rx: impl Peripheral

+ 'd, tx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_with_config_typed(uart.map_into(), config, rx, tx) + ) -> Result { + Self::new_typed(uart.map_into(), config, rx, tx) } } @@ -1114,21 +1086,11 @@ where { /// Create a new UART instance in [`Blocking`] mode. pub fn new_typed( - uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_with_config_typed(uart, Config::default(), rx, tx) - } - - /// Create a new UART instance with configuration options in - /// [`Blocking`] mode. - pub fn new_with_config_typed( uart: impl Peripheral

+ 'd, config: Config, rx: impl Peripheral

+ 'd, tx: impl Peripheral

+ 'd, - ) -> Result { + ) -> Result { UartBuilder::new(uart).with_tx(tx).with_rx(rx).init(config) } @@ -1251,26 +1213,6 @@ where sync_regs(register_block); } - /// Listen for the given interrupts - pub fn listen(&mut self, interrupts: impl Into>) { - self.tx.uart.info().enable_listen(interrupts.into(), true) - } - - /// Unlisten the given interrupts - pub fn unlisten(&mut self, interrupts: impl Into>) { - self.tx.uart.info().enable_listen(interrupts.into(), false) - } - - /// Gets asserted interrupts - pub fn interrupts(&mut self) -> EnumSet { - self.tx.uart.info().interrupts() - } - - /// Resets asserted interrupts - pub fn clear_interrupts(&mut self, interrupts: EnumSet) { - self.tx.uart.info().clear_interrupts(interrupts) - } - /// Write a byte out over the UART pub fn write_byte(&mut self, word: u8) -> nb::Result<(), Error> { self.tx.write_byte(word) @@ -1287,13 +1229,13 @@ where } /// Change the configuration. - pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { self.rx.apply_config(config)?; Ok(()) } #[inline(always)] - fn init(&mut self, config: Config) -> Result<(), Error> { + fn init(&mut self, config: Config) -> Result<(), ConfigError> { cfg_if::cfg_if! { if #[cfg(any(esp32, esp32s2))] { // Nothing to do @@ -1377,6 +1319,31 @@ where } } +impl Uart<'_, Blocking, T> +where + T: Instance, +{ + /// Listen for the given interrupts + pub fn listen(&mut self, interrupts: impl Into>) { + self.tx.uart.info().enable_listen(interrupts.into(), true) + } + + /// Unlisten the given interrupts + pub fn unlisten(&mut self, interrupts: impl Into>) { + self.tx.uart.info().enable_listen(interrupts.into(), false) + } + + /// Gets asserted interrupts + pub fn interrupts(&mut self) -> EnumSet { + self.tx.uart.info().interrupts() + } + + /// Resets asserted interrupts + pub fn clear_interrupts(&mut self, interrupts: EnumSet) { + self.tx.uart.info().clear_interrupts(interrupts) + } +} + impl ufmt_write::uWrite for Uart<'_, M, T> where T: Instance, @@ -2020,9 +1987,14 @@ pub mod lp_uart { } impl LpUart { - /// Initialize the UART driver using the default configuration + /// Initialize the UART driver using the provided configuration // TODO: CTS and RTS pins - pub fn new(uart: LP_UART, _tx: LowPowerOutput<'_, 5>, _rx: LowPowerInput<'_, 4>) -> Self { + pub fn new( + uart: LP_UART, + config: Config, + _tx: LowPowerOutput<'_, 5>, + _rx: LowPowerInput<'_, 4>, + ) -> Self { let lp_io = unsafe { crate::peripherals::LP_IO::steal() }; let lp_aon = unsafe { crate::peripherals::LP_AON::steal() }; @@ -2037,11 +2009,6 @@ pub mod lp_uart { lp_io.gpio(4).modify(|_, w| unsafe { w.mcu_sel().bits(1) }); lp_io.gpio(5).modify(|_, w| unsafe { w.mcu_sel().bits(1) }); - Self::new_with_config(uart, Config::default()) - } - - /// Initialize the UART driver using the provided configuration - pub fn new_with_config(uart: LP_UART, config: Config) -> Self { let mut me = Self { uart }; // Set UART mode - do nothing for LP @@ -2332,7 +2299,7 @@ impl Info { crate::interrupt::disable(crate::Cpu::current(), self.interrupt); } - fn apply_config(&self, config: &Config) -> Result<(), Error> { + fn apply_config(&self, config: &Config) -> Result<(), ConfigError> { self.set_rx_fifo_full_threshold(config.rx_fifo_full_threshold)?; self.set_rx_timeout(config.rx_timeout, config.symbol_length())?; self.change_baud(config.baudrate, config.clock_source); @@ -2350,13 +2317,13 @@ impl Info { /// Configures the RX-FIFO threshold /// /// # Errors - /// `Err(Error::InvalidArgument)` if provided value exceeds maximum value + /// [`Err(ConfigError::UnsupportedFifoThreshold)`][ConfigError::UnsupportedFifoThreshold] if provided value exceeds maximum value /// for SOC : /// - `esp32` **0x7F** /// - `esp32c6`, `esp32h2` **0xFF** /// - `esp32c3`, `esp32c2`, `esp32s2` **0x1FF** /// - `esp32s3` **0x3FF** - fn set_rx_fifo_full_threshold(&self, threshold: u16) -> Result<(), Error> { + fn set_rx_fifo_full_threshold(&self, threshold: u16) -> Result<(), ConfigError> { #[cfg(esp32)] const MAX_THRHD: u16 = 0x7F; #[cfg(any(esp32c6, esp32h2))] @@ -2367,7 +2334,7 @@ impl Info { const MAX_THRHD: u16 = 0x3FF; if threshold > MAX_THRHD { - return Err(Error::InvalidArgument); + return Err(ConfigError::UnsupportedFifoThreshold); } self.register_block() @@ -2384,12 +2351,13 @@ impl Info { /// triggering a timeout. Pass None to disable the timeout. /// /// # Errors - /// `Err(Error::InvalidArgument)` if the provided value exceeds the maximum - /// value for SOC : + /// [`Err(ConfigError::UnsupportedTimeout)`][ConfigError::UnsupportedTimeout] if the provided value exceeds + /// the maximum value for SOC : /// - `esp32`: Symbol size is fixed to 8, do not pass a value > **0x7F**. /// - `esp32c2`, `esp32c3`, `esp32c6`, `esp32h2`, esp32s2`, esp32s3`: The /// value you pass times the symbol size must be <= **0x3FF** - fn set_rx_timeout(&self, timeout: Option, _symbol_len: u8) -> Result<(), Error> { + // TODO: the above should be a per-chip doc line. + fn set_rx_timeout(&self, timeout: Option, _symbol_len: u8) -> Result<(), ConfigError> { cfg_if::cfg_if! { if #[cfg(esp32)] { const MAX_THRHD: u8 = 0x7F; // 7 bits @@ -2409,7 +2377,7 @@ impl Info { let timeout_reg = timeout as u16 * _symbol_len as u16; if timeout_reg > MAX_THRHD { - return Err(Error::InvalidArgument); + return Err(ConfigError::UnsupportedTimeout); } cfg_if::cfg_if! { diff --git a/examples/src/bin/embassy_serial.rs b/examples/src/bin/embassy_serial.rs index 643bac9285c..bf552412eb1 100644 --- a/examples/src/bin/embassy_serial.rs +++ b/examples/src/bin/embassy_serial.rs @@ -89,7 +89,7 @@ async fn main(spawner: Spawner) { let config = Config::default().rx_fifo_full_threshold(READ_BUF_SIZE as u16); - let mut uart0 = Uart::new_with_config(peripherals.UART0, config, rx_pin, tx_pin) + let mut uart0 = Uart::new(peripherals.UART0, config, rx_pin, tx_pin) .unwrap() .into_async(); uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); diff --git a/examples/src/bin/embassy_spi.rs b/examples/src/bin/embassy_spi.rs index de95e0714f6..1006ac47f22 100644 --- a/examples/src/bin/embassy_spi.rs +++ b/examples/src/bin/embassy_spi.rs @@ -57,12 +57,13 @@ async fn main(_spawner: Spawner) { let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new_with_config( + let mut spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_sck(sclk) .with_mosi(mosi) .with_miso(miso) diff --git a/examples/src/bin/ieee802154_sniffer.rs b/examples/src/bin/ieee802154_sniffer.rs index e8ac85cffdc..05babb0c4fe 100644 --- a/examples/src/bin/ieee802154_sniffer.rs +++ b/examples/src/bin/ieee802154_sniffer.rs @@ -8,7 +8,11 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{prelude::*, reset::software_reset, uart::Uart}; +use esp_hal::{ + prelude::*, + reset::software_reset, + uart::{self, Uart}, +}; use esp_ieee802154::{Config, Ieee802154}; use esp_println::println; @@ -25,7 +29,13 @@ fn main() -> ! { } } - let mut uart0 = Uart::new(peripherals.UART0, &mut rx_pin, &mut tx_pin).unwrap(); + let mut uart0 = Uart::new( + peripherals.UART0, + uart::Config::default(), + &mut rx_pin, + &mut tx_pin, + ) + .unwrap(); // read two characters which get parsed as the channel let mut cnt = 0; diff --git a/examples/src/bin/spi_loopback.rs b/examples/src/bin/spi_loopback.rs index 191ebdee802..74b0ed963e3 100644 --- a/examples/src/bin/spi_loopback.rs +++ b/examples/src/bin/spi_loopback.rs @@ -37,12 +37,13 @@ fn main() -> ! { let miso = unsafe { miso_mosi.clone_unchecked() }; - let mut spi = Spi::new_with_config( + let mut spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_sck(sclk) .with_miso(miso) // order matters .with_mosi(miso_mosi) // order matters diff --git a/examples/src/bin/spi_loopback_dma_psram.rs b/examples/src/bin/spi_loopback_dma_psram.rs index 02d53a00274..2d666807cbe 100644 --- a/examples/src/bin/spi_loopback_dma_psram.rs +++ b/examples/src/bin/spi_loopback_dma_psram.rs @@ -88,12 +88,13 @@ fn main() -> ! { let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); // Need to set miso first so that mosi can overwrite the // output connection (because we are using the same pin to loop back) - let mut spi = Spi::new_with_config( + let mut spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_sck(sclk) .with_miso(miso) .with_mosi(mosi) diff --git a/hil-test/tests/embassy_interrupt_spi_dma.rs b/hil-test/tests/embassy_interrupt_spi_dma.rs index c59f51e5431..a1dfc346cad 100644 --- a/hil-test/tests/embassy_interrupt_spi_dma.rs +++ b/hil-test/tests/embassy_interrupt_spi_dma.rs @@ -118,12 +118,13 @@ mod test { let (_, mosi) = hil_test::common_test_pins!(peripherals); - let mut spi = Spi::new_with_config( + let mut spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(10000.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_miso(unsafe { mosi.clone_unchecked() }) .with_mosi(mosi) .with_dma(dma_channel1) @@ -131,12 +132,13 @@ mod test { .into_async(); #[cfg(any(esp32, esp32s2, esp32s3))] - let other_peripheral = Spi::new_with_config( + let other_peripheral = Spi::new( peripherals.SPI3, Config::default() .with_frequency(10000.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_dma(dma_channel2) .into_async(); @@ -225,12 +227,13 @@ mod test { let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new_with_config( + let mut spi = Spi::new( peripherals.spi, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_dma(peripherals.dma_channel) .with_buffers(dma_rx_buf, dma_tx_buf) .into_async(); diff --git a/hil-test/tests/i2c.rs b/hil-test/tests/i2c.rs index 8f786717c9b..b30980ab8b8 100644 --- a/hil-test/tests/i2c.rs +++ b/hil-test/tests/i2c.rs @@ -41,6 +41,7 @@ mod tests { // Create a new peripheral object with the described wiring and standard // I2C clock speed: let i2c = I2c::new(peripherals.I2C0, Config::default()) + .unwrap() .with_sda(sda) .with_scl(scl); diff --git a/hil-test/tests/lcd_cam.rs b/hil-test/tests/lcd_cam.rs index c682ee25f73..1ff8942d7a0 100644 --- a/hil-test/tests/lcd_cam.rs +++ b/hil-test/tests/lcd_cam.rs @@ -10,7 +10,7 @@ use esp_hal::{ dma_buffers, gpio::Level, lcd_cam::{ - cam::{Camera, RxEightBits}, + cam::{self, Camera, RxEightBits}, lcd::{ dpi, dpi::{Dpi, Format, FrameTiming}, @@ -77,6 +77,7 @@ mod tests { polarity: Polarity::IdleHigh, phase: Phase::ShiftLow, }; + config.frequency = 500u32.kHz(); config.format = Format { enable_2byte_mode: false, ..Default::default() @@ -100,7 +101,8 @@ mod tests { config.de_idle_level = Level::Low; config.disable_black_region = false; - let dpi = Dpi::new(lcd_cam.lcd, tx_channel, 500u32.kHz(), config) + let dpi = Dpi::new(lcd_cam.lcd, tx_channel, config) + .unwrap() .with_vsync(vsync_out) .with_hsync(hsync_out) .with_de(de_out) @@ -114,12 +116,16 @@ mod tests { .with_data6(d6_out) .with_data7(d7_out); + let mut cam_config = cam::Config::default(); + cam_config.frequency = 1u32.MHz(); + let camera = Camera::new( lcd_cam.cam, rx_channel, RxEightBits::new(d0_in, d1_in, d2_in, d3_in, d4_in, d5_in, d6_in, d7_in), - 1u32.MHz(), + cam_config, ) + .unwrap() .with_ctrl_pins_and_de(vsync_in, hsync_in, de_in) .with_pixel_clock(pclk_in); diff --git a/hil-test/tests/lcd_cam_i8080.rs b/hil-test/tests/lcd_cam_i8080.rs index 0ce6e842cd6..1eb15a5ed7f 100644 --- a/hil-test/tests/lcd_cam_i8080.rs +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -75,7 +75,12 @@ mod tests { fn test_i8080_8bit(ctx: Context<'static>) { let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin); - let i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, 20.MHz(), Config::default()); + let i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, { + let mut config = Config::default(); + config.frequency = 20.MHz(); + config + }) + .unwrap(); let xfer = i8080.send(Command::::None, 0, ctx.dma_buf).unwrap(); xfer.wait().0.unwrap(); @@ -132,9 +137,14 @@ mod tests { NoPin, ); - let mut i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, 20.MHz(), Config::default()) - .with_cs(cs_signal) - .with_ctrl_pins(NoPin, NoPin); + let mut i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, { + let mut config = Config::default(); + config.frequency = 20.MHz(); + config + }) + .unwrap() + .with_cs(cs_signal) + .with_ctrl_pins(NoPin, NoPin); // This is to make the test values look more intuitive. i8080.set_bit_order(BitOrder::Inverted); @@ -244,9 +254,14 @@ mod tests { unit3_signal, ); - let mut i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, 20.MHz(), Config::default()) - .with_cs(cs_signal) - .with_ctrl_pins(NoPin, NoPin); + let mut i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, { + let mut config = Config::default(); + config.frequency = 20.MHz(); + config + }) + .unwrap() + .with_cs(cs_signal) + .with_ctrl_pins(NoPin, NoPin); // This is to make the test values look more intuitive. i8080.set_bit_order(BitOrder::Inverted); diff --git a/hil-test/tests/lcd_cam_i8080_async.rs b/hil-test/tests/lcd_cam_i8080_async.rs index f9c49f8f4f0..f72d6dec896 100644 --- a/hil-test/tests/lcd_cam_i8080_async.rs +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -51,7 +51,12 @@ mod tests { async fn test_i8080_8bit(ctx: Context<'static>) { let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin); - let i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, 20.MHz(), Config::default()); + let i8080 = I8080::new(ctx.lcd_cam.lcd, ctx.dma, pins, { + let mut config = Config::default(); + config.frequency = 20.MHz(); + config + }) + .unwrap(); let mut transfer = i8080.send(Command::::None, 0, ctx.dma_buf).unwrap(); diff --git a/hil-test/tests/qspi.rs b/hil-test/tests/qspi.rs index 95033daf631..0ac67b93915 100644 --- a/hil-test/tests/qspi.rs +++ b/hil-test/tests/qspi.rs @@ -200,12 +200,13 @@ mod tests { } } - let spi = Spi::new_with_config( + let spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), - ); + ) + .unwrap(); Context { spi, diff --git a/hil-test/tests/spi_full_duplex.rs b/hil-test/tests/spi_full_duplex.rs index 1a5517b8412..76b09cf47f2 100644 --- a/hil-test/tests/spi_full_duplex.rs +++ b/hil-test/tests/spi_full_duplex.rs @@ -73,11 +73,11 @@ mod tests { let (mosi_loopback_pcnt, mosi) = mosi.split(); // Need to set miso first so that mosi can overwrite the // output connection (because we are using the same pin to loop back) - let spi = - Spi::new_with_config(peripherals.SPI2, Config::default().with_frequency(10.MHz())) - .with_sck(sclk) - .with_miso(unsafe { mosi.clone_unchecked() }) - .with_mosi(mosi); + let spi = Spi::new(peripherals.SPI2, Config::default().with_frequency(10.MHz())) + .unwrap() + .with_sck(sclk) + .with_miso(unsafe { mosi.clone_unchecked() }) + .with_mosi(mosi); let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000); diff --git a/hil-test/tests/spi_half_duplex_read.rs b/hil-test/tests/spi_half_duplex_read.rs index 27564c02a6a..5daba6dc8f8 100644 --- a/hil-test/tests/spi_half_duplex_read.rs +++ b/hil-test/tests/spi_half_duplex_read.rs @@ -46,12 +46,13 @@ mod tests { } } - let spi = Spi::new_with_config( + let spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_sck(sclk) .with_miso(miso) .with_dma(dma_channel); diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index a58ea3c4977..d65e452a2b6 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -50,12 +50,13 @@ mod tests { let (mosi_loopback, mosi) = mosi.split(); - let spi = Spi::new_with_config( + let spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_sck(sclk) .with_mosi(mosi) .with_dma(dma_channel); diff --git a/hil-test/tests/spi_half_duplex_write_psram.rs b/hil-test/tests/spi_half_duplex_write_psram.rs index 52fe8f75ca4..ff3ad7fc208 100644 --- a/hil-test/tests/spi_half_duplex_write_psram.rs +++ b/hil-test/tests/spi_half_duplex_write_psram.rs @@ -62,12 +62,13 @@ mod tests { let (mosi_loopback, mosi) = mosi.split(); - let spi = Spi::new_with_config( + let spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_sck(sclk) .with_mosi(mosi) .with_dma(dma_channel); diff --git a/hil-test/tests/uart.rs b/hil-test/tests/uart.rs index 85c5ab90418..4fed45a776a 100644 --- a/hil-test/tests/uart.rs +++ b/hil-test/tests/uart.rs @@ -31,7 +31,7 @@ mod tests { let (rx, tx) = pin.split(); - let uart = Uart::new(peripherals.UART1, rx, tx).unwrap(); + let uart = Uart::new(peripherals.UART1, uart::Config::default(), rx, tx).unwrap(); Context { uart } } diff --git a/hil-test/tests/uart_async.rs b/hil-test/tests/uart_async.rs index 7a36307c80d..82eede75cb7 100644 --- a/hil-test/tests/uart_async.rs +++ b/hil-test/tests/uart_async.rs @@ -6,7 +6,10 @@ #![no_std] #![no_main] -use esp_hal::{uart::Uart, Async}; +use esp_hal::{ + uart::{self, Uart}, + Async, +}; use hil_test as _; struct Context { @@ -24,7 +27,9 @@ mod tests { let (rx, tx) = hil_test::common_test_pins!(peripherals); - let uart = Uart::new(peripherals.UART0, rx, tx).unwrap().into_async(); + let uart = Uart::new(peripherals.UART0, uart::Config::default(), rx, tx) + .unwrap() + .into_async(); Context { uart } } diff --git a/hil-test/tests/uart_regression.rs b/hil-test/tests/uart_regression.rs index 841f17d7f23..a88124196d1 100644 --- a/hil-test/tests/uart_regression.rs +++ b/hil-test/tests/uart_regression.rs @@ -11,7 +11,7 @@ mod tests { use esp_hal::{ gpio::OutputPin, prelude::*, - uart::{UartRx, UartTx}, + uart::{self, UartRx, UartTx}, }; use hil_test as _; use nb::block; @@ -23,7 +23,7 @@ mod tests { let (rx, mut tx) = hil_test::common_test_pins!(peripherals); - let mut rx = UartRx::new(peripherals.UART1, rx).unwrap(); + let mut rx = UartRx::new(peripherals.UART1, uart::Config::default(), rx).unwrap(); // start reception _ = rx.read_byte(); // this will just return WouldBlock @@ -31,7 +31,7 @@ mod tests { unsafe { tx.set_output_high(false, esp_hal::Internal::conjure()) }; // set up TX and send a byte - let mut tx = UartTx::new(peripherals.UART0, tx).unwrap(); + let mut tx = UartTx::new(peripherals.UART0, uart::Config::default(), tx).unwrap(); tx.flush_tx().unwrap(); tx.write_bytes(&[0x42]).unwrap(); diff --git a/hil-test/tests/uart_tx_rx.rs b/hil-test/tests/uart_tx_rx.rs index 8274a9043db..20c3fc18612 100644 --- a/hil-test/tests/uart_tx_rx.rs +++ b/hil-test/tests/uart_tx_rx.rs @@ -7,7 +7,7 @@ use esp_hal::{ prelude::*, - uart::{UartRx, UartTx}, + uart::{self, UartRx, UartTx}, Blocking, }; use hil_test as _; @@ -29,8 +29,8 @@ mod tests { let (rx, tx) = hil_test::common_test_pins!(peripherals); - let tx = UartTx::new(peripherals.UART0, tx).unwrap(); - let rx = UartRx::new(peripherals.UART1, rx).unwrap(); + let tx = UartTx::new(peripherals.UART0, uart::Config::default(), tx).unwrap(); + let rx = UartRx::new(peripherals.UART1, uart::Config::default(), rx).unwrap(); Context { rx, tx } } diff --git a/hil-test/tests/uart_tx_rx_async.rs b/hil-test/tests/uart_tx_rx_async.rs index 4f02cf63bda..8fbb0c226c2 100644 --- a/hil-test/tests/uart_tx_rx_async.rs +++ b/hil-test/tests/uart_tx_rx_async.rs @@ -7,7 +7,7 @@ #![no_main] use esp_hal::{ - uart::{UartRx, UartTx}, + uart::{self, UartRx, UartTx}, Async, }; use hil_test as _; @@ -28,8 +28,12 @@ mod tests { let (rx, tx) = hil_test::common_test_pins!(peripherals); - let tx = UartTx::new(peripherals.UART0, tx).unwrap().into_async(); - let rx = UartRx::new(peripherals.UART1, rx).unwrap().into_async(); + let tx = UartTx::new(peripherals.UART0, uart::Config::default(), tx) + .unwrap() + .into_async(); + let rx = UartRx::new(peripherals.UART1, uart::Config::default(), rx) + .unwrap() + .into_async(); Context { rx, tx } } diff --git a/qa-test/src/bin/embassy_i2c.rs b/qa-test/src/bin/embassy_i2c.rs index e48f903944c..45a58efcec7 100644 --- a/qa-test/src/bin/embassy_i2c.rs +++ b/qa-test/src/bin/embassy_i2c.rs @@ -38,6 +38,7 @@ async fn main(_spawner: Spawner) { config.frequency = 400.kHz(); config }) + .unwrap() .with_sda(peripherals.GPIO4) .with_scl(peripherals.GPIO5) .into_async(); diff --git a/qa-test/src/bin/embassy_i2c_bmp180_calibration_data.rs b/qa-test/src/bin/embassy_i2c_bmp180_calibration_data.rs index 328ee3aa7b4..77af5f4c76d 100644 --- a/qa-test/src/bin/embassy_i2c_bmp180_calibration_data.rs +++ b/qa-test/src/bin/embassy_i2c_bmp180_calibration_data.rs @@ -37,6 +37,7 @@ async fn main(_spawner: Spawner) { config.frequency = 400.kHz(); config }) + .unwrap() .with_sda(peripherals.GPIO4) .with_scl(peripherals.GPIO5) .into_async(); diff --git a/qa-test/src/bin/i2c_bmp180_calibration_data.rs b/qa-test/src/bin/i2c_bmp180_calibration_data.rs index 2d765fc31c2..593765d16ee 100644 --- a/qa-test/src/bin/i2c_bmp180_calibration_data.rs +++ b/qa-test/src/bin/i2c_bmp180_calibration_data.rs @@ -25,6 +25,7 @@ fn main() -> ! { // Create a new peripheral object with the described wiring and standard // I2C clock speed: let mut i2c = I2c::new(peripherals.I2C0, Config::default()) + .unwrap() .with_sda(peripherals.GPIO4) .with_scl(peripherals.GPIO5); diff --git a/qa-test/src/bin/i2c_display.rs b/qa-test/src/bin/i2c_display.rs index c950def05fb..5c64ee40cb9 100644 --- a/qa-test/src/bin/i2c_display.rs +++ b/qa-test/src/bin/i2c_display.rs @@ -38,6 +38,7 @@ fn main() -> ! { // Create a new peripheral object with the described wiring // and standard I2C clock speed let i2c = I2c::new(peripherals.I2C0, Config::default()) + .unwrap() .with_sda(peripherals.GPIO4) .with_scl(peripherals.GPIO5); diff --git a/qa-test/src/bin/lcd_cam_ov2640.rs b/qa-test/src/bin/lcd_cam_ov2640.rs index 14f8b690383..0a5c9314640 100644 --- a/qa-test/src/bin/lcd_cam_ov2640.rs +++ b/qa-test/src/bin/lcd_cam_ov2640.rs @@ -34,7 +34,7 @@ use esp_hal::{ master::{Config, I2c}, }, lcd_cam::{ - cam::{Camera, RxEightBits}, + cam::{self, Camera, RxEightBits}, LcdCam, }, prelude::*, @@ -65,8 +65,12 @@ fn main() -> ! { peripherals.GPIO16, ); + let mut cam_config = cam::Config::default(); + cam_config.frequency = 20u32.MHz(); + let lcd_cam = LcdCam::new(peripherals.LCD_CAM); - let camera = Camera::new(lcd_cam.cam, peripherals.DMA_CH0, cam_data_pins, 20u32.MHz()) + let camera = Camera::new(lcd_cam.cam, peripherals.DMA_CH0, cam_data_pins, cam_config) + .unwrap() .with_master_clock(cam_xclk) .with_pixel_clock(cam_pclk) .with_ctrl_pins(cam_vsync, cam_href); @@ -76,6 +80,7 @@ fn main() -> ! { delay.delay_millis(500u32); let i2c = I2c::new(peripherals.I2C0, Config::default()) + .unwrap() .with_sda(cam_siod) .with_scl(cam_sioc); diff --git a/qa-test/src/bin/lcd_dpi.rs b/qa-test/src/bin/lcd_dpi.rs index 7b3b0e04fd6..5b85b7ef16a 100644 --- a/qa-test/src/bin/lcd_dpi.rs +++ b/qa-test/src/bin/lcd_dpi.rs @@ -62,6 +62,7 @@ fn main() -> ! { peripherals.I2C0, i2c::master::Config::default().with_frequency(400.kHz()), ) + .unwrap() .with_sda(peripherals.GPIO47) .with_scl(peripherals.GPIO48); @@ -137,6 +138,7 @@ fn main() -> ! { polarity: Polarity::IdleLow, phase: Phase::ShiftLow, }; + config.frequency = 16.MHz(); config.format = Format { enable_2byte_mode: true, ..Default::default() @@ -160,7 +162,8 @@ fn main() -> ! { config.de_idle_level = Level::Low; config.disable_black_region = false; - let mut dpi = Dpi::new(lcd_cam.lcd, tx_channel, 16.MHz(), config) + let mut dpi = Dpi::new(lcd_cam.lcd, tx_channel, config) + .unwrap() .with_vsync(vsync_pin) .with_hsync(peripherals.GPIO46) .with_de(peripherals.GPIO17) diff --git a/qa-test/src/bin/lcd_i8080.rs b/qa-test/src/bin/lcd_i8080.rs index 88d8c96aab4..662201d1a9b 100644 --- a/qa-test/src/bin/lcd_i8080.rs +++ b/qa-test/src/bin/lcd_i8080.rs @@ -67,14 +67,11 @@ fn main() -> ! { ); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); - let i8080 = I8080::new( - lcd_cam.lcd, - peripherals.DMA_CH0, - tx_pins, - 20.MHz(), - Config::default(), - ) - .with_ctrl_pins(lcd_rs, lcd_wr); + let mut i8080_config = Config::default(); + i8080_config.frequency = 20.MHz(); + let i8080 = I8080::new(lcd_cam.lcd, peripherals.DMA_CH0, tx_pins, i8080_config) + .unwrap() + .with_ctrl_pins(lcd_rs, lcd_wr); // Note: This isn't provided in the HAL since different drivers may require // different considerations, like how to manage the CS pin, the CD pin, diff --git a/qa-test/src/bin/qspi_flash.rs b/qa-test/src/bin/qspi_flash.rs index 7b960f9fdd8..04e8133e4a6 100644 --- a/qa-test/src/bin/qspi_flash.rs +++ b/qa-test/src/bin/qspi_flash.rs @@ -75,12 +75,13 @@ fn main() -> ! { let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new_with_config( + let mut spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_sck(sclk) .with_mosi(mosi) .with_miso(miso) diff --git a/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs b/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs index 3faae70cacc..58b6feff24e 100644 --- a/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs +++ b/qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs @@ -61,12 +61,13 @@ fn main() -> ! { } } - let mut spi = Spi::new_with_config( + let mut spi = Spi::new( peripherals.SPI2, Config::default() .with_frequency(100.kHz()) .with_mode(SpiMode::Mode0), ) + .unwrap() .with_sck(sclk) .with_mosi(mosi) .with_miso(miso) From b6117d5040de965de510f575112a70d03112cf15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Thu, 28 Nov 2024 11:52:11 +0100 Subject: [PATCH 22/27] Use opt-level to detect debug profile (#2622) --- examples/build.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/build.rs b/examples/build.rs index 848be4d2612..6b0dafb09ba 100644 --- a/examples/build.rs +++ b/examples/build.rs @@ -2,8 +2,11 @@ fn main() { // Allow building examples in CI in debug mode println!("cargo:rustc-check-cfg=cfg(is_not_release)"); println!("cargo:rerun-if-env-changed=CI"); - #[cfg(debug_assertions)] if std::env::var("CI").is_err() { - println!("cargo::rustc-cfg=is_not_release"); + if let Ok(level) = std::env::var("OPT_LEVEL") { + if level == "0" || level == "1" { + println!("cargo::rustc-cfg=is_not_release"); + } + } } } From 2d87bb000267b971a9d6c801119bb0a20d8bd4da Mon Sep 17 00:00:00 2001 From: Danila Gornushko Date: Thu, 28 Nov 2024 16:07:36 +0300 Subject: [PATCH 23/27] Add heap usage stats with defmt (#2619) * feat(esp-alloc): Add heap usage stats and provide `esp_alloc::get_info!()` macro * refactor(esp-alloc): Feature gate internal memory usage that requires extra computation. - Introduce the `internal-heap-stats` feature for `esp-alloc`. - Add `esp_alloc::get_info!()` to `psram_quad` example to show usage and ensure coverage of the feature in tests. * refactor(esp-alloc): Remove `get_info!()` macro in favour of documenting `HEAP.stats()` * Implement defmt::Format for HeapStats and RegionStats * rustfmt * show usage percent + move bar drawing logic to separate functions * update doc comments * Fixed a typo in qa-test/src/bin/psram_quad.rs Co-authored-by: Scott Mabin * minor improvements to write bar functions * Aligned the indentation in Cargo.toml Co-authored-by: Kirill Mikhailov <62840029+playfulFence@users.noreply.github.com> * Fixed a typo in docs Co-authored-by: Kirill Mikhailov <62840029+playfulFence@users.noreply.github.com> * Nitpicking x2 Co-authored-by: Kirill Mikhailov <62840029+playfulFence@users.noreply.github.com> * Surround a function call with backticks Co-authored-by: Kirill Mikhailov <62840029+playfulFence@users.noreply.github.com> * rustfmt --------- Co-authored-by: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Co-authored-by: Scott Mabin Co-authored-by: Kirill Mikhailov <62840029+playfulFence@users.noreply.github.com> --- esp-alloc/CHANGELOG.md | 2 + esp-alloc/Cargo.toml | 14 ++ esp-alloc/src/lib.rs | 292 +++++++++++++++++++++++++++++++++- qa-test/src/bin/psram_quad.rs | 4 +- 4 files changed, 308 insertions(+), 4 deletions(-) diff --git a/esp-alloc/CHANGELOG.md b/esp-alloc/CHANGELOG.md index 423eea685bf..18abc6f75b3 100644 --- a/esp-alloc/CHANGELOG.md +++ b/esp-alloc/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `esp_alloc::HEAP.stats()` can now be used to get heap usage informations (#2137) + ### Changed ### Fixed diff --git a/esp-alloc/Cargo.toml b/esp-alloc/Cargo.toml index 7f190893f36..17e20420926 100644 --- a/esp-alloc/Cargo.toml +++ b/esp-alloc/Cargo.toml @@ -23,10 +23,24 @@ default-target = "riscv32imc-unknown-none-elf" features = ["nightly"] [dependencies] +defmt = { version = "0.3.8", optional = true } +cfg-if = "1.0.0" critical-section = "1.1.3" enumset = "1.1.5" linked_list_allocator = { version = "0.10.5", default-features = false, features = ["const_mut_refs"] } +document-features = "0.2.10" [features] default = [] nightly = [] + +## Implement `defmt::Format` on certain types. +defmt = ["dep:defmt"] + +## Enable this feature if you want to keep stats about the internal heap usage such as: +## - Max memory usage since initialization of the heap +## - Total allocated memory since initialization of the heap +## - Total freed memory since initialization of the heap +## +## ⚠️ Note: Enabling this feature will require extra computation every time alloc/dealloc is called. +internal-heap-stats = [] diff --git a/esp-alloc/src/lib.rs b/esp-alloc/src/lib.rs index a24d966d31a..e20257cb14a 100644 --- a/esp-alloc/src/lib.rs +++ b/esp-alloc/src/lib.rs @@ -51,7 +51,28 @@ //! ```rust //! let large_buffer: Vec = Vec::with_capacity_in(1048576, &PSRAM_ALLOCATOR); //! ``` - +//! +//! You can also get stats about the heap usage at anytime with: +//! ```rust +//! let stats: HeapStats = esp_alloc::HEAP.stats(); +//! // HeapStats implements the Display and defmt::Format traits, so you can pretty-print the heap stats. +//! println!("{}", stats); +//! ``` +//! +//! ```txt +//! HEAP INFO +//! Size: 131068 +//! Current usage: 46148 +//! Max usage: 46148 +//! Total freed: 0 +//! Total allocated: 46148 +//! Memory Layout: +//! Internal | ████████████░░░░░░░░░░░░░░░░░░░░░░░ | Used: 35% (Used 46148 of 131068, free: 84920) +//! Unused | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | +//! Unused | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | +//! ``` +//! ## Feature Flags +#![doc = document_features::document_features!()] #![no_std] #![cfg_attr(feature = "nightly", feature(allocator_api))] #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] @@ -63,6 +84,7 @@ use core::alloc::{AllocError, Allocator}; use core::{ alloc::{GlobalAlloc, Layout}, cell::RefCell, + fmt::Display, ptr::{self, NonNull}, }; @@ -76,7 +98,22 @@ pub static HEAP: EspHeap = EspHeap::empty(); const NON_REGION: Option = None; -#[derive(EnumSetType)] +const BAR_WIDTH: usize = 35; + +fn write_bar(f: &mut core::fmt::Formatter<'_>, usage_percent: usize) -> core::fmt::Result { + let used_blocks = BAR_WIDTH * usage_percent / 100; + (0..used_blocks).try_for_each(|_| write!(f, "█"))?; + (used_blocks..BAR_WIDTH).try_for_each(|_| write!(f, "░")) +} + +#[cfg(feature = "defmt")] +fn write_bar_defmt(fmt: defmt::Formatter, usage_percent: usize) { + let used_blocks = BAR_WIDTH * usage_percent / 100; + (0..used_blocks).for_each(|_| defmt::write!(fmt, "█")); + (used_blocks..BAR_WIDTH).for_each(|_| defmt::write!(fmt, "░")); +} + +#[derive(EnumSetType, Debug)] /// Describes the properties of a memory region pub enum MemoryCapability { /// Memory must be internal; specifically it should not disappear when @@ -86,6 +123,75 @@ pub enum MemoryCapability { External, } +/// Stats for a heap region +#[derive(Debug)] +pub struct RegionStats { + /// Total usable size of the heap region in bytes. + size: usize, + + /// Currently used size of the heap region in bytes. + used: usize, + + /// Free size of the heap region in bytes. + free: usize, + + /// Capabilities of the memory region. + capabilities: EnumSet, +} + +impl Display for RegionStats { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let usage_percent = self.used * 100 / self.size; + + // Display Memory type + if self.capabilities.contains(MemoryCapability::Internal) { + write!(f, "Internal")?; + } else if self.capabilities.contains(MemoryCapability::External) { + write!(f, "External")?; + } else { + write!(f, "Unknown")?; + } + + write!(f, " | ")?; + + write_bar(f, usage_percent)?; + + write!( + f, + " | Used: {}% (Used {} of {}, free: {})", + usage_percent, self.used, self.size, self.free + ) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for RegionStats { + fn format(&self, fmt: defmt::Formatter) { + let usage_percent = self.used * 100 / self.size; + + if self.capabilities.contains(MemoryCapability::Internal) { + defmt::write!(fmt, "Internal"); + } else if self.capabilities.contains(MemoryCapability::External) { + defmt::write!(fmt, "External"); + } else { + defmt::write!(fmt, "Unknown"); + } + + defmt::write!(fmt, " | "); + + write_bar_defmt(fmt, usage_percent); + + defmt::write!( + fmt, + " | Used: {}% (Used {} of {}, free: {})", + usage_percent, + self.used, + self.size, + self.free + ); + } +} + /// A memory region to be used as heap memory pub struct HeapRegion { heap: Heap, @@ -112,6 +218,104 @@ impl HeapRegion { Self { heap, capabilities } } + + /// Return stats for the current memory region + pub fn stats(&self) -> RegionStats { + RegionStats { + size: self.heap.size(), + used: self.heap.used(), + free: self.heap.free(), + capabilities: self.capabilities, + } + } +} + +/// Stats for a heap allocator +/// +/// Enable the "internal-heap-stats" feature if you want collect additional heap +/// informations at the cost of extra cpu time during every alloc/dealloc. +#[derive(Debug)] +pub struct HeapStats { + /// Granular stats for all the configured memory regions. + region_stats: [Option; 3], + + /// Total size of all combined heap regions in bytes. + size: usize, + + /// Current usage of the heap across all configured regions in bytes. + current_usage: usize, + + /// Estimation of the max used heap in bytes. + #[cfg(feature = "internal-heap-stats")] + max_usage: usize, + + /// Estimation of the total allocated bytes since initialization. + #[cfg(feature = "internal-heap-stats")] + total_allocated: usize, + + /// Estimation of the total freed bytes since initialization. + #[cfg(feature = "internal-heap-stats")] + total_freed: usize, +} + +impl Display for HeapStats { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + writeln!(f, "HEAP INFO")?; + writeln!(f, "Size: {}", self.size)?; + writeln!(f, "Current usage: {}", self.current_usage)?; + #[cfg(feature = "internal-heap-stats")] + { + writeln!(f, "Max usage: {}", self.max_usage)?; + writeln!(f, "Total freed: {}", self.total_freed)?; + writeln!(f, "Total allocated: {}", self.total_allocated)?; + } + writeln!(f, "Memory Layout: ")?; + for region in self.region_stats.iter() { + if let Some(region) = region.as_ref() { + region.fmt(f)?; + writeln!(f)?; + } else { + // Display unused memory regions + write!(f, "Unused | ")?; + write_bar(f, 0)?; + writeln!(f, " |")?; + } + } + Ok(()) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for HeapStats { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "HEAP INFO\n"); + defmt::write!(fmt, "Size: {}\n", self.size); + defmt::write!(fmt, "Current usage: {}\n", self.current_usage); + #[cfg(feature = "internal-heap-stats")] + { + defmt::write!(fmt, "Max usage: {}\n", self.max_usage); + defmt::write!(fmt, "Total freed: {}\n", self.total_freed); + defmt::write!(fmt, "Total allocated: {}\n", self.total_allocated); + } + defmt::write!(fmt, "Memory Layout:\n"); + for region in self.region_stats.iter() { + if let Some(region) = region.as_ref() { + defmt::write!(fmt, "{}\n", region); + } else { + defmt::write!(fmt, "Unused | "); + write_bar_defmt(fmt, 0); + defmt::write!(fmt, " |\n"); + } + } + } +} + +/// Internal stats to keep track across multiple regions. +#[cfg(feature = "internal-heap-stats")] +struct InternalHeapStats { + max_usage: usize, + total_allocated: usize, + total_freed: usize, } /// A memory allocator @@ -120,6 +324,8 @@ impl HeapRegion { /// memory in regions satisfying specific needs. pub struct EspHeap { heap: Mutex; 3]>>, + #[cfg(feature = "internal-heap-stats")] + internal_heap_stats: Mutex>, } impl EspHeap { @@ -127,6 +333,12 @@ impl EspHeap { pub const fn empty() -> Self { EspHeap { heap: Mutex::new(RefCell::new([NON_REGION; 3])), + #[cfg(feature = "internal-heap-stats")] + internal_heap_stats: Mutex::new(RefCell::new(InternalHeapStats { + max_usage: 0, + total_allocated: 0, + total_freed: 0, + })), } } @@ -189,6 +401,51 @@ impl EspHeap { }) } + /// Return usage stats for the [Heap]. + /// + /// Note: + /// [HeapStats] directly implements [Display], so this function can be + /// called from within `println!()` to pretty-print the usage of the + /// heap. + pub fn stats(&self) -> HeapStats { + const EMPTY_REGION_STAT: Option = None; + let mut region_stats: [Option; 3] = [EMPTY_REGION_STAT; 3]; + + critical_section::with(|cs| { + let mut used = 0; + let mut free = 0; + let regions = self.heap.borrow_ref(cs); + for (id, region) in regions.iter().enumerate() { + if let Some(region) = region.as_ref() { + let stats = region.stats(); + free += stats.free; + used += stats.used; + region_stats[id] = Some(region.stats()); + } + } + + cfg_if::cfg_if! { + if #[cfg(feature = "internal-heap-stats")] { + let internal_heap_stats = self.internal_heap_stats.borrow_ref(cs); + HeapStats { + region_stats, + size: free + used, + current_usage: used, + max_usage: internal_heap_stats.max_usage, + total_allocated: internal_heap_stats.total_allocated, + total_freed: internal_heap_stats.total_freed, + } + } else { + HeapStats { + region_stats, + size: free + used, + current_usage: used, + } + } + } + }) + } + /// Returns an estimate of the amount of bytes available. pub fn free(&self) -> usize { self.free_caps(EnumSet::empty()) @@ -232,6 +489,8 @@ impl EspHeap { layout: Layout, ) -> *mut u8 { critical_section::with(|cs| { + #[cfg(feature = "internal-heap-stats")] + let before = self.used(); let mut regions = self.heap.borrow_ref_mut(cs); let mut iter = (*regions).iter_mut().filter(|region| { if region.is_some() { @@ -256,7 +515,22 @@ impl EspHeap { } }; - res.map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) + res.map_or(ptr::null_mut(), |allocation| { + #[cfg(feature = "internal-heap-stats")] + { + let mut internal_heap_stats = self.internal_heap_stats.borrow_ref_mut(cs); + drop(regions); + // We need to call used because [linked_list_allocator::Heap] does internal size + // alignment so we cannot use the size provided by the layout. + let used = self.used(); + + internal_heap_stats.total_allocated += used - before; + internal_heap_stats.max_usage = + core::cmp::max(internal_heap_stats.max_usage, used); + } + + allocation.as_ptr() + }) }) } } @@ -272,6 +546,8 @@ unsafe impl GlobalAlloc for EspHeap { } critical_section::with(|cs| { + #[cfg(feature = "internal-heap-stats")] + let before = self.used(); let mut regions = self.heap.borrow_ref_mut(cs); let mut iter = (*regions).iter_mut(); @@ -280,6 +556,16 @@ unsafe impl GlobalAlloc for EspHeap { region.heap.deallocate(NonNull::new_unchecked(ptr), layout); } } + + #[cfg(feature = "internal-heap-stats")] + { + let mut internal_heap_stats = self.internal_heap_stats.borrow_ref_mut(cs); + drop(regions); + // We need to call `used()` because [linked_list_allocator::Heap] does internal + // size alignment so we cannot use the size provided by the + // layout. + internal_heap_stats.total_freed += before - self.used(); + } }) } } diff --git a/qa-test/src/bin/psram_quad.rs b/qa-test/src/bin/psram_quad.rs index 6eb9a3db411..e5df76ee466 100644 --- a/qa-test/src/bin/psram_quad.rs +++ b/qa-test/src/bin/psram_quad.rs @@ -3,7 +3,7 @@ //! You need an ESP32, ESP32-S2 or ESP32-S3 with at least 2 MB of PSRAM memory. //% CHIPS: esp32 esp32s2 esp32s3 -//% FEATURES: esp-hal/quad-psram +//% FEATURES: esp-hal/quad-psram esp-alloc/internal-heap-stats #![no_std] #![no_main] @@ -52,6 +52,8 @@ fn main() -> ! { let string = String::from("A string allocated in PSRAM"); println!("'{}' allocated at {:p}", &string, string.as_ptr()); + println!("{}", esp_alloc::HEAP.stats()); + println!("done"); loop {} From 2d3fdeb876b773122032e29bbd12e110f9652f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 2 Dec 2024 09:03:46 +0100 Subject: [PATCH 24/27] Don't require importing macro for esp-config (#2630) --- esp-config/CHANGELOG.md | 2 ++ esp-config/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/esp-config/CHANGELOG.md b/esp-config/CHANGELOG.md index eb5872f2c1c..94c435aa723 100644 --- a/esp-config/CHANGELOG.md +++ b/esp-config/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Users no longer have to manually import `esp_config_int_parse`. (#2630) + ### Changed ### Removed diff --git a/esp-config/src/lib.rs b/esp-config/src/lib.rs index 2ef0c18f9a9..e5133d27fe3 100644 --- a/esp-config/src/lib.rs +++ b/esp-config/src/lib.rs @@ -29,7 +29,7 @@ macro_rules! esp_config_int { ( $ty:ty, $var:expr ) => { const { const BYTES: &[u8] = env!($var).as_bytes(); - esp_config_int_parse!($ty, BYTES) + $crate::esp_config_int_parse!($ty, BYTES) } }; } From 9f3476b00651c541769b685299156496beb9aa8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 2 Dec 2024 11:49:06 +0100 Subject: [PATCH 25/27] Unstable documentation proof of concept (#2628) * Unstable documentation proof of concept * Hide the red banner in esp-wifi documentation * Changelog --- esp-hal/CHANGELOG.md | 1 + esp-hal/Cargo.toml | 4 ++++ esp-hal/MIGRATING-0.22.md | 6 ++++++ esp-hal/src/gpio/mod.rs | 1 + esp-hal/src/lib.rs | 3 ++- esp-metadata/src/lib.rs | 3 +++ esp-wifi/src/lib.rs | 2 +- hil-test/Cargo.toml | 2 +- qa-test/Cargo.toml | 2 +- xtask/src/cargo.rs | 12 ++++++++++++ xtask/src/lib.rs | 6 +++++- 11 files changed, 37 insertions(+), 5 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 17af6b314db..6fefe8f4f46 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `timer::Timer` trait has three new methods, `wait`, `async_interrupt_handler` and `peripheral_interrupt` (#2586) - Configuration structs in the I2C, SPI, and UART drivers now implement the Builder Lite pattern (#2614) - Added `I8080::apply_config`, `DPI::apply_config` and `Camera::apply_config` (#2610) +- Introduced the `unstable` feature which will be used to restrict stable APIs to a subset of esp-hal. (#2628) ### Changed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 68ab0ade890..af575e016d8 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -40,6 +40,7 @@ enumset = "1.1.5" esp-build = { version = "0.1.0", path = "../esp-build" } esp-synopsys-usb-otg = { version = "0.4.2", optional = true, features = ["fs", "esp32sx"] } fugit = "0.3.7" +instability = "0.3" log = { version = "0.4.22", optional = true } nb = "1.1.0" paste = "1.0.15" @@ -152,6 +153,9 @@ octal-psram = [] # This feature is intended for testing; you probably don't want to enable it: ci = ["defmt", "bluetooth"] +# Enables APIs that are not stable and thus come with no stability guarantees. +unstable = [] + [lints.clippy] mixed_attributes_style = "allow" diff --git a/esp-hal/MIGRATING-0.22.md b/esp-hal/MIGRATING-0.22.md index bb941d96f82..2a63d18555d 100644 --- a/esp-hal/MIGRATING-0.22.md +++ b/esp-hal/MIGRATING-0.22.md @@ -1,5 +1,11 @@ # Migration Guide from 0.22.x to v1.0.0-beta.0 +Starting with this release, unstable parts of esp-hal will be gated behind the `unstable` feature. +The `unstable` feature itself is unstable, we might change the way we hide APIs without notice. +Unstable APIs are not covered by semver guarantees, they may break even between patch releases. + +Please refer to the documentation to see which APIs are marked as unstable. + ## DMA changes ### Accessing channel objects diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index 25c43ade559..65b938234c1 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -1864,6 +1864,7 @@ where P: OutputPin, { /// Set the GPIO to output mode. + #[instability::unstable] pub fn set_as_output(&mut self) { self.pin.set_to_push_pull_output(private::Internal); } diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index aafe8b21ea8..42f3ee3d207 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr( - docsrs, + all(docsrs, not(not_really_docsrs)), doc = "

You might want to browse the esp-hal documentation on the esp-rs website instead.

The documentation here on docs.rs is built for a single chip only (ESP32-C6, in particular), while on the esp-rs website you can select your exact chip from the list of supported devices. Available peripherals and their APIs change depending on the chip.

\n\n
\n\n" )] //! # Bare-metal (`no_std`) HAL for all Espressif ESP32 devices. @@ -140,6 +140,7 @@ #![allow(asm_sub_register, async_fn_in_trait, stable_features)] #![cfg_attr(xtensa, feature(asm_experimental_arch))] #![deny(missing_docs, rust_2018_idioms)] +#![cfg_attr(docsrs, feature(doc_cfg))] #![no_std] // MUST be the first module diff --git a/esp-metadata/src/lib.rs b/esp-metadata/src/lib.rs index 891bfa9038d..3915602a3f1 100644 --- a/esp-metadata/src/lib.rs +++ b/esp-metadata/src/lib.rs @@ -237,6 +237,9 @@ impl Config { /// /// This is required to avoid triggering the unexpected-cfgs lint. fn define_all_possible_symbols() { + // Used by our documentation builds to prevent the huge red warning banner. + println!("cargo:rustc-check-cfg=cfg(not_really_docsrs)"); + for chip in Chip::iter() { let config = Config::for_chip(&chip); for symbol in config.all() { diff --git a/esp-wifi/src/lib.rs b/esp-wifi/src/lib.rs index 924799eac92..482e4a33417 100644 --- a/esp-wifi/src/lib.rs +++ b/esp-wifi/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr( - docsrs, + all(docsrs, not(not_really_docsrs)), doc = "

You might want to browse the esp-wifi documentation on the esp-rs website instead.

The documentation here on docs.rs is built for a single chip only (ESP32-C3, in particular), while on the esp-rs website you can select your exact chip from the list of supported devices. Available peripherals and their APIs might change depending on the chip.

\n\n
\n\n" )] //! This documentation is built for the diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index cc273714e98..19dc56ed5c3 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -206,7 +206,7 @@ embedded-hal-async = "1.0.0" embedded-hal-nb = "1.0.0" esp-alloc = { path = "../esp-alloc", optional = true } esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "defmt", "semihosting"] } -esp-hal = { path = "../esp-hal", features = ["digest"], optional = true } +esp-hal = { path = "../esp-hal", features = ["digest", "unstable"], optional = true } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } esp-wifi = { path = "../esp-wifi", optional = true, features = ["wifi"] } portable-atomic = "1.9.0" diff --git a/qa-test/Cargo.toml b/qa-test/Cargo.toml index d7760ce52e9..2a1ded08a39 100644 --- a/qa-test/Cargo.toml +++ b/qa-test/Cargo.toml @@ -13,7 +13,7 @@ embedded-graphics = "0.8.1" embedded-hal-async = "1.0.0" esp-alloc = { path = "../esp-alloc" } esp-backtrace = { path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] } -esp-hal = { path = "../esp-hal" } +esp-hal = { path = "../esp-hal", features = ["unstable"] } esp-hal-embassy = { path = "../esp-hal-embassy" } esp-println = { path = "../esp-println", features = ["log"] } lis3dh-async = "0.9.3" diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 8ebd2cd7cf7..68ddc09cf11 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -1,6 +1,7 @@ //! Tools for working with Cargo. use std::{ + ffi::OsStr, path::Path, process::{Command, Stdio}, }; @@ -17,6 +18,16 @@ pub enum CargoAction { /// Execute cargo with the given arguments and from the specified directory. pub fn run(args: &[String], cwd: &Path) -> Result<()> { + run_with_env::<[(&str, &str); 0], _, _>(args, cwd, []) +} + +/// Execute cargo with the given arguments and from the specified directory. +pub fn run_with_env(args: &[String], cwd: &Path, envs: I) -> Result<()> +where + I: IntoIterator, + K: AsRef, + V: AsRef, +{ if !cwd.is_dir() { bail!("The `cwd` argument MUST be a directory"); } @@ -30,6 +41,7 @@ pub fn run(args: &[String], cwd: &Path) -> Result<()> { let status = Command::new(get_cargo()) .args(args) .current_dir(cwd) + .envs(envs) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) .stdin(Stdio::inherit()) diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 44402fb26cf..2bd27f56014 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -137,7 +137,11 @@ pub fn build_documentation(workspace: &Path, package: Package, chip: Chip) -> Re log::debug!("{args:#?}"); // Execute `cargo doc` from the package root: - cargo::run(&args, &package_path)?; + cargo::run_with_env( + &args, + &package_path, + [("RUSTDOCFLAGS", "--cfg docsrs --cfg not_really_docsrs")], + )?; let docs_path = windows_safe_path( &workspace From 891a12e13f2cce3d97aff7c1e4b2489463116577 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Mon, 2 Dec 2024 06:45:05 -0800 Subject: [PATCH 26/27] Derive Builder Lite pattern for HAL configuration, update examples/tests (#2645) * Derive Builder Lite pattern for `esp_hal::Config` and `WatchdogConfig` * User builder pattern for `esp_hal::Config` in examples/tests * Update `CHANGELOG.md` --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/clock/mod.rs | 22 ++----------------- esp-hal/src/config.rs | 10 ++++----- esp-hal/src/lib.rs | 10 +++------ examples/src/bin/wifi_80211_tx.rs | 7 ++---- examples/src/bin/wifi_access_point.rs | 7 ++---- .../src/bin/wifi_access_point_with_sta.rs | 7 ++---- examples/src/bin/wifi_bench.rs | 7 ++---- examples/src/bin/wifi_ble.rs | 7 ++---- examples/src/bin/wifi_coex.rs | 7 ++---- examples/src/bin/wifi_csi.rs | 7 ++---- examples/src/bin/wifi_dhcp.rs | 7 ++---- examples/src/bin/wifi_dhcp_smoltcp_nal.rs | 7 ++---- examples/src/bin/wifi_embassy_access_point.rs | 7 ++---- .../bin/wifi_embassy_access_point_with_sta.rs | 7 ++---- examples/src/bin/wifi_embassy_bench.rs | 7 ++---- examples/src/bin/wifi_embassy_ble.rs | 7 ++---- examples/src/bin/wifi_embassy_dhcp.rs | 7 ++---- examples/src/bin/wifi_embassy_esp_now.rs | 7 ++---- .../src/bin/wifi_embassy_esp_now_duplex.rs | 7 ++---- examples/src/bin/wifi_embassy_trouble.rs | 7 ++---- examples/src/bin/wifi_esp_now.rs | 7 ++---- examples/src/bin/wifi_sniffer.rs | 7 ++---- examples/src/bin/wifi_static_ip.rs | 7 ++---- hil-test/tests/aes.rs | 7 ++---- hil-test/tests/ecc.rs | 7 ++---- hil-test/tests/esp_wifi_floats.rs | 7 ++---- hil-test/tests/interrupt.rs | 7 ++---- hil-test/tests/sha.rs | 3 +-- qa-test/src/bin/embassy_executor_benchmark.rs | 7 ++---- 30 files changed, 62 insertions(+), 159 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 6fefe8f4f46..5a5b59b1df3 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Configuration structs in the I2C, SPI, and UART drivers now implement the Builder Lite pattern (#2614) - Added `I8080::apply_config`, `DPI::apply_config` and `Camera::apply_config` (#2610) - Introduced the `unstable` feature which will be used to restrict stable APIs to a subset of esp-hal. (#2628) +- HAL configuration structs now implement the Builder Lite pattern (#2645) ### Changed diff --git a/esp-hal/src/clock/mod.rs b/esp-hal/src/clock/mod.rs index e7bc554ff22..39950ac419e 100644 --- a/esp-hal/src/clock/mod.rs +++ b/esp-hal/src/clock/mod.rs @@ -38,26 +38,8 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! // Initialize with the highest possible frequency for this chip -//! let peripherals = esp_hal::init({ -//! let mut config = esp_hal::Config::default(); -//! config.cpu_clock = CpuClock::max(); -//! config -//! }); -//! -//! // Initialize with custom clock frequency -//! // let peripherals = esp_hal::init({ -//! // let mut config = esp_hal::Config::default(); -#![cfg_attr( - not(any(esp32c2, esp32h2)), - doc = "// config.cpu_clock = CpuClock::Clock160MHz;" -)] -#![cfg_attr(esp32c2, doc = "// config.cpu_clock = CpuClock::Clock120MHz;")] -#![cfg_attr(esp32h2, doc = "// config.cpu_clock = CpuClock::Clock96MHz;")] -//! // config -//! // }); -//! // -//! // Initialize with default clock frequency for this chip -//! // let peripherals = esp_hal::init(esp_hal::Config::default()); +//! let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); +//! let peripherals = esp_hal::init(config); //! # } //! ``` diff --git a/esp-hal/src/config.rs b/esp-hal/src/config.rs index be63d1aa26f..1ab01573299 100644 --- a/esp-hal/src/config.rs +++ b/esp-hal/src/config.rs @@ -22,10 +22,10 @@ //! ### Custom initialization //! ```rust, no_run #![doc = crate::before_snippet!()] -//! let mut config = esp_hal::Config::default(); -//! config.cpu_clock = CpuClock::max(); -//! config.watchdog.rwdt = -//! esp_hal::config::WatchdogStatus::Enabled(fugit::MicrosDurationU64::millis(1000 as u64)); +//! let config = +//! esp_hal::Config::default().with_cpu_clock(CpuClock::max()). +//! with_watchdog(esp_hal::config::WatchdogConfig::default(). +//! with_rwdt(esp_hal::config::WatchdogStatus::Enabled(fugit::MicrosDurationU64::millis(1000u64)))); //! let peripherals = esp_hal::init(config); //! # } //! ``` @@ -42,7 +42,7 @@ pub enum WatchdogStatus { /// Watchdog configuration. #[non_exhaustive] -#[derive(Default)] +#[derive(Default, procmacros::BuilderLite)] pub struct WatchdogConfig { #[cfg(not(any(esp32, esp32s2)))] /// Enable the super watchdog timer, which has a trigger time of slightly diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 42f3ee3d207..59c8f7e5bf9 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -75,12 +75,8 @@ //! //! #[entry] //! fn main() -> ! { -//! let peripherals = esp_hal::init({ -//! let mut config = esp_hal::Config::default(); -//! // Configure the CPU to run at the maximum frequency. -//! config.cpu_clock = CpuClock::max(); -//! config -//! }); +//! let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); +//! let peripherals = esp_hal::init(config); //! //! // Set GPIO0 as an output, and set its state high initially. //! let mut led = Output::new(peripherals.GPIO0, Level::High); @@ -482,7 +478,7 @@ use crate::{ /// /// For usage examples, see the [config module documentation](crate::config). #[non_exhaustive] -#[derive(Default)] +#[derive(Default, procmacros::BuilderLite)] pub struct Config { /// The CPU clock configuration. pub cpu_clock: CpuClock, diff --git a/examples/src/bin/wifi_80211_tx.rs b/examples/src/bin/wifi_80211_tx.rs index d05782790de..857061a301b 100644 --- a/examples/src/bin/wifi_80211_tx.rs +++ b/examples/src/bin/wifi_80211_tx.rs @@ -31,11 +31,8 @@ const MAC_ADDRESS: [u8; 6] = [0x00, 0x80, 0x41, 0x13, 0x37, 0x42]; #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_access_point.rs b/examples/src/bin/wifi_access_point.rs index 2356687058d..ae0459de33e 100644 --- a/examples/src/bin/wifi_access_point.rs +++ b/examples/src/bin/wifi_access_point.rs @@ -40,11 +40,8 @@ use smoltcp::iface::{SocketSet, SocketStorage}; #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_access_point_with_sta.rs b/examples/src/bin/wifi_access_point_with_sta.rs index 4321ca14259..b6a4b5454c3 100644 --- a/examples/src/bin/wifi_access_point_with_sta.rs +++ b/examples/src/bin/wifi_access_point_with_sta.rs @@ -46,11 +46,8 @@ const PASSWORD: &str = env!("PASSWORD"); #[entry] fn main() -> ! { esp_println::logger::init_logger(log::LevelFilter::Info); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_bench.rs b/examples/src/bin/wifi_bench.rs index e561c0f2de6..4c132b06c19 100644 --- a/examples/src/bin/wifi_bench.rs +++ b/examples/src/bin/wifi_bench.rs @@ -57,11 +57,8 @@ const UPLOAD_DOWNLOAD_PORT: u16 = 4323; #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_ble.rs b/examples/src/bin/wifi_ble.rs index 84e383e0787..83192f83afc 100644 --- a/examples/src/bin/wifi_ble.rs +++ b/examples/src/bin/wifi_ble.rs @@ -37,11 +37,8 @@ use esp_wifi::{ble::controller::BleConnector, init}; #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_coex.rs b/examples/src/bin/wifi_coex.rs index c6926b3bc09..9ecdee2c966 100644 --- a/examples/src/bin/wifi_coex.rs +++ b/examples/src/bin/wifi_coex.rs @@ -53,11 +53,8 @@ const PASSWORD: &str = env!("PASSWORD"); #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); static mut HEAP: core::mem::MaybeUninit<[u8; 72 * 1024]> = core::mem::MaybeUninit::uninit(); diff --git a/examples/src/bin/wifi_csi.rs b/examples/src/bin/wifi_csi.rs index fd06cfe4987..599efa2a983 100644 --- a/examples/src/bin/wifi_csi.rs +++ b/examples/src/bin/wifi_csi.rs @@ -45,11 +45,8 @@ const PASSWORD: &str = env!("PASSWORD"); #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_dhcp.rs b/examples/src/bin/wifi_dhcp.rs index 81b9541575f..190a129ef7a 100644 --- a/examples/src/bin/wifi_dhcp.rs +++ b/examples/src/bin/wifi_dhcp.rs @@ -47,11 +47,8 @@ const PASSWORD: &str = env!("PASSWORD"); #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_dhcp_smoltcp_nal.rs b/examples/src/bin/wifi_dhcp_smoltcp_nal.rs index a4ad4ca6da7..e912d7547bc 100644 --- a/examples/src/bin/wifi_dhcp_smoltcp_nal.rs +++ b/examples/src/bin/wifi_dhcp_smoltcp_nal.rs @@ -49,11 +49,8 @@ const PASSWORD: &str = env!("PASSWORD"); #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_embassy_access_point.rs b/examples/src/bin/wifi_embassy_access_point.rs index 3ceca80c802..4a2abef7641 100644 --- a/examples/src/bin/wifi_embassy_access_point.rs +++ b/examples/src/bin/wifi_embassy_access_point.rs @@ -57,11 +57,8 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_embassy_access_point_with_sta.rs b/examples/src/bin/wifi_embassy_access_point_with_sta.rs index fd52e832839..2af40305a89 100644 --- a/examples/src/bin/wifi_embassy_access_point_with_sta.rs +++ b/examples/src/bin/wifi_embassy_access_point_with_sta.rs @@ -65,11 +65,8 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_embassy_bench.rs b/examples/src/bin/wifi_embassy_bench.rs index 30536e2a327..8ce9a7ab73e 100644 --- a/examples/src/bin/wifi_embassy_bench.rs +++ b/examples/src/bin/wifi_embassy_bench.rs @@ -68,11 +68,8 @@ static mut TX_BUFFER: [u8; TX_BUFFER_SIZE] = [0; TX_BUFFER_SIZE]; #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); static mut HEAP: core::mem::MaybeUninit<[u8; 32 * 1024]> = core::mem::MaybeUninit::uninit(); diff --git a/examples/src/bin/wifi_embassy_ble.rs b/examples/src/bin/wifi_embassy_ble.rs index 17d52f21133..3e3c82c813c 100644 --- a/examples/src/bin/wifi_embassy_ble.rs +++ b/examples/src/bin/wifi_embassy_ble.rs @@ -50,11 +50,8 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(_spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_embassy_dhcp.rs b/examples/src/bin/wifi_embassy_dhcp.rs index 2f514c352a3..e023839d0cb 100644 --- a/examples/src/bin/wifi_embassy_dhcp.rs +++ b/examples/src/bin/wifi_embassy_dhcp.rs @@ -50,11 +50,8 @@ const PASSWORD: &str = env!("PASSWORD"); #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_embassy_esp_now.rs b/examples/src/bin/wifi_embassy_esp_now.rs index fc3b9302263..e26151ab04c 100644 --- a/examples/src/bin/wifi_embassy_esp_now.rs +++ b/examples/src/bin/wifi_embassy_esp_now.rs @@ -36,11 +36,8 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(_spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_embassy_esp_now_duplex.rs b/examples/src/bin/wifi_embassy_esp_now_duplex.rs index 8f0b3e646df..b69d03fc8cc 100644 --- a/examples/src/bin/wifi_embassy_esp_now_duplex.rs +++ b/examples/src/bin/wifi_embassy_esp_now_duplex.rs @@ -36,11 +36,8 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_embassy_trouble.rs b/examples/src/bin/wifi_embassy_trouble.rs index 7f8af099bec..31a6c2b5ecf 100644 --- a/examples/src/bin/wifi_embassy_trouble.rs +++ b/examples/src/bin/wifi_embassy_trouble.rs @@ -44,11 +44,8 @@ macro_rules! mk_static { #[esp_hal_embassy::main] async fn main(_s: Spawner) { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_esp_now.rs b/examples/src/bin/wifi_esp_now.rs index dc32a787900..9f23f1ebd4d 100644 --- a/examples/src/bin/wifi_esp_now.rs +++ b/examples/src/bin/wifi_esp_now.rs @@ -25,11 +25,8 @@ use esp_wifi::{ #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_sniffer.rs b/examples/src/bin/wifi_sniffer.rs index d6c26840c88..e89dc9ec971 100644 --- a/examples/src/bin/wifi_sniffer.rs +++ b/examples/src/bin/wifi_sniffer.rs @@ -29,11 +29,8 @@ static KNOWN_SSIDS: Mutex>> = Mutex::new(RefCell::new(B #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/examples/src/bin/wifi_static_ip.rs b/examples/src/bin/wifi_static_ip.rs index 8344a610305..e06f8e65e21 100644 --- a/examples/src/bin/wifi_static_ip.rs +++ b/examples/src/bin/wifi_static_ip.rs @@ -45,11 +45,8 @@ const GATEWAY_IP: &str = env!("GATEWAY_IP"); #[entry] fn main() -> ! { esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(72 * 1024); diff --git a/hil-test/tests/aes.rs b/hil-test/tests/aes.rs index 67fc6c29c91..bf474107cf6 100644 --- a/hil-test/tests/aes.rs +++ b/hil-test/tests/aes.rs @@ -22,11 +22,8 @@ mod tests { #[init] fn init() -> Context<'static> { - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); let aes = Aes::new(peripherals.AES); Context { aes } diff --git a/hil-test/tests/ecc.rs b/hil-test/tests/ecc.rs index 04a36dface7..773043d1e76 100644 --- a/hil-test/tests/ecc.rs +++ b/hil-test/tests/ecc.rs @@ -54,11 +54,8 @@ mod tests { #[init] fn init() -> Context<'static> { - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); let ecc = Ecc::new(peripherals.ECC); let rng = Rng::new(peripherals.RNG); diff --git a/hil-test/tests/esp_wifi_floats.rs b/hil-test/tests/esp_wifi_floats.rs index 51ee012f4d2..d8eff121747 100644 --- a/hil-test/tests/esp_wifi_floats.rs +++ b/hil-test/tests/esp_wifi_floats.rs @@ -58,11 +58,8 @@ mod tests { fn test_init() -> Peripherals { esp_alloc::heap_allocator!(72 * 1024); - esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }) + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + esp_hal::init(config) } #[test] diff --git a/hil-test/tests/interrupt.rs b/hil-test/tests/interrupt.rs index 7388bf75ccb..54fc77bfb48 100644 --- a/hil-test/tests/interrupt.rs +++ b/hil-test/tests/interrupt.rs @@ -64,11 +64,8 @@ mod tests { #[init] fn init() -> Context { - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); cfg_if::cfg_if! { diff --git a/hil-test/tests/sha.rs b/hil-test/tests/sha.rs index 157592af313..3a97d7618ec 100644 --- a/hil-test/tests/sha.rs +++ b/hil-test/tests/sha.rs @@ -169,8 +169,7 @@ mod tests { // FIXME: max speed fails...? let config = esp_hal::Config::default(); } else { - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); } } diff --git a/qa-test/src/bin/embassy_executor_benchmark.rs b/qa-test/src/bin/embassy_executor_benchmark.rs index f85ffdcb00d..e1945bfca9a 100644 --- a/qa-test/src/bin/embassy_executor_benchmark.rs +++ b/qa-test/src/bin/embassy_executor_benchmark.rs @@ -55,11 +55,8 @@ static TASK1: TaskStorage = TaskStorage::new(); #[esp_hal_embassy::main] async fn main(spawner: Spawner) { - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CLOCK; - config - }); + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); let systimer = SystemTimer::new(peripherals.SYSTIMER); esp_hal_embassy::init(systimer.alarm0); println!("Embassy initialized!"); From a6a83d3bb5d5d92baae11861ee4ba4483eee7f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 2 Dec 2024 16:35:10 +0100 Subject: [PATCH 27/27] Track async GPIOs in memory (#2625) * Track async GPIOs in memory * Add an example * Deduplicate interrupt handling * Changelog * Add gpio_bank_1 symbol * Derive EnumCount * Try to fix issues around manual listen calls and multi-core * Fix test * Update esp-hal/src/gpio/mod.rs Co-authored-by: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com> * Do not prevent pending interrupt from being handled * Remove unnecessary unpin * Add a note about interrupt status flags --------- Co-authored-by: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com> --- esp-hal/CHANGELOG.md | 4 + esp-hal/src/gpio/mod.rs | 403 ++++++++++++++++++++------ esp-hal/src/soc/esp32/gpio.rs | 10 +- esp-metadata/CHANGELOG.md | 1 + esp-metadata/devices/esp32.toml | 1 + esp-metadata/devices/esp32p4.toml | 1 + esp-metadata/devices/esp32s2.toml | 1 + esp-metadata/devices/esp32s3.toml | 1 + hil-test/tests/gpio.rs | 8 + hil-test/tests/gpio_custom_handler.rs | 21 +- 10 files changed, 345 insertions(+), 106 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 5a5b59b1df3..0c5fc26f8d6 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `I8080::apply_config`, `DPI::apply_config` and `Camera::apply_config` (#2610) - Introduced the `unstable` feature which will be used to restrict stable APIs to a subset of esp-hal. (#2628) - HAL configuration structs now implement the Builder Lite pattern (#2645) +- Added `OutputOpenDrain::unlisten` (#2625) +- Added `{Input, Flex}::wait_for` (#2625) ### Changed @@ -50,10 +52,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Interrupt handling related functions are only provided for Blocking UART. (#2610) - Changed how `Spi`, (split or unsplit) `Uart`, `LpUart`, `I8080`, `Camera`, `DPI` and `I2C` drivers are constructed (#2610) - I8080, camera, DPI: The various standalone configuration options have been merged into `Config` (#2610) +- Dropped GPIO futures stop listening for interrupts (#2625) ### Fixed - Xtensa devices now correctly enable the `esp-hal-procmacros/rtc-slow` feature (#2594) +- User-bound GPIO interrupt handlers should no longer interfere with async pins. (#2625) ### Removed diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index 65b938234c1..5baf33bde5c 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -55,8 +55,9 @@ //! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/index.html //! [Inverting TX and RX Pins]: crate::uart#inverting-rx-and-tx-pins -use portable_atomic::{AtomicPtr, Ordering}; +use portable_atomic::{AtomicPtr, AtomicU32, Ordering}; use procmacros::ram; +use strum::EnumCount; #[cfg(any(lp_io, rtc_cntl))] use crate::peripherals::gpio::{handle_rtcio, handle_rtcio_with_resistors}; @@ -454,7 +455,7 @@ pub trait OutputPin: Pin + Into + 'static { gpio.func_out_sel_cfg(self.number() as usize) .modify(|_, w| unsafe { w.out_sel().bits(OutputSignal::GPIO as OutputSignalType) }); - #[cfg(any(esp32c3, esp32s3))] + #[cfg(usb_device)] disable_usb_pads(self.number()); io_mux_reg(self.number()).modify(|_, w| unsafe { @@ -546,16 +547,16 @@ pub trait TouchPin: Pin { } #[doc(hidden)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, EnumCount)] pub enum GpioRegisterAccess { Bank0, - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Bank1, } impl From for GpioRegisterAccess { fn from(_gpio_num: usize) -> Self { - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] if _gpio_num >= 32 { return GpioRegisterAccess::Bank1; } @@ -565,6 +566,21 @@ impl From for GpioRegisterAccess { } impl GpioRegisterAccess { + fn async_operations(self) -> &'static AtomicU32 { + static FLAGS: [AtomicU32; GpioRegisterAccess::COUNT] = + [const { AtomicU32::new(0) }; GpioRegisterAccess::COUNT]; + + &FLAGS[self as usize] + } + + fn offset(self) -> u8 { + match self { + Self::Bank0 => 0, + #[cfg(gpio_bank_1)] + Self::Bank1 => 32, + } + } + fn write_out_en(self, word: u32, enable: bool) { if enable { self.write_out_en_set(word); @@ -576,7 +592,7 @@ impl GpioRegisterAccess { fn write_out_en_clear(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_out_en_clear(word), - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Self::Bank1 => Bank1GpioRegisterAccess::write_out_en_clear(word), } } @@ -584,7 +600,7 @@ impl GpioRegisterAccess { fn write_out_en_set(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_out_en_set(word), - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Self::Bank1 => Bank1GpioRegisterAccess::write_out_en_set(word), } } @@ -592,7 +608,7 @@ impl GpioRegisterAccess { fn read_input(self) -> u32 { match self { Self::Bank0 => Bank0GpioRegisterAccess::read_input(), - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Self::Bank1 => Bank1GpioRegisterAccess::read_input(), } } @@ -600,7 +616,7 @@ impl GpioRegisterAccess { fn read_output(self) -> u32 { match self { Self::Bank0 => Bank0GpioRegisterAccess::read_output(), - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Self::Bank1 => Bank1GpioRegisterAccess::read_output(), } } @@ -608,7 +624,7 @@ impl GpioRegisterAccess { fn read_interrupt_status(self) -> u32 { match self { Self::Bank0 => Bank0GpioRegisterAccess::read_interrupt_status(), - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Self::Bank1 => Bank1GpioRegisterAccess::read_interrupt_status(), } } @@ -616,7 +632,7 @@ impl GpioRegisterAccess { fn write_interrupt_status_clear(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_interrupt_status_clear(word), - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Self::Bank1 => Bank1GpioRegisterAccess::write_interrupt_status_clear(word), } } @@ -632,7 +648,7 @@ impl GpioRegisterAccess { fn write_output_set(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_output_set(word), - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Self::Bank1 => Bank1GpioRegisterAccess::write_output_set(word), } } @@ -640,7 +656,7 @@ impl GpioRegisterAccess { fn write_output_clear(self, word: u32) { match self { Self::Bank0 => Bank0GpioRegisterAccess::write_output_clear(word), - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(gpio_bank_1)] Self::Bank1 => Bank1GpioRegisterAccess::write_output_clear(word), } } @@ -692,10 +708,10 @@ impl Bank0GpioRegisterAccess { } } -#[cfg(any(esp32, esp32s2, esp32s3))] +#[cfg(gpio_bank_1)] struct Bank1GpioRegisterAccess; -#[cfg(any(esp32, esp32s2, esp32s3))] +#[cfg(gpio_bank_1)] impl Bank1GpioRegisterAccess { fn write_out_en_clear(word: u32) { unsafe { GPIO::steal() } @@ -863,14 +879,10 @@ impl InterruptConfigurable for Io { /// Install the given interrupt handler replacing any previously set /// handler. /// - /// ⚠️ Be careful when using this together with the async API: - /// - /// - The async driver will disable any interrupts whose status is not - /// cleared by the user handler. - /// - Clearing the interrupt status in the user handler will prevent the - /// async driver from detecting the interrupt, silently disabling the - /// corresponding pin's async API. - /// - You will not be notified if you make a mistake. + /// Note that when using interrupt handlers registered by this function, + /// we clear the interrupt status register for you. This is NOT the case + /// if you register the interrupt handler directly, by defining a + /// `#[no_mangle] unsafe extern "C" fn GPIO()` function. fn set_interrupt_handler(&mut self, handler: InterruptHandler) { for core in crate::Cpu::other() { crate::interrupt::disable(core, Interrupt::GPIO); @@ -883,22 +895,46 @@ impl InterruptConfigurable for Io { #[ram] extern "C" fn user_gpio_interrupt_handler() { - USER_INTERRUPT_HANDLER.call(); - - default_gpio_interrupt_handler(); + handle_pin_interrupts(|| USER_INTERRUPT_HANDLER.call()); } #[ram] extern "C" fn default_gpio_interrupt_handler() { - handle_pin_interrupts(on_pin_irq); + handle_pin_interrupts(|| ()); } #[ram] -fn on_pin_irq(pin_nr: u8) { - // FIXME: async handlers signal completion by disabling the interrupt, but this - // conflicts with user handlers. - set_int_enable(pin_nr, 0, 0, false); - asynch::PIN_WAKERS[pin_nr as usize].wake(); // wake task +fn handle_pin_interrupts(user_handler: fn()) { + let intrs_bank0 = InterruptStatusRegisterAccess::Bank0.interrupt_status_read(); + + #[cfg(gpio_bank_1)] + let intrs_bank1 = InterruptStatusRegisterAccess::Bank1.interrupt_status_read(); + + user_handler(); + + let banks = [ + (GpioRegisterAccess::Bank0, intrs_bank0), + #[cfg(gpio_bank_1)] + (GpioRegisterAccess::Bank1, intrs_bank1), + ]; + + for (bank, intrs) in banks { + // Get the mask of active async pins and also unmark them in the same go. + let async_pins = bank.async_operations().fetch_and(!intrs, Ordering::Relaxed); + + // Wake up the tasks + let mut intr_bits = intrs & async_pins; + while intr_bits != 0 { + let pin_pos = intr_bits.trailing_zeros(); + intr_bits -= 1 << pin_pos; + + let pin_nr = pin_pos as u8 + bank.offset(); + asynch::PIN_WAKERS[pin_nr as usize].wake(); + set_int_enable(pin_nr, Some(0), 0, false); + } + + bank.write_interrupt_status_clear(intrs); + } } #[doc(hidden)] @@ -1445,7 +1481,74 @@ where self.pin.level() } - /// Listen for interrupts + /// Listen for interrupts. + /// + /// The interrupts will be handled by the handler set using + /// [`Io::set_interrupt_handler`]. All GPIO pins share the same + /// interrupt handler. + /// + /// Note that [`Event::LowLevel`] and [`Event::HighLevel`] are fired + /// continuously when the pin is low or high, respectively. You must use + /// a custom interrupt handler to stop listening for these events, + /// otherwise your program will be stuck in a loop as long as the pin is + /// reading the corresponding level. + /// + /// ## Example: print something when a button is pressed. + /// + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// use esp_hal::gpio::{Event, Input, Pull, Io}; + /// + /// let mut io = Io::new(peripherals.IO_MUX); + /// io.set_interrupt_handler(handler); + /// + /// // Set up the input and store it in the static variable. + /// // This example uses a push button that is high when not + /// // pressed and low when pressed. + /// let mut button = Input::new(peripherals.GPIO5, Pull::Up); + /// + /// critical_section::with(|cs| { + /// // Here we are listening for a low level to demonstrate + /// // that you need to stop listening for level interrupts, + /// // but usually you'd probably use `FallingEdge`. + /// button.listen(Event::LowLevel); + /// BUTTON.borrow_ref_mut(cs).replace(button); + /// }); + /// # } + /// + /// // Outside of your `main` function: + /// + /// # use esp_hal::gpio::Input; + /// use core::cell::RefCell; + /// use critical_section::Mutex; + /// + /// // You will need to store the `Input` object in a static variable so + /// // that the interrupt handler can access it. + /// static BUTTON: Mutex>> = + /// Mutex::new(RefCell::new(None)); + /// + /// #[handler] + /// fn handler() { + /// critical_section::with(|cs| { + /// let mut button = BUTTON.borrow_ref_mut(cs); + /// let Some(button) = button.as_mut() else { + /// // Some other interrupt has occurred + /// // before the button was set up. + /// return; + /// }; + /// + /// if button.is_interrupt_set() { + /// print!("Button pressed"); + /// + /// // If you want to stop listening for interrupts, you need to + /// // call `unlisten` here. If you comment this line, the + /// // interrupt will fire continuously while the button + /// // is pressed. + /// button.unlisten(); + /// } + /// }); + /// } + /// ``` #[inline] pub fn listen(&mut self, event: Event) { self.pin.listen(event); @@ -1660,12 +1763,20 @@ where self.pin.level() } - /// Listen for interrupts + /// Listen for interrupts. + /// + /// See [`Input::listen`] for more information and an example. #[inline] pub fn listen(&mut self, event: Event) { self.pin.listen(event); } + /// Stop listening for interrupts. + #[inline] + pub fn unlisten(&mut self) { + self.pin.unlisten(); + } + /// Clear the interrupt status bit for this Pin #[inline] pub fn clear_interrupt(&mut self) { @@ -1815,21 +1926,30 @@ where set_int_enable( self.pin.number(), - gpio_intr_enable(int_enable, nmi_enable), + Some(gpio_intr_enable(int_enable, nmi_enable)), event as u8, wake_up_from_light_sleep, ) } - /// Listen for interrupts + /// Listen for interrupts. + /// + /// See [`Input::listen`] for more information and an example. #[inline] pub fn listen(&mut self, event: Event) { self.listen_with_options(event, true, false, false) } - /// Stop listening for interrupts + /// Stop listening for interrupts. + #[inline] pub fn unlisten(&mut self) { - set_int_enable(self.pin.number(), 0, 0, false); + set_int_enable(self.pin.number(), Some(0), 0, false); + } + + /// Check if the pin is listening for interrupts. + #[inline] + pub fn is_listening(&self) -> bool { + is_int_enabled(self.pin.number()) } /// Clear the interrupt status bit for this Pin @@ -2151,56 +2271,39 @@ pub(crate) mod internal { } } -fn is_listening(pin_num: u8) -> bool { - let bits = unsafe { GPIO::steal() } - .pin(pin_num as usize) - .read() - .int_ena() - .bits(); - bits != 0 -} - -fn set_int_enable(gpio_num: u8, int_ena: u8, int_type: u8, wake_up_from_light_sleep: bool) { +/// Set GPIO event listening. +/// +/// - `gpio_num`: the pin to configure +/// - `int_ena`: maskable and non-maskable CPU interrupt bits. None to leave +/// unchanged. +/// - `int_type`: interrupt type, see [Event] (or 0 to disable) +/// - `wake_up_from_light_sleep`: whether to wake up from light sleep +fn set_int_enable(gpio_num: u8, int_ena: Option, int_type: u8, wake_up_from_light_sleep: bool) { unsafe { GPIO::steal() } .pin(gpio_num as usize) .modify(|_, w| unsafe { - w.int_ena().bits(int_ena); + if let Some(int_ena) = int_ena { + w.int_ena().bits(int_ena); + } w.int_type().bits(int_type); w.wakeup_enable().bit(wake_up_from_light_sleep) }); } -#[ram] -fn handle_pin_interrupts(handle: impl Fn(u8)) { - let intrs_bank0 = InterruptStatusRegisterAccess::Bank0.interrupt_status_read(); - - #[cfg(any(esp32, esp32s2, esp32s3))] - let intrs_bank1 = InterruptStatusRegisterAccess::Bank1.interrupt_status_read(); - - let mut intr_bits = intrs_bank0; - while intr_bits != 0 { - let pin_nr = intr_bits.trailing_zeros(); - handle(pin_nr as u8); - intr_bits -= 1 << pin_nr; - } - - // clear interrupt bits - Bank0GpioRegisterAccess::write_interrupt_status_clear(intrs_bank0); - - #[cfg(any(esp32, esp32s2, esp32s3))] - { - let mut intr_bits = intrs_bank1; - while intr_bits != 0 { - let pin_nr = intr_bits.trailing_zeros(); - handle(pin_nr as u8 + 32); - intr_bits -= 1 << pin_nr; - } - Bank1GpioRegisterAccess::write_interrupt_status_clear(intrs_bank1); - } +fn is_int_enabled(gpio_num: u8) -> bool { + unsafe { GPIO::steal() } + .pin(gpio_num as usize) + .read() + .int_ena() + .bits() + != 0 } mod asynch { - use core::task::{Context, Poll}; + use core::{ + future::poll_fn, + task::{Context, Poll}, + }; use super::*; use crate::asynch::AtomicWaker; @@ -2213,34 +2316,85 @@ mod asynch { where P: InputPin, { - async fn wait_for(&mut self, event: Event) { - self.listen(event); - PinFuture::new(self.pin.number()).await + /// Wait until the pin experiences a particular [`Event`]. + /// + /// The GPIO driver will disable listening for the event once it occurs, + /// or if the `Future` is dropped. + /// + /// Note that calling this function will overwrite previous + /// [`listen`][Self::listen] operations for this pin. + #[inline] + pub async fn wait_for(&mut self, event: Event) { + let mut future = PinFuture { + pin: unsafe { self.clone_unchecked() }, + }; + + // Make sure this pin is not being processed by an interrupt handler. + if future.pin.is_listening() { + set_int_enable( + future.pin.number(), + None, // Do not disable handling pending interrupts. + 0, // Disable generating new events + false, + ); + poll_fn(|cx| { + if future.pin.is_interrupt_set() { + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + } + + // At this point the pin is no longer listening, we can safely + // do our setup. + + // Mark pin as async. + future + .pin + .gpio_bank(private::Internal) + .async_operations() + .fetch_or(future.pin_mask(), Ordering::Relaxed); + + future.pin.listen(event); + + future.await } - /// Wait until the pin is high. If it is already high, return - /// immediately. + /// Wait until the pin is high. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_high(&mut self) { self.wait_for(Event::HighLevel).await } - /// Wait until the pin is low. If it is already low, return immediately. + /// Wait until the pin is low. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_low(&mut self) { self.wait_for(Event::LowLevel).await } /// Wait for the pin to undergo a transition from low to high. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_rising_edge(&mut self) { self.wait_for(Event::RisingEdge).await } /// Wait for the pin to undergo a transition from high to low. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_falling_edge(&mut self) { self.wait_for(Event::FallingEdge).await } /// Wait for the pin to undergo any transition, i.e low to high OR high /// to low. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_any_edge(&mut self) { self.wait_for(Event::AnyEdge).await } @@ -2250,60 +2404,117 @@ mod asynch { where P: InputPin, { - /// Wait until the pin is high. If it is already high, return - /// immediately. + /// Wait until the pin experiences a particular [`Event`]. + /// + /// The GPIO driver will disable listening for the event once it occurs, + /// or if the `Future` is dropped. + /// + /// Note that calling this function will overwrite previous + /// [`listen`][Self::listen] operations for this pin. + #[inline] + pub async fn wait_for(&mut self, event: Event) { + self.pin.wait_for(event).await + } + + /// Wait until the pin is high. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_high(&mut self) { self.pin.wait_for_high().await } - /// Wait until the pin is low. If it is already low, return immediately. + /// Wait until the pin is low. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_low(&mut self) { self.pin.wait_for_low().await } /// Wait for the pin to undergo a transition from low to high. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_rising_edge(&mut self) { self.pin.wait_for_rising_edge().await } /// Wait for the pin to undergo a transition from high to low. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_falling_edge(&mut self) { self.pin.wait_for_falling_edge().await } /// Wait for the pin to undergo any transition, i.e low to high OR high /// to low. + /// + /// See [Self::wait_for] for more information. pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await } } #[must_use = "futures do nothing unless you `.await` or poll them"] - struct PinFuture { - pin_num: u8, + struct PinFuture<'d, P: InputPin> { + pin: Flex<'d, P>, } - impl PinFuture { - fn new(pin_num: u8) -> Self { - Self { pin_num } + impl<'d, P: InputPin> PinFuture<'d, P> { + fn pin_mask(&self) -> u32 { + let bank = self.pin.gpio_bank(private::Internal); + 1 << (self.pin.number() - bank.offset()) + } + + fn is_done(&self) -> bool { + // Only the interrupt handler should clear the async bit, and only if the + // specific pin is handling an interrupt. + self.pin + .gpio_bank(private::Internal) + .async_operations() + .load(Ordering::Acquire) + & self.pin_mask() + == 0 } } - impl core::future::Future for PinFuture { + impl core::future::Future for PinFuture<'_, P> { type Output = (); fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - PIN_WAKERS[self.pin_num as usize].register(cx.waker()); + PIN_WAKERS[self.pin.number() as usize].register(cx.waker()); - // if pin is no longer listening its been triggered - // therefore the future has resolved - if !is_listening(self.pin_num) { + if self.is_done() { Poll::Ready(()) } else { Poll::Pending } } } + + impl Drop for PinFuture<'_, P> { + fn drop(&mut self) { + // If the pin isn't listening, the future has either been dropped before setup, + // or the interrupt has already been handled. + if self.pin.is_listening() { + // Make sure the future isn't dropped while the interrupt is being handled. + // This prevents tricky drop-and-relisten scenarios. + + set_int_enable( + self.pin.number(), + None, // Do not disable handling pending interrupts. + 0, // Disable generating new events + false, + ); + + while self.pin.is_interrupt_set() {} + + // Unmark pin as async + self.pin + .gpio_bank(private::Internal) + .async_operations() + .fetch_and(!self.pin_mask(), Ordering::Relaxed); + } + } + } } mod embedded_hal_impls { diff --git a/esp-hal/src/soc/esp32/gpio.rs b/esp-hal/src/soc/esp32/gpio.rs index 78156ff5cb8..2c0e38c4c6a 100644 --- a/esp-hal/src/soc/esp32/gpio.rs +++ b/esp-hal/src/soc/esp32/gpio.rs @@ -787,14 +787,8 @@ pub(crate) enum InterruptStatusRegisterAccess { impl InterruptStatusRegisterAccess { pub(crate) fn interrupt_status_read(self) -> u32 { match self { - Self::Bank0 => match Cpu::current() { - Cpu::ProCpu => unsafe { GPIO::steal() }.pcpu_int().read().bits(), - Cpu::AppCpu => unsafe { GPIO::steal() }.acpu_int().read().bits(), - }, - Self::Bank1 => match Cpu::current() { - Cpu::ProCpu => unsafe { GPIO::steal() }.pcpu_int1().read().bits(), - Cpu::AppCpu => unsafe { GPIO::steal() }.acpu_int1().read().bits(), - }, + Self::Bank0 => unsafe { GPIO::steal() }.status().read().bits(), + Self::Bank1 => unsafe { GPIO::steal() }.status1().read().bits(), } } } diff --git a/esp-metadata/CHANGELOG.md b/esp-metadata/CHANGELOG.md index d362ae1a250..00db06c63a9 100644 --- a/esp-metadata/CHANGELOG.md +++ b/esp-metadata/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Introduced the `wifi6` symbol (#2612) +- Introduced the `gpio_bank_1` symbol (#2625) ### Fixed diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index e5a70a666f4..7ae624ea982 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -62,6 +62,7 @@ symbols = [ "timg_timer1", "touch", "large_intr_status", + "gpio_bank_1", # ROM capabilities "rom_crc_le", diff --git a/esp-metadata/devices/esp32p4.toml b/esp-metadata/devices/esp32p4.toml index 9d745becd85..c973e4ff759 100644 --- a/esp-metadata/devices/esp32p4.toml +++ b/esp-metadata/devices/esp32p4.toml @@ -95,4 +95,5 @@ symbols = [ # Additional peripherals defined by us (the developers): "clic", "very_large_intr_status", + "gpio_bank_1", ] diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index 2b556cd00cf..32db8a32335 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -58,6 +58,7 @@ symbols = [ "ulp_riscv_core", "timg_timer1", "large_intr_status", + "gpio_bank_1", # ROM capabilities "rom_crc_le", diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index 96fbe57278a..31e1f3cf4b0 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -72,6 +72,7 @@ symbols = [ "ulp_riscv_core", "timg_timer1", "very_large_intr_status", + "gpio_bank_1", # ROM capabilities "rom_crc_le", diff --git a/hil-test/tests/gpio.rs b/hil-test/tests/gpio.rs index 72cf652340f..6b6888083bc 100644 --- a/hil-test/tests/gpio.rs +++ b/hil-test/tests/gpio.rs @@ -119,6 +119,14 @@ mod tests { assert_eq!(test_gpio1.is_high(), false); } + #[test] + async fn waiting_for_level_does_not_hang(ctx: Context) { + let mut test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down); + let _test_gpio2 = Output::new(ctx.test_gpio2, Level::High); + + test_gpio1.wait_for_high().await; + } + #[test] fn gpio_output(ctx: Context) { let mut test_gpio2 = Output::new(ctx.test_gpio2, Level::Low); diff --git a/hil-test/tests/gpio_custom_handler.rs b/hil-test/tests/gpio_custom_handler.rs index 480a067b57f..c7033206740 100644 --- a/hil-test/tests/gpio_custom_handler.rs +++ b/hil-test/tests/gpio_custom_handler.rs @@ -13,7 +13,7 @@ use embassy_time::{Duration, Timer}; use esp_hal::{ - gpio::{AnyPin, Input, Io, Level, Output, Pull}, + gpio::{AnyPin, Flex, Input, Io, Level, Output, Pull}, interrupt::InterruptConfigurable, macros::handler, timer::timg::TimerGroup, @@ -23,7 +23,16 @@ use portable_atomic::{AtomicUsize, Ordering}; #[no_mangle] unsafe extern "C" fn GPIO() { - // do nothing, prevents binding the default handler + // Prevents binding the default handler, but we need to clear the GPIO + // interrupts by hand. + let peripherals = esp_hal::peripherals::Peripherals::steal(); + + let (gpio1, _) = hil_test::common_test_pins!(peripherals); + + // Using flex will not mutate the pin. + let mut gpio1 = Flex::new(gpio1); + + gpio1.clear_interrupt(); } #[handler] @@ -71,6 +80,14 @@ mod tests { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); + // We need to enable the GPIO interrupt, otherwise the async Future's + // setup or Drop implementation hangs. + esp_hal::interrupt::enable( + esp_hal::peripherals::Interrupt::GPIO, + esp_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + let counter = drive_pins(gpio1, gpio2).await; // GPIO is bound to something else, so we don't expect the async API to work.