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:
parent
60d39e9f33
commit
76ed67ad35
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user