mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 04:40:52 +00:00
SPI slave support (#580)
* Duplicate spi to spi_slave * Restore spi * Add barebones SPI slave mode, DMA only. This setup allows registering buffers for future transactions the master does (lowering cs, toggling sclk, and raising cs again). The transfer struct returned from the registration API will complete its wait() or return true from is_done() after cs has been raised. Copied from spi.rs, so most of the changes are deleting code that handles e.g. segmented transfers or synchronous operations. Fix non-c3 devices' builds * Limit spi_slave to non-pdma devices * SPI slave DMA example Ensure the API "feels" right. Since there's no way to route GPIOs to other peripherals, we choose four other wires and bit-bang SPI for the master side, relying on the person running the example to connect the bus. This way we ensure the slave code works, since we created the master ourselves. Also, it's not really possible to use a second ESP device as the master anyway: all the digital lines have glitches on startup, and those glitches cause the slave's DMA engine to skip descriptors (it thinks they're intended CS indicators); this causes it to lose data. Then, fix the bitbang master (recording the progression here) - When bitbanging, iterate the bits by "for _ in 0..8", instead of the broken "for _ in [0..8]". The latter only runs the iteration once, since there's only one list given ... and because the code uses _ instead of a real loop variable, type checking didn't save us. - When bitbanging, send the bits out (and read them in) MSB first, since that's actually how we have the slave configured. * Add changelog entry * Split DMA prepare_transfer into two fns. The first does everything but write to the start bit and check for an error. The second does those. We need 2 fns because the SPI slave needs to start the transfer only after resetting the various afifo hardware components (if it starts the transfer before, the first 8 bytes will be lost when that reset happens). Use the split fns everywhere. Also split flush(). It needs to be pollable, so split it into one fn that polls and one that waits until the poll returns clear. Also call the poll fn from the is_done() fn, so we don't trample in-progress transfers. * Make example code fill rx buffer before transfer This way we can tell if it's ever touching certain bytes - 0xff is never added to the master transmit buffer. While I'm changing this, make the slave tx buffer never contain 0xff either (go from 254 to 0). --------- Co-authored-by: Jesse Braham <jessebraham@users.noreply.github.com>
This commit is contained in:
parent
47821e6b3b
commit
0aa0232f1b
@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Adding async support for RSA peripheral(doesn't work properly for `esp32` chip - issue will be created)(#790)
|
||||
- Added sleep support for ESP32-C3 with timer and GPIO wakeups (#795)
|
||||
- Support for ULP-RISCV including Delay and GPIO (#840)
|
||||
- Add bare-bones SPI slave support, DMA only (#580)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -442,18 +442,24 @@ pub mod dma {
|
||||
self.channel.tx.is_done();
|
||||
self.channel.rx.is_done();
|
||||
|
||||
self.channel.tx.prepare_transfer(
|
||||
self.dma_peripheral(),
|
||||
false,
|
||||
write_buffer_ptr,
|
||||
write_buffer_len,
|
||||
)?;
|
||||
self.channel.rx.prepare_transfer(
|
||||
false,
|
||||
self.dma_peripheral(),
|
||||
read_buffer_ptr,
|
||||
read_buffer_len,
|
||||
)?;
|
||||
self.channel
|
||||
.tx
|
||||
.prepare_transfer_without_start(
|
||||
self.dma_peripheral(),
|
||||
false,
|
||||
write_buffer_ptr,
|
||||
write_buffer_len,
|
||||
)
|
||||
.and_then(|_| self.channel.tx.start_transfer())?;
|
||||
self.channel
|
||||
.rx
|
||||
.prepare_transfer_without_start(
|
||||
false,
|
||||
self.dma_peripheral(),
|
||||
read_buffer_ptr,
|
||||
read_buffer_len,
|
||||
)
|
||||
.and_then(|_| self.channel.rx.start_transfer())?;
|
||||
self.enable_dma(true);
|
||||
self.enable_interrupt();
|
||||
self.set_mode(mode);
|
||||
|
@ -277,7 +277,7 @@ pub trait RxPrivate {
|
||||
|
||||
fn init_channel(&mut self);
|
||||
|
||||
fn prepare_transfer(
|
||||
fn prepare_transfer_without_start(
|
||||
&mut self,
|
||||
circular: bool,
|
||||
peri: DmaPeripheral,
|
||||
@ -285,6 +285,8 @@ pub trait RxPrivate {
|
||||
len: usize,
|
||||
) -> Result<(), DmaError>;
|
||||
|
||||
fn start_transfer(&mut self) -> Result<(), DmaError>;
|
||||
|
||||
fn listen_ch_in_done(&self);
|
||||
|
||||
fn clear_ch_in_done(&self);
|
||||
@ -331,7 +333,7 @@ where
|
||||
R::set_in_priority(priority);
|
||||
}
|
||||
|
||||
fn prepare_transfer(
|
||||
fn prepare_transfer_without_start(
|
||||
&mut self,
|
||||
descriptors: &mut [u32],
|
||||
circular: bool,
|
||||
@ -382,13 +384,16 @@ where
|
||||
R::reset_in();
|
||||
R::set_in_descriptors(descriptors.as_ptr() as u32);
|
||||
R::set_in_peripheral(peri as u8);
|
||||
Ok(())
|
||||
}
|
||||
fn start_transfer(&mut self) -> Result<(), DmaError> {
|
||||
R::start_in();
|
||||
|
||||
if R::has_in_descriptor_error() {
|
||||
return Err(DmaError::DescriptorError);
|
||||
Err(DmaError::DescriptorError)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_done(&self) -> bool {
|
||||
@ -435,7 +440,7 @@ where
|
||||
self.rx_impl.init(burst_mode, priority);
|
||||
}
|
||||
|
||||
fn prepare_transfer(
|
||||
fn prepare_transfer_without_start(
|
||||
&mut self,
|
||||
circular: bool,
|
||||
peri: DmaPeripheral,
|
||||
@ -464,8 +469,11 @@ where
|
||||
self.read_buffer_start = data;
|
||||
|
||||
self.rx_impl
|
||||
.prepare_transfer(self.descriptors, circular, peri, data, len)?;
|
||||
Ok(())
|
||||
.prepare_transfer_without_start(self.descriptors, circular, peri, data, len)
|
||||
}
|
||||
|
||||
fn start_transfer(&mut self) -> Result<(), DmaError> {
|
||||
self.rx_impl.start_transfer()
|
||||
}
|
||||
|
||||
fn listen_ch_in_done(&self) {
|
||||
@ -616,7 +624,7 @@ pub trait TxPrivate {
|
||||
|
||||
fn init_channel(&mut self);
|
||||
|
||||
fn prepare_transfer(
|
||||
fn prepare_transfer_without_start(
|
||||
&mut self,
|
||||
peri: DmaPeripheral,
|
||||
circular: bool,
|
||||
@ -624,6 +632,8 @@ pub trait TxPrivate {
|
||||
len: usize,
|
||||
) -> Result<(), DmaError>;
|
||||
|
||||
fn start_transfer(&mut self) -> Result<(), DmaError>;
|
||||
|
||||
fn clear_ch_out_done(&self);
|
||||
|
||||
fn is_ch_out_done_set(&self) -> bool;
|
||||
@ -661,7 +671,7 @@ where
|
||||
R::set_out_priority(priority);
|
||||
}
|
||||
|
||||
fn prepare_transfer(
|
||||
fn prepare_transfer_without_start(
|
||||
&mut self,
|
||||
descriptors: &mut [u32],
|
||||
circular: bool,
|
||||
@ -712,13 +722,17 @@ where
|
||||
R::reset_out();
|
||||
R::set_out_descriptors(descriptors.as_ptr() as u32);
|
||||
R::set_out_peripheral(peri as u8);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_transfer(&mut self) -> Result<(), DmaError> {
|
||||
R::start_out();
|
||||
|
||||
if R::has_out_descriptor_error() {
|
||||
return Err(DmaError::DescriptorError);
|
||||
Err(DmaError::DescriptorError)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_ch_out_done(&self) {
|
||||
@ -800,7 +814,7 @@ where
|
||||
R::init_channel();
|
||||
}
|
||||
|
||||
fn prepare_transfer(
|
||||
fn prepare_transfer_without_start(
|
||||
&mut self,
|
||||
peri: DmaPeripheral,
|
||||
circular: bool,
|
||||
@ -827,9 +841,11 @@ where
|
||||
self.buffer_len = len;
|
||||
|
||||
self.tx_impl
|
||||
.prepare_transfer(self.descriptors, circular, peri, data, len)?;
|
||||
.prepare_transfer_without_start(self.descriptors, circular, peri, data, len)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
fn start_transfer(&mut self) -> Result<(), DmaError> {
|
||||
self.tx_impl.start_transfer()
|
||||
}
|
||||
|
||||
fn clear_ch_out_done(&self) {
|
||||
|
@ -798,12 +798,14 @@ where
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
self.tx_channel.prepare_transfer(
|
||||
self.register_access.get_dma_peripheral(),
|
||||
false,
|
||||
ptr,
|
||||
data.len(),
|
||||
)?;
|
||||
self.tx_channel
|
||||
.prepare_transfer_without_start(
|
||||
self.register_access.get_dma_peripheral(),
|
||||
false,
|
||||
ptr,
|
||||
data.len(),
|
||||
)
|
||||
.and_then(|_| self.tx_channel.start_transfer())?;
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
@ -832,12 +834,14 @@ where
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
self.tx_channel.prepare_transfer(
|
||||
self.register_access.get_dma_peripheral(),
|
||||
circular,
|
||||
ptr,
|
||||
len,
|
||||
)?;
|
||||
self.tx_channel
|
||||
.prepare_transfer_without_start(
|
||||
self.register_access.get_dma_peripheral(),
|
||||
circular,
|
||||
ptr,
|
||||
len,
|
||||
)
|
||||
.and_then(|_| self.tx_channel.start_transfer())?;
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
@ -870,12 +874,14 @@ where
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
self.tx_channel.prepare_transfer(
|
||||
self.register_access.get_dma_peripheral(),
|
||||
circular,
|
||||
ptr,
|
||||
len,
|
||||
)?;
|
||||
self.tx_channel
|
||||
.prepare_transfer_without_start(
|
||||
self.register_access.get_dma_peripheral(),
|
||||
circular,
|
||||
ptr,
|
||||
len,
|
||||
)
|
||||
.and_then(|_| self.tx_channel.start_transfer())?;
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
@ -964,12 +970,14 @@ where
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
self.rx_channel.prepare_transfer(
|
||||
false,
|
||||
self.register_access.get_dma_peripheral(),
|
||||
ptr,
|
||||
data.len(),
|
||||
)?;
|
||||
self.rx_channel
|
||||
.prepare_transfer_without_start(
|
||||
false,
|
||||
self.register_access.get_dma_peripheral(),
|
||||
ptr,
|
||||
data.len(),
|
||||
)
|
||||
.and_then(|_| self.rx_channel.start_transfer())?;
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
@ -1002,12 +1010,14 @@ where
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
self.rx_channel.prepare_transfer(
|
||||
circular,
|
||||
self.register_access.get_dma_peripheral(),
|
||||
ptr,
|
||||
len,
|
||||
)?;
|
||||
self.rx_channel
|
||||
.prepare_transfer_without_start(
|
||||
circular,
|
||||
self.register_access.get_dma_peripheral(),
|
||||
ptr,
|
||||
len,
|
||||
)
|
||||
.and_then(|_| self.rx_channel.start_transfer())?;
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
@ -1047,12 +1057,14 @@ where
|
||||
// Enable corresponding interrupts if needed
|
||||
|
||||
// configure DMA outlink
|
||||
self.rx_channel.prepare_transfer(
|
||||
circular,
|
||||
self.register_access.get_dma_peripheral(),
|
||||
ptr,
|
||||
len,
|
||||
)?;
|
||||
self.rx_channel
|
||||
.prepare_transfer_without_start(
|
||||
circular,
|
||||
self.register_access.get_dma_peripheral(),
|
||||
ptr,
|
||||
len,
|
||||
)
|
||||
.and_then(|_| self.rx_channel.start_transfer())?;
|
||||
|
||||
// set I2S_TX_STOP_EN if needed
|
||||
|
||||
|
@ -138,6 +138,8 @@ pub mod rtc_cntl;
|
||||
pub mod sha;
|
||||
#[cfg(any(spi0, spi1, spi2, spi3))]
|
||||
pub mod spi;
|
||||
#[cfg(all(any(spi0, spi1, spi2, spi3), not(pdma)))]
|
||||
pub mod spi_slave;
|
||||
#[cfg(any(dport, pcr, system))]
|
||||
pub mod system;
|
||||
#[cfg(systimer)]
|
||||
|
@ -1209,7 +1209,8 @@ where
|
||||
self.tx_channel.is_done();
|
||||
|
||||
self.tx_channel
|
||||
.prepare_transfer(DmaPeripheral::ParlIo, false, ptr, len)?;
|
||||
.prepare_transfer_without_start(DmaPeripheral::ParlIo, false, ptr, len)
|
||||
.and_then(|_| self.tx_channel.start_transfer())?;
|
||||
|
||||
loop {
|
||||
if Instance::is_tx_ready() {
|
||||
@ -1331,7 +1332,8 @@ where
|
||||
Instance::set_rx_bytes(len as u16);
|
||||
|
||||
self.rx_channel
|
||||
.prepare_transfer(false, DmaPeripheral::ParlIo, ptr, len)?;
|
||||
.prepare_transfer_without_start(false, DmaPeripheral::ParlIo, ptr, len)
|
||||
.and_then(|_| self.rx_channel.start_transfer())?;
|
||||
|
||||
Instance::set_rx_reg_update();
|
||||
|
||||
|
@ -70,6 +70,12 @@ pub use crate::spi::{
|
||||
Instance as _esp_hal_spi_Instance,
|
||||
InstanceDma as _esp_hal_spi_InstanceDma,
|
||||
};
|
||||
#[cfg(all(any(spi0, spi1, spi2, spi3), not(pdma)))]
|
||||
pub use crate::spi_slave::{
|
||||
dma::WithDmaSpi2 as _esp_hal_spi_slave_dma_WithDmaSpi2,
|
||||
Instance as _esp_hal_spi_slave_Instance,
|
||||
InstanceDma as _esp_hal_spi_slave_InstanceDma,
|
||||
};
|
||||
#[cfg(any(dport, pcr, system))]
|
||||
pub use crate::system::SystemExt as _esp_hal_system_SystemExt;
|
||||
#[cfg(any(timg0, timg1))]
|
||||
|
@ -1901,18 +1901,20 @@ where
|
||||
self.update();
|
||||
|
||||
reset_dma_before_load_dma_dscr(reg_block);
|
||||
tx.prepare_transfer(
|
||||
tx.prepare_transfer_without_start(
|
||||
self.dma_peripheral(),
|
||||
false,
|
||||
write_buffer_ptr,
|
||||
write_buffer_len,
|
||||
)?;
|
||||
rx.prepare_transfer(
|
||||
)
|
||||
.and_then(|_| tx.start_transfer())?;
|
||||
rx.prepare_transfer_without_start(
|
||||
false,
|
||||
self.dma_peripheral(),
|
||||
read_buffer_ptr,
|
||||
read_buffer_len,
|
||||
)?;
|
||||
)
|
||||
.and_then(|_| rx.start_transfer())?;
|
||||
|
||||
self.clear_dma_interrupts();
|
||||
reset_dma_before_usr_cmd(reg_block);
|
||||
@ -1948,7 +1950,8 @@ where
|
||||
self.update();
|
||||
|
||||
reset_dma_before_load_dma_dscr(reg_block);
|
||||
tx.prepare_transfer(self.dma_peripheral(), false, ptr, len)?;
|
||||
tx.prepare_transfer_without_start(self.dma_peripheral(), false, ptr, len)
|
||||
.and_then(|_| tx.start_transfer())?;
|
||||
|
||||
self.clear_dma_interrupts();
|
||||
reset_dma_before_usr_cmd(reg_block);
|
||||
@ -1973,7 +1976,8 @@ where
|
||||
self.update();
|
||||
|
||||
reset_dma_before_load_dma_dscr(reg_block);
|
||||
rx.prepare_transfer(false, self.dma_peripheral(), ptr, len)?;
|
||||
rx.prepare_transfer_without_start(false, self.dma_peripheral(), ptr, len)
|
||||
.and_then(|_| rx.start_transfer())?;
|
||||
|
||||
self.clear_dma_interrupts();
|
||||
reset_dma_before_usr_cmd(reg_block);
|
||||
|
1089
esp-hal-common/src/spi_slave.rs
Normal file
1089
esp-hal-common/src/spi_slave.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -82,12 +82,12 @@ fn main() -> ! {
|
||||
|
||||
let transfer = spi.dma_transfer(send, receive).unwrap();
|
||||
// here we could do something else while DMA transfer is in progress
|
||||
let mut i = 0;
|
||||
let mut n = 0;
|
||||
// Check is_done until the transfer is almost done (32000 bytes at 100kHz is
|
||||
// 2.56 seconds), then move to wait().
|
||||
while !transfer.is_done() && i < 10 {
|
||||
while !transfer.is_done() && n < 10 {
|
||||
delay.delay_ms(250u32);
|
||||
i += 1;
|
||||
n += 1;
|
||||
}
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
|
@ -82,12 +82,12 @@ fn main() -> ! {
|
||||
|
||||
let transfer = spi.dma_transfer(send, receive).unwrap();
|
||||
// here we could do something else while DMA transfer is in progress
|
||||
let mut i = 0;
|
||||
let mut n = 0;
|
||||
// Check is_done until the transfer is almost done (32000 bytes at 100kHz is
|
||||
// 2.56 seconds), then move to wait().
|
||||
while !transfer.is_done() && i < 10 {
|
||||
while !transfer.is_done() && n < 10 {
|
||||
delay.delay_ms(250u32);
|
||||
i += 1;
|
||||
n += 1;
|
||||
}
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
|
170
esp32c2-hal/examples/spi_slave_dma.rs
Normal file
170
esp32c2-hal/examples/spi_slave_dma.rs
Normal file
@ -0,0 +1,170 @@
|
||||
//! SPI slave loopback test using DMA
|
||||
//!
|
||||
//! Following pins are used for the slave:
|
||||
//! SCLK GPIO6
|
||||
//! MISO GPIO2
|
||||
//! MOSI GPIO7
|
||||
//! CS GPIO10
|
||||
//!
|
||||
//! Following pins are used for the (bitbang) master:
|
||||
//! SCLK GPIO5
|
||||
//! MISO GPIO1
|
||||
//! MOSI GPIO8
|
||||
//! CS GPIO9
|
||||
//!
|
||||
//! Depending on your target and the board you are using you have to change the
|
||||
//! pins.
|
||||
//!
|
||||
//! This example transfers data via SPI.
|
||||
//! Connect corresponding master and slave pins to see the outgoing data is read
|
||||
//! as incoming data. The master-side pins are chosen to make these connections
|
||||
//! easy for the barebones ESP32C3 chip; all are immediate neighbors of the
|
||||
//! slave-side pins except SCLK. SCLK is between MOSI and VDD3P3_RTC on the
|
||||
//! barebones ESP32C3, so no immediate neighbor is available.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp32c2_hal::{
|
||||
clock::ClockControl,
|
||||
dma::DmaPriority,
|
||||
gdma::Gdma,
|
||||
gpio::IO,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
spi_slave::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let system = peripherals.SYSTEM.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
// Disable the watchdog timers. For the ESP32-C2, this includes the Super WDT,
|
||||
// the RTC WDT, and the TIMG WDT.
|
||||
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
|
||||
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||
let mut wdt0 = timer_group0.wdt;
|
||||
|
||||
rtc.swd.disable();
|
||||
rtc.rwdt.disable();
|
||||
wdt0.disable();
|
||||
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
let slave_sclk = io.pins.gpio6;
|
||||
let mut master_sclk = io.pins.gpio5.into_push_pull_output();
|
||||
let slave_miso = io.pins.gpio2;
|
||||
let master_miso = io.pins.gpio1.into_floating_input();
|
||||
let slave_mosi = io.pins.gpio7;
|
||||
let mut master_mosi = io.pins.gpio8.into_push_pull_output();
|
||||
let slave_cs = io.pins.gpio10;
|
||||
let mut master_cs = io.pins.gpio9.into_push_pull_output();
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
master_mosi.set_low().unwrap();
|
||||
|
||||
let dma = Gdma::new(peripherals.DMA);
|
||||
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,
|
||||
slave_sclk,
|
||||
slave_mosi,
|
||||
slave_miso,
|
||||
slave_cs,
|
||||
SpiMode::Mode0,
|
||||
)
|
||||
.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 master_send = &mut [0u8; 3200];
|
||||
let master_receive = &mut [0u8; 3200];
|
||||
let mut slave_send = buffer1();
|
||||
let mut slave_receive = buffer2();
|
||||
let mut i = 0;
|
||||
|
||||
for (i, v) in master_send.iter_mut().enumerate() {
|
||||
*v = (i % 255) as u8;
|
||||
}
|
||||
for (i, v) in slave_send.iter_mut().enumerate() {
|
||||
*v = (254 - (i % 255)) as u8;
|
||||
}
|
||||
|
||||
loop {
|
||||
master_send[0] = i;
|
||||
master_send[master_send.len() - 1] = i;
|
||||
slave_send[0] = i;
|
||||
slave_send[slave_send.len() - 1] = i;
|
||||
slave_receive.fill(0xff);
|
||||
i = i.wrapping_add(1);
|
||||
|
||||
let transfer = spi.dma_transfer(slave_send, 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().unwrap();
|
||||
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().unwrap();
|
||||
} else {
|
||||
master_mosi.set_low().unwrap();
|
||||
}
|
||||
master_sclk.set_low().unwrap();
|
||||
b <<= 1;
|
||||
rb <<= 1;
|
||||
// Delay to ensure the SPI peripheral notices the low clock.
|
||||
// One microsecond is about twice as long as we need to pause,
|
||||
// but it'll still work.
|
||||
delay.delay_us(1u32);
|
||||
master_sclk.set_high().unwrap();
|
||||
if master_miso.is_high().unwrap() {
|
||||
rb |= 1;
|
||||
}
|
||||
}
|
||||
master_receive[j] = rb;
|
||||
}
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
(slave_receive, slave_send, spi) = transfer.wait().unwrap();
|
||||
println!(
|
||||
"slave got {:x?} .. {:x?}, master got {:x?} .. {:x?}",
|
||||
&slave_receive[..10],
|
||||
&slave_receive[slave_receive.len() - 10..],
|
||||
&master_receive[..10],
|
||||
&master_receive[master_receive.len() - 10..]
|
||||
);
|
||||
|
||||
delay.delay_ms(250u32);
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer1() -> &'static mut [u8; 3200] {
|
||||
static mut BUFFER: [u8; 3200] = [0u8; 3200];
|
||||
unsafe { &mut BUFFER }
|
||||
}
|
||||
|
||||
fn buffer2() -> &'static mut [u8; 3200] {
|
||||
static mut BUFFER: [u8; 3200] = [0u8; 3200];
|
||||
unsafe { &mut BUFFER }
|
||||
}
|
@ -82,12 +82,12 @@ fn main() -> ! {
|
||||
|
||||
let transfer = spi.dma_transfer(send, receive).unwrap();
|
||||
// here we could do something else while DMA transfer is in progress
|
||||
let mut i = 0;
|
||||
let mut n = 0;
|
||||
// Check is_done until the transfer is almost done (32000 bytes at 100kHz is
|
||||
// 2.56 seconds), then move to wait().
|
||||
while !transfer.is_done() && i < 10 {
|
||||
while !transfer.is_done() && n < 10 {
|
||||
delay.delay_ms(250u32);
|
||||
i += 1;
|
||||
n += 1;
|
||||
}
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
|
172
esp32c3-hal/examples/spi_slave_dma.rs
Normal file
172
esp32c3-hal/examples/spi_slave_dma.rs
Normal file
@ -0,0 +1,172 @@
|
||||
//! SPI slave loopback test using DMA
|
||||
//!
|
||||
//! Following pins are used for the slave:
|
||||
//! SCLK GPIO6
|
||||
//! MISO GPIO2
|
||||
//! MOSI GPIO7
|
||||
//! CS GPIO10
|
||||
//!
|
||||
//! Following pins are used for the (bitbang) master:
|
||||
//! SCLK GPIO5
|
||||
//! MISO GPIO1
|
||||
//! MOSI GPIO8
|
||||
//! CS GPIO9
|
||||
//!
|
||||
//! Depending on your target and the board you are using you have to change the
|
||||
//! pins.
|
||||
//!
|
||||
//! This example transfers data via SPI.
|
||||
//! Connect corresponding master and slave pins to see the outgoing data is read
|
||||
//! as incoming data. The master-side pins are chosen to make these connections
|
||||
//! easy for the barebones ESP32C3 chip; all are immediate neighbors of the
|
||||
//! slave-side pins except SCLK. SCLK is between MOSI and VDD3P3_RTC on the
|
||||
//! barebones ESP32C3, so no immediate neighbor is available.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp32c3_hal::{
|
||||
clock::ClockControl,
|
||||
dma::DmaPriority,
|
||||
gdma::Gdma,
|
||||
gpio::IO,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
spi_slave::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let system = peripherals.SYSTEM.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
// Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT,
|
||||
// the RTC WDT, and the TIMG WDTs.
|
||||
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;
|
||||
|
||||
rtc.swd.disable();
|
||||
rtc.rwdt.disable();
|
||||
wdt0.disable();
|
||||
wdt1.disable();
|
||||
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
let slave_sclk = io.pins.gpio6;
|
||||
let mut master_sclk = io.pins.gpio5.into_push_pull_output();
|
||||
let slave_miso = io.pins.gpio2;
|
||||
let master_miso = io.pins.gpio1.into_floating_input();
|
||||
let slave_mosi = io.pins.gpio7;
|
||||
let mut master_mosi = io.pins.gpio8.into_push_pull_output();
|
||||
let slave_cs = io.pins.gpio10;
|
||||
let mut master_cs = io.pins.gpio9.into_push_pull_output();
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
master_mosi.set_low().unwrap();
|
||||
|
||||
let dma = Gdma::new(peripherals.DMA);
|
||||
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,
|
||||
slave_sclk,
|
||||
slave_mosi,
|
||||
slave_miso,
|
||||
slave_cs,
|
||||
SpiMode::Mode0,
|
||||
)
|
||||
.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 master_send = &mut [0u8; 32000];
|
||||
let master_receive = &mut [0u8; 32000];
|
||||
let mut slave_send = buffer1();
|
||||
let mut slave_receive = buffer2();
|
||||
let mut i = 0;
|
||||
|
||||
for (i, v) in master_send.iter_mut().enumerate() {
|
||||
*v = (i % 255) as u8;
|
||||
}
|
||||
for (i, v) in slave_send.iter_mut().enumerate() {
|
||||
*v = (254 - (i % 255)) as u8;
|
||||
}
|
||||
|
||||
loop {
|
||||
master_send[0] = i;
|
||||
master_send[master_send.len() - 1] = i;
|
||||
slave_send[0] = i;
|
||||
slave_send[slave_send.len() - 1] = i;
|
||||
slave_receive.fill(0xff);
|
||||
i = i.wrapping_add(1);
|
||||
|
||||
let transfer = spi.dma_transfer(slave_send, 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().unwrap();
|
||||
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().unwrap();
|
||||
} else {
|
||||
master_mosi.set_low().unwrap();
|
||||
}
|
||||
master_sclk.set_low().unwrap();
|
||||
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().unwrap();
|
||||
if master_miso.is_high().unwrap() {
|
||||
rb |= 1;
|
||||
}
|
||||
}
|
||||
master_receive[j] = rb;
|
||||
}
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
(slave_receive, slave_send, spi) = transfer.wait().unwrap();
|
||||
println!(
|
||||
"slave got {:x?} .. {:x?}, master got {:x?} .. {:x?}",
|
||||
&slave_receive[..10],
|
||||
&slave_receive[slave_receive.len() - 10..],
|
||||
&master_receive[..10],
|
||||
&master_receive[master_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 }
|
||||
}
|
@ -82,12 +82,12 @@ fn main() -> ! {
|
||||
|
||||
let transfer = spi.dma_transfer(send, receive).unwrap();
|
||||
// here we could do something else while DMA transfer is in progress
|
||||
let mut i = 0;
|
||||
let mut n = 0;
|
||||
// Check is_done until the transfer is almost done (32000 bytes at 100kHz is
|
||||
// 2.56 seconds), then move to wait().
|
||||
while !transfer.is_done() && i < 10 {
|
||||
while !transfer.is_done() && n < 10 {
|
||||
delay.delay_ms(250u32);
|
||||
i += 1;
|
||||
n += 1;
|
||||
}
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
|
172
esp32c6-hal/examples/spi_slave_dma.rs
Normal file
172
esp32c6-hal/examples/spi_slave_dma.rs
Normal file
@ -0,0 +1,172 @@
|
||||
//! SPI slave loopback test using DMA
|
||||
//!
|
||||
//! Following pins are used for the slave:
|
||||
//! SCLK GPIO6
|
||||
//! MISO GPIO2
|
||||
//! MOSI GPIO7
|
||||
//! CS GPIO10
|
||||
//!
|
||||
//! Following pins are used for the (bitbang) master:
|
||||
//! SCLK GPIO5
|
||||
//! MISO GPIO1
|
||||
//! MOSI GPIO8
|
||||
//! CS GPIO9
|
||||
//!
|
||||
//! Depending on your target and the board you are using you have to change the
|
||||
//! pins.
|
||||
//!
|
||||
//! This example transfers data via SPI.
|
||||
//! Connect corresponding master and slave pins to see the outgoing data is read
|
||||
//! as incoming data. The master-side pins are chosen to make these connections
|
||||
//! easy for the barebones ESP32C3 chip; all are immediate neighbors of the
|
||||
//! slave-side pins except SCLK. SCLK is between MOSI and VDD3P3_RTC on the
|
||||
//! barebones ESP32C3, so no immediate neighbor is available.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp32c6_hal::{
|
||||
clock::ClockControl,
|
||||
dma::DmaPriority,
|
||||
gdma::Gdma,
|
||||
gpio::IO,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
spi_slave::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let system = peripherals.SYSTEM.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
// Disable the watchdog timers. For the ESP32-C3, 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);
|
||||
let mut wdt0 = timer_group0.wdt;
|
||||
let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
|
||||
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 slave_sclk = io.pins.gpio6;
|
||||
let mut master_sclk = io.pins.gpio5.into_push_pull_output();
|
||||
let slave_miso = io.pins.gpio2;
|
||||
let master_miso = io.pins.gpio1.into_floating_input();
|
||||
let slave_mosi = io.pins.gpio7;
|
||||
let mut master_mosi = io.pins.gpio8.into_push_pull_output();
|
||||
let slave_cs = io.pins.gpio10;
|
||||
let mut master_cs = io.pins.gpio9.into_push_pull_output();
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
master_mosi.set_low().unwrap();
|
||||
|
||||
let dma = Gdma::new(peripherals.DMA);
|
||||
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,
|
||||
slave_sclk,
|
||||
slave_mosi,
|
||||
slave_miso,
|
||||
slave_cs,
|
||||
SpiMode::Mode0,
|
||||
)
|
||||
.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 master_send = &mut [0u8; 32000];
|
||||
let master_receive = &mut [0u8; 32000];
|
||||
let mut slave_send = buffer1();
|
||||
let mut slave_receive = buffer2();
|
||||
let mut i = 0;
|
||||
|
||||
for (i, v) in master_send.iter_mut().enumerate() {
|
||||
*v = (i % 255) as u8;
|
||||
}
|
||||
for (i, v) in slave_send.iter_mut().enumerate() {
|
||||
*v = (254 - (i % 255)) as u8;
|
||||
}
|
||||
|
||||
loop {
|
||||
master_send[0] = i;
|
||||
master_send[master_send.len() - 1] = i;
|
||||
slave_send[0] = i;
|
||||
slave_send[slave_send.len() - 1] = i;
|
||||
slave_receive.fill(0xff);
|
||||
i = i.wrapping_add(1);
|
||||
|
||||
let transfer = spi.dma_transfer(slave_send, 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().unwrap();
|
||||
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().unwrap();
|
||||
} else {
|
||||
master_mosi.set_low().unwrap();
|
||||
}
|
||||
master_sclk.set_low().unwrap();
|
||||
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().unwrap();
|
||||
if master_miso.is_high().unwrap() {
|
||||
rb |= 1;
|
||||
}
|
||||
}
|
||||
master_receive[j] = rb;
|
||||
}
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
(slave_receive, slave_send, spi) = transfer.wait().unwrap();
|
||||
println!(
|
||||
"slave got {:x?} .. {:x?}, master got {:x?} .. {:x?}",
|
||||
&slave_receive[..10],
|
||||
&slave_receive[slave_receive.len() - 10..],
|
||||
&master_receive[..10],
|
||||
&master_receive[master_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 }
|
||||
}
|
172
esp32h2-hal/examples/spi_slave_dma.rs
Normal file
172
esp32h2-hal/examples/spi_slave_dma.rs
Normal file
@ -0,0 +1,172 @@
|
||||
//! SPI slave loopback test using DMA
|
||||
//!
|
||||
//! Following pins are used for the slave:
|
||||
//! SCLK GPIO6
|
||||
//! MISO GPIO2
|
||||
//! MOSI GPIO7
|
||||
//! CS GPIO10
|
||||
//!
|
||||
//! Following pins are used for the (bitbang) master:
|
||||
//! SCLK GPIO5
|
||||
//! MISO GPIO1
|
||||
//! MOSI GPIO8
|
||||
//! CS GPIO9
|
||||
//!
|
||||
//! Depending on your target and the board you are using you have to change the
|
||||
//! pins.
|
||||
//!
|
||||
//! This example transfers data via SPI.
|
||||
//! Connect corresponding master and slave pins to see the outgoing data is read
|
||||
//! as incoming data. The master-side pins are chosen to make these connections
|
||||
//! easy for the barebones ESP32C3 chip; all are immediate neighbors of the
|
||||
//! slave-side pins except SCLK. SCLK is between MOSI and VDD3P3_RTC on the
|
||||
//! barebones ESP32C3, so no immediate neighbor is available.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp32h2_hal::{
|
||||
clock::ClockControl,
|
||||
dma::DmaPriority,
|
||||
gdma::Gdma,
|
||||
gpio::IO,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
spi_slave::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let system = peripherals.SYSTEM.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
// Disable the watchdog timers. For the ESP32-C6, 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);
|
||||
let mut wdt0 = timer_group0.wdt;
|
||||
let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
|
||||
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 slave_sclk = io.pins.gpio4;
|
||||
let mut master_sclk = io.pins.gpio5.into_push_pull_output();
|
||||
let slave_miso = io.pins.gpio2;
|
||||
let master_miso = io.pins.gpio1.into_floating_input();
|
||||
let slave_mosi = io.pins.gpio12;
|
||||
let mut master_mosi = io.pins.gpio11.into_push_pull_output();
|
||||
let slave_cs = io.pins.gpio10;
|
||||
let mut master_cs = io.pins.gpio9.into_push_pull_output();
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
master_mosi.set_low().unwrap();
|
||||
|
||||
let dma = Gdma::new(peripherals.DMA);
|
||||
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,
|
||||
slave_sclk,
|
||||
slave_mosi,
|
||||
slave_miso,
|
||||
slave_cs,
|
||||
SpiMode::Mode0,
|
||||
)
|
||||
.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 master_send = &mut [0u8; 32000];
|
||||
let master_receive = &mut [0u8; 32000];
|
||||
let mut slave_send = buffer1();
|
||||
let mut slave_receive = buffer2();
|
||||
let mut i = 0;
|
||||
|
||||
for (i, v) in master_send.iter_mut().enumerate() {
|
||||
*v = (i % 255) as u8;
|
||||
}
|
||||
for (i, v) in slave_send.iter_mut().enumerate() {
|
||||
*v = (254 - (i % 255)) as u8;
|
||||
}
|
||||
|
||||
loop {
|
||||
master_send[0] = i;
|
||||
master_send[master_send.len() - 1] = i;
|
||||
slave_send[0] = i;
|
||||
slave_send[slave_send.len() - 1] = i;
|
||||
slave_receive.fill(0xff);
|
||||
i = i.wrapping_add(1);
|
||||
|
||||
let transfer = spi.dma_transfer(slave_send, 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().unwrap();
|
||||
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().unwrap();
|
||||
} else {
|
||||
master_mosi.set_low().unwrap();
|
||||
}
|
||||
master_sclk.set_low().unwrap();
|
||||
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().unwrap();
|
||||
if master_miso.is_high().unwrap() {
|
||||
rb |= 1;
|
||||
}
|
||||
}
|
||||
master_receive[j] = rb;
|
||||
}
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
(slave_receive, slave_send, spi) = transfer.wait().unwrap();
|
||||
println!(
|
||||
"slave got {:x?} .. {:x?}, master got {:x?} .. {:x?}",
|
||||
&slave_receive[..10],
|
||||
&slave_receive[slave_receive.len() - 10..],
|
||||
&master_receive[..10],
|
||||
&master_receive[master_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 }
|
||||
}
|
@ -82,12 +82,12 @@ fn main() -> ! {
|
||||
|
||||
let transfer = spi.dma_transfer(send, receive).unwrap();
|
||||
// here we could do something else while DMA transfer is in progress
|
||||
let mut i = 0;
|
||||
let mut n = 0;
|
||||
// Check is_done until the transfer is almost done (32000 bytes at 100kHz is
|
||||
// 2.56 seconds), then move to wait().
|
||||
while !transfer.is_done() && i < 10 {
|
||||
while !transfer.is_done() && n < 10 {
|
||||
delay.delay_ms(250u32);
|
||||
i += 1;
|
||||
n += 1;
|
||||
}
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
|
@ -82,12 +82,12 @@ fn main() -> ! {
|
||||
|
||||
let transfer = spi.dma_transfer(send, receive).unwrap();
|
||||
// here we could do something else while DMA transfer is in progress
|
||||
let mut i = 0;
|
||||
let mut n = 0;
|
||||
// Check is_done until the transfer is almost done (32000 bytes at 100kHz is
|
||||
// 2.56 seconds), then move to wait().
|
||||
while !transfer.is_done() && i < 10 {
|
||||
while !transfer.is_done() && n < 10 {
|
||||
delay.delay_ms(250u32);
|
||||
i += 1;
|
||||
n += 1;
|
||||
}
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
|
172
esp32s3-hal/examples/spi_slave_dma.rs
Normal file
172
esp32s3-hal/examples/spi_slave_dma.rs
Normal file
@ -0,0 +1,172 @@
|
||||
//! SPI slave loopback test using DMA
|
||||
//!
|
||||
//! Following pins are used for the slave:
|
||||
//! SCLK GPIO6
|
||||
//! MISO GPIO2
|
||||
//! MOSI GPIO7
|
||||
//! CS GPIO10
|
||||
//!
|
||||
//! Following pins are used for the (bitbang) master:
|
||||
//! SCLK GPIO5
|
||||
//! MISO GPIO1
|
||||
//! MOSI GPIO8
|
||||
//! CS GPIO9
|
||||
//!
|
||||
//! Depending on your target and the board you are using you have to change the
|
||||
//! pins.
|
||||
//!
|
||||
//! This example transfers data via SPI.
|
||||
//! Connect corresponding master and slave pins to see the outgoing data is read
|
||||
//! as incoming data. The master-side pins are chosen to make these connections
|
||||
//! easy for the barebones ESP32C3 chip; all are immediate neighbors of the
|
||||
//! slave-side pins except SCLK. SCLK is between MOSI and VDD3P3_RTC on the
|
||||
//! barebones ESP32C3, so no immediate neighbor is available.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use esp32s3_hal::{
|
||||
clock::ClockControl,
|
||||
dma::DmaPriority,
|
||||
gdma::Gdma,
|
||||
gpio::IO,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
spi_slave::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
};
|
||||
use esp_backtrace as _;
|
||||
use esp_println::println;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let system = peripherals.SYSTEM.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
// Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT,
|
||||
// the RTC WDT, and the TIMG WDTs.
|
||||
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;
|
||||
|
||||
rtc.swd.disable();
|
||||
rtc.rwdt.disable();
|
||||
wdt0.disable();
|
||||
wdt1.disable();
|
||||
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
let slave_sclk = io.pins.gpio6;
|
||||
let mut master_sclk = io.pins.gpio5.into_push_pull_output();
|
||||
let slave_miso = io.pins.gpio2;
|
||||
let master_miso = io.pins.gpio1.into_floating_input();
|
||||
let slave_mosi = io.pins.gpio7;
|
||||
let mut master_mosi = io.pins.gpio8.into_push_pull_output();
|
||||
let slave_cs = io.pins.gpio10;
|
||||
let mut master_cs = io.pins.gpio9.into_push_pull_output();
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
master_mosi.set_low().unwrap();
|
||||
|
||||
let dma = Gdma::new(peripherals.DMA);
|
||||
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,
|
||||
slave_sclk,
|
||||
slave_mosi,
|
||||
slave_miso,
|
||||
slave_cs,
|
||||
SpiMode::Mode0,
|
||||
)
|
||||
.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 master_send = &mut [0u8; 32000];
|
||||
let master_receive = &mut [0u8; 32000];
|
||||
let mut slave_send = buffer1();
|
||||
let mut slave_receive = buffer2();
|
||||
let mut i = 0;
|
||||
|
||||
for (i, v) in master_send.iter_mut().enumerate() {
|
||||
*v = (i % 255) as u8;
|
||||
}
|
||||
for (i, v) in slave_send.iter_mut().enumerate() {
|
||||
*v = (254 - (i % 255)) as u8;
|
||||
}
|
||||
|
||||
loop {
|
||||
master_send[0] = i;
|
||||
master_send[master_send.len() - 1] = i;
|
||||
slave_send[0] = i;
|
||||
slave_send[slave_send.len() - 1] = i;
|
||||
slave_receive.fill(0xff);
|
||||
i = i.wrapping_add(1);
|
||||
|
||||
let transfer = spi.dma_transfer(slave_send, 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().unwrap();
|
||||
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().unwrap();
|
||||
} else {
|
||||
master_mosi.set_low().unwrap();
|
||||
}
|
||||
master_sclk.set_low().unwrap();
|
||||
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().unwrap();
|
||||
if master_miso.is_high().unwrap() {
|
||||
rb |= 1;
|
||||
}
|
||||
}
|
||||
master_receive[j] = rb;
|
||||
}
|
||||
master_cs.set_high().unwrap();
|
||||
master_sclk.set_low().unwrap();
|
||||
// the buffers and spi is moved into the transfer and we can get it back via
|
||||
// `wait`
|
||||
(slave_receive, slave_send, spi) = transfer.wait().unwrap();
|
||||
println!(
|
||||
"slave got {:x?} .. {:x?}, master got {:x?} .. {:x?}",
|
||||
&slave_receive[..10],
|
||||
&slave_receive[slave_receive.len() - 10..],
|
||||
&master_receive[..10],
|
||||
&master_receive[master_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 }
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user