diff --git a/esp-hal/src/peripheral.rs b/esp-hal/src/peripheral.rs index 9ee91716550..2a23725ae8d 100644 --- a/esp-hal/src/peripheral.rs +++ b/esp-hal/src/peripheral.rs @@ -18,6 +18,8 @@ use core::{ /// /// but it is the size of `T` not the size /// of a pointer. This is useful if T is a zero sized type. +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PeripheralRef<'a, T> { inner: T, _lifetime: PhantomData<&'a mut T>, diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index 5f74703be11..bc842bcde3e 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -41,7 +41,6 @@ //! let sclk = peripherals.GPIO0; //! let miso = peripherals.GPIO2; //! let mosi = peripherals.GPIO1; -//! let cs = peripherals.GPIO5; //! //! let mut spi = Spi::new( //! peripherals.SPI2, @@ -50,8 +49,7 @@ //! .unwrap() //! .with_sck(sclk) //! .with_mosi(mosi) -//! .with_miso(miso) -//! .with_cs(cs); +//! .with_miso(miso); //! # } //! ``` //! @@ -89,7 +87,7 @@ use crate::{ /// Enumeration of possible SPI interrupt events. #[cfg(gdma)] -#[derive(Debug, EnumSetType)] +#[derive(Debug, Hash, EnumSetType)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum SpiInterrupt { @@ -101,10 +99,7 @@ pub enum SpiInterrupt { } /// The size of the FIFO buffer for SPI -#[cfg(not(esp32s2))] -const FIFO_SIZE: usize = 64; -#[cfg(esp32s2)] -const FIFO_SIZE: usize = 72; +const FIFO_SIZE: usize = if cfg!(esp32s2) { 72 } else { 64 }; /// Padding byte for empty write transfers const EMPTY_WRITE_PAD: u8 = 0x00; @@ -115,42 +110,43 @@ const MAX_DMA_SIZE: usize = 32736; /// /// Used to define specific commands sent over the SPI bus. /// Can be [Command::None] if command phase should be suppressed. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Command { /// No command is sent. None, - /// Command1. + /// A 1-bit command. Command1(u16, SpiDataMode), - /// Command2. + /// A 2-bit command. Command2(u16, SpiDataMode), - /// Command3. + /// A 3-bit command. Command3(u16, SpiDataMode), - /// Command4. + /// A 4-bit command. Command4(u16, SpiDataMode), - /// Command5. + /// A 5-bit command. Command5(u16, SpiDataMode), - /// Command6. + /// A 6-bit command. Command6(u16, SpiDataMode), - /// Command7. + /// A 7-bit command. Command7(u16, SpiDataMode), - /// Command8. + /// A 8-bit command. Command8(u16, SpiDataMode), - /// Command9. + /// A 9-bit command. Command9(u16, SpiDataMode), - /// Command10. + /// A 10-bit command. Command10(u16, SpiDataMode), - /// Command11. + /// A 11-bit command. Command11(u16, SpiDataMode), - /// Command12. + /// A 12-bit command. Command12(u16, SpiDataMode), - /// Command13. + /// A 13-bit command. Command13(u16, SpiDataMode), - /// Command14. + /// A 14-bit command. Command14(u16, SpiDataMode), - /// Command15. + /// A 15-bit command. Command15(u16, SpiDataMode), - /// Command16. + /// A 16-bit command. Command16(u16, SpiDataMode), } @@ -230,74 +226,75 @@ impl Command { /// /// This can be used to specify the address phase of SPI transactions. /// Can be [Address::None] if address phase should be suppressed. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Address { /// No address phase. None, - /// Address with 1-bit. + /// A 1-bit address. Address1(u32, SpiDataMode), - /// Address with 2-bit. + /// A 2-bit address. Address2(u32, SpiDataMode), - /// Address with 3-bit. + /// A 3-bit address. Address3(u32, SpiDataMode), - /// Address with 4-bit. + /// A 4-bit address. Address4(u32, SpiDataMode), - /// Address with 5-bit. + /// A 5-bit address. Address5(u32, SpiDataMode), - /// Address with 6-bit. + /// A 6-bit address. Address6(u32, SpiDataMode), - /// Address with 7-bit. + /// A 7-bit address. Address7(u32, SpiDataMode), - /// Address with 8-bit. + /// A 8-bit address. Address8(u32, SpiDataMode), - /// Address with 9-bit. + /// A 9-bit address. Address9(u32, SpiDataMode), - /// Address with 10-bit. + /// A 10-bit address. Address10(u32, SpiDataMode), - /// Address with 11-bit. + /// A 11-bit address. Address11(u32, SpiDataMode), - /// Address with 12-bit. + /// A 12-bit address. Address12(u32, SpiDataMode), - /// Address with 13-bit. + /// A 13-bit address. Address13(u32, SpiDataMode), - /// Address with 14-bit. + /// A 14-bit address. Address14(u32, SpiDataMode), - /// Address with 15-bit. + /// A 15-bit address. Address15(u32, SpiDataMode), - /// Address with 16-bit. + /// A 16-bit address. Address16(u32, SpiDataMode), - /// Address with 17-bit. + /// A 17-bit address. Address17(u32, SpiDataMode), - /// Address with 18-bit. + /// A 18-bit address. Address18(u32, SpiDataMode), - /// Address with 19-bit. + /// A 19-bit address. Address19(u32, SpiDataMode), - /// Address with 20-bit. + /// A 20-bit address. Address20(u32, SpiDataMode), - /// Address with 21-bit. + /// A 21-bit address. Address21(u32, SpiDataMode), - /// Address with 22-bit. + /// A 22-bit address. Address22(u32, SpiDataMode), - /// Address with 23-bit. + /// A 23-bit address. Address23(u32, SpiDataMode), - /// Address with 24-bit. + /// A 24-bit address. Address24(u32, SpiDataMode), - /// Address with 25-bit. + /// A 25-bit address. Address25(u32, SpiDataMode), - /// Address with 26-bit. + /// A 26-bit address. Address26(u32, SpiDataMode), - /// Address with 27-bit. + /// A 27-bit address. Address27(u32, SpiDataMode), - /// Address with 28-bit. + /// A 28-bit address. Address28(u32, SpiDataMode), - /// Address with 29-bit. + /// A 29-bit address. Address29(u32, SpiDataMode), - /// Address with 30-bit. + /// A 30-bit address. Address30(u32, SpiDataMode), - /// Address with 31-bit. + /// A 31-bit address. Address31(u32, SpiDataMode), - /// Address with 32-bit. + /// A 32-bit address. Address32(u32, SpiDataMode), } @@ -422,14 +419,14 @@ impl Address { } /// SPI peripheral configuration -#[derive(Clone, Copy, Debug, procmacros::BuilderLite)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct Config { - /// SPI clock frequency + /// SPI bus clock frequency. pub frequency: HertzU32, - /// SPI mode + /// SPI sample/shift mode. pub mode: SpiMode, /// Bit order of the read data. @@ -445,18 +442,21 @@ impl Default for Config { Config { frequency: 1_u32.MHz(), mode: SpiMode::Mode0, - read_bit_order: SpiBitOrder::MSBFirst, - write_bit_order: SpiBitOrder::MSBFirst, + read_bit_order: SpiBitOrder::MsbFirst, + write_bit_order: SpiBitOrder::MsbFirst, } } } /// Configuration errors. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ConfigError {} /// SPI peripheral driver +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Spi<'d, M, T = AnySpi> { spi: PeripheralRef<'d, T>, _mode: PhantomData, @@ -486,10 +486,6 @@ where } /// Write bytes to SPI. - /// - /// Copies the content of `words` in chunks of 64 bytes into the SPI - /// transmission FIFO. If `words` is longer than 64 bytes, multiple - /// sequential transfers are performed. pub fn write_bytes(&mut self, words: &[u8]) -> Result<(), Error> { self.driver().write_bytes(words)?; self.driver().flush()?; @@ -531,6 +527,7 @@ where /// This method prepares the SPI instance for DMA transfers using SPI /// and returns an instance of `SpiDma` that supports DMA /// operations. + #[instability::unstable] pub fn with_dma(self, channel: impl Peripheral

+ 'd) -> SpiDma<'d, Blocking, T> where CH: DmaChannelFor, @@ -638,6 +635,7 @@ where /// /// Sets the specified pin to push-pull output and connects it to the SPI CS /// signal. + #[instability::unstable] pub fn with_cs(self, cs: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(cs); cs.set_to_push_pull_output(private::Internal); @@ -713,6 +711,7 @@ where T: Instance, { /// Half-duplex read. + #[instability::unstable] pub fn half_duplex_read( &mut self, data_mode: SpiDataMode, @@ -737,7 +736,7 @@ where dummy, buffer.is_empty(), data_mode, - ); + )?; self.driver().configure_datalen(buffer.len(), 0); self.driver().start_operation(); @@ -746,6 +745,7 @@ where } /// Half-duplex write. + #[instability::unstable] pub fn half_duplex_write( &mut self, data_mode: SpiDataMode, @@ -775,6 +775,11 @@ where data_mode = address.mode(); address = Address::None; } + + if dummy > 0 { + // FIXME: https://github.com/esp-rs/esp-hal/issues/2240 + return Err(Error::Unsupported); + } } } @@ -786,7 +791,7 @@ where dummy, buffer.is_empty(), data_mode, - ); + )?; if !buffer.is_empty() { // re-using the full-duplex write here @@ -812,17 +817,11 @@ mod dma { asynch::{DmaRxFuture, DmaTxFuture}, Channel, DmaRxBuf, - DmaRxBuffer, DmaTxBuf, - DmaTxBuffer, EmptyBuf, PeripheralDmaChannel, - Rx, - Tx, }, interrupt::InterruptConfigurable, - Async, - Blocking, }; /// A DMA capable SPI instance. @@ -1100,6 +1099,11 @@ mod dma { address: Address, dummy: u8, ) -> Result<(), Error> { + if dummy > 0 { + // FIXME: https://github.com/esp-rs/esp-hal/issues/2240 + return Err(Error::Unsupported); + } + let bytes_to_write = address.width().div_ceil(8); // The address register is read in big-endian order, // we have to prepare the emulated write in the same way. @@ -1115,7 +1119,7 @@ mod dma { dummy, bytes_to_write == 0, address.mode(), - ); + )?; // FIXME: we could use self.start_transfer_dma if the address buffer was part of // the (yet-to-be-created) State struct. @@ -1411,7 +1415,7 @@ mod dma { dummy, bytes_to_read == 0, data_mode, - ); + )?; self.start_transfer_dma(false, bytes_to_read, 0, buffer, &mut EmptyBuf) } @@ -1476,7 +1480,7 @@ mod dma { dummy, bytes_to_write == 0, data_mode, - ); + )?; self.start_transfer_dma(false, 0, bytes_to_write, &mut EmptyBuf, buffer) } @@ -1749,7 +1753,7 @@ mod dma { buffer: &mut [u8], ) -> Result<(), Error> { if buffer.len() > self.rx_buf.capacity() { - return Err(Error::DmaError(DmaError::Overflow)); + return Err(Error::from(DmaError::Overflow)); } self.wait_for_idle(); self.rx_buf.set_length(buffer.len()); @@ -1783,7 +1787,7 @@ mod dma { buffer: &[u8], ) -> Result<(), Error> { if buffer.len() > self.tx_buf.capacity() { - return Err(Error::DmaError(DmaError::Overflow)); + return Err(Error::from(DmaError::Overflow)); } self.wait_for_idle(); self.tx_buf.fill(buffer); @@ -1828,7 +1832,6 @@ mod dma { }; use super::*; - use crate::Async; struct DropGuard { inner: ManuallyDrop, @@ -1952,7 +1955,7 @@ mod dma { spi.wait_for_idle_async().await; let bytes_read = self.rx_buf.read_received_data(read_chunk); - assert_eq!(bytes_read, read_chunk.len()); + debug_assert_eq!(bytes_read, read_chunk.len()); } spi.defuse(); @@ -1987,7 +1990,7 @@ mod dma { spi.wait_for_idle_async().await; let bytes_read = self.rx_buf.read_received_data(chunk); - assert_eq!(bytes_read, chunk.len()); + debug_assert_eq!(bytes_read, chunk.len()); } spi.defuse(); @@ -2421,7 +2424,7 @@ impl Info { cmd_mode: SpiDataMode, address_mode: SpiDataMode, data_mode: SpiDataMode, - ) { + ) -> Result<(), Error> { let reg_block = self.register_block(); reg_block.ctrl().modify(|_, w| { w.fcmd_dual().bit(cmd_mode == SpiDataMode::Dual); @@ -2435,6 +2438,7 @@ impl Info { w.fwrite_dual().bit(data_mode == SpiDataMode::Dual); w.fwrite_quad().bit(data_mode == SpiDataMode::Quad) }); + Ok(()) } #[cfg(esp32)] @@ -2443,11 +2447,12 @@ impl Info { cmd_mode: SpiDataMode, address_mode: SpiDataMode, data_mode: SpiDataMode, - ) { + ) -> Result<(), Error> { let reg_block = self.register_block(); match cmd_mode { SpiDataMode::Single => (), - _ => panic!("Only 1-bit command supported"), + // FIXME: more detailed error - Only 1-bit commands are supported. + _ => return Err(Error::Unsupported), } match address_mode { @@ -2482,8 +2487,11 @@ impl Info { w.fwrite_quad().clear_bit() }); } - _ => panic!("Unsupported combination of data-modes"), + // FIXME: more detailed error - Unsupported combination of data-modes, + _ => return Err(Error::Unsupported), } + + Ok(()) } // taken from https://github.com/apache/incubator-nuttx/blob/8267a7618629838231256edfa666e44b5313348e/arch/risc-v/src/esp32c3/esp32c3_spi.c#L496 @@ -2670,12 +2678,12 @@ impl Info { let reg_block = self.register_block(); let read_value = match read_order { - SpiBitOrder::MSBFirst => 0, - SpiBitOrder::LSBFirst => 1, + SpiBitOrder::MsbFirst => 0, + SpiBitOrder::LsbFirst => 1, }; let write_value = match write_order { - SpiBitOrder::MSBFirst => 0, - SpiBitOrder::LSBFirst => 1, + SpiBitOrder::MsbFirst => 0, + SpiBitOrder::LsbFirst => 1, }; reg_block.ctrl().modify(|_, w| unsafe { w.rd_bit_order().bits(read_value); @@ -2689,12 +2697,12 @@ impl Info { let reg_block = self.register_block(); let read_value = match read_order { - SpiBitOrder::MSBFirst => false, - SpiBitOrder::LSBFirst => true, + SpiBitOrder::MsbFirst => false, + SpiBitOrder::LsbFirst => true, }; let write_value = match write_order { - SpiBitOrder::MSBFirst => false, - SpiBitOrder::LSBFirst => true, + SpiBitOrder::MsbFirst => false, + SpiBitOrder::LsbFirst => true, }; reg_block.ctrl().modify(|_, w| { w.rd_bit_order().bit(read_value); @@ -2869,8 +2877,8 @@ impl Info { dummy: u8, no_mosi_miso: bool, data_mode: SpiDataMode, - ) { - self.init_spi_data_mode(cmd.mode(), address.mode(), data_mode); + ) -> Result<(), Error> { + self.init_spi_data_mode(cmd.mode(), address.mode(), data_mode)?; let reg_block = self.register_block(); reg_block.user().modify(|_, w| { @@ -2911,6 +2919,8 @@ impl Info { // set cmd, address, dummy cycles self.set_up_common_phases(cmd, address, dummy); + + Ok(()) } fn set_up_common_phases(&self, cmd: Command, address: Address, dummy: u8) { diff --git a/esp-hal/src/spi/mod.rs b/esp-hal/src/spi/mod.rs index f3546f65c82..396c4d55e23 100644 --- a/esp-hal/src/spi/mod.rs +++ b/esp-hal/src/spi/mod.rs @@ -15,11 +15,13 @@ pub mod master; pub mod slave; /// SPI errors -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { /// Error occurred due to a DMA-related issue. + #[cfg(any(doc, feature = "unstable"))] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] DmaError(DmaError), /// Error indicating that the maximum DMA transfer size was exceeded. MaxDmaTransferSizeExceeded, @@ -33,12 +35,22 @@ pub enum Error { Unknown, } +#[doc(hidden)] +#[cfg(any(doc, feature = "unstable"))] impl From for Error { fn from(value: DmaError) -> Self { Error::DmaError(value) } } +#[doc(hidden)] +#[cfg(not(any(doc, feature = "unstable")))] +impl From for Error { + fn from(_value: DmaError) -> Self { + Error::Unknown + } +} + impl embedded_hal::spi::Error for Error { fn kind(&self) -> embedded_hal::spi::ErrorKind { embedded_hal::spi::ErrorKind::Other @@ -50,7 +62,7 @@ impl embedded_hal::spi::Error for Error { /// /// These modes control the clock signal's idle state and when data is sampled /// and shifted. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SpiMode { /// Mode 0 (CPOL = 0, CPHA = 0): Clock is low when idle, data is captured on @@ -68,17 +80,17 @@ pub enum SpiMode { } /// SPI Bit Order -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SpiBitOrder { /// Most Significant Bit (MSB) is transmitted first. - MSBFirst, + MsbFirst, /// Least Significant Bit (LSB) is transmitted first. - LSBFirst, + LsbFirst, } /// SPI data mode -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SpiDataMode { /// `Single` Data Mode - 1 bit, 2 wires. @@ -87,6 +99,9 @@ pub enum SpiDataMode { Dual, /// `Quad` Data Mode - 4 bit, 4 wires Quad, + #[cfg(spi_octal)] + /// `Octal` Data Mode - 8 bit, 8 wires + Octal, } crate::any_peripheral! { diff --git a/esp-hal/src/system.rs b/esp-hal/src/system.rs index c69da05fd90..c5045827f78 100755 --- a/esp-hal/src/system.rs +++ b/esp-hal/src/system.rs @@ -29,7 +29,7 @@ pub(crate) const KEEP_ENABLED: &[Peripheral] = &[ // FIXME: This enum needs to be public because it's exposed via a bunch of traits, but it's not // useful to users. #[doc(hidden)] -#[derive(Debug, Clone, Copy, PartialEq, EnumCount, EnumIter)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumCount, EnumIter)] #[repr(u8)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Peripheral { @@ -162,7 +162,8 @@ pub(crate) fn disable_peripherals() { }) } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) struct PeripheralGuard { peripheral: Peripheral, } diff --git a/esp-metadata/devices/esp32p4.toml b/esp-metadata/devices/esp32p4.toml index c973e4ff759..0e71bb8ece5 100644 --- a/esp-metadata/devices/esp32p4.toml +++ b/esp-metadata/devices/esp32p4.toml @@ -96,4 +96,5 @@ symbols = [ "clic", "very_large_intr_status", "gpio_bank_1", + "spi_octal", ] diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index c9c70c97d78..c8e73cb5489 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -59,6 +59,7 @@ symbols = [ "timg_timer1", "large_intr_status", "gpio_bank_1", + "spi_octal", # ROM capabilities "rom_crc_le", diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index 8dfc2fe1ae2..56d02544eee 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -74,6 +74,7 @@ symbols = [ "timg_timer1", "very_large_intr_status", "gpio_bank_1", + "spi_octal", # ROM capabilities "rom_crc_le", diff --git a/examples/src/bin/embassy_spi.rs b/examples/src/bin/embassy_spi.rs index 1006ac47f22..c6838f7bd8c 100644 --- a/examples/src/bin/embassy_spi.rs +++ b/examples/src/bin/embassy_spi.rs @@ -13,7 +13,7 @@ //! CS => GPIO5 //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: embassy embassy-generic-timers +//% FEATURES: embassy embassy-generic-timers esp-hal/unstable #![no_std] #![no_main] diff --git a/examples/src/bin/spi_loopback.rs b/examples/src/bin/spi_loopback.rs index 74b0ed963e3..71d9f7d5312 100644 --- a/examples/src/bin/spi_loopback.rs +++ b/examples/src/bin/spi_loopback.rs @@ -11,6 +11,7 @@ //! This example transfers data via SPI. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: esp-hal/unstable #![no_std] #![no_main] diff --git a/examples/src/bin/spi_loopback_dma_psram.rs b/examples/src/bin/spi_loopback_dma_psram.rs index faceedb7342..da399c75265 100644 --- a/examples/src/bin/spi_loopback_dma_psram.rs +++ b/examples/src/bin/spi_loopback_dma_psram.rs @@ -16,7 +16,7 @@ //! If your module is quad PSRAM then you need to change the `psram` feature in the //! in the features line below to `quad-psram`. -//% FEATURES: esp-hal/log esp-hal/octal-psram +//% FEATURES: esp-hal/log esp-hal/octal-psram esp-hal/unstable //% CHIPS: esp32s3 #![no_std] diff --git a/examples/src/bin/spi_slave_dma.rs b/examples/src/bin/spi_slave_dma.rs index 5f92755e2eb..881f3607d67 100644 --- a/examples/src/bin/spi_slave_dma.rs +++ b/examples/src/bin/spi_slave_dma.rs @@ -25,6 +25,7 @@ //! so no immediate neighbor is available. //% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: esp-hal/unstable #![no_std] #![no_main]