From 3f7181fece63482222624f43041b3a5f4a27d868 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Wed, 8 Feb 2023 11:02:03 +0000 Subject: [PATCH] Async SPI (#385) * ground work for async dma (gdma only atm) * Add async DMA (GDMA) - esp32c3/esp32c2 * Add Async SPI impl for esp32c3/c2 * Remove private modules from DMA * add async spi example for esp32c3 * Switch to assoc wakers instead of a static array * add support for esp32/esp32s2 * add support for esp32s3 * run fmt * add c2 example, fix CI * Remove redundant comments --- .github/workflows/ci.yml | 10 + esp-hal-common/Cargo.toml | 3 +- esp-hal-common/src/dma/gdma.rs | 103 +- esp-hal-common/src/dma/mod.rs | 1414 +++++++++++++++++---------- esp-hal-common/src/dma/pdma.rs | 140 ++- esp-hal-common/src/lib.rs | 1 + esp-hal-common/src/spi.rs | 141 +++ esp32-hal/Cargo.toml | 4 + esp32-hal/examples/embassy_spi.rs | 127 +++ esp32c2-hal/Cargo.toml | 4 + esp32c2-hal/examples/embassy_spi.rs | 125 +++ esp32c3-hal/Cargo.toml | 4 + esp32c3-hal/examples/embassy_spi.rs | 128 +++ esp32s2-hal/Cargo.toml | 4 + esp32s2-hal/examples/embassy_spi.rs | 127 +++ esp32s3-hal/Cargo.toml | 4 + esp32s3-hal/examples/embassy_spi.rs | 133 +++ 17 files changed, 1893 insertions(+), 579 deletions(-) create mode 100644 esp32-hal/examples/embassy_spi.rs create mode 100644 esp32c2-hal/examples/embassy_spi.rs create mode 100644 esp32c3-hal/examples/embassy_spi.rs create mode 100644 esp32s2-hal/examples/embassy_spi.rs create mode 100644 esp32s3-hal/examples/embassy_spi.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95a43bd05..201b2e3cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,8 @@ jobs: run: cd esp32-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0 - name: check esp32-hal (async, gpio) run: cd esp32-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-timg0,async + - name: check esp32-hal (async, spi) + run: cd esp32-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-timg0,async esp32c2-hal: runs-on: ubuntu-latest @@ -77,6 +79,8 @@ jobs: run: cd esp32c2-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0 - name: check esp32c2-hal (async, gpio) run: cd esp32c2-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-systick,async + - name: check esp32c2-hal (async, spi) + run: cd esp32c2-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-systick,async esp32c3-hal: runs-on: ubuntu-latest @@ -111,6 +115,8 @@ jobs: run: cargo check --manifest-path=esp32c3-hal/Cargo.toml --target=riscv32imc-unknown-none-elf --example=embassy_hello_world --features=embassy,embassy-time-timg0 - name: check esp32c3-hal (async, gpio) run: cd esp32c3-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-systick,async + - name: check esp32c3-hal (async, spi) + run: cd esp32c3-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-systick,async esp32s2-hal: runs-on: ubuntu-latest @@ -139,6 +145,8 @@ jobs: run: cd esp32s2-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0 - name: check esp32s2-hal (async, gpio) run: cd esp32s2-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-timg0,async + - name: check esp32s2-hal (async, spi) + run: cd esp32s2-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-timg0,async esp32s3-hal: runs-on: ubuntu-latest @@ -170,6 +178,8 @@ jobs: run: cd esp32s3-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-timg0 - name: check esp32s3-hal (async, gpio) run: cd esp32s3-hal/ && cargo check --example=embassy_wait --features=embassy,embassy-time-timg0,async + - name: check esp32s3-hal (async, spi) + run: cd esp32s3-hal/ && cargo check --example=embassy_spi --features=embassy,embassy-time-timg0,async # -------------------------------------------------------------------------- # MSRV diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 9b51bfbce..c59e662b0 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -33,6 +33,7 @@ usb-device = { version = "0.2.9", optional = true } embedded-hal-async = { version = "0.2.0-alpha.0", optional = true } embassy-sync = { version = "0.1.0", optional = true } embassy-time = { version = "0.1.0", features = ["nightly"], optional = true } +embassy-futures = { version = "0.1.0", optional = true } # RISC-V esp-riscv-rt = { version = "0.1.0", optional = true } @@ -81,7 +82,7 @@ ufmt = ["ufmt-write"] vectored = ["procmacros/interrupt"] # Implement the `embedded-hal-async==1.0.0-alpha.x` traits -async = ["embedded-hal-async", "eh1", "embassy-sync"] +async = ["embedded-hal-async", "eh1", "embassy-sync", "embassy-futures"] embassy = ["embassy-time"] embassy-time-systick = [] diff --git a/esp-hal-common/src/dma/gdma.rs b/esp-hal-common/src/dma/gdma.rs index c156b5481..1eda25228 100644 --- a/esp-hal-common/src/dma/gdma.rs +++ b/esp-hal-common/src/dma/gdma.rs @@ -1,7 +1,7 @@ //! Direct Memory Access use crate::{ - dma::gdma::private::*, + dma::*, peripheral::PeripheralRef, system::{Peripheral, PeripheralClockControl}, }; @@ -263,15 +263,89 @@ macro_rules! impl_channel { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.[].read().inlink_dscr_bf0().bits() as usize } + + fn is_listening_in_eof() -> bool { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(esp32s3)] { + dma.[].read().in_suc_eof().bit_is_set() + } else { + dma.[].read().in_suc_eof().bit_is_set() + } + } + } + fn is_listening_out_eof() -> bool { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(esp32s3)] { + dma.[].read().out_total_eof().bit_is_set() + } else { + dma.[].read().out_total_eof().bit_is_set() + } + } + } + + fn listen_in_eof() { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(esp32s3)] { + dma.[].modify(|_, w| w.in_suc_eof().set_bit()) + } else { + dma.[].modify(|_, w| w.in_suc_eof().set_bit()) + } + } + } + fn listen_out_eof() { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(esp32s3)] { + dma.[].modify(|_, w| w.out_total_eof().set_bit()) + } else { + dma.[].modify(|_, w| w.out_total_eof().set_bit()) + } + } + } + fn unlisten_in_eof() { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(esp32s3)] { + dma.[].modify(|_, w| w.in_suc_eof().clear_bit()) + } else { + dma.[].modify(|_, w| w.in_suc_eof().clear_bit()) + } + } + } + fn unlisten_out_eof() { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + cfg_if::cfg_if! { + if #[cfg(esp32s3)] { + dma.[].modify(|_, w| w.out_total_eof().clear_bit()) + } else { + dma.[].modify(|_, w| w.out_total_eof().clear_bit()) + } + } + } } pub struct [] {} - impl<'a> TxChannel<[]> for [] {} + impl<'a> TxChannel<[]> for [] { + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); + &WAKER + } + } pub struct [] {} - impl<'a> RxChannel<[]> for [] {} + impl<'a> RxChannel<[]> for [] { + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); + &WAKER + } + } pub struct [] {} @@ -337,20 +411,15 @@ macro_rules! impl_channel { }; } -/// Crate private implementatin details -pub(crate) mod private { - use crate::dma::{private::*, *}; - - impl_channel!(0); - #[cfg(not(esp32c2))] - impl_channel!(1); - #[cfg(not(esp32c2))] - impl_channel!(2); - #[cfg(esp32s3)] - impl_channel!(3); - #[cfg(esp32s3)] - impl_channel!(4); -} +impl_channel!(0); +#[cfg(not(esp32c2))] +impl_channel!(1); +#[cfg(not(esp32c2))] +impl_channel!(2); +#[cfg(esp32s3)] +impl_channel!(3); +#[cfg(esp32s3)] +impl_channel!(4); /// GDMA Peripheral /// diff --git a/esp-hal-common/src/dma/mod.rs b/esp-hal-common/src/dma/mod.rs index 74ad39317..e3584048b 100644 --- a/esp-hal-common/src/dma/mod.rs +++ b/esp-hal-common/src/dma/mod.rs @@ -4,8 +4,6 @@ use core::{marker::PhantomData, sync::atomic::compiler_fence}; -use self::private::PeripheralMarker; - #[cfg(gdma)] pub mod gdma; #[cfg(pdma)] @@ -186,597 +184,651 @@ pub trait I2s0Peripheral: I2sPeripheral + PeripheralMarker {} pub trait I2s1Peripheral: I2sPeripheral + PeripheralMarker {} /// DMA Rx -pub trait Rx: private::RxPrivate {} +pub trait Rx: RxPrivate {} /// DMA Tx -pub trait Tx: private::TxPrivate {} +pub trait Tx: TxPrivate {} -/// Crate private implementatin details -pub(crate) mod private { - use super::*; +/// Marker trait +pub trait PeripheralMarker {} - /// Marker trait - pub trait PeripheralMarker {} +/// The functions here are not meant to be used outside the HAL +pub trait RxPrivate { + fn init(&mut self, burst_mode: bool, priority: DmaPriority); - /// The functions here are not meant to be used outside the HAL - pub trait RxPrivate { - fn init(&mut self, burst_mode: bool, priority: DmaPriority); + fn init_channel(&mut self); - fn init_channel(&mut self); + fn prepare_transfer( + &mut self, + circular: bool, + peri: DmaPeripheral, + data: *mut u8, + len: usize, + ) -> Result<(), DmaError>; - fn prepare_transfer( - &mut self, - circular: bool, - peri: DmaPeripheral, - data: *mut u8, - len: usize, - ) -> Result<(), DmaError>; + fn is_done(&self) -> bool; - fn is_done(&mut self) -> bool; + fn is_listening_eof(&self) -> bool; - fn available(&mut self) -> usize; + fn listen_eof(&self); - fn pop(&mut self, data: &mut [u8]) -> Result; + fn unlisten_eof(&self); - fn drain_buffer(&mut self, dst: &mut [u8]) -> Result; + fn available(&mut self) -> usize; + + fn pop(&mut self, data: &mut [u8]) -> Result; + + fn drain_buffer(&mut self, dst: &mut [u8]) -> Result; + + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; +} + +pub trait RxChannel +where + R: RegisterAccess, +{ + fn init(&mut self, burst_mode: bool, priority: DmaPriority) { + R::set_in_burstmode(burst_mode); + R::set_in_priority(priority); } - pub trait RxChannel - where - R: RegisterAccess, - { - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - R::set_in_burstmode(burst_mode); - R::set_in_priority(priority); + fn prepare_transfer( + &mut self, + descriptors: &mut [u32], + circular: bool, + peri: DmaPeripheral, + data: *mut u8, + len: usize, + ) -> Result<(), DmaError> { + for descr in descriptors.iter_mut() { + *descr = 0; } - fn prepare_transfer( - &mut self, - descriptors: &mut [u32], - circular: bool, - peri: DmaPeripheral, - data: *mut u8, - len: usize, - ) -> Result<(), DmaError> { - for descr in descriptors.iter_mut() { - *descr = 0; - } + compiler_fence(core::sync::atomic::Ordering::SeqCst); - compiler_fence(core::sync::atomic::Ordering::SeqCst); + let mut processed = 0; + let mut descr = 0; + loop { + let chunk_size = usize::min(CHUNK_SIZE, len - processed); + let last = processed + chunk_size >= len; - let mut processed = 0; - let mut descr = 0; - loop { - let chunk_size = usize::min(CHUNK_SIZE, len - processed); - let last = processed + chunk_size >= len; + descriptors[descr + 1] = data as u32 + processed as u32; - descriptors[descr + 1] = data as u32 + processed as u32; + let mut dw0 = &mut descriptors[descr]; - let mut dw0 = &mut descriptors[descr]; + dw0.set_suc_eof(false); + dw0.set_owner(Owner::Dma); + dw0.set_size(chunk_size as u16); // align to 32 bits? + dw0.set_length(0); // actual size of the data!? - dw0.set_suc_eof(false); - dw0.set_owner(Owner::Dma); - dw0.set_size(chunk_size as u16); // align to 32 bits? - dw0.set_length(0); // actual size of the data!? - - if !last { - descriptors[descr + 2] = - (&descriptors[descr + 3]) as *const _ as *const () as u32; + if !last { + descriptors[descr + 2] = (&descriptors[descr + 3]) as *const _ as *const () as u32; + } else { + descriptors[descr + 2] = if circular { + descriptors.as_ptr() as *const () as u32 } else { - descriptors[descr + 2] = if circular { - descriptors.as_ptr() as *const () as u32 - } else { - 0 - }; - } - - processed += chunk_size; - descr += 3; - - if processed >= len { - break; - } - } - - R::clear_in_interrupts(); - R::reset_in(); - R::set_in_descriptors(descriptors.as_ptr() as u32); - R::set_in_peripheral(peri as u8); - R::start_in(); - - if R::has_in_descriptor_error() { - return Err(DmaError::DescriptorError); - } - - Ok(()) - } - - fn is_done(&mut self) -> bool { - R::is_in_done() - } - - fn last_in_dscr_address(&self) -> usize { - R::last_in_dscr_address() - } - } - - pub struct ChannelRx<'a, T, R> - where - T: RxChannel, - R: RegisterAccess, - { - pub descriptors: &'a mut [u32], - pub burst_mode: bool, - pub rx_impl: T, - pub read_descr_ptr: *const u32, - pub available: usize, - pub last_seen_handled_descriptor_ptr: *const u32, - pub read_buffer_start: *const u8, - pub _phantom: PhantomData, - } - - impl<'a, T, R> Rx for ChannelRx<'a, T, R> - where - T: RxChannel, - R: RegisterAccess, - { - } - - impl<'a, T, R> RxPrivate for ChannelRx<'a, T, R> - where - T: RxChannel, - R: RegisterAccess, - { - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - self.rx_impl.init(burst_mode, priority); - } - - fn prepare_transfer( - &mut self, - circular: bool, - peri: DmaPeripheral, - data: *mut u8, - len: usize, - ) -> Result<(), DmaError> { - if self.descriptors.len() % 3 != 0 { - return Err(DmaError::InvalidDescriptorSize); - } - - if self.descriptors.len() / 3 < len / CHUNK_SIZE { - return Err(DmaError::OutOfDescriptors); - } - - if self.burst_mode && (len % 4 != 0 || data as u32 % 4 != 0) { - return Err(DmaError::InvalidAlignment); - } - - if circular && len < CHUNK_SIZE * 2 { - return Err(DmaError::BufferTooSmall); - } - - self.available = 0; - self.read_descr_ptr = self.descriptors.as_ptr() as *const u32; - self.last_seen_handled_descriptor_ptr = core::ptr::null(); - self.read_buffer_start = data; - - self.rx_impl - .prepare_transfer(self.descriptors, circular, peri, data, len)?; - Ok(()) - } - - fn is_done(&mut self) -> bool { - self.rx_impl.is_done() - } - - fn init_channel(&mut self) { - R::init_channel(); - } - - fn available(&mut self) -> usize { - if self.last_seen_handled_descriptor_ptr.is_null() { - self.last_seen_handled_descriptor_ptr = self.descriptors.as_mut_ptr(); - return 0; - } - - if self.available != 0 { - return self.available; - } - - let descr_address = self.last_seen_handled_descriptor_ptr as *mut u32; - let mut dw0 = unsafe { &mut descr_address.read_volatile() }; - - if dw0.get_owner() == Owner::Cpu && dw0.get_length() != 0 { - let descriptor_buffer = - unsafe { descr_address.offset(1).read_volatile() } as *const u8; - let next_descriptor = - unsafe { descr_address.offset(2).read_volatile() } as *const u32; - - self.read_buffer_start = descriptor_buffer; - self.available = dw0.get_length() as usize; - - dw0.set_owner(Owner::Dma); - dw0.set_length(0); - dw0.set_suc_eof(false); - - unsafe { - descr_address.write_volatile(*dw0); - } - - if !next_descriptor.is_null() { - self.last_seen_handled_descriptor_ptr = next_descriptor; - } else { - self.last_seen_handled_descriptor_ptr = self.descriptors.as_ptr(); - } - } - - self.available - } - - fn pop(&mut self, data: &mut [u8]) -> Result { - let avail = self.available; - - if avail < data.len() { - return Err(super::DmaError::Exhausted); - } - - unsafe { - let dst = data.as_mut_ptr(); - let src = self.read_buffer_start; - let count = self.available; - core::ptr::copy_nonoverlapping(src, dst, count); - } - - self.available = 0; - Ok(data.len()) - } - - fn drain_buffer(&mut self, dst: &mut [u8]) -> Result { - let mut len: usize = 0; - let mut dscr = self.descriptors.as_ptr() as *mut u32; - loop { - let mut dw0 = unsafe { &mut dscr.read_volatile() }; - let buffer_ptr = unsafe { dscr.offset(1).read_volatile() } as *const u8; - let next_dscr = unsafe { dscr.offset(2).read_volatile() } as *const u8; - let chunk_len = dw0.get_length() as usize; - unsafe { - core::ptr::copy_nonoverlapping( - buffer_ptr, - dst.as_mut_ptr().offset(len as isize), - chunk_len, - ) + 0 }; - - len += chunk_len; - - if next_dscr.is_null() { - break; - } - - dscr = unsafe { dscr.offset(3) }; } - Ok(len) + processed += chunk_size; + descr += 3; + + if processed >= len { + break; + } } + + R::clear_in_interrupts(); + R::reset_in(); + R::set_in_descriptors(descriptors.as_ptr() as u32); + R::set_in_peripheral(peri as u8); + R::start_in(); + + if R::has_in_descriptor_error() { + return Err(DmaError::DescriptorError); + } + + Ok(()) } - /// The functions here are not meant to be used outside the HAL - pub trait TxPrivate { - fn init(&mut self, burst_mode: bool, priority: DmaPriority); - - fn init_channel(&mut self); - - fn prepare_transfer( - &mut self, - peri: DmaPeripheral, - circular: bool, - data: *const u8, - len: usize, - ) -> Result<(), DmaError>; - - fn is_done(&mut self) -> bool; - - fn available(&mut self) -> usize; - - fn push(&mut self, data: &[u8]) -> Result; + fn is_done(&self) -> bool { + R::is_in_done() } - pub trait TxChannel - where - R: RegisterAccess, - { - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - R::set_out_burstmode(burst_mode); - R::set_out_priority(priority); - } - - fn prepare_transfer( - &mut self, - descriptors: &mut [u32], - circular: bool, - peri: DmaPeripheral, - data: *const u8, - len: usize, - ) -> Result<(), DmaError> { - for descr in descriptors.iter_mut() { - *descr = 0; - } - - compiler_fence(core::sync::atomic::Ordering::SeqCst); - - let mut processed = 0; - let mut descr = 0; - loop { - let chunk_size = usize::min(CHUNK_SIZE, len - processed); - let last = processed + chunk_size >= len; - - descriptors[descr + 1] = data as u32 + processed as u32; - - let mut dw0 = &mut descriptors[descr]; - - dw0.set_suc_eof(last); - dw0.set_owner(Owner::Dma); - dw0.set_size(chunk_size as u16); // align to 32 bits? - dw0.set_length(chunk_size as u16); // actual size of the data!? - - if !last { - descriptors[descr + 2] = - (&descriptors[descr + 3]) as *const _ as *const () as u32; - } else { - if !circular { - descriptors[descr + 2] = 0; - } else { - descriptors[descr + 2] = descriptors.as_ptr() as u32; - } - } - - processed += chunk_size; - descr += 3; - - if processed >= len { - break; - } - } - - R::clear_out_interrupts(); - R::reset_out(); - R::set_out_descriptors(descriptors.as_ptr() as u32); - R::set_out_peripheral(peri as u8); - R::start_out(); - - if R::has_out_descriptor_error() { - return Err(DmaError::DescriptorError); - } - - Ok(()) - } - - fn is_done(&mut self) -> bool { - R::is_out_done() - } - - fn descriptors_handled(&self) -> bool { - R::is_out_eof_interrupt_set() - } - - fn reset_descriptors_handled(&self) { - R::reset_out_eof_interrupt(); - } - - fn last_out_dscr_address(&self) -> usize { - R::last_out_dscr_address() - } + fn last_in_dscr_address(&self) -> usize { + R::last_in_dscr_address() } - pub struct ChannelTx<'a, T, R> - where - T: TxChannel, - R: RegisterAccess, - { - pub descriptors: &'a mut [u32], - #[allow(unused)] - pub burst_mode: bool, - pub tx_impl: T, - pub write_offset: usize, - pub write_descr_ptr: *const u32, - pub available: usize, - pub last_seen_handled_descriptor_ptr: *const u32, - pub buffer_start: *const u8, - pub buffer_len: usize, - pub _phantom: PhantomData, + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; +} + +pub struct ChannelRx<'a, T, R> +where + T: RxChannel, + R: RegisterAccess, +{ + pub descriptors: &'a mut [u32], + pub burst_mode: bool, + pub rx_impl: T, + pub read_descr_ptr: *const u32, + pub available: usize, + pub last_seen_handled_descriptor_ptr: *const u32, + pub read_buffer_start: *const u8, + pub _phantom: PhantomData, +} + +impl<'a, T, R> Rx for ChannelRx<'a, T, R> +where + T: RxChannel, + R: RegisterAccess, +{ +} + +impl<'a, T, R> RxPrivate for ChannelRx<'a, T, R> +where + T: RxChannel, + R: RegisterAccess, +{ + fn init(&mut self, burst_mode: bool, priority: DmaPriority) { + self.rx_impl.init(burst_mode, priority); } - impl<'a, T, R> Tx for ChannelTx<'a, T, R> - where - T: TxChannel, - R: RegisterAccess, - { + fn prepare_transfer( + &mut self, + circular: bool, + peri: DmaPeripheral, + data: *mut u8, + len: usize, + ) -> Result<(), DmaError> { + if self.descriptors.len() % 3 != 0 { + return Err(DmaError::InvalidDescriptorSize); + } + + if self.descriptors.len() / 3 < len / CHUNK_SIZE { + return Err(DmaError::OutOfDescriptors); + } + + if self.burst_mode && (len % 4 != 0 || data as u32 % 4 != 0) { + return Err(DmaError::InvalidAlignment); + } + + if circular && len < CHUNK_SIZE * 2 { + return Err(DmaError::BufferTooSmall); + } + + self.available = 0; + self.read_descr_ptr = self.descriptors.as_ptr() as *const u32; + self.last_seen_handled_descriptor_ptr = core::ptr::null(); + self.read_buffer_start = data; + + self.rx_impl + .prepare_transfer(self.descriptors, circular, peri, data, len)?; + Ok(()) } - impl<'a, T, R> TxPrivate for ChannelTx<'a, T, R> - where - T: TxChannel, - R: RegisterAccess, - { - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - self.tx_impl.init(burst_mode, priority); + fn is_done(&self) -> bool { + self.rx_impl.is_done() + } + + fn init_channel(&mut self) { + R::init_channel(); + } + + fn available(&mut self) -> usize { + if self.last_seen_handled_descriptor_ptr.is_null() { + self.last_seen_handled_descriptor_ptr = self.descriptors.as_mut_ptr(); + return 0; } - fn init_channel(&mut self) { - R::init_channel(); + if self.available != 0 { + return self.available; } - fn prepare_transfer( - &mut self, - peri: DmaPeripheral, - circular: bool, - data: *const u8, - len: usize, - ) -> Result<(), DmaError> { - if self.descriptors.len() % 3 != 0 { - return Err(DmaError::InvalidDescriptorSize); - } + let descr_address = self.last_seen_handled_descriptor_ptr as *mut u32; + let mut dw0 = unsafe { &mut descr_address.read_volatile() }; - if self.descriptors.len() / 3 < len / CHUNK_SIZE { - return Err(DmaError::OutOfDescriptors); - } + if dw0.get_owner() == Owner::Cpu && dw0.get_length() != 0 { + let descriptor_buffer = unsafe { descr_address.offset(1).read_volatile() } as *const u8; + let next_descriptor = unsafe { descr_address.offset(2).read_volatile() } as *const u32; - if circular && len < CHUNK_SIZE * 2 { - return Err(DmaError::BufferTooSmall); - } + self.read_buffer_start = descriptor_buffer; + self.available = dw0.get_length() as usize; - self.write_offset = 0; - self.available = 0; - self.write_descr_ptr = self.descriptors.as_ptr() as *const u32; - self.last_seen_handled_descriptor_ptr = self.descriptors.as_ptr() as *const u32; - self.buffer_start = data; - self.buffer_len = len; - - self.tx_impl - .prepare_transfer(self.descriptors, circular, peri, data, len)?; - - Ok(()) - } - - fn is_done(&mut self) -> bool { - self.tx_impl.is_done() - } - - fn available(&mut self) -> usize { - if self.tx_impl.descriptors_handled() { - self.tx_impl.reset_descriptors_handled(); - let descr_address = self.tx_impl.last_out_dscr_address() as *const u32; - - if descr_address >= self.last_seen_handled_descriptor_ptr { - let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; - - unsafe { - while ptr < descr_address as *const u32 { - let mut dw0 = &mut ptr.read_volatile(); - self.available += dw0.get_length() as usize; - ptr = ptr.offset(3); - } - } - } else { - let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; - - unsafe { - loop { - if ptr.offset(2).read_volatile() == 0 { - break; - } - - let mut dw0 = &mut ptr.read_volatile(); - self.available += dw0.get_length() as usize; - ptr = ptr.offset(3); - } - } - } - - if self.available >= self.buffer_len { - unsafe { - let segment_len = - (&mut self.write_descr_ptr.read_volatile()).get_length() as usize; - self.available -= segment_len; - self.write_offset = (self.write_offset + segment_len) % self.buffer_len; - let next_descriptor = - self.write_descr_ptr.offset(2).read_volatile() as *const u32; - self.write_descr_ptr = if next_descriptor.is_null() { - self.descriptors.as_ptr() as *const u32 - } else { - next_descriptor - } - } - } - - self.last_seen_handled_descriptor_ptr = descr_address; - } - - self.available - } - - fn push(&mut self, data: &[u8]) -> Result { - let avail = self.available(); - - if avail < data.len() { - return Err(super::DmaError::Overflow); - } + dw0.set_owner(Owner::Dma); + dw0.set_length(0); + dw0.set_suc_eof(false); unsafe { - let src = data.as_ptr(); - let dst = self.buffer_start.offset(self.write_offset as isize) as *mut u8; - let count = usize::min(data.len(), self.buffer_len - self.write_offset); - core::ptr::copy_nonoverlapping(src, dst, count); + descr_address.write_volatile(*dw0); } - if self.write_offset + data.len() >= self.buffer_len { - let remainder = (self.write_offset + data.len()) % self.buffer_len; - let dst = self.buffer_start as *mut u8; - unsafe { - let src = data.as_ptr().offset((data.len() - remainder) as isize); - core::ptr::copy_nonoverlapping(src, dst, remainder); + if !next_descriptor.is_null() { + self.last_seen_handled_descriptor_ptr = next_descriptor; + } else { + self.last_seen_handled_descriptor_ptr = self.descriptors.as_ptr(); + } + } + + self.available + } + + fn pop(&mut self, data: &mut [u8]) -> Result { + let avail = self.available; + + if avail < data.len() { + return Err(DmaError::Exhausted); + } + + unsafe { + let dst = data.as_mut_ptr(); + let src = self.read_buffer_start; + let count = self.available; + core::ptr::copy_nonoverlapping(src, dst, count); + } + + self.available = 0; + Ok(data.len()) + } + + fn drain_buffer(&mut self, dst: &mut [u8]) -> Result { + let mut len: usize = 0; + let mut dscr = self.descriptors.as_ptr() as *mut u32; + loop { + let mut dw0 = unsafe { &mut dscr.read_volatile() }; + let buffer_ptr = unsafe { dscr.offset(1).read_volatile() } as *const u8; + let next_dscr = unsafe { dscr.offset(2).read_volatile() } as *const u8; + let chunk_len = dw0.get_length() as usize; + unsafe { + core::ptr::copy_nonoverlapping( + buffer_ptr, + dst.as_mut_ptr().offset(len as isize), + chunk_len, + ) + }; + + len += chunk_len; + + if next_dscr.is_null() { + break; + } + + dscr = unsafe { dscr.offset(3) }; + } + + Ok(len) + } + + fn is_listening_eof(&self) -> bool { + R::is_listening_in_eof() + } + + fn listen_eof(&self) { + R::listen_in_eof() + } + + fn unlisten_eof(&self) { + R::unlisten_in_eof() + } + + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + T::waker() + } +} + +/// The functions here are not meant to be used outside the HAL +pub trait TxPrivate { + fn init(&mut self, burst_mode: bool, priority: DmaPriority); + + fn init_channel(&mut self); + + fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + circular: bool, + data: *const u8, + len: usize, + ) -> Result<(), DmaError>; + + fn is_done(&self) -> bool; + + fn is_listening_eof(&self) -> bool; + + fn listen_eof(&self); + + fn unlisten_eof(&self); + + fn available(&mut self) -> usize; + + fn push(&mut self, data: &[u8]) -> Result; + + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; +} + +pub trait TxChannel +where + R: RegisterAccess, +{ + fn init(&mut self, burst_mode: bool, priority: DmaPriority) { + R::set_out_burstmode(burst_mode); + R::set_out_priority(priority); + } + + fn prepare_transfer( + &mut self, + descriptors: &mut [u32], + circular: bool, + peri: DmaPeripheral, + data: *const u8, + len: usize, + ) -> Result<(), DmaError> { + for descr in descriptors.iter_mut() { + *descr = 0; + } + + compiler_fence(core::sync::atomic::Ordering::SeqCst); + + let mut processed = 0; + let mut descr = 0; + loop { + let chunk_size = usize::min(CHUNK_SIZE, len - processed); + let last = processed + chunk_size >= len; + + descriptors[descr + 1] = data as u32 + processed as u32; + + let mut dw0 = &mut descriptors[descr]; + + dw0.set_suc_eof(last); + dw0.set_owner(Owner::Dma); + dw0.set_size(chunk_size as u16); // align to 32 bits? + dw0.set_length(chunk_size as u16); // actual size of the data!? + + if !last { + descriptors[descr + 2] = (&descriptors[descr + 3]) as *const _ as *const () as u32; + } else { + if !circular { + descriptors[descr + 2] = 0; + } else { + descriptors[descr + 2] = descriptors.as_ptr() as u32; } } - let mut forward = data.len(); - loop { + processed += chunk_size; + descr += 3; + + if processed >= len { + break; + } + } + + R::clear_out_interrupts(); + R::reset_out(); + R::set_out_descriptors(descriptors.as_ptr() as u32); + R::set_out_peripheral(peri as u8); + R::start_out(); + + if R::has_out_descriptor_error() { + return Err(DmaError::DescriptorError); + } + + Ok(()) + } + + fn is_done(&self) -> bool { + R::is_out_done() + } + + fn descriptors_handled(&self) -> bool { + R::is_out_eof_interrupt_set() + } + + fn reset_descriptors_handled(&self) { + R::reset_out_eof_interrupt(); + } + + fn last_out_dscr_address(&self) -> usize { + R::last_out_dscr_address() + } + + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; +} + +pub struct ChannelTx<'a, T, R> +where + T: TxChannel, + R: RegisterAccess, +{ + pub descriptors: &'a mut [u32], + #[allow(unused)] + pub burst_mode: bool, + pub tx_impl: T, + pub write_offset: usize, + pub write_descr_ptr: *const u32, + pub available: usize, + pub last_seen_handled_descriptor_ptr: *const u32, + pub buffer_start: *const u8, + pub buffer_len: usize, + pub _phantom: PhantomData, +} + +impl<'a, T, R> Tx for ChannelTx<'a, T, R> +where + T: TxChannel, + R: RegisterAccess, +{ +} + +impl<'a, T, R> TxPrivate for ChannelTx<'a, T, R> +where + T: TxChannel, + R: RegisterAccess, +{ + fn init(&mut self, burst_mode: bool, priority: DmaPriority) { + self.tx_impl.init(burst_mode, priority); + } + + fn init_channel(&mut self) { + R::init_channel(); + } + + fn prepare_transfer( + &mut self, + peri: DmaPeripheral, + circular: bool, + data: *const u8, + len: usize, + ) -> Result<(), DmaError> { + if self.descriptors.len() % 3 != 0 { + return Err(DmaError::InvalidDescriptorSize); + } + + if self.descriptors.len() / 3 < len / CHUNK_SIZE { + return Err(DmaError::OutOfDescriptors); + } + + if circular && len < CHUNK_SIZE * 2 { + return Err(DmaError::BufferTooSmall); + } + + self.write_offset = 0; + self.available = 0; + self.write_descr_ptr = self.descriptors.as_ptr() as *const u32; + self.last_seen_handled_descriptor_ptr = self.descriptors.as_ptr() as *const u32; + self.buffer_start = data; + self.buffer_len = len; + + self.tx_impl + .prepare_transfer(self.descriptors, circular, peri, data, len)?; + + Ok(()) + } + + fn is_done(&self) -> bool { + self.tx_impl.is_done() + } + + fn available(&mut self) -> usize { + if self.tx_impl.descriptors_handled() { + self.tx_impl.reset_descriptors_handled(); + let descr_address = self.tx_impl.last_out_dscr_address() as *const u32; + + if descr_address >= self.last_seen_handled_descriptor_ptr { + let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; + + unsafe { + while ptr < descr_address as *const u32 { + let mut dw0 = &mut ptr.read_volatile(); + self.available += dw0.get_length() as usize; + ptr = ptr.offset(3); + } + } + } else { + let mut ptr = self.last_seen_handled_descriptor_ptr as *const u32; + + unsafe { + loop { + if ptr.offset(2).read_volatile() == 0 { + break; + } + + let mut dw0 = &mut ptr.read_volatile(); + self.available += dw0.get_length() as usize; + ptr = ptr.offset(3); + } + } + } + + if self.available >= self.buffer_len { unsafe { - let next_descriptor = - self.write_descr_ptr.offset(2).read_volatile() as *const u32; let segment_len = (&mut self.write_descr_ptr.read_volatile()).get_length() as usize; + self.available -= segment_len; + self.write_offset = (self.write_offset + segment_len) % self.buffer_len; + let next_descriptor = + self.write_descr_ptr.offset(2).read_volatile() as *const u32; self.write_descr_ptr = if next_descriptor.is_null() { self.descriptors.as_ptr() as *const u32 } else { next_descriptor - }; - - if forward <= segment_len { - break; - } - - forward -= segment_len; - - if forward == 0 { - break; } } } - self.write_offset = (self.write_offset + data.len()) % self.buffer_len; - self.available -= data.len(); - - Ok(data.len()) + self.last_seen_handled_descriptor_ptr = descr_address; } + + self.available } - pub trait RegisterAccess { - fn init_channel(); - fn set_out_burstmode(burst_mode: bool); - fn set_out_priority(priority: DmaPriority); - fn clear_out_interrupts(); - fn reset_out(); - fn set_out_descriptors(address: u32); - fn has_out_descriptor_error() -> bool; - fn set_out_peripheral(peripheral: u8); - fn start_out(); - fn is_out_done() -> bool; - fn is_out_eof_interrupt_set() -> bool; - fn reset_out_eof_interrupt(); - fn last_out_dscr_address() -> usize; + fn push(&mut self, data: &[u8]) -> Result { + let avail = self.available(); - fn set_in_burstmode(burst_mode: bool); - fn set_in_priority(priority: DmaPriority); - fn clear_in_interrupts(); - fn reset_in(); - fn set_in_descriptors(address: u32); - fn has_in_descriptor_error() -> bool; - fn set_in_peripheral(peripheral: u8); - fn start_in(); - fn is_in_done() -> bool; - fn last_in_dscr_address() -> usize; + if avail < data.len() { + return Err(DmaError::Overflow); + } + + unsafe { + let src = data.as_ptr(); + let dst = self.buffer_start.offset(self.write_offset as isize) as *mut u8; + let count = usize::min(data.len(), self.buffer_len - self.write_offset); + core::ptr::copy_nonoverlapping(src, dst, count); + } + + if self.write_offset + data.len() >= self.buffer_len { + let remainder = (self.write_offset + data.len()) % self.buffer_len; + let dst = self.buffer_start as *mut u8; + unsafe { + let src = data.as_ptr().offset((data.len() - remainder) as isize); + core::ptr::copy_nonoverlapping(src, dst, remainder); + } + } + + let mut forward = data.len(); + loop { + unsafe { + let next_descriptor = self.write_descr_ptr.offset(2).read_volatile() as *const u32; + let segment_len = (&mut self.write_descr_ptr.read_volatile()).get_length() as usize; + self.write_descr_ptr = if next_descriptor.is_null() { + self.descriptors.as_ptr() as *const u32 + } else { + next_descriptor + }; + + if forward <= segment_len { + break; + } + + forward -= segment_len; + + if forward == 0 { + break; + } + } + } + + self.write_offset = (self.write_offset + data.len()) % self.buffer_len; + self.available -= data.len(); + + Ok(data.len()) + } + + fn is_listening_eof(&self) -> bool { + R::is_listening_out_eof() + } + + fn listen_eof(&self) { + R::listen_out_eof() + } + + fn unlisten_eof(&self) { + R::unlisten_out_eof() + } + + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + T::waker() } } +pub trait RegisterAccess { + fn init_channel(); + fn set_out_burstmode(burst_mode: bool); + fn set_out_priority(priority: DmaPriority); + fn clear_out_interrupts(); + fn reset_out(); + fn set_out_descriptors(address: u32); + fn has_out_descriptor_error() -> bool; + fn set_out_peripheral(peripheral: u8); + fn start_out(); + fn is_out_done() -> bool; + fn is_out_eof_interrupt_set() -> bool; + fn reset_out_eof_interrupt(); + fn last_out_dscr_address() -> usize; + + fn set_in_burstmode(burst_mode: bool); + fn set_in_priority(priority: DmaPriority); + fn clear_in_interrupts(); + fn reset_in(); + fn set_in_descriptors(address: u32); + fn has_in_descriptor_error() -> bool; + fn set_in_peripheral(peripheral: u8); + fn start_in(); + fn is_in_done() -> bool; + fn last_in_dscr_address() -> usize; + + fn is_listening_in_eof() -> bool; + fn is_listening_out_eof() -> bool; + + fn listen_in_eof(); + fn listen_out_eof(); + fn unlisten_in_eof(); + fn unlisten_out_eof(); +} /// DMA Channel pub struct Channel where @@ -802,3 +854,313 @@ pub trait DmaTransferRxTx: Drop { /// Wait for the transfer to finish. fn wait(self) -> (BR, BT, T); } + +#[cfg(feature = "async")] +pub(crate) mod asynch { + use core::task::Poll; + + use super::*; + use crate::macros::interrupt; + + pub struct DmaTxFuture<'a, TX> { + pub(crate) tx: &'a mut TX, + _a: (), + } + + impl<'a, TX> DmaTxFuture<'a, TX> + where + TX: Tx, + { + pub fn new(tx: &'a mut TX) -> Self { + tx.listen_eof(); + Self { tx, _a: () } + } + } + + impl<'a, TX> core::future::Future for DmaTxFuture<'a, TX> + where + TX: Tx, + { + type Output = (); // TODO handle DMA errors + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll { + TX::waker().register(cx.waker()); + if self.tx.is_listening_eof() { + Poll::Pending + } else { + Poll::Ready(()) + } + } + } + + pub struct DmaRxFuture<'a, RX> { + pub(crate) rx: &'a mut RX, + _a: (), + } + + impl<'a, RX> DmaRxFuture<'a, RX> + where + RX: Rx, + { + pub fn new(rx: &'a mut RX) -> Self { + rx.listen_eof(); + Self { rx, _a: () } + } + } + + impl<'a, RX> core::future::Future for DmaRxFuture<'a, RX> + where + RX: Rx, + { + type Output = (); // TODO handle DMA errors + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> Poll { + RX::waker().register(cx.waker()); + if self.rx.is_listening_eof() { + Poll::Pending + } else { + Poll::Ready(()) + } + } + } + + #[cfg(esp32c2)] + mod interrupt { + use super::*; + + #[interrupt] + fn DMA_CH0() { + use crate::dma::gdma::{ + Channel0 as Channel, + Channel0RxImpl as ChannelRxImpl, + Channel0TxImpl as ChannelTxImpl, + }; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + } + + #[cfg(esp32c3)] + mod interrupt { + use super::*; + + #[interrupt] + fn DMA_CH0() { + use crate::dma::gdma::{ + Channel0 as Channel, + Channel0RxImpl as ChannelRxImpl, + Channel0TxImpl as ChannelTxImpl, + }; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_CH1() { + use crate::dma::gdma::{ + Channel1 as Channel, + Channel1RxImpl as ChannelRxImpl, + Channel1TxImpl as ChannelTxImpl, + }; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_CH2() { + use crate::dma::gdma::{ + Channel2 as Channel, + Channel2RxImpl as ChannelRxImpl, + Channel2TxImpl as ChannelTxImpl, + }; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + } + + #[cfg(esp32s3)] + mod interrupt { + use super::*; + + #[interrupt] + fn DMA_IN_CH0() { + use crate::dma::gdma::{Channel0 as Channel, Channel0RxImpl as ChannelRxImpl}; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_OUT_CH0() { + use crate::dma::gdma::{Channel0 as Channel, Channel0TxImpl as ChannelTxImpl}; + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_IN_CH1() { + use crate::dma::gdma::{Channel1 as Channel, Channel1RxImpl as ChannelRxImpl}; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_OUT_CH1() { + use crate::dma::gdma::{Channel1 as Channel, Channel1TxImpl as ChannelTxImpl}; + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_IN_CH3() { + use crate::dma::gdma::{Channel3 as Channel, Channel3RxImpl as ChannelRxImpl}; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_OUT_CH3() { + use crate::dma::gdma::{Channel3 as Channel, Channel3TxImpl as ChannelTxImpl}; + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_IN_CH4() { + use crate::dma::gdma::{Channel4 as Channel, Channel4RxImpl as ChannelRxImpl}; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + } + + #[interrupt] + fn DMA_OUT_CH4() { + use crate::dma::gdma::{Channel4 as Channel, Channel4TxImpl as ChannelTxImpl}; + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + } + + #[cfg(any(esp32s2, esp32))] + mod interrupt { + use super::*; + + #[interrupt] + fn SPI2_DMA() { + use crate::dma::pdma::{ + Spi2DmaChannel as Channel, + Spi2DmaChannelRxImpl as ChannelRxImpl, + Spi2DmaChannelTxImpl as ChannelTxImpl, + }; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + + #[interrupt] + fn SPI3_DMA() { + use crate::dma::pdma::{ + Spi3DmaChannel as Channel, + Spi3DmaChannelRxImpl as ChannelRxImpl, + Spi3DmaChannelTxImpl as ChannelTxImpl, + }; + + if Channel::is_in_done() { + Channel::clear_in_interrupts(); + Channel::unlisten_in_eof(); + ChannelRxImpl::waker().wake() + } + + if Channel::is_out_done() { + Channel::clear_out_interrupts(); + Channel::unlisten_out_eof(); + ChannelTxImpl::waker().wake() + } + } + } +} diff --git a/esp-hal-common/src/dma/pdma.rs b/esp-hal-common/src/dma/pdma.rs index b1a105b5a..946e83fea 100644 --- a/esp-hal-common/src/dma/pdma.rs +++ b/esp-hal-common/src/dma/pdma.rs @@ -1,7 +1,7 @@ //! Direct Memory Access use crate::{ - dma::pdma::private::*, + dma::*, peripheral::PeripheralRef, system::{Peripheral, PeripheralClockControl}, }; @@ -84,7 +84,8 @@ macro_rules! ImplSpiChannel { fn is_out_done() -> bool { let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_raw.read().out_done_int_raw().bit() + // FIXME this should be out_total_eof_int_raw? but on esp32 this interrupt doesn't seem to fire + spi.dma_int_raw.read().out_eof_int_raw().bit() } fn last_out_dscr_address() -> usize { @@ -162,15 +163,57 @@ macro_rules! ImplSpiChannel { let spi = unsafe { &*crate::peripherals::[]::PTR }; spi.inlink_dscr_bf0.read().dma_inlink_dscr_bf0().bits() as usize } + + fn is_listening_in_eof() -> bool { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.read().in_suc_eof_int_ena().bit_is_set() + } + + fn is_listening_out_eof() -> bool { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.read().out_total_eof_int_ena().bit_is_set() + } + + fn listen_in_eof() { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.modify(|_, w| w.in_suc_eof_int_ena().set_bit()); + } + + fn listen_out_eof() { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.modify(|_, w| w.out_total_eof_int_ena().set_bit()); + } + + fn unlisten_in_eof() { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.modify(|_, w| w.in_suc_eof_int_ena().clear_bit()); + } + + fn unlisten_out_eof() { + let spi = unsafe { &*crate::peripherals::[]::PTR }; + spi.dma_int_ena.modify(|_, w| w.out_total_eof_int_ena().clear_bit()); + } } pub struct [] {} - impl<'a> TxChannel<[]> for [] {} + impl<'a> TxChannel<[]> for [] { + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); + &WAKER + } + } pub struct [] {} - impl<'a> RxChannel<[]> for [] {} + impl<'a> RxChannel<[]> for [] { + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); + &WAKER + } + } pub struct [] {} @@ -368,15 +411,47 @@ macro_rules! ImplI2sChannel { let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; reg_block.inlink_dscr_bf0.read().inlink_dscr_bf0().bits() as usize } + + fn is_listening_in_eof() -> bool { + todo!() + } + fn is_listening_out_eof() -> bool { + todo!() + } + + fn listen_in_eof() { + todo!() + } + fn listen_out_eof() { + todo!() + } + fn unlisten_in_eof() { + todo!() + } + fn unlisten_out_eof() { + todo!() + } } pub struct [] {} - impl<'a> TxChannel<[]> for [] {} + impl<'a> TxChannel<[]> for [] { + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); + &WAKER + } + } pub struct [] {} - impl<'a> RxChannel<[]> for [] {} + impl<'a> RxChannel<[]> for [] { + #[cfg(feature = "async")] + fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); + &WAKER + } + } pub struct [] {} @@ -436,42 +511,37 @@ macro_rules! ImplI2sChannel { }; } -/// Crate private implementation details -pub(crate) mod private { - use crate::dma::{private::*, *}; +pub struct Spi2DmaSuitablePeripheral {} +impl PeripheralMarker for Spi2DmaSuitablePeripheral {} +impl SpiPeripheral for Spi2DmaSuitablePeripheral {} +impl Spi2Peripheral for Spi2DmaSuitablePeripheral {} - pub struct Spi2DmaSuitablePeripheral {} - impl PeripheralMarker for Spi2DmaSuitablePeripheral {} - impl SpiPeripheral for Spi2DmaSuitablePeripheral {} - impl Spi2Peripheral for Spi2DmaSuitablePeripheral {} +pub struct Spi3DmaSuitablePeripheral {} +impl PeripheralMarker for Spi3DmaSuitablePeripheral {} +impl SpiPeripheral for Spi3DmaSuitablePeripheral {} +impl Spi3Peripheral for Spi3DmaSuitablePeripheral {} - pub struct Spi3DmaSuitablePeripheral {} - impl PeripheralMarker for Spi3DmaSuitablePeripheral {} - impl SpiPeripheral for Spi3DmaSuitablePeripheral {} - impl Spi3Peripheral for Spi3DmaSuitablePeripheral {} +ImplSpiChannel!(2); +ImplSpiChannel!(3); - ImplSpiChannel!(2); - ImplSpiChannel!(3); +pub struct I2s0DmaSuitablePeripheral {} +impl PeripheralMarker for I2s0DmaSuitablePeripheral {} +impl I2sPeripheral for I2s0DmaSuitablePeripheral {} +impl I2s0Peripheral for I2s0DmaSuitablePeripheral {} - pub struct I2s0DmaSuitablePeripheral {} - impl PeripheralMarker for I2s0DmaSuitablePeripheral {} - impl I2sPeripheral for I2s0DmaSuitablePeripheral {} - impl I2s0Peripheral for I2s0DmaSuitablePeripheral {} +#[cfg(esp32)] +ImplI2sChannel!(0, "I2S0"); - #[cfg(esp32)] - ImplI2sChannel!(0, "I2S0"); +#[cfg(esp32s2)] +ImplI2sChannel!(0, "I2S"); - #[cfg(esp32s2)] - ImplI2sChannel!(0, "I2S"); +pub struct I2s1DmaSuitablePeripheral {} +impl PeripheralMarker for I2s1DmaSuitablePeripheral {} +impl I2sPeripheral for I2s1DmaSuitablePeripheral {} +impl I2s1Peripheral for I2s1DmaSuitablePeripheral {} - pub struct I2s1DmaSuitablePeripheral {} - impl PeripheralMarker for I2s1DmaSuitablePeripheral {} - impl I2sPeripheral for I2s1DmaSuitablePeripheral {} - impl I2s1Peripheral for I2s1DmaSuitablePeripheral {} - - #[cfg(esp32)] - ImplI2sChannel!(1, "I2S1"); -} +#[cfg(esp32)] +ImplI2sChannel!(1, "I2S1"); /// DMA Peripheral /// diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 63fe691fc..1fec8139f 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -22,6 +22,7 @@ #![cfg_attr(xtensa, feature(asm_experimental_arch))] #![cfg_attr(feature = "async", allow(incomplete_features))] #![cfg_attr(feature = "async", feature(async_fn_in_trait))] +#![cfg_attr(feature = "async", feature(impl_trait_projections))] #[cfg_attr(esp32, path = "peripherals/esp32.rs")] #[cfg_attr(esp32c3, path = "peripherals/esp32c3.rs")] diff --git a/esp-hal-common/src/spi.rs b/esp-hal-common/src/spi.rs index 4d69e6b0d..a94c44d25 100644 --- a/esp-hal-common/src/spi.rs +++ b/esp-hal-common/src/spi.rs @@ -577,6 +577,147 @@ pub mod dma { } } + #[cfg(feature = "async")] + mod asynch { + use super::*; + + impl<'d, T, TX, RX, P> embedded_hal_async::spi::SpiBusWrite for SpiDma<'d, T, TX, RX, P> + where + T: InstanceDma, + TX: Tx, + RX: Rx, + P: SpiPeripheral, + { + async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + for chunk in words.chunks(MAX_DMA_SIZE) { + self.spi.start_write_bytes_dma( + chunk.as_ptr(), + chunk.len(), + &mut self.channel.tx, + )?; + + crate::dma::asynch::DmaTxFuture::new(&mut self.channel.tx).await; + + // FIXME: in the future we should use the peripheral DMA status registers to + // await on both the dma transfer _and_ the peripherals status + self.spi.flush()?; + } + + Ok(()) + } + } + + impl<'d, T, TX, RX, P> embedded_hal_async::spi::SpiBusFlush for SpiDma<'d, T, TX, RX, P> + where + T: InstanceDma, + TX: Tx, + RX: Rx, + P: SpiPeripheral, + { + async fn flush(&mut self) -> Result<(), Self::Error> { + // TODO use async flush in the future + self.spi.flush() + } + } + + impl<'d, T, TX, RX, P> embedded_hal_async::spi::SpiBusRead for SpiDma<'d, T, TX, RX, P> + where + T: InstanceDma, + TX: Tx, + RX: Rx, + P: SpiPeripheral, + { + async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.spi.start_read_bytes_dma( + words.as_mut_ptr(), + words.len(), + &mut self.channel.rx, + )?; + + crate::dma::asynch::DmaRxFuture::new(&mut self.channel.rx).await; + + Ok(()) + } + } + + impl<'d, T, TX, RX, P> embedded_hal_async::spi::SpiBus for SpiDma<'d, T, TX, RX, P> + where + T: InstanceDma, + TX: Tx, + RX: Rx, + P: SpiPeripheral, + { + async fn transfer<'a>( + &'a mut self, + read: &'a mut [u8], + write: &'a [u8], + ) -> Result<(), Self::Error> { + let mut idx = 0; + loop { + let write_idx = isize::min(idx, write.len() as isize); + let write_len = usize::min(write.len() - idx as usize, MAX_DMA_SIZE); + + let read_idx = isize::min(idx, read.len() as isize); + let read_len = usize::min(read.len() - idx as usize, MAX_DMA_SIZE); + + self.spi.start_transfer_dma( + unsafe { write.as_ptr().offset(write_idx) }, + write_len, + unsafe { read.as_mut_ptr().offset(read_idx) }, + read_len, + &mut self.channel.tx, + &mut self.channel.rx, + )?; + + embassy_futures::join::join( + crate::dma::asynch::DmaTxFuture::new(&mut self.channel.tx), + crate::dma::asynch::DmaRxFuture::new(&mut self.channel.rx), + ) + .await; + + // FIXME: in the future we should use the peripheral DMA status registers to + // await on both the dma transfer _and_ the peripherals status + self.spi.flush()?; + + idx += MAX_DMA_SIZE as isize; + if idx >= write.len() as isize && idx >= read.len() as isize { + break; + } + } + + Ok(()) + } + + async fn transfer_in_place<'a>( + &'a mut self, + words: &'a mut [u8], + ) -> Result<(), Self::Error> { + for chunk in words.chunks_mut(MAX_DMA_SIZE) { + self.spi.start_transfer_dma( + chunk.as_ptr(), + chunk.len(), + chunk.as_mut_ptr(), + chunk.len(), + &mut self.channel.tx, + &mut self.channel.rx, + )?; + + embassy_futures::join::join( + crate::dma::asynch::DmaTxFuture::new(&mut self.channel.tx), + crate::dma::asynch::DmaRxFuture::new(&mut self.channel.rx), + ) + .await; + + // FIXME: in the future we should use the peripheral DMA status registers to + // await on both the dma transfer _and_ the peripherals status + self.spi.flush()?; + } + + Ok(()) + } + } + } + #[cfg(feature = "eh1")] mod ehal1 { use embedded_hal_1::spi::{SpiBus, SpiBusFlush, SpiBusRead, SpiBusWrite}; diff --git a/esp32-hal/Cargo.toml b/esp32-hal/Cargo.toml index 9624ecaf9..538d4fe6a 100644 --- a/esp32-hal/Cargo.toml +++ b/esp32-hal/Cargo.toml @@ -77,3 +77,7 @@ required-features = ["embassy"] [[example]] name = "embassy_wait" required-features = ["embassy", "async"] + +[[example]] +name = "embassy_spi" +required-features = ["embassy", "async"] diff --git a/esp32-hal/examples/embassy_spi.rs b/esp32-hal/examples/embassy_spi.rs new file mode 100644 index 000000000..213a11702 --- /dev/null +++ b/esp32-hal/examples/embassy_spi.rs @@ -0,0 +1,127 @@ +//! embassy hello world +//! +//! This is an example of running the embassy executor with multiple tasks +//! concurrently. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32_hal::{ + clock::ClockControl, + dma::{DmaPriority, *}, + embassy, + pdma::*, + peripherals::Peripherals, + prelude::*, + spi::{dma::SpiDma, Spi, SpiMode}, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use static_cell::StaticCell; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +pub type SpiType<'d> = SpiDma< + 'd, + esp32_hal::peripherals::SPI2, + ChannelTx<'d, Spi2DmaChannelTxImpl, Spi2DmaChannel>, + ChannelRx<'d, Spi2DmaChannelRxImpl, Spi2DmaChannel>, + Spi2DmaSuitablePeripheral, +>; + +#[embassy_executor::task] +async fn spi_task(spi: &'static mut SpiType<'static>) { + let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; + loop { + let mut buffer = [0; 8]; + esp_println::println!("Sending bytes"); + embedded_hal_async::spi::SpiBus::transfer(spi, &mut buffer, &send_buffer) + .await + .unwrap(); + esp_println::println!("Bytes recieved: {:?}", buffer); + Timer::after(Duration::from_millis(5_000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[xtensa_lx_rt::entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + esp32_hal::interrupt::enable( + esp32_hal::peripherals::Interrupt::SPI2_DMA, + esp32_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio19; + let miso = io.pins.gpio25; + let mosi = io.pins.gpio23; + let cs = io.pins.gpio22; + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.spi2channel; + + let descriptors = singleton!([0u32; 8 * 3]); + let rx_descriptors = singleton!([0u32; 8 * 3]); + + let spi = singleton!(Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + descriptors, + rx_descriptors, + DmaPriority::Priority0, + ))); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(spi_task(spi)).ok(); + }); +} diff --git a/esp32c2-hal/Cargo.toml b/esp32c2-hal/Cargo.toml index 69631711a..c914ea9e0 100644 --- a/esp32c2-hal/Cargo.toml +++ b/esp32c2-hal/Cargo.toml @@ -72,4 +72,8 @@ required-features = ["embassy"] [[example]] name = "embassy_wait" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_spi" required-features = ["embassy", "async"] \ No newline at end of file diff --git a/esp32c2-hal/examples/embassy_spi.rs b/esp32c2-hal/examples/embassy_spi.rs new file mode 100644 index 000000000..53a2fc484 --- /dev/null +++ b/esp32c2-hal/examples/embassy_spi.rs @@ -0,0 +1,125 @@ +//! embassy hello world +//! +//! This is an example of running the embassy executor with multiple tasks +//! concurrently. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32c2_hal::{ + clock::ClockControl, + dma::{DmaPriority, *}, + embassy, + gdma::*, + peripherals::Peripherals, + prelude::*, + spi::{dma::SpiDma, Spi, SpiMode}, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use static_cell::StaticCell; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +pub type SpiType<'d> = SpiDma< + 'd, + esp32c2_hal::peripherals::SPI2, + ChannelTx<'d, Channel0TxImpl, esp32c2_hal::gdma::Channel0>, + ChannelRx<'d, Channel0RxImpl, esp32c2_hal::gdma::Channel0>, + SuitablePeripheral0, +>; + +#[embassy_executor::task] +async fn spi_task(spi: &'static mut SpiType<'static>) { + let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; + loop { + let mut buffer = [0; 8]; + esp_println::println!("Sending bytes"); + embedded_hal_async::spi::SpiBus::transfer(spi, &mut buffer, &send_buffer) + .await + .unwrap(); + esp_println::println!("Bytes recieved: {:?}", buffer); + Timer::after(Duration::from_millis(5_000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[esp_riscv_rt::entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c2_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + esp32c2_hal::interrupt::enable( + esp32c2_hal::peripherals::Interrupt::DMA_CH0, + esp32c2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio6; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio7; + let cs = io.pins.gpio10; + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let descriptors = singleton!([0u32; 8 * 3]); + let rx_descriptors = singleton!([0u32; 8 * 3]); + + let spi = singleton!(Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + descriptors, + rx_descriptors, + DmaPriority::Priority0, + ))); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(spi_task(spi)).ok(); + }); +} diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index 194a15583..c37d38fed 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -85,3 +85,7 @@ required-features = ["embassy", "async"] [profile.dev] opt-level = 1 + +[[example]] +name = "embassy_spi" +required-features = ["embassy", "async"] diff --git a/esp32c3-hal/examples/embassy_spi.rs b/esp32c3-hal/examples/embassy_spi.rs new file mode 100644 index 000000000..6f2bd3819 --- /dev/null +++ b/esp32c3-hal/examples/embassy_spi.rs @@ -0,0 +1,128 @@ +//! embassy hello world +//! +//! This is an example of running the embassy executor with multiple tasks +//! concurrently. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32c3_hal::{ + clock::ClockControl, + dma::{DmaPriority, *}, + embassy, + gdma::*, + peripherals::Peripherals, + prelude::*, + spi::{dma::SpiDma, Spi, SpiMode}, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use static_cell::StaticCell; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +pub type SpiType<'d> = SpiDma< + 'd, + esp32c3_hal::peripherals::SPI2, + ChannelTx<'d, Channel0TxImpl, esp32c3_hal::gdma::Channel0>, + ChannelRx<'d, Channel0RxImpl, esp32c3_hal::gdma::Channel0>, + SuitablePeripheral0, +>; + +#[embassy_executor::task] +async fn spi_task(spi: &'static mut SpiType<'static>) { + let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; + loop { + let mut buffer = [0; 8]; + esp_println::println!("Sending bytes"); + embedded_hal_async::spi::SpiBus::transfer(spi, &mut buffer, &send_buffer) + .await + .unwrap(); + esp_println::println!("Bytes recieved: {:?}", buffer); + Timer::after(Duration::from_millis(5_000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[esp_riscv_rt::entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + esp32c3_hal::interrupt::enable( + esp32c3_hal::peripherals::Interrupt::DMA_CH0, + esp32c3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio6; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio7; + let cs = io.pins.gpio10; + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let descriptors = singleton!([0u32; 8 * 3]); + let rx_descriptors = singleton!([0u32; 8 * 3]); + + let spi = singleton!(Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + descriptors, + rx_descriptors, + DmaPriority::Priority0, + ))); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(spi_task(spi)).ok(); + }); +} diff --git a/esp32s2-hal/Cargo.toml b/esp32s2-hal/Cargo.toml index d430c1af1..f18944e27 100644 --- a/esp32s2-hal/Cargo.toml +++ b/esp32s2-hal/Cargo.toml @@ -82,4 +82,8 @@ required-features = ["embassy"] [[example]] name = "embassy_wait" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_spi" required-features = ["embassy", "async"] \ No newline at end of file diff --git a/esp32s2-hal/examples/embassy_spi.rs b/esp32s2-hal/examples/embassy_spi.rs new file mode 100644 index 000000000..e4655ef8f --- /dev/null +++ b/esp32s2-hal/examples/embassy_spi.rs @@ -0,0 +1,127 @@ +//! embassy hello world +//! +//! This is an example of running the embassy executor with multiple tasks +//! concurrently. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32s2_hal::{ + clock::ClockControl, + dma::{DmaPriority, *}, + embassy, + pdma::*, + peripherals::Peripherals, + prelude::*, + spi::{dma::SpiDma, Spi, SpiMode}, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use static_cell::StaticCell; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +pub type SpiType<'d> = SpiDma< + 'd, + esp32s2_hal::peripherals::SPI2, + ChannelTx<'d, Spi2DmaChannelTxImpl, Spi2DmaChannel>, + ChannelRx<'d, Spi2DmaChannelRxImpl, Spi2DmaChannel>, + Spi2DmaSuitablePeripheral, +>; + +#[embassy_executor::task] +async fn spi_task(spi: &'static mut SpiType<'static>) { + let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; + loop { + let mut buffer = [0; 8]; + esp_println::println!("Sending bytes"); + embedded_hal_async::spi::SpiBus::transfer(spi, &mut buffer, &send_buffer) + .await + .unwrap(); + esp_println::println!("Bytes recieved: {:?}", buffer); + Timer::after(Duration::from_millis(5_000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[xtensa_lx_rt::entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32s2_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + esp32s2_hal::interrupt::enable( + esp32s2_hal::peripherals::Interrupt::SPI2_DMA, + esp32s2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio36; + let miso = io.pins.gpio37; + let mosi = io.pins.gpio35; + let cs = io.pins.gpio34; + + let dma = Dma::new(system.dma, &mut system.peripheral_clock_control); + let dma_channel = dma.spi2channel; + + let descriptors = singleton!([0u32; 8 * 3]); + let rx_descriptors = singleton!([0u32; 8 * 3]); + + let spi = singleton!(Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + descriptors, + rx_descriptors, + DmaPriority::Priority0, + ))); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(spi_task(spi)).ok(); + }); +} diff --git a/esp32s3-hal/Cargo.toml b/esp32s3-hal/Cargo.toml index 2c908704e..165ce9b57 100644 --- a/esp32s3-hal/Cargo.toml +++ b/esp32s3-hal/Cargo.toml @@ -82,4 +82,8 @@ required-features = ["embassy"] [[example]] name = "embassy_wait" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_spi" required-features = ["embassy", "async"] \ No newline at end of file diff --git a/esp32s3-hal/examples/embassy_spi.rs b/esp32s3-hal/examples/embassy_spi.rs new file mode 100644 index 000000000..5c425ba26 --- /dev/null +++ b/esp32s3-hal/examples/embassy_spi.rs @@ -0,0 +1,133 @@ +//! embassy hello world +//! +//! This is an example of running the embassy executor with multiple tasks +//! concurrently. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32s3_hal::{ + clock::ClockControl, + dma::{DmaPriority, *}, + embassy, + gdma::*, + peripherals::Peripherals, + prelude::*, + spi::{dma::SpiDma, Spi, SpiMode}, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_backtrace as _; +use static_cell::StaticCell; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + let (x,) = STATIC_CELL.init(($val,)); + x + }}; +} + +pub type SpiType<'d> = SpiDma< + 'd, + esp32s3_hal::peripherals::SPI2, + ChannelTx<'d, Channel0TxImpl, esp32s3_hal::gdma::Channel0>, + ChannelRx<'d, Channel0RxImpl, esp32s3_hal::gdma::Channel0>, + SuitablePeripheral0, +>; + +#[embassy_executor::task] +async fn spi_task(spi: &'static mut SpiType<'static>) { + let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; + loop { + let mut buffer = [0; 8]; + esp_println::println!("Sending bytes"); + embedded_hal_async::spi::SpiBus::transfer(spi, &mut buffer, &send_buffer) + .await + .unwrap(); + esp_println::println!("Bytes recieved: {:?}", buffer); + Timer::after(Duration::from_millis(5_000)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[xtensa_lx_rt::entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.RTC_CNTL); + let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32s3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + esp32s3_hal::interrupt::enable( + esp32s3_hal::peripherals::Interrupt::DMA_IN_CH0, + esp32s3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + esp32s3_hal::interrupt::enable( + esp32s3_hal::peripherals::Interrupt::DMA_OUT_CH0, + esp32s3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio6; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio7; + let cs = io.pins.gpio10; + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let descriptors = singleton!([0u32; 8 * 3]); + let rx_descriptors = singleton!([0u32; 8 * 3]); + + let spi = singleton!(Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + descriptors, + rx_descriptors, + DmaPriority::Priority0, + ))); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(spi_task(spi)).ok(); + }); +}