From 3f7a675b1edd4de3cb2e2e8d17d54eb50d3975c9 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Mon, 7 Mar 2022 17:39:25 +0100 Subject: [PATCH] Implement SPI --- esp-hal-common/src/lib.rs | 2 + esp-hal-common/src/spi.rs | 605 +++++++++++++++++++++++++++ esp32-hal/examples/spi_loopback.rs | 64 +++ esp32-hal/src/lib.rs | 2 +- esp32c3-hal/examples/spi_loopback.rs | 72 ++++ esp32c3-hal/src/lib.rs | 2 +- esp32s2-hal/examples/spi_loopback.rs | 64 +++ esp32s2-hal/src/lib.rs | 1 + esp32s3-hal/examples/spi_loopback.rs | 64 +++ esp32s3-hal/src/lib.rs | 2 +- 10 files changed, 875 insertions(+), 3 deletions(-) create mode 100644 esp-hal-common/src/spi.rs create mode 100644 esp32-hal/examples/spi_loopback.rs create mode 100644 esp32c3-hal/examples/spi_loopback.rs create mode 100644 esp32s2-hal/examples/spi_loopback.rs create mode 100644 esp32s3-hal/examples/spi_loopback.rs diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 67427580a..e746e8621 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -39,6 +39,7 @@ pub mod prelude; #[cfg(not(feature = "esp32c3"))] pub mod rtc_cntl; pub mod serial; +pub mod spi; pub mod timer; pub use delay::Delay; @@ -49,6 +50,7 @@ pub use procmacros::ram; #[cfg(not(feature = "esp32c3"))] pub use rtc_cntl::RtcCntl; pub use serial::Serial; +pub use spi::Spi; pub use timer::Timer; /// Enumeration of CPU cores diff --git a/esp-hal-common/src/spi.rs b/esp-hal-common/src/spi.rs new file mode 100644 index 000000000..2db8cd10b --- /dev/null +++ b/esp-hal-common/src/spi.rs @@ -0,0 +1,605 @@ +//! # Serial Peripheral Interface +//! To construct the SPI instances, use the `Spi::new` functions. +//! +//! ## Initialisation example +//! ```rust +//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); +//! let sclk = io.pins.gpio12; +//! let miso = io.pins.gpio11; +//! let mosi = io.pins.gpio13; +//! let cs = io.pins.gpio10; +//! +//! let mut spi = hal::spi::Spi::new( +//! peripherals.SPI2, +//! sclk, +//! mosi, +//! miso, +//! cs, +//! 100_000, +//! embedded_hal::spi::MODE_0, +//! &mut peripherals.SYSTEM, +//! ); +//! ``` + +use core::convert::Infallible; + +use embedded_hal::spi::{FullDuplex, Mode}; + +use crate::{ + pac::spi2::RegisterBlock, + types::{InputSignal, OutputSignal}, + InputPin, + OutputPin, +}; + +#[cfg(feature = "esp32")] +type System = crate::pac::DPORT; +#[cfg(not(feature = "esp32"))] +type System = crate::pac::SYSTEM; + +pub struct Spi { + spi: T, +} + +impl Spi +where + T: Instance, +{ + /// Constructs an SPI instance in 8bit dataframe mode. + pub fn new< + SCK: OutputPin, + MOSI: OutputPin, + MISO: InputPin, + CS: OutputPin, + >( + spi: T, + mut sck: SCK, + mut mosi: MOSI, + mut miso: MISO, + mut cs: CS, + frequency: u32, + mode: Mode, + system: &mut System, + ) -> Self { + sck.set_to_push_pull_output() + .connect_peripheral_to_output(spi.sclk_signal()); + + mosi.set_to_push_pull_output() + .connect_peripheral_to_output(spi.mosi_signal()); + + miso.set_to_input() + .connect_input_to_peripheral(spi.miso_signal()); + + cs.set_to_push_pull_output() + .connect_peripheral_to_output(spi.cs_signal()); + + spi.enable_peripheral(system); + + let mut spi = Self { spi: spi }; + + spi.spi.setup(frequency); + + spi.spi.init(); + spi.spi.set_data_mode(mode); + + spi + } + + /// Return the raw interface to the underlying peripheral instance + pub fn free(self) -> T { + self.spi + } +} + +impl FullDuplex for Spi +where + T: Instance, +{ + type Error = Infallible; + + fn read(&mut self) -> nb::Result { + self.spi.read() + } + + fn send(&mut self, word: u8) -> nb::Result<(), Self::Error> { + self.spi.send(word) + } +} + +impl embedded_hal::blocking::spi::Transfer for Spi +where + T: Instance, +{ + type Error = Infallible; + + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + self.spi.transfer(words) + } +} + +impl embedded_hal::blocking::spi::Write for Spi +where + T: Instance, +{ + type Error = Infallible; + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + let mut words_mut = [0u8; 256]; + words_mut[0..words.len()].clone_from_slice(&words[0..words.len()]); + self.spi.transfer(&mut words_mut[0..words.len()])?; + Ok(()) + } +} + +pub trait Instance { + fn register_block(&self) -> &RegisterBlock; + + fn sclk_signal(&self) -> OutputSignal; + + fn mosi_signal(&self) -> OutputSignal; + + fn miso_signal(&self) -> InputSignal; + + fn cs_signal(&self) -> OutputSignal; + + fn enable_peripheral(&self, system: &mut System); + + fn init(&mut self) { + let reg_block = self.register_block(); + reg_block.user.modify(|_, w| { + w.usr_miso_highpart() + .clear_bit() + .usr_miso_highpart() + .clear_bit() + .doutdin() + .set_bit() + .usr_miso() + .set_bit() + .usr_mosi() + .set_bit() + .cs_hold() + .set_bit() + .usr_dummy_idle() + .set_bit() + .usr_addr() + .clear_bit() + .usr_command() + .clear_bit() + }); + + #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] + reg_block.clk_gate.modify(|_, w| { + w.clk_en() + .set_bit() + .mst_clk_active() + .set_bit() + .mst_clk_sel() + .set_bit() + }); + + reg_block.ctrl.write(|w| unsafe { w.bits(0) }); + + #[cfg(not(feature = "esp32"))] + reg_block.misc.write(|w| unsafe { w.bits(0) }); + + reg_block.slave.write(|w| unsafe { w.bits(0) }); + } + + // taken from https://github.com/apache/incubator-nuttx/blob/8267a7618629838231256edfa666e44b5313348e/arch/risc-v/src/esp32c3/esp32c3_spi.c#L496 + fn setup(&mut self, frequency: u32) { + const APB_CLK_FREQ: u32 = 80_000_000; // TODO this might not be always true + let reg_val: u32; + let duty_cycle = 128; + + // In HW, n, h and l fields range from 1 to 64, pre ranges from 1 to 8K. + // The value written to register is one lower than the used value. + + if frequency > ((APB_CLK_FREQ / 4) * 3) { + // Using APB frequency directly will give us the best result here. + reg_val = 1 << 31; + } else { + /* For best duty cycle resolution, we want n to be as close to 32 as + * possible, but we also need a pre/n combo that gets us as close as + * possible to the intended frequency. To do this, we bruteforce n and + * calculate the best pre to go along with that. If there's a choice + * between pre/n combos that give the same result, use the one with the + * higher n. + */ + + let mut pre: i32; + let n: i32; + let mut h: i32; + let l: i32; + let mut bestn: i32 = -1; + let mut bestpre: i32 = -1; + let mut besterr: i32 = 0; + let mut errval: i32; + + /* Start at n = 2. We need to be able to set h/l so we have at least + * one high and one low pulse. + */ + + for n in 2..64 { + /* Effectively, this does: + * pre = round((APB_CLK_FREQ / n) / frequency) + */ + + pre = ((APB_CLK_FREQ as i32/ n) + (frequency as i32 / 2)) / frequency as i32; + + if pre <= 0 { + pre = 1; + } + + if pre > 16 { + pre = 16; + } + + errval = (APB_CLK_FREQ as i32 / (pre as i32 * n as i32) - frequency as i32).abs(); + if bestn == -1 || errval <= besterr { + besterr = errval; + bestn = n as i32; + bestpre = pre as i32; + } + } + + n = bestn; + pre = bestpre as i32; + l = n; + + /* Effectively, this does: + * h = round((duty_cycle * n) / 256) + */ + + h = (duty_cycle * n + 127) / 256; + if h <= 0 { + h = 1; + } + + reg_val = ((l as u32 - 1) << 0) | + ((h as u32 - 1) << 6) | + ((n as u32 - 1) << 12) | + ((pre as u32 - 1) << 18); + } + + self.register_block() + .clock + .write(|w| unsafe { w.bits(reg_val) }); + } + + #[cfg(not(feature = "esp32"))] + fn set_data_mode(&mut self, data_mode: embedded_hal::spi::Mode) -> &mut Self { + let reg_block = self.register_block(); + + match data_mode { + embedded_hal::spi::MODE_0 => { + reg_block.misc.modify(|_, w| w.ck_idle_edge().clear_bit()); + reg_block.user.modify(|_, w| w.ck_out_edge().clear_bit()); + } + embedded_hal::spi::MODE_1 => { + reg_block.misc.modify(|_, w| w.ck_idle_edge().clear_bit()); + reg_block.user.modify(|_, w| w.ck_out_edge().set_bit()); + } + embedded_hal::spi::MODE_2 => { + reg_block.misc.modify(|_, w| w.ck_idle_edge().set_bit()); + reg_block.user.modify(|_, w| w.ck_out_edge().set_bit()); + } + embedded_hal::spi::MODE_3 => { + reg_block.misc.modify(|_, w| w.ck_idle_edge().set_bit()); + reg_block.user.modify(|_, w| w.ck_out_edge().clear_bit()); + } + } + self + } + + #[cfg(feature = "esp32")] + fn set_data_mode(&mut self, data_mode: embedded_hal::spi::Mode) -> &mut Self { + let reg_block = self.register_block(); + + match data_mode { + embedded_hal::spi::MODE_0 => { + reg_block.pin.modify(|_, w| w.ck_idle_edge().clear_bit()); + reg_block.user.modify(|_, w| w.ck_out_edge().clear_bit()); + } + embedded_hal::spi::MODE_1 => { + reg_block.pin.modify(|_, w| w.ck_idle_edge().clear_bit()); + reg_block.user.modify(|_, w| w.ck_out_edge().set_bit()); + } + embedded_hal::spi::MODE_2 => { + reg_block.pin.modify(|_, w| w.ck_idle_edge().set_bit()); + reg_block.user.modify(|_, w| w.ck_out_edge().set_bit()); + } + embedded_hal::spi::MODE_3 => { + reg_block.pin.modify(|_, w| w.ck_idle_edge().set_bit()); + reg_block.user.modify(|_, w| w.ck_out_edge().clear_bit()); + } + } + self + } + + fn read(&mut self) -> nb::Result { + let reg_block = self.register_block(); + + if reg_block.cmd.read().usr().bit_is_set() { + return Err(nb::Error::WouldBlock); + } + + Ok(u32::try_into(reg_block.w0.read().bits()).unwrap_or_default()) + } + + fn send(&mut self, word: u8) -> nb::Result<(), Infallible> { + let reg_block = self.register_block(); + + if reg_block.cmd.read().usr().bit_is_set() { + return Err(nb::Error::WouldBlock); + } + + self.configure_datalen(8); + + reg_block.w0.write(|w| unsafe { w.bits(word.into()) }); + + self.update(); + + reg_block.cmd.modify(|_, w| w.usr().set_bit()); + + Ok(()) + } + + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Infallible> { + let reg_block = self.register_block(); + + for chunk in words.chunks_mut(64) { + self.configure_datalen(chunk.len() as u32 * 8); + + let mut fifo_ptr = reg_block.w0.as_ptr(); + for chunk in chunk.chunks(4) { + let mut u32_as_bytes = [0u8; 4]; + u32_as_bytes[0..(chunk.len())].clone_from_slice(&chunk); + let reg_val: u32 = u32::from_le_bytes(u32_as_bytes); + + unsafe { + *fifo_ptr = reg_val; + fifo_ptr = fifo_ptr.offset(1); + }; + } + + self.update(); + + reg_block.cmd.modify(|_, w| w.usr().set_bit()); + + while reg_block.cmd.read().usr().bit_is_set() { + // wait + } + + let mut fifo_ptr = reg_block.w0.as_ptr(); + for index in (0..chunk.len()).step_by(4) { + let reg_val = unsafe { *fifo_ptr }; + let bytes = reg_val.to_le_bytes(); + + let len = usize::min(chunk.len(), index + 4) - index; + chunk[index..(index + len)].clone_from_slice(&bytes[0..len]); + + unsafe { + fifo_ptr = fifo_ptr.offset(1); + }; + } + } + + Ok(words) + } + + #[cfg(not(any(feature = "esp32", feature = "esp32s2")))] + fn update(&self) { + let reg_block = self.register_block(); + + reg_block.cmd.modify(|_, w| w.update().set_bit()); + + while reg_block.cmd.read().update().bit_is_set() { + // wait + } + } + + #[cfg(any(feature = "esp32", feature = "esp32s2"))] + fn update(&self) { + // not need/available on ESP32/ESP32S2 + } + + fn configure_datalen(&self, len: u32) { + let reg_block = self.register_block(); + + #[cfg(any(feature = "esp32c3", feature = "esp32s3"))] + reg_block + .ms_dlen + .write(|w| unsafe { w.ms_data_bitlen().bits(len - 1) }); + + #[cfg(not(any(feature = "esp32c3", feature = "esp32s3")))] + { + reg_block + .mosi_dlen + .write(|w| unsafe { w.usr_mosi_dbitlen().bits(len - 1) }); + + reg_block + .miso_dlen + .write(|w| unsafe { w.usr_miso_dbitlen().bits(len - 1) }); + } + } +} + +#[cfg(any(feature = "esp32c3"))] +impl Instance for crate::pac::SPI2 { + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } + + #[inline(always)] + fn sclk_signal(&self) -> OutputSignal { + OutputSignal::FSPICLK_MUX + } + + #[inline(always)] + fn mosi_signal(&self) -> OutputSignal { + OutputSignal::FSPID + } + + #[inline(always)] + fn miso_signal(&self) -> InputSignal { + InputSignal::FSPIQ + } + + #[inline(always)] + fn cs_signal(&self) -> OutputSignal { + OutputSignal::FSPICS0 + } + + #[inline(always)] + fn enable_peripheral(&self, system: &mut System) { + // enable peripheral + system + .perip_clk_en0 + .modify(|_, w| w.spi2_clk_en().set_bit()); + + // Take the peripheral out of any pre-existing reset state + // (shouldn't be the case after a fresh startup, but better be safe) + system.perip_rst_en0.modify(|_, w| w.spi2_rst().clear_bit()); + } +} + +#[cfg(any(feature = "esp32"))] +impl Instance for crate::pac::SPI2 { + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } + + #[inline(always)] + fn sclk_signal(&self) -> OutputSignal { + OutputSignal::HSPICLK + } + + #[inline(always)] + fn mosi_signal(&self) -> OutputSignal { + OutputSignal::HSPID + } + + #[inline(always)] + fn miso_signal(&self) -> InputSignal { + InputSignal::HSPIQ + } + + #[inline(always)] + fn cs_signal(&self) -> OutputSignal { + OutputSignal::HSPICS0 + } + + #[inline(always)] + fn enable_peripheral(&self, system: &mut System) { + system.perip_clk_en.modify(|_, w| w.spi2_clk_en().set_bit()); + system.perip_rst_en.modify(|_, w| w.spi2_rst().clear_bit()); + } +} + +#[cfg(any(feature = "esp32"))] +impl Instance for crate::pac::SPI3 { + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } + + #[inline(always)] + fn sclk_signal(&self) -> OutputSignal { + OutputSignal::VSPICLK + } + + #[inline(always)] + fn mosi_signal(&self) -> OutputSignal { + OutputSignal::VSPID + } + + #[inline(always)] + fn miso_signal(&self) -> InputSignal { + InputSignal::VSPIQ + } + + #[inline(always)] + fn cs_signal(&self) -> OutputSignal { + OutputSignal::VSPICS0 + } + + #[inline(always)] + fn enable_peripheral(&self, system: &mut System) { + system.perip_clk_en.modify(|_, w| w.spi3_clk_en().set_bit()); + system.perip_rst_en.modify(|_, w| w.spi3_rst().clear_bit()); + } +} + +#[cfg(any(feature = "esp32s2", feature = "esp32s3"))] +impl Instance for crate::pac::SPI2 { + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } + + #[inline(always)] + fn sclk_signal(&self) -> OutputSignal { + OutputSignal::FSPICLK + } + + #[inline(always)] + fn mosi_signal(&self) -> OutputSignal { + OutputSignal::FSPID + } + + #[inline(always)] + fn miso_signal(&self) -> InputSignal { + InputSignal::FSPIQ + } + + #[inline(always)] + fn cs_signal(&self) -> OutputSignal { + OutputSignal::FSPICS0 + } + + #[inline(always)] + fn enable_peripheral(&self, system: &mut System) { + system + .perip_clk_en0 + .modify(|_, w| w.spi2_clk_en().set_bit()); + system.perip_rst_en0.modify(|_, w| w.spi2_rst().clear_bit()); + } +} + +#[cfg(any(feature = "esp32s2", feature = "esp32s3"))] +impl Instance for crate::pac::SPI3 { + #[inline(always)] + fn register_block(&self) -> &RegisterBlock { + self + } + + #[inline(always)] + fn sclk_signal(&self) -> OutputSignal { + OutputSignal::SPI3_CLK + } + + #[inline(always)] + fn mosi_signal(&self) -> OutputSignal { + OutputSignal::SPI3_D + } + + #[inline(always)] + fn miso_signal(&self) -> InputSignal { + InputSignal::SPI3_Q + } + + #[inline(always)] + fn cs_signal(&self) -> OutputSignal { + OutputSignal::SPI3_CS0 + } + + #[inline(always)] + fn enable_peripheral(&self, system: &mut System) { + system + .perip_clk_en0 + .modify(|_, w| w.spi3_clk_en().set_bit()); + system.perip_rst_en0.modify(|_, w| w.spi3_rst().clear_bit()); + } +} diff --git a/esp32-hal/examples/spi_loopback.rs b/esp32-hal/examples/spi_loopback.rs new file mode 100644 index 000000000..5fceec6b5 --- /dev/null +++ b/esp32-hal/examples/spi_loopback.rs @@ -0,0 +1,64 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO19 +//! MISO GPIO25 +//! MOSI GPIO23 +//! CS GPIO22 +//! +//! 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 core::fmt::Write; + +use esp32_hal::{gpio::IO, pac::Peripherals, prelude::*, Delay, RtcCntl, Serial, Timer}; +use panic_halt as _; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + + // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); + let mut timer0 = Timer::new(peripherals.TIMG0); + let mut serial0 = Serial::new(peripherals.UART0).unwrap(); + + timer0.disable(); + rtc_cntl.set_wdt_global_enable(false); + + 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 mut spi = esp32_hal::spi::Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100_000, + embedded_hal::spi::MODE_0, + &mut peripherals.DPORT, + ); + + let delay = Delay::new(); + + loop { + let mut data = [0xde, 0xca, 0xfb, 0xad]; + spi.transfer(&mut data).unwrap(); + writeln!(serial0, "{:x?}", data).ok(); + + delay.delay(250_000); + } +} diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index ea38b88af..69579a81e 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] pub use embedded_hal as ehal; -pub use esp_hal_common::{i2c, pac, prelude, Delay, RtcCntl, Serial, Timer}; +pub use esp_hal_common::{i2c, pac, prelude, spi, Delay, RtcCntl, Serial, Timer}; pub use self::gpio::IO; diff --git a/esp32c3-hal/examples/spi_loopback.rs b/esp32c3-hal/examples/spi_loopback.rs new file mode 100644 index 000000000..f217a79d3 --- /dev/null +++ b/esp32c3-hal/examples/spi_loopback.rs @@ -0,0 +1,72 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO6 +//! MISO GPIO2 +//! MOSI GPIO7 +//! CS GPIO10 +//! +//! 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 core::fmt::Write; + +use esp32c3_hal::{gpio::IO, pac::Peripherals, prelude::*, Delay, RtcCntl, Serial, Timer}; +use panic_halt as _; +use riscv_rt::entry; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + + // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); + let mut timer0 = Timer::new(peripherals.TIMG0); + let mut timer1 = Timer::new(peripherals.TIMG1); + let mut serial0 = Serial::new(peripherals.UART0).unwrap(); + + rtc_cntl.set_super_wdt_enable(false); + rtc_cntl.set_wdt_enable(false); + timer0.disable(); + timer1.disable(); + + peripherals + .SYSTEM + .sysclk_conf + .modify(|_, w| unsafe { w.soc_clk_sel().bits(1) }); + + 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 mut spi = esp32c3_hal::spi::Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100_000, + embedded_hal::spi::MODE_0, + &mut peripherals.SYSTEM, + ); + + let delay = Delay::new(peripherals.SYSTIMER); + + loop { + let mut data = [0xde, 0xca, 0xfb, 0xad]; + spi.transfer(&mut data).unwrap(); + writeln!(serial0, "{:x?}", data).ok(); + + delay.delay(250_000); + } +} diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index dcfd40f3e..5c8895826 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] pub use embedded_hal as ehal; -pub use esp_hal_common::{i2c, pac, prelude, Delay, Serial, Timer}; +pub use esp_hal_common::{i2c, pac, prelude, spi, Delay, Serial, Timer}; #[cfg(not(feature = "normalboot"))] use riscv_rt::pre_init; diff --git a/esp32s2-hal/examples/spi_loopback.rs b/esp32s2-hal/examples/spi_loopback.rs new file mode 100644 index 000000000..2dbace3ce --- /dev/null +++ b/esp32s2-hal/examples/spi_loopback.rs @@ -0,0 +1,64 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO36 +//! MISO GPIO35 +//! MOSI GPIO37 +//! CS GPIO34 +//! +//! 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 core::fmt::Write; + +use esp32s2_hal::{gpio::IO, pac::Peripherals, prelude::*, Delay, RtcCntl, Serial, Timer}; +use panic_halt as _; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + + // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); + let mut timer0 = Timer::new(peripherals.TIMG0); + let mut serial0 = Serial::new(peripherals.UART0).unwrap(); + + timer0.disable(); + rtc_cntl.set_wdt_global_enable(false); + + 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 mut spi = esp32s2_hal::spi::Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100_000, + embedded_hal::spi::MODE_0, + &mut peripherals.SYSTEM, + ); + + let delay = Delay::new(); + + loop { + let mut data = [0xde, 0xca, 0xfb, 0xad]; + spi.transfer(&mut data).unwrap(); + writeln!(serial0, "{:x?}", data).ok(); + + delay.delay(250_000); + } +} diff --git a/esp32s2-hal/src/lib.rs b/esp32s2-hal/src/lib.rs index 1640eab8b..f523b315d 100644 --- a/esp32s2-hal/src/lib.rs +++ b/esp32s2-hal/src/lib.rs @@ -6,6 +6,7 @@ pub use esp_hal_common::{ pac, prelude, ram, + spi, Delay, RtcCntl, Serial, diff --git a/esp32s3-hal/examples/spi_loopback.rs b/esp32s3-hal/examples/spi_loopback.rs new file mode 100644 index 000000000..7eb8de261 --- /dev/null +++ b/esp32s3-hal/examples/spi_loopback.rs @@ -0,0 +1,64 @@ +//! SPI loopback test +//! +//! Folowing pins are used: +//! SCLK GPIO12 +//! MISO GPIO11 +//! MOSI GPIO13 +//! CS GPIO10 +//! +//! 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 core::fmt::Write; + +use esp32s3_hal::{gpio::IO, pac::Peripherals, prelude::*, Delay, RtcCntl, Serial, Timer}; +use panic_halt as _; +use xtensa_lx_rt::entry; + +#[entry] +fn main() -> ! { + let mut peripherals = Peripherals::take().unwrap(); + + // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT, + // the RTC WDT, and the TIMG WDTs. + let mut rtc_cntl = RtcCntl::new(peripherals.RTC_CNTL); + let mut timer0 = Timer::new(peripherals.TIMG0); + let mut serial0 = Serial::new(peripherals.UART0).unwrap(); + + timer0.disable(); + rtc_cntl.set_wdt_global_enable(false); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let sclk = io.pins.gpio12; + let miso = io.pins.gpio11; + let mosi = io.pins.gpio13; + let cs = io.pins.gpio10; + + let mut spi = esp32s3_hal::spi::Spi::new( + peripherals.SPI2, + sclk, + mosi, + miso, + cs, + 100_000, + embedded_hal::spi::MODE_0, + &mut peripherals.SYSTEM, + ); + + let delay = Delay::new(); + + loop { + let mut data = [0xde, 0xca, 0xfb, 0xad]; + spi.transfer(&mut data).unwrap(); + writeln!(serial0, "{:x?}", data).ok(); + + delay.delay(250_000); + } +} diff --git a/esp32s3-hal/src/lib.rs b/esp32s3-hal/src/lib.rs index 27f17c93f..f80b83a2f 100644 --- a/esp32s3-hal/src/lib.rs +++ b/esp32s3-hal/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] pub use embedded_hal as ehal; -pub use esp_hal_common::{i2c, pac, prelude, ram, Delay, RtcCntl, Serial, Timer}; +pub use esp_hal_common::{i2c, pac, prelude, ram, spi, Delay, RtcCntl, Serial, Timer}; pub use self::gpio::IO;