ESP32-S2: Add SPI Slave support (#1562)

* SPI Slave for ESP32 / ESP32-S2

* CHANGELOG.md

* Clippy...

* Revert ESP32 changes and keep ESP32-S2 only

* changelog

* review comments

---------

Co-authored-by: bjoernQ <bjoern.quentin@mobile-j.de>
This commit is contained in:
Juraj Sadel 2024-05-22 15:21:22 +02:00 committed by GitHub
parent 60d39e9f33
commit 76ed67ad35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 167 additions and 242 deletions

View File

@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `time::current_time` API (#1503)
- ESP32-S3: Add LCD_CAM Camera driver (#1483)
- `embassy-usb` support (#1517)
- SPI Slave support for ESP32-S2 (#1562)
### Fixed

View File

@ -7,7 +7,7 @@
use crate::dma::DmaError;
pub mod master;
#[cfg(all(any(spi0, spi1, spi2, spi3), not(pdma)))]
#[cfg(not(esp32))]
pub mod slave;
/// SPI errors

View File

@ -186,14 +186,6 @@ pub mod dma {
) -> SpiDma<'d, crate::peripherals::SPI2, C, DmaMode> {
channel.tx.init_channel(); // no need to call this for both, TX and RX
#[cfg(esp32)]
match self.data_mode {
SpiMode::Mode0 | SpiMode::Mode2 => {
self.spi.invert_i_edge();
}
_ => {}
}
SpiDma {
spi: self.spi,
channel,
@ -215,14 +207,6 @@ pub mod dma {
) -> SpiDma<'d, crate::peripherals::SPI3, C, DmaMode> {
channel.tx.init_channel(); // no need to call this for both, TX and RX
#[cfg(esp32)]
match self.data_mode {
SpiMode::Mode0 | SpiMode::Mode2 => {
self.spi.invert_i_edge();
}
_ => {}
}
SpiDma {
spi: self.spi,
channel,
@ -423,12 +407,14 @@ where
self.enable_dma();
reset_dma_before_load_dma_dscr(reg_block);
tx.prepare_transfer_without_start(
self.dma_peripheral(),
false,
write_buffer_ptr,
write_buffer_len,
)?;
rx.prepare_transfer_without_start(
false,
self.dma_peripheral(),
@ -436,19 +422,20 @@ where
read_buffer_len,
)?;
self.clear_dma_interrupts();
reset_dma_before_usr_cmd(reg_block);
// On the esp32, all full-duplex transfers are single, and all half-duplex
// transfers use the cmd/addr/dummy/data sequence (but are still
// single).
#[cfg(not(esp32))]
reg_block
.dma_conf()
.modify(|_, w| w.dma_slv_seg_trans_en().clear_bit());
self.clear_dma_interrupts();
self.setup_for_flush();
reg_block.cmd().modify(|_, w| w.usr().set_bit());
rx.start_transfer()?;
tx.start_transfer()?;
Ok(rx.start_transfer()?)
Ok(())
}
fn start_write_bytes_dma(
@ -464,20 +451,22 @@ where
self.enable_dma();
reset_dma_before_load_dma_dscr(reg_block);
tx.prepare_transfer_without_start(self.dma_peripheral(), false, ptr, len)?;
self.clear_dma_interrupts();
reset_dma_before_usr_cmd(reg_block);
// On the esp32, all full-duplex transfers are single, and all half-duplex
// transfers use the cmd/addr/dummy/data sequence (but are still
// single).
#[cfg(not(esp32))]
reg_block
.dma_conf()
.modify(|_, w| w.dma_slv_seg_trans_en().clear_bit());
Ok(tx.start_transfer()?)
self.clear_dma_interrupts();
self.setup_for_flush();
reg_block.cmd().modify(|_, w| w.usr().set_bit());
tx.start_transfer()?;
Ok(())
}
unsafe fn start_read_bytes_dma(
@ -493,22 +482,21 @@ where
self.enable_dma();
reset_dma_before_load_dma_dscr(reg_block);
unsafe {
rx.prepare_transfer_without_start(false, self.dma_peripheral(), ptr, len)?;
}
rx.prepare_transfer_without_start(false, self.dma_peripheral(), ptr, len)?;
self.clear_dma_interrupts();
reset_dma_before_usr_cmd(reg_block);
// On the esp32, all full-duplex transfers are single, and all half-duplex
// transfers use the cmd/addr/dummy/data sequence (but are still
// single).
#[cfg(not(esp32))]
reg_block
.dma_conf()
.modify(|_, w| w.dma_slv_seg_trans_en().clear_bit());
Ok(rx.start_transfer()?)
self.clear_dma_interrupts();
self.setup_for_flush();
reg_block.cmd().modify(|_, w| w.usr().set_bit());
rx.start_transfer()?;
Ok(())
}
fn dma_peripheral(&self) -> DmaPeripheral {
@ -533,7 +521,7 @@ where
});
}
#[cfg(any(esp32, esp32s2))]
#[cfg(esp32s2)]
fn enable_dma(&self) {
// for non GDMA this is done in `assign_tx_device` / `assign_rx_device`
}
@ -555,10 +543,10 @@ where
});
}
#[cfg(any(esp32, esp32s2))]
#[cfg(esp32s2)]
fn clear_dma_interrupts(&self) {
let reg_block = self.register_block();
reg_block.dma_int_clr.write(|w| {
reg_block.dma_int_clr().write(|w| {
w.inlink_dscr_empty()
.clear_bit_by_one()
.outlink_dscr_error()
@ -581,7 +569,7 @@ where
}
}
#[cfg(not(any(esp32, esp32s2)))]
#[cfg(not(esp32s2))]
fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) {
reg_block.dma_conf().modify(|_, w| {
w.rx_afifo_rst()
@ -593,15 +581,18 @@ fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) {
});
}
#[cfg(any(esp32, esp32s2))]
fn reset_dma_before_usr_cmd(_reg_block: &RegisterBlock) {}
#[cfg(esp32s2)]
fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) {
reg_block.slave().modify(|_, w| w.soft_reset().set_bit());
reg_block.slave().modify(|_, w| w.soft_reset().clear_bit());
}
#[cfg(not(any(esp32, esp32s2)))]
#[cfg(not(esp32s2))]
fn reset_dma_before_load_dma_dscr(_reg_block: &RegisterBlock) {}
#[cfg(any(esp32, esp32s2))]
#[cfg(esp32s2)]
fn reset_dma_before_load_dma_dscr(reg_block: &RegisterBlock) {
reg_block.dma_conf.modify(|_, w| {
reg_block.dma_conf().modify(|_, w| {
w.out_rst()
.set_bit()
.in_rst()
@ -611,6 +602,27 @@ fn reset_dma_before_load_dma_dscr(reg_block: &RegisterBlock) {
.ahbm_rst()
.set_bit()
});
#[cfg(esp32s2)]
reg_block
.dma_conf()
.modify(|_, w| w.dma_infifo_full_clr().set_bit());
reg_block.dma_conf().modify(|_, w| {
w.out_rst()
.clear_bit()
.in_rst()
.clear_bit()
.ahbm_fifo_rst()
.clear_bit()
.ahbm_rst()
.clear_bit()
});
#[cfg(esp32s2)]
reg_block
.dma_conf()
.modify(|_, w| w.dma_infifo_full_clr().clear_bit());
}
impl<TX, RX> InstanceDma<TX, RX> for crate::peripherals::SPI2
@ -647,6 +659,11 @@ pub trait Instance: private::Sealed {
/// Initialize for full-duplex 1 bit mode
fn init(&mut self) {
let reg_block = self.register_block();
reg_block.clock().write(|w| unsafe { w.bits(0) });
reg_block.user().write(|w| unsafe { w.bits(0) });
reg_block.ctrl().write(|w| unsafe { w.bits(0) });
reg_block.slave().write(|w| w.mode().set_bit());
reg_block.user().modify(|_, w| {
@ -655,18 +672,20 @@ pub trait Instance: private::Sealed {
.doutdin()
.set_bit()
.usr_miso()
.set_bit()
.clear_bit()
.usr_mosi()
.set_bit()
.clear_bit()
.usr_dummy_idle()
.clear_bit()
.usr_addr()
.clear_bit()
.usr_command()
.clear_bit()
.sio()
.clear_bit()
});
#[cfg(not(any(esp32, esp32s2)))]
#[cfg(not(esp32s2))]
reg_block.clk_gate().modify(|_, w| {
w.clk_en()
.clear_bit()
@ -676,7 +695,7 @@ pub trait Instance: private::Sealed {
.clear_bit()
});
#[cfg(not(any(esp32, esp32s2)))]
#[cfg(not(esp32s2))]
reg_block.ctrl().modify(|_, w| {
w.q_pol()
.clear_bit()
@ -691,14 +710,9 @@ pub trait Instance: private::Sealed {
.ctrl()
.modify(|_, w| w.q_pol().clear_bit().d_pol().clear_bit().wp().clear_bit());
#[cfg(esp32)]
reg_block.ctrl().modify(|_, w| w.wp().clear_bit());
#[cfg(not(esp32))]
reg_block.misc().write(|w| unsafe { w.bits(0) });
}
#[cfg(not(esp32))]
fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self {
let reg_block = self.register_block();
@ -708,7 +722,7 @@ pub trait Instance: private::Sealed {
.user()
.modify(|_, w| w.tsck_i_edge().clear_bit().rsck_i_edge().clear_bit());
#[cfg(esp32s2)]
reg_block.ctrl1.modify(|_, w| w.clk_mode_13().clear_bit());
reg_block.ctrl1().modify(|_, w| w.clk_mode_13().clear_bit());
#[cfg(not(esp32s2))]
reg_block.slave().modify(|_, w| w.clk_mode_13().clear_bit());
}
@ -743,53 +757,14 @@ pub trait Instance: private::Sealed {
self
}
#[cfg(esp32)]
fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self {
let reg_block = self.register_block();
match data_mode {
SpiMode::Mode0 => {
reg_block.pin.modify(|_, w| w.ck_idle_edge().set_bit());
reg_block.user.modify(|_, w| w.ck_i_edge().clear_bit());
}
SpiMode::Mode1 => {
reg_block.pin.modify(|_, w| w.ck_idle_edge().set_bit());
reg_block.user.modify(|_, w| w.ck_i_edge().set_bit());
}
SpiMode::Mode2 => {
reg_block.pin.modify(|_, w| w.ck_idle_edge().clear_bit());
reg_block.user.modify(|_, w| w.ck_i_edge().set_bit());
}
SpiMode::Mode3 => {
reg_block.pin.modify(|_, w| w.ck_idle_edge().clear_bit());
reg_block.user.modify(|_, w| w.ck_i_edge().clear_bit());
}
}
self
}
// The ESP32 needs its _edge bits inverted in DMA slave mode, when in mode 0 or
// 2. set_data_mode above sets the registers up for non-DMA mode.
#[cfg(esp32)]
fn invert_i_edge(&self) {
let reg_block = self.register_block();
reg_block
.pin
.modify(|r, w| w.ck_idle_edge().variant(r.ck_idle_edge().bit_is_clear()));
reg_block
.user
.modify(|r, w| w.ck_i_edge().variant(r.ck_i_edge().bit_is_clear()));
}
fn is_bus_busy(&self) -> bool {
let reg_block = self.register_block();
#[cfg(any(esp32, esp32s2))]
#[cfg(esp32s2)]
{
reg_block.slave.read().trans_done().bit_is_clear()
reg_block.slave().read().trans_done().bit_is_clear()
}
#[cfg(not(any(esp32, esp32s2)))]
#[cfg(not(esp32s2))]
{
reg_block.dma_int_raw().read().trans_done().bit_is_clear()
}
@ -806,11 +781,11 @@ pub trait Instance: private::Sealed {
// Clear the transaction-done interrupt flag so flush() can work properly. Not
// used in DMA mode.
fn setup_for_flush(&self) {
#[cfg(any(esp32, esp32s2))]
#[cfg(esp32s2)]
self.register_block()
.slave()
.modify(|_, w| w.trans_done().clear_bit());
#[cfg(not(any(esp32, esp32s2)))]
#[cfg(not(esp32s2))]
self.register_block()
.dma_int_clr()
.write(|w| w.trans_done().clear_bit_by_one());
@ -855,82 +830,6 @@ impl Instance for crate::peripherals::SPI2 {
}
}
#[cfg(esp32)]
impl Instance for crate::peripherals::SPI2 {
#[inline(always)]
fn register_block(&self) -> &RegisterBlock {
self
}
#[inline(always)]
fn sclk_signal(&self) -> InputSignal {
InputSignal::HSPICLK
}
#[inline(always)]
fn mosi_signal(&self) -> InputSignal {
InputSignal::HSPID
}
#[inline(always)]
fn miso_signal(&self) -> OutputSignal {
OutputSignal::HSPIQ
}
#[inline(always)]
fn cs_signal(&self) -> InputSignal {
InputSignal::HSPICS0
}
#[inline(always)]
fn enable_peripheral(&self) {
PeripheralClockControl::enable(crate::system::Peripheral::Spi2);
}
#[inline(always)]
fn spi_num(&self) -> u8 {
2
}
}
#[cfg(esp32)]
impl Instance for crate::peripherals::SPI3 {
#[inline(always)]
fn register_block(&self) -> &RegisterBlock {
self
}
#[inline(always)]
fn sclk_signal(&self) -> InputSignal {
InputSignal::VSPICLK
}
#[inline(always)]
fn mosi_signal(&self) -> InputSignal {
InputSignal::VSPID
}
#[inline(always)]
fn miso_signal(&self) -> OutputSignal {
OutputSignal::VSPIQ
}
#[inline(always)]
fn cs_signal(&self) -> InputSignal {
InputSignal::VSPICS0
}
#[inline(always)]
fn enable_peripheral(&self) {
PeripheralClockControl::enable(crate::system::Peripheral::Spi3)
}
#[inline(always)]
fn spi_num(&self) -> u8 {
3
}
}
#[cfg(any(esp32s2, esp32s3))]
impl Instance for crate::peripherals::SPI2 {
#[inline(always)]

View File

@ -1,6 +1,7 @@
//! SPI slave loopback test using DMA
//!
//! Following pins are used for the slave:
//! Following pins are used for the (bitbang) slave:
//!
//! SCLK GPIO0
//! MISO GPIO1
//! MOSI GPIO2
@ -23,10 +24,11 @@
//! pins except SCLK. SCLK is between MOSI and VDD3P3_RTC on the barebones chip,
//! so no immediate neighbor is available.
//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s3
//% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
#![no_std]
#![no_main]
#![feature(asm_experimental_arch)]
use esp_backtrace as _;
use esp_hal::{
@ -34,7 +36,7 @@ use esp_hal::{
delay::Delay,
dma::{Dma, DmaPriority},
dma_buffers,
gpio::{Input, Io, Level, Output, Pull},
gpio::{Gpio4, Gpio5, Gpio8, Gpio9, Input, Io, Level, Output, Pull},
peripherals::Peripherals,
prelude::*,
spi::{
@ -65,7 +67,13 @@ fn main() -> ! {
master_mosi.set_low();
let dma = Dma::new(peripherals.DMA);
let dma_channel = dma.channel0;
cfg_if::cfg_if! {
if #[cfg(feature = "esp32s2")] {
let dma_channel = dma.spi2channel;
} else {
let dma_channel = dma.channel0;
}
}
let (tx_buffer, mut tx_descriptors, rx_buffer, mut rx_descriptors) = dma_buffers!(32000);
@ -108,39 +116,23 @@ fn main() -> ! {
slave_receive.fill(0xff);
i = i.wrapping_add(1);
println!("Iteration {i}");
println!("Do `dma_transfer`");
let transfer = spi
.dma_transfer(&mut slave_send, &mut slave_receive)
.unwrap();
// Bit-bang out the contents of master_send and read into master_receive
// as quickly as manageable. MSB first. Mode 0, so sampled on the rising
// edge and set on the falling edge.
master_cs.set_low();
for (j, v) in master_send.iter().enumerate() {
let mut b = *v;
let mut rb = 0u8;
for _ in 0..8 {
if b & 128 != 0 {
master_mosi.set_high();
} else {
master_mosi.set_low();
}
master_sclk.set_low();
b <<= 1;
rb <<= 1;
// NB: adding about 24 NOPs here makes the clock's duty cycle
// run at about 50% ... but we don't strictly need the delay,
// either.
master_sclk.set_high();
if master_miso.is_high() {
rb |= 1;
}
}
master_receive[j] = rb;
}
master_cs.set_high();
master_sclk.set_low();
// the buffers and spi is moved into the transfer and we can get it back via
// `wait`
bitbang_master(
master_send,
master_receive,
&mut master_cs,
&mut master_mosi,
&mut master_sclk,
&master_miso,
);
transfer.wait().unwrap();
println!(
"slave got {:x?} .. {:x?}, master got {:x?} .. {:x?}",
@ -152,25 +144,19 @@ fn main() -> ! {
delay.delay_millis(250);
println!("Do `dma_read`");
slave_receive.fill(0xff);
let transfer = spi.dma_read(&mut slave_receive).unwrap();
master_cs.set_high();
master_cs.set_low();
for v in master_send.iter() {
let mut b = *v;
for _ in 0..8 {
if b & 128 != 0 {
master_mosi.set_high();
} else {
master_mosi.set_low();
}
b <<= 1;
master_sclk.set_low();
master_sclk.set_high();
}
}
master_cs.set_high();
bitbang_master(
master_send,
master_receive,
&mut master_cs,
&mut master_mosi,
&mut master_sclk,
&master_miso,
);
transfer.wait().unwrap();
println!(
"slave got {:x?} .. {:x?}",
@ -179,31 +165,70 @@ fn main() -> ! {
);
delay.delay_millis(250);
println!("Do `dma_write`");
let transfer = spi.dma_write(&mut slave_send).unwrap();
master_receive.fill(0);
master_cs.set_low();
for (j, _) in master_send.iter().enumerate() {
let mut rb = 0u8;
for _ in 0..8 {
master_sclk.set_low();
rb <<= 1;
master_sclk.set_high();
if master_miso.is_high() {
rb |= 1;
}
}
master_receive[j] = rb;
}
master_cs.set_high();
transfer.wait().unwrap();
bitbang_master(
master_send,
master_receive,
&mut master_cs,
&mut master_mosi,
&mut master_sclk,
&master_miso,
);
transfer.wait().unwrap();
println!(
"master got {:x?} .. {:x?}",
&master_receive[..10],
&master_receive[master_receive.len() - 10..],
);
delay.delay_millis(250);
println!();
}
}
fn bitbang_master(
master_send: &[u8],
master_receive: &mut [u8],
master_cs: &mut Output<Gpio9>,
master_mosi: &mut Output<Gpio8>,
master_sclk: &mut Output<Gpio4>,
master_miso: &Input<Gpio5>,
) {
// Bit-bang out the contents of master_send and read into master_receive
// as quickly as manageable. MSB first. Mode 0, so sampled on the rising
// edge and set on the falling edge.
master_cs.set_low();
for (j, v) in master_send.iter().enumerate() {
let mut b = *v;
let mut rb = 0u8;
for _ in 0..8 {
if b & 128 != 0 {
master_mosi.set_high();
} else {
master_mosi.set_low();
}
master_sclk.set_low();
b <<= 1;
rb <<= 1;
// NB: adding about 24 NOPs here makes the clock's duty cycle
// run at about 50% ... but we don't strictly need the delay,
// either.
master_sclk.set_high();
if master_miso.is_high() {
rb |= 1;
}
}
master_receive[j] = rb;
}
master_sclk.set_low();
master_cs.set_high();
master_sclk.set_low();
}