diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1f93e6e928..b3ce58f7eef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -236,8 +236,8 @@ jobs: run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-timg0 # - name: check esp32h2-hal (async, gpio) # run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_wait --features=embassy,embassy-time-systick,async - # - name: check esp32h2-hal (async, spi) - # run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_spi --features=embassy,embassy-time-systick,async + - name: check esp32h2-hal (async, spi) + run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_spi --features=embassy,embassy-time-systick,async - name: check esp32h2-hal (interrupt-preemption) run: cd esp32h2-hal/ && cargo check --example=interrupt_preemption --features=interrupt-preemption @@ -437,6 +437,9 @@ jobs: - name: rustfmt (esp32s3-hal) run: cargo fmt --all --manifest-path=esp32s3-hal/Cargo.toml -- --check + # -------------------------------------------------------------------------- + # Changelog + changelog: name: Changelog runs-on: ubuntu-latest @@ -450,5 +453,3 @@ jobs: changeLogPath: CHANGELOG.md skipLabels: "skip-changelog" missingUpdateErrorMessage: "Please add a changelog entry in the CHANGELOG.md file." - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aed8851bb4..737a94cde1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add a fn to poll DMA transfers - Add initial support for LEDC in ESP32-H2 (#560) - Add initial support for ASSIST_DEBUG in ESP32-H2 (#566) +- Add all `SPI` examples for the ESP32-H2 (#549) ### Changed diff --git a/esp-hal-common/devices/esp32h2.toml b/esp-hal-common/devices/esp32h2.toml index 7a296636948..9ff3b545eb0 100644 --- a/esp-hal-common/devices/esp32h2.toml +++ b/esp-hal-common/devices/esp32h2.toml @@ -7,10 +7,10 @@ peripherals = [ "aes", "apb_saradc", "assist_debug", + "dma", # "ds", # "ecc", "efuse", - # "gdma", "gpio", # "hmac", # "hp_apm", @@ -44,9 +44,9 @@ peripherals = [ "rsa", "sha", # "soc_etm", - # "spi0", - # "spi1", - # "spi2", + "spi0", + "spi1", + "spi2", "systimer", # "tee", "timg0", @@ -62,5 +62,6 @@ peripherals = [ "adc", "assist_debug_sp_monitor", "assist_debug_region_monitor", + "gdma", "plic", ] diff --git a/esp-hal-common/src/clock/mod.rs b/esp-hal-common/src/clock/mod.rs index 57012f5576e..6c325592f8e 100644 --- a/esp-hal-common/src/clock/mod.rs +++ b/esp-hal-common/src/clock/mod.rs @@ -128,6 +128,8 @@ pub struct Clocks<'d> { pub crypto_pwm_clock: HertzU32, #[cfg(esp32c6)] pub crypto_clock: HertzU32, + #[cfg(esp32h2)] + pub pll_48m_clock: HertzU32, // TODO chip specific additional ones as needed } @@ -153,6 +155,8 @@ impl<'d> Clocks<'d> { crypto_pwm_clock: raw_clocks.crypto_pwm_clock, #[cfg(esp32c6)] crypto_clock: raw_clocks.crypto_clock, + #[cfg(esp32h2)] + pll_48m_clock: raw_clocks.pll_48m_clock, } } } @@ -169,6 +173,8 @@ pub struct RawClocks { pub crypto_pwm_clock: HertzU32, #[cfg(esp32c6)] pub crypto_clock: HertzU32, + #[cfg(esp32h2)] + pub pll_48m_clock: HertzU32, // TODO chip specific additional ones as needed } @@ -448,6 +454,7 @@ impl<'d> ClockControl<'d> { apb_clock: HertzU32::MHz(32), xtal_clock: HertzU32::MHz(32), i2c_clock: HertzU32::MHz(32), + pll_48m_clock: HertzU32::MHz(48), }, } } @@ -481,6 +488,7 @@ impl<'d> ClockControl<'d> { apb_clock: apb_freq.frequency(), xtal_clock: xtal_freq.frequency(), i2c_clock: HertzU32::MHz(32), + pll_48m_clock: HertzU32::MHz(48), }, } } diff --git a/esp-hal-common/src/dma/gdma.rs b/esp-hal-common/src/dma/gdma.rs index f5dc2c9d8bb..239d12af1a5 100644 --- a/esp-hal-common/src/dma/gdma.rs +++ b/esp-hal-common/src/dma/gdma.rs @@ -36,7 +36,7 @@ macro_rules! impl_channel { fn clear_out_interrupts() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(any(esp32c6, esp32s3)))] + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] dma.[].write(|w| { w.out_eof() .set_bit() @@ -52,7 +52,7 @@ macro_rules! impl_channel { .set_bit() }); - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2))] dma.[].write(|w| { w.out_eof() .set_bit() @@ -105,9 +105,9 @@ macro_rules! impl_channel { fn has_out_descriptor_error() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(any(esp32c6, esp32s3)))] + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().out_dscr_err().bit(); - #[cfg(any(esp32c6, esp32s3))] + #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().out_dscr_err().bit(); ret @@ -128,9 +128,9 @@ macro_rules! impl_channel { fn is_out_done() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(any(esp32c6, esp32s3)))] + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().out_total_eof().bit(); - #[cfg(any(esp32c6, esp32s3))] + #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().out_total_eof().bit(); ret @@ -144,9 +144,9 @@ macro_rules! impl_channel { fn is_out_eof_interrupt_set() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(any(esp32c6, esp32s3)))] + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().out_eof().bit(); - #[cfg(any(esp32c6, esp32s3))] + #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().out_eof().bit(); ret @@ -155,13 +155,13 @@ macro_rules! impl_channel { fn reset_out_eof_interrupt() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(any(esp32c6, esp32s3)))] + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] dma.[].write(|w| { w.out_eof() .set_bit() }); - #[cfg(any(esp32c6, esp32s3))] + #[cfg(any(esp32c6, esp32h2, esp32s3))] dma.[].write(|w| { w.out_eof() .set_bit() @@ -187,7 +187,7 @@ macro_rules! impl_channel { fn clear_in_interrupts() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(any(esp32c6, esp32s3)))] + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] dma.[].write(|w| { w.in_suc_eof() .set_bit() @@ -205,7 +205,7 @@ macro_rules! impl_channel { .set_bit() }); - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2, esp32h2))] dma.[].write(|w| { w.in_suc_eof() .set_bit() @@ -262,9 +262,9 @@ macro_rules! impl_channel { fn has_in_descriptor_error() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(any(esp32c6, esp32s3)))] + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().in_dscr_err().bit(); - #[cfg(any(esp32c6, esp32s3))] + #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().in_dscr_err().bit(); ret @@ -285,9 +285,9 @@ macro_rules! impl_channel { fn is_in_done() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; - #[cfg(not(any(esp32c6, esp32s3)))] + #[cfg(not(any(esp32c6, esp32h2, esp32s3)))] let ret = dma.[].read().in_suc_eof().bit(); - #[cfg(any(esp32c6, esp32s3))] + #[cfg(any(esp32c6, esp32h2, esp32s3))] let ret = dma.[].read().in_suc_eof().bit(); ret @@ -301,7 +301,7 @@ macro_rules! impl_channel { fn is_listening_in_eof() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32s3))] { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].read().in_suc_eof().bit_is_set() } else { dma.[].read().in_suc_eof().bit_is_set() @@ -312,7 +312,7 @@ macro_rules! impl_channel { fn is_listening_out_eof() -> bool { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32s3))] { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].read().out_total_eof().bit_is_set() } else { dma.[].read().out_total_eof().bit_is_set() @@ -323,7 +323,7 @@ macro_rules! impl_channel { fn listen_in_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32s3))] { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].modify(|_, w| w.in_suc_eof().set_bit()) } else { dma.[].modify(|_, w| w.in_suc_eof().set_bit()) @@ -334,7 +334,7 @@ macro_rules! impl_channel { fn listen_out_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32s3))] { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].modify(|_, w| w.out_total_eof().set_bit()) } else { dma.[].modify(|_, w| w.out_total_eof().set_bit()) @@ -345,7 +345,7 @@ macro_rules! impl_channel { fn unlisten_in_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32s3))] { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].modify(|_, w| w.in_suc_eof().clear_bit()) } else { dma.[].modify(|_, w| w.in_suc_eof().clear_bit()) @@ -356,7 +356,7 @@ macro_rules! impl_channel { fn unlisten_out_eof() { let dma = unsafe { &*crate::peripherals::DMA::PTR }; cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32s3))] { + if #[cfg(any(esp32c6, esp32h2, esp32s3))] { dma.[].modify(|_, w| w.out_total_eof().clear_bit()) } else { dma.[].modify(|_, w| w.out_total_eof().clear_bit()) diff --git a/esp-hal-common/src/dma/mod.rs b/esp-hal-common/src/dma/mod.rs index f4e1fbde41c..6eaf84074f8 100644 --- a/esp-hal-common/src/dma/mod.rs +++ b/esp-hal-common/src/dma/mod.rs @@ -1028,7 +1028,7 @@ pub(crate) mod asynch { } } - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2))] mod interrupt { use super::*; diff --git a/esp-hal-common/src/soc/esp32h2/gpio.rs b/esp-hal-common/src/soc/esp32h2/gpio.rs index f4b8c01f0a3..34a78e14726 100644 --- a/esp-hal-common/src/soc/esp32h2/gpio.rs +++ b/esp-hal-common/src/soc/esp32h2/gpio.rs @@ -159,7 +159,7 @@ pub enum OutputSignal { PARL_TX_DATA7 = 54, I2CEXT1_SCL = 55, I2CEXT1_SDA = 56, - FSPICLK_OUT_MUX = 63, + FSPICLK_MUX = 63, FSPIQ = 64, FSPID = 65, FSPIHD = 66, @@ -201,7 +201,7 @@ pub enum OutputSignal { CTE_ANT13 = 109, CTE_ANT14 = 110, CTE_ANT15 = 111, - SPICLK_OUT_MUX = 114, + SPICLK = 114, SPICS0 = 115, SPICS1 = 116, SPIQ = 121, @@ -216,12 +216,12 @@ pub enum OutputSignal { // FIXME: add alternate function numbers/signals where necessary crate::gpio::gpio! { - (0, 0, InputOutput) - (1, 0, InputOutputAnalog) - (2, 0, InputOutputAnalog) - (3, 0, InputOutputAnalog) - (4, 0, InputOutputAnalog) - (5, 0, InputOutputAnalog) + (0, 0, InputOutputAnalog (2 => FSPIQ) (2 => FSPIQ)) + (1, 0, InputOutputAnalog (2 => FSPICS0) (2 => FSPICS0)) + (2, 0, InputOutputAnalog (2 => FSPIWP) (2 => FSPIWP)) + (3, 0, InputOutputAnalog (2 => FSPIHD) (2 => FSPIHD)) + (4, 0, InputOutputAnalog (2 => FSPICLK) (2 => FSPICLK_MUX)) + (5, 0, InputOutputAnalog (2 => FSPID) (2 => FSPID)) (6, 0, InputOutput) (7, 0, InputOutput) (8, 0, InputOutput) @@ -231,19 +231,19 @@ crate::gpio::gpio! { (12, 0, InputOutput) (13, 0, InputOutput) (14, 0, InputOutput) - (15, 0, InputOutput) - (16, 0, InputOutput) - (17, 0, InputOutput) - (18, 0, InputOutput) - (19, 0, InputOutput) - (20, 0, InputOutput) + (15, 0, InputOutput () (0 => SPICS0)) + (16, 0, InputOutput (0 => SPIQ) (0 => SPIQ)) + (17, 0, InputOutput (0 => SPIWP) (0 => SPIWP)) + (18, 0, InputOutput (0 => SPIHD) (0 => SPIHD)) + (19, 0, InputOutput () (0 => SPICLK)) + (20, 0, InputOutput (0 => SPID) (0 => SPID)) (21, 0, InputOutput) (22, 0, InputOutput) - (23, 0, InputOutput) - (24, 0, InputOutput) - (25, 0, InputOutput) - (26, 0, InputOutput) - (27, 0, InputOutput) + (23, 0, InputOutput () (2 => FSPICS1)) + (24, 0, InputOutput () (2 => FSPICS2)) + (25, 0, InputOutput () (2 => FSPICS3)) + (26, 0, InputOutput () (2 => FSPICS4)) + (27, 0, InputOutput () (2 => FSPICS5)) } crate::gpio::analog! { diff --git a/esp-hal-common/src/soc/esp32h2/peripherals.rs b/esp-hal-common/src/soc/esp32h2/peripherals.rs index 7b2cbdbf428..8bee1da35c2 100644 --- a/esp-hal-common/src/soc/esp32h2/peripherals.rs +++ b/esp-hal-common/src/soc/esp32h2/peripherals.rs @@ -9,10 +9,10 @@ crate::peripherals! { AES => true, APB_SARADC => true, ASSIST_DEBUG => true, + DMA => true, // DS => true, // ECC => true, EFUSE => true, - // GDMA => true, GPIO => true, // HMAC => true, // HP_APM => true, @@ -46,9 +46,9 @@ crate::peripherals! { RSA => true, SHA => true, // SOC_ETM => true, - // SPI0 => true, - // SPI1 => true, - // SPI2 => true, + SPI0 => true, + SPI1 => true, + SPI2 => true, SYSTIMER => true, // TEE => true, TIMG0 => true, diff --git a/esp-hal-common/src/spi.rs b/esp-hal-common/src/spi.rs index 4c2727e422a..ad3b9f1a51e 100644 --- a/esp-hal-common/src/spi.rs +++ b/esp-hal-common/src/spi.rs @@ -1977,7 +1977,7 @@ where } } - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] fn enable_dma(&self) { let reg_block = self.register_block(); reg_block.dma_conf.modify(|_, w| w.dma_tx_ena().set_bit()); @@ -1989,7 +1989,7 @@ where // for non GDMA this is done in `assign_tx_device` / `assign_rx_device` } - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] fn clear_dma_interrupts(&self) { let reg_block = self.register_block(); reg_block.dma_int_clr.write(|w| { @@ -2153,12 +2153,13 @@ pub trait Instance { .set_bit() }); - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2))] unsafe { - let pcr = &*esp32c6::PCR::PTR; - - // use default clock source PLL_F80M_CLK - pcr.spi2_clkm_conf.modify(|_, w| w.spi2_clkm_sel().bits(1)); + // use default clock source PLL_F80M_CLK (ESP32-C6) and + // PLL_F48M_CLK (ESP32-H2) + (&*crate::peripherals::PCR::PTR) + .spi2_clkm_conf + .modify(|_, w| w.spi2_clkm_sel().bits(1)); } #[cfg(not(any(esp32, esp32s2)))] @@ -2392,7 +2393,11 @@ pub trait Instance { // taken from https://github.com/apache/incubator-nuttx/blob/8267a7618629838231256edfa666e44b5313348e/arch/risc-v/src/esp32c3/esp32c3_spi.c#L496 fn setup(&mut self, frequency: HertzU32, clocks: &Clocks) { // FIXME: this might not be always true + #[cfg(not(esp32h2))] let apb_clk_freq: HertzU32 = HertzU32::Hz(clocks.apb_clock.to_Hz()); + // ESP32-H2 is using PLL_48M_CLK source instead of APB_CLK + #[cfg(esp32h2)] + let apb_clk_freq: HertzU32 = HertzU32::Hz(clocks.pll_48m_clock.to_Hz()); let reg_val: u32; let duty_cycle = 128; @@ -2761,9 +2766,9 @@ pub trait Instance { .set_bit() }); - #[cfg(esp32c6)] + #[cfg(any(esp32c6, esp32h2))] unsafe { - let pcr = &*esp32c6::PCR::PTR; + let pcr = &*crate::peripherals::PCR::PTR; // use default clock source PLL_F80M_CLK pcr.spi2_clkm_conf.modify(|_, w| w.spi2_clkm_sel().bits(1)); @@ -2922,12 +2927,12 @@ pub trait Instance { let reg_block = self.register_block(); let len = if len > 0 { len - 1 } else { 0 }; - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] reg_block .ms_dlen .write(|w| unsafe { w.ms_data_bitlen().bits(len) }); - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32s3)))] + #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3)))] { reg_block .mosi_dlen @@ -2940,7 +2945,7 @@ pub trait Instance { } } -#[cfg(any(esp32c2, esp32c3, esp32c6))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))] impl Instance for crate::peripherals::SPI2 { #[inline(always)] fn register_block(&self) -> &RegisterBlock { @@ -2978,7 +2983,7 @@ impl Instance for crate::peripherals::SPI2 { } } -#[cfg(any(esp32c2, esp32c3, esp32c6))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))] impl ExtendedInstance for crate::peripherals::SPI2 { #[inline(always)] fn sio0_input_signal(&self) -> InputSignal { diff --git a/esp32h2-hal/Cargo.toml b/esp32h2-hal/Cargo.toml index 4c27e78693f..f53fd77408e 100644 --- a/esp32h2-hal/Cargo.toml +++ b/esp32h2-hal/Cargo.toml @@ -62,6 +62,14 @@ embassy-time-systick = ["esp-hal-common/embassy-time-systick", "embassy-time/tic embassy-time-timg0 = ["esp-hal-common/embassy-time-timg0", "embassy-time/tick-hz-1_000_000"] interrupt-preemption = ["esp-hal-common/interrupt-preemption"] +[[example]] +name = "spi_eh1_loopback" +required-features = ["eh1"] + +[[example]] +name = "spi_eh1_device_loopback" +required-features = ["eh1"] + [[example]] name = "embassy_hello_world" required-features = ["embassy"] @@ -70,6 +78,10 @@ required-features = ["embassy"] name = "embassy_i2c" required-features = ["embassy", "async", "embassy-time-systick"] +[[example]] +name = "embassy_spi" +required-features = ["embassy", "async"] + [[example]] name = "interrupt_preemption" required-features = ["interrupt-preemption"] diff --git a/esp32h2-hal/examples/embassy_spi.rs b/esp32h2-hal/examples/embassy_spi.rs new file mode 100644 index 00000000000..b6a8dc46dba --- /dev/null +++ b/esp32h2-hal/examples/embassy_spi.rs @@ -0,0 +1,155 @@ +//! Embassy SPI +//! +//! Folowing pins are used: +//! SCLK GPIO1 +//! MISO GPIO2 +//! MOSI GPIO3 +//! CS GPIO11 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. +//! +//! This is an example of running the embassy executor with SPI. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32h2_hal::{ + clock::ClockControl, + dma::{DmaPriority, *}, + embassy, + gdma::*, + peripherals::Peripherals, + prelude::*, + spi::{dma::SpiDma, FullDuplexMode, 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, + esp32h2_hal::peripherals::SPI2, + ChannelTx<'d, Channel0TxImpl, esp32h2_hal::gdma::Channel0>, + ChannelRx<'d, Channel0RxImpl, esp32h2_hal::gdma::Channel0>, + SuitablePeripheral0, + FullDuplexMode, +>; + +#[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(); + +#[entry] +fn main() -> ! { + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + 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, + esp32h2_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + esp32h2_hal::interrupt::enable( + esp32h2_hal::peripherals::Interrupt::DMA_IN_CH0, + esp32h2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + esp32h2_hal::interrupt::enable( + esp32h2_hal::peripherals::Interrupt::DMA_OUT_CH0, + esp32h2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio1; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio3; + let cs = io.pins.gpio11; + + 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/esp32h2-hal/examples/qspi_flash.rs b/esp32h2-hal/examples/qspi_flash.rs new file mode 100644 index 00000000000..59961e54381 --- /dev/null +++ b/esp32h2-hal/examples/qspi_flash.rs @@ -0,0 +1,201 @@ +//! SPI write and read a flash chip +//! +//! Folowing pins are used: +//! SCLK GPIO1 +//! MISOI/IO0 GPIO2 +//! MOSI/IO1 GPIO3 +//! IO2 GPIO4 +//! IO3 GPIO5 +//! CS GPIO11 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! Connect a flash chip (GD25Q64C was used) and make sure QE in the status +//! register is set. + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Address, Command, Spi, SpiDataMode, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::{print, println}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio1; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio3; + let sio2 = io.pins.gpio4; + let sio3 = io.pins.gpio5; + let cs = io.pins.gpio11; + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let mut spi = Spi::new_half_duplex( + peripherals.SPI2, + Some(sclk), + Some(mosi), + Some(miso), + Some(sio2), + Some(sio3), + Some(cs), + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + &mut descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + )); + + let mut delay = Delay::new(&clocks); + + // DMA buffer require a static life-time + let send = send_buffer(); + let mut receive = receive_buffer(); + let mut zero_buf = zero_buffer(); + + // write enable + let transfer = spi + .write( + SpiDataMode::Single, + Command::Command8(0x06, SpiDataMode::Single), + Address::None, + 0, + zero_buf, + ) + .unwrap(); + (zero_buf, spi) = transfer.wait(); + delay.delay_ms(250u32); + + // erase sector + let transfer = spi + .write( + SpiDataMode::Single, + Command::Command8(0x20, SpiDataMode::Single), + Address::Address24(0x000000, SpiDataMode::Single), + 0, + zero_buf, + ) + .unwrap(); + (zero_buf, spi) = transfer.wait(); + delay.delay_ms(250u32); + + // write enable + let transfer = spi + .write( + SpiDataMode::Single, + Command::Command8(0x06, SpiDataMode::Single), + Address::None, + 0, + zero_buf, + ) + .unwrap(); + (_, spi) = transfer.wait(); + delay.delay_ms(250u32); + + // write data / program page + send.fill(b'!'); + send[0..][..5].copy_from_slice(&b"Hello"[..]); + let transfer = spi + .write( + SpiDataMode::Quad, + Command::Command8(0x32, SpiDataMode::Single), + Address::Address24(0x000000, SpiDataMode::Single), + 0, + send, + ) + .unwrap(); + (_, spi) = transfer.wait(); + delay.delay_ms(250u32); + + loop { + // quad fast read + let transfer = spi + .read( + SpiDataMode::Quad, + Command::Command8(0xeb, SpiDataMode::Single), + Address::Address32(0x000000 << 8, SpiDataMode::Quad), + 4, + receive, + ) + .unwrap(); + + // here we could do something else while DMA transfer is in progress + // the buffers and spi is moved into the transfer and we can get it back via + // `wait` + (receive, spi) = transfer.wait(); + + println!("{:x?}", &receive); + for b in &mut receive.iter() { + if *b >= 32 && *b <= 127 { + print!("{}", *b as char); + } else { + print!("."); + } + } + println!(); + + delay.delay_ms(250u32); + } +} + +fn zero_buffer() -> &'static mut [u8; 0] { + static mut BUFFER: [u8; 0] = [0u8; 0]; + unsafe { &mut BUFFER } +} + +fn send_buffer() -> &'static mut [u8; 256] { + static mut BUFFER: [u8; 256] = [0u8; 256]; + unsafe { &mut BUFFER } +} + +fn receive_buffer() -> &'static mut [u8; 320] { + static mut BUFFER: [u8; 320] = [0u8; 320]; + unsafe { &mut BUFFER } +} diff --git a/esp32h2-hal/examples/spi_eh1_device_loopback.rs b/esp32h2-hal/examples/spi_eh1_device_loopback.rs new file mode 100644 index 00000000000..d6702764020 --- /dev/null +++ b/esp32h2-hal/examples/spi_eh1_device_loopback.rs @@ -0,0 +1,161 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO1 +//! MISO GPIO2 +//! MOSI GPIO3 +//! CS 1 GPIO11 +//! CS 2 GPIO12 +//! CS 3 GPIO25 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use embedded_hal_1::spi::SpiDevice; +use esp32h2_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Spi, SpiBusController, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::{print, println}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio1; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio3; + + let spi_controller = SpiBusController::from_spi(Spi::new_no_cs( + peripherals.SPI2, + sclk, + mosi, + miso, + 1000u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + )); + let mut spi_device_1 = spi_controller.add_device(io.pins.gpio11); + let mut spi_device_2 = spi_controller.add_device(io.pins.gpio12); + let mut spi_device_3 = spi_controller.add_device(io.pins.gpio25); + + let mut delay = Delay::new(&clocks); + println!("=== SPI example with embedded-hal-1 traits ==="); + + loop { + // --- Symmetric transfer (Read as much as we write) --- + print!("Starting symmetric transfer..."); + let write = [0xde, 0xad, 0xbe, 0xef]; + let mut read: [u8; 4] = [0x00u8; 4]; + + spi_device_1.transfer(&mut read[..], &write[..]).unwrap(); + assert_eq!(write, read); + spi_device_2.transfer(&mut read[..], &write[..]).unwrap(); + spi_device_3.transfer(&mut read[..], &write[..]).unwrap(); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Asymmetric transfer (Read more than we write) --- + print!("Starting asymetric transfer (read > write)..."); + let mut read: [u8; 4] = [0x00; 4]; + + spi_device_1 + .transfer(&mut read[0..2], &write[..]) + .expect("Asymmetric transfer failed"); + assert_eq!(write[0], read[0]); + assert_eq!(read[2], 0x00u8); + spi_device_2 + .transfer(&mut read[0..2], &write[..]) + .expect("Asymmetric transfer failed"); + spi_device_3 + .transfer(&mut read[0..2], &write[..]) + .expect("Asymmetric transfer failed"); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Symmetric transfer with huge buffer --- + // Only your RAM is the limit! + print!("Starting huge transfer..."); + let mut write = [0x55u8; 4096]; + for byte in 0..write.len() { + write[byte] = byte as u8; + } + let mut read = [0x00u8; 4096]; + + spi_device_1 + .transfer(&mut read[..], &write[..]) + .expect("Huge transfer failed"); + assert_eq!(write, read); + spi_device_2 + .transfer(&mut read[..], &write[..]) + .expect("Huge transfer failed"); + spi_device_3 + .transfer(&mut read[..], &write[..]) + .expect("Huge transfer failed"); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Symmetric transfer with huge buffer in-place (No additional allocation + // needed) --- + print!("Starting huge transfer (in-place)..."); + let mut write = [0x55u8; 4096]; + for byte in 0..write.len() { + write[byte] = byte as u8; + } + + spi_device_1 + .transfer_in_place(&mut write[..]) + .expect("Huge transfer failed"); + for byte in 0..write.len() { + assert_eq!(write[byte], byte as u8); + } + spi_device_2 + .transfer_in_place(&mut write[..]) + .expect("Huge transfer failed"); + spi_device_3 + .transfer_in_place(&mut write[..]) + .expect("Huge transfer failed"); + println!(" SUCCESS"); + delay.delay_ms(250u32); + } +} diff --git a/esp32h2-hal/examples/spi_eh1_loopback.rs b/esp32h2-hal/examples/spi_eh1_loopback.rs new file mode 100644 index 00000000000..6c572d85d38 --- /dev/null +++ b/esp32h2-hal/examples/spi_eh1_loopback.rs @@ -0,0 +1,133 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO1 +//! MISO GPIO2 +//! MOSI GPIO3 +//! CS GPIO11 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use embedded_hal_1::spi::SpiBus; +use esp32h2_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Spi, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::{print, println}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio1; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio3; + let cs = io.pins.gpio11; + + let mut spi = Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 1000u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ); + + let mut delay = Delay::new(&clocks); + println!("=== SPI example with embedded-hal-1 traits ==="); + + loop { + // --- Symmetric transfer (Read as much as we write) --- + print!("Starting symmetric transfer..."); + let write = [0xde, 0xad, 0xbe, 0xef]; + let mut read: [u8; 4] = [0x00u8; 4]; + + SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Symmetric transfer failed"); + assert_eq!(write, read); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Asymmetric transfer (Read more than we write) --- + print!("Starting asymetric transfer (read > write)..."); + let mut read: [u8; 4] = [0x00; 4]; + + SpiBus::transfer(&mut spi, &mut read[0..2], &write[..]) + .expect("Asymmetric transfer failed"); + assert_eq!(write[0], read[0]); + assert_eq!(read[2], 0x00u8); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Symmetric transfer with huge buffer --- + // Only your RAM is the limit! + print!("Starting huge transfer..."); + let mut write = [0x55u8; 4096]; + for byte in 0..write.len() { + write[byte] = byte as u8; + } + let mut read = [0x00u8; 4096]; + + SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Huge transfer failed"); + assert_eq!(write, read); + println!(" SUCCESS"); + delay.delay_ms(250u32); + + // --- Symmetric transfer with huge buffer in-place (No additional allocation + // needed) --- + print!("Starting huge transfer (in-place)..."); + let mut write = [0x55u8; 4096]; + for byte in 0..write.len() { + write[byte] = byte as u8; + } + + SpiBus::transfer_in_place(&mut spi, &mut write[..]).expect("Huge transfer failed"); + for byte in 0..write.len() { + assert_eq!(write[byte], byte as u8); + } + println!(" SUCCESS"); + delay.delay_ms(250u32); + } +} diff --git a/esp32h2-hal/examples/spi_halfduplex_read_manufacturer_id.rs b/esp32h2-hal/examples/spi_halfduplex_read_manufacturer_id.rs new file mode 100644 index 00000000000..8264ada4f42 --- /dev/null +++ b/esp32h2-hal/examples/spi_halfduplex_read_manufacturer_id.rs @@ -0,0 +1,124 @@ +//! SPI read manufacturer id from flash chip +//! +//! Folowing pins are used: +//! SCLK GPIO1 +//! MISOI/IO0 GPIO2 +//! MOSI/IO1 GPIO3 +//! IO2 GPIO4 +//! IO3 GPIO5 +//! CS GPIO11 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! Connect a flash chip (GD25Q64C was used) and make sure QE in the status +//! register is set. + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Address, Command, HalfDuplexReadWrite, Spi, SpiDataMode, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio1; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio3; + let sio2 = io.pins.gpio4; + let sio3 = io.pins.gpio5; + let cs = io.pins.gpio11; + + let mut spi = Spi::new_half_duplex( + peripherals.SPI2, + Some(sclk), + Some(mosi), + Some(miso), + Some(sio2), + Some(sio3), + Some(cs), + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ); + + let mut delay = Delay::new(&clocks); + + loop { + // READ MANUFACTURER ID FROM FLASH CHIP + let mut data = [0u8; 2]; + spi.read( + SpiDataMode::Single, + Command::Command8(0x90, SpiDataMode::Single), + Address::Address24(0x000000, SpiDataMode::Single), + 0, + &mut data, + ) + .unwrap(); + println!("Single {:x?}", data); + delay.delay_ms(250u32); + + // READ MANUFACTURER ID FROM FLASH CHIP + let mut data = [0u8; 2]; + spi.read( + SpiDataMode::Dual, + Command::Command8(0x92, SpiDataMode::Single), + Address::Address32(0x000000_00, SpiDataMode::Dual), + 0, + &mut data, + ) + .unwrap(); + println!("Dual {:x?}", data); + delay.delay_ms(250u32); + + // READ MANUFACTURER ID FROM FLASH CHIP + let mut data = [0u8; 2]; + spi.read( + SpiDataMode::Quad, + Command::Command8(0x94, SpiDataMode::Single), + Address::Address32(0x000000_00, SpiDataMode::Quad), + 4, + &mut data, + ) + .unwrap(); + println!("Quad {:x?}", data); + delay.delay_ms(1500u32); + } +} diff --git a/esp32h2-hal/examples/spi_loopback.rs b/esp32h2-hal/examples/spi_loopback.rs new file mode 100644 index 00000000000..29cc6ccc390 --- /dev/null +++ b/esp32h2-hal/examples/spi_loopback.rs @@ -0,0 +1,87 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO1 +//! MISO GPIO2 +//! MOSI GPIO3 +//! CS GPIO11 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Spi, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio1; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio3; + let cs = io.pins.gpio11; + + let mut spi = Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ); + + let mut delay = Delay::new(&clocks); + + loop { + let mut data = [0xde, 0xca, 0xfb, 0xad]; + spi.transfer(&mut data).unwrap(); + println!("{:x?}", data); + + delay.delay_ms(250u32); + } +} diff --git a/esp32h2-hal/examples/spi_loopback_dma.rs b/esp32h2-hal/examples/spi_loopback_dma.rs new file mode 100644 index 00000000000..4b19bf688aa --- /dev/null +++ b/esp32h2-hal/examples/spi_loopback_dma.rs @@ -0,0 +1,131 @@ +//! SPI loopback test using DMA +//! +//! Folowing pins are used: +//! SCLK GPIO1 +//! MISO GPIO2 +//! MOSI GPIO3 +//! CS GPIO11 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This example transfers data via SPI. +//! Connect MISO and MOSI pins to see the outgoing data is read as incoming +//! data. + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + dma::DmaPriority, + gdma::Gdma, + gpio::IO, + peripherals::Peripherals, + prelude::*, + spi::{Spi, SpiMode}, + timer::TimerGroup, + Delay, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio1; + let miso = io.pins.gpio2; + let mosi = io.pins.gpio3; + let cs = io.pins.gpio11; + + let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control); + let dma_channel = dma.channel0; + + let mut descriptors = [0u32; 8 * 3]; + let mut rx_descriptors = [0u32; 8 * 3]; + + let mut spi = Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100u32.kHz(), + SpiMode::Mode0, + &mut system.peripheral_clock_control, + &clocks, + ) + .with_dma(dma_channel.configure( + false, + &mut descriptors, + &mut rx_descriptors, + DmaPriority::Priority0, + )); + + let mut delay = Delay::new(&clocks); + + // DMA buffer require a static life-time + let mut send = buffer1(); + let mut receive = buffer2(); + let mut i = 0; + + for (i, v) in send.iter_mut().enumerate() { + *v = (i % 255) as u8; + } + + loop { + send[0] = i; + send[send.len() - 1] = i; + i = i.wrapping_add(1); + + let transfer = spi.dma_transfer(send, receive).unwrap(); + // here we could do something else while DMA transfer is in progress + // the buffers and spi is moved into the transfer and we can get it back via + // `wait` + (receive, send, spi) = transfer.wait(); + println!( + "{:x?} .. {:x?}", + &receive[..10], + &receive[receive.len() - 10..] + ); + + delay.delay_ms(250u32); + } +} + +fn buffer1() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} + +fn buffer2() -> &'static mut [u8; 32000] { + static mut BUFFER: [u8; 32000] = [0u8; 32000]; + unsafe { &mut BUFFER } +} diff --git a/esp32h2-hal/ld/bl-riscv-link.x b/esp32h2-hal/ld/bl-riscv-link.x index dadd7e7a579..58e8ee7763a 100644 --- a/esp32h2-hal/ld/bl-riscv-link.x +++ b/esp32h2-hal/ld/bl-riscv-link.x @@ -19,7 +19,7 @@ PROVIDE(MachineExternal = DefaultHandler); PROVIDE(DefaultHandler = DefaultInterruptHandler); PROVIDE(ExceptionHandler = DefaultExceptionHandler); -/* The ESP32-C2 and ESP32-C3 have interrupt IDs 1-31, while the ESP32-C6 and ESP32-H2 has +/* The ESP32-C2 and ESP32-C3 have interrupt IDs 1-31, while the ESP32-C6 and ESP32-H2 have IDs 0-31, so we much define the handler for the one additional interrupt ID: */ PROVIDE(interrupt0 = DefaultHandler); @@ -87,4 +87,4 @@ INCLUDE "rwtext.x" INCLUDE "rtc_fast.x" /* End of Shared sections */ -INCLUDE "debug.x" \ No newline at end of file +INCLUDE "debug.x" diff --git a/esp32h2-hal/ld/db-riscv-link.x b/esp32h2-hal/ld/db-riscv-link.x index 3b1a667e000..c06f798d03b 100644 --- a/esp32h2-hal/ld/db-riscv-link.x +++ b/esp32h2-hal/ld/db-riscv-link.x @@ -19,7 +19,7 @@ PROVIDE(MachineExternal = DefaultHandler); PROVIDE(DefaultHandler = DefaultInterruptHandler); PROVIDE(ExceptionHandler = DefaultExceptionHandler); -/* The ESP32-C2 and ESP32-C3 have interrupt IDs 1-31, while the ESP32-C6 has +/* The ESP32-C2 and ESP32-C3 have interrupt IDs 1-31, while the ESP32-C6 and ESP32-H2 have IDs 0-31, so we much define the handler for the one additional interrupt ID: */ PROVIDE(interrupt0 = DefaultHandler);