mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-30 13:50:38 +00:00
SPI: Implement more SPI traits from embedded-hal 1.0.0-alpha.8 (#101)
* common/spi: Turn fifo size into const instead of hard-coding it into the code in various places. * common/spi: Alias `write_bytes` to `send_bytes` since they share the same interface and the same code anyway. * common/spi: Implement `read_bytes` as counterpart to `send_bytes` that is responsible only for reading bytes received via SPI. * common/spi: Rewrite `transfer` to use `send_bytes` and `read_bytes` under the hood and remove duplicate code. * common/spi: Create submodule for embedded_hal_1 that is re-exported when the `eh1` feature flag is active. This removes lots of duplicate `#[cfg(...)]` macros previously part of the code. * common/spi: Implement `SpiBus` and `SpiBusWrite` traits from the `embedded-hal 1.0.0-alpha.8`. * common/spi: Make `mosi` pin optional * esp32-hal: Add new SPI example with `eh1` traits * esp32-hal/examples/spi_eh1: Add huge transfer and bump the SPI speed to 1 MHz. * common/spi: Apply rustfmt * common/spi: Use `memcpy` to read from registers This cuts down the time between consecutive transfers from about 2 ms to less than 1 ms. * WIP: common/spi: Use `ptr::copy` to fill write FIFO cutting down the time between transfers from just below 1 ms to ~370 us. The implementation is currently broken in that it will always fill the entire FIFO from the input it is given, even if that isn't FIFO-sized... * common/spi: Add more documentation * esp32/examples/spi_eh1: Fix `transfer_in_place` * esp32/examples/spi_eh1: Add conditional compile and compile a dummy instead when the "eh1" feature isn't present. * esp32-hal: Ignore spi_eh1 example in normal builds, where the feature flag "eh1" isn't given. Building the example directly via `cargo build --example spi_eh1_loopback` will now print an error that this requires a feature flag to be active. * common/spi: Use `write_bytes` and drop `send_bytes` instead. Previoulsy, both served the same purpose, but `send_bytes` was introduced more recently and is hence less likely to cause breaking changes in existing code. * common/spi: Fix mosi pin setup * Add SPI examples with ehal 1.0.0-alpha8 traits to all targets. * common/spi: Fix `read` behavior The previous `read` implementation would only read the contents of the SPI receive FIFO and return that as data. However, the `SpiBusRead` trait defines that while reading, bytes should be written out to the bus (Because SPI is transactional, without writing nothing can be read). Reimplements the `embedded-hal` traits to correctly implement this behavior. * common/spi: Use full FIFO size on all variants All esp variants except for the esp32s2 have a 64 byte FIFO, whereas the esp32s2 has a 72 byte FIFO. * common/spi: Use common pad byte for empty writes * common/spi: Fix reading bytes from FIFO by reverting to the old method of reading 32 bytes at a time and assembling the return buffer from that. It turns out that the previous `core::slice::from_raw_parts()` doesn't work for the esp32s2 and esp32s3 variants, returning bogus data even though the correct data is present in the registers. * common/spi: Fix typos * examples: Fix spi_eh1_loopback examples
This commit is contained in:
parent
6e037b08ca
commit
2fe27536aa
@ -22,18 +22,23 @@
|
||||
//! );
|
||||
//! ```
|
||||
|
||||
use core::convert::Infallible;
|
||||
|
||||
use fugit::HertzU32;
|
||||
|
||||
use crate::{
|
||||
clock::Clocks,
|
||||
pac::spi2::RegisterBlock,
|
||||
system::PeripheralClockControl,
|
||||
types::{InputSignal, OutputSignal},
|
||||
InputPin,
|
||||
OutputPin,
|
||||
InputPin, OutputPin,
|
||||
};
|
||||
use core::convert::Infallible;
|
||||
use fugit::HertzU32;
|
||||
|
||||
/// The size of the FIFO buffer for SPI
|
||||
#[cfg(not(feature = "esp32s2"))]
|
||||
const FIFO_SIZE: usize = 64;
|
||||
#[cfg(feature = "esp32s2")]
|
||||
const FIFO_SIZE: usize = 72;
|
||||
/// Padding byte for empty write transfers
|
||||
const EMPTY_WRITE_PAD: u8 = 0x00u8;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum SpiMode {
|
||||
@ -200,41 +205,129 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1")]
|
||||
impl<T> embedded_hal_1::spi::ErrorType for Spi<T> {
|
||||
type Error = Infallible;
|
||||
}
|
||||
pub use ehal1::*;
|
||||
|
||||
#[cfg(feature = "eh1")]
|
||||
impl<T> embedded_hal_1::spi::nb::FullDuplex for Spi<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
self.spi.read_byte()
|
||||
mod ehal1 {
|
||||
use super::*;
|
||||
use embedded_hal_1::spi::blocking::{SpiBus, SpiBusFlush, SpiBusRead, SpiBusWrite};
|
||||
use embedded_hal_1::spi::nb::FullDuplex;
|
||||
|
||||
|
||||
impl<T> embedded_hal_1::spi::ErrorType for Spi<T> {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
self.spi.write_byte(word)
|
||||
}
|
||||
}
|
||||
impl<T> FullDuplex for Spi<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
self.spi.read_byte()
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1")]
|
||||
impl<T> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
self.spi.send_bytes(words)
|
||||
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
|
||||
self.spi.write_byte(word)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "eh1")]
|
||||
impl<T> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.spi.flush()
|
||||
impl<T> SpiBusWrite for Spi<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
/// See also: [`write_bytes`].
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
self.spi.write_bytes(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SpiBusRead for Spi<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
/// See also: [`read_bytes`].
|
||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.spi.read_bytes(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SpiBus for Spi<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
/// Write out data from `write`, read response into `read`.
|
||||
///
|
||||
/// `read` and `write` are allowed to have different lengths. If `write` is longer, all
|
||||
/// other bytes received are discarded. If `read` is longer, [`EMPTY_WRITE_PAD`] is written
|
||||
/// out as necessary until enough bytes have been read. Reading and writing happens
|
||||
/// simultaneously.
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
// Optimizations
|
||||
if read.len() == 0 {
|
||||
SpiBusWrite::write(self, write)?;
|
||||
} else if write.len() == 0 {
|
||||
SpiBusRead::read(self, read)?;
|
||||
}
|
||||
|
||||
let mut write_from = 0;
|
||||
let mut read_from = 0;
|
||||
|
||||
loop {
|
||||
// How many bytes we write in this chunk
|
||||
let write_inc = core::cmp::min(FIFO_SIZE, write.len() - write_from);
|
||||
let write_to = write_from + write_inc;
|
||||
// How many bytes we read in this chunk
|
||||
let read_inc = core::cmp::min(FIFO_SIZE, read.len() - read_from);
|
||||
let read_to = read_from + read_inc;
|
||||
|
||||
if (write_inc == 0) && (read_inc == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if write_to < read_to {
|
||||
// Read more than we write, must pad writing part with zeros
|
||||
let mut empty = [EMPTY_WRITE_PAD; FIFO_SIZE];
|
||||
empty[0..write_inc].copy_from_slice(&write[write_from..write_to]);
|
||||
SpiBusWrite::write(self, &empty)?;
|
||||
} else {
|
||||
SpiBusWrite::write(self, &write[write_from..write_to])?;
|
||||
}
|
||||
|
||||
SpiBusFlush::flush(self)?;
|
||||
|
||||
if read_inc > 0 {
|
||||
self.spi.read_bytes_from_fifo(&mut read[read_from..read_to])?;
|
||||
}
|
||||
|
||||
write_from = write_to;
|
||||
read_from = read_to;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Transfer data in place.
|
||||
///
|
||||
/// Writes data from `words` out on the bus and stores the reply into `words`. A convenient
|
||||
/// wrapper around [`write`](SpiBusWrite::write), [`flush`](SpiBusFlush::flush) and
|
||||
/// [`read`](SpiBusRead::read).
|
||||
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
// Since we have the traits so neatly implemented above, use them!
|
||||
for chunk in words.chunks_mut(FIFO_SIZE) {
|
||||
SpiBusWrite::write(self, chunk)?;
|
||||
SpiBusFlush::flush(self)?;
|
||||
self.spi.read_bytes_from_fifo(chunk)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SpiBusFlush for Spi<T>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.spi.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,12 +401,12 @@ pub trait Instance {
|
||||
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.
|
||||
*/
|
||||
* 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 mut bestn: i32 = -1;
|
||||
@ -322,15 +415,16 @@ pub trait Instance {
|
||||
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.
|
||||
*/
|
||||
* one high and one low pulse.
|
||||
*/
|
||||
|
||||
for n in 2..64 {
|
||||
for n in 2..64 {
|
||||
/* Effectively, this does:
|
||||
* pre = round((APB_CLK_FREQ / n) / frequency)
|
||||
*/
|
||||
* pre = round((APB_CLK_FREQ / n) / frequency)
|
||||
*/
|
||||
|
||||
pre = ((apb_clk_freq.raw() as i32/ n) + (frequency.raw() as i32 / 2)) / frequency.raw() as i32;
|
||||
pre = ((apb_clk_freq.raw() as i32 / n) + (frequency.raw() as i32 / 2))
|
||||
/ frequency.raw() as i32;
|
||||
|
||||
if pre <= 0 {
|
||||
pre = 1;
|
||||
@ -340,7 +434,9 @@ pub trait Instance {
|
||||
pre = 16;
|
||||
}
|
||||
|
||||
errval = (apb_clk_freq.raw() as i32 / (pre as i32 * n as i32) - frequency.raw() as i32).abs();
|
||||
errval = (apb_clk_freq.raw() as i32 / (pre as i32 * n as i32)
|
||||
- frequency.raw() as i32)
|
||||
.abs();
|
||||
if bestn == -1 || errval <= besterr {
|
||||
besterr = errval;
|
||||
bestn = n as i32;
|
||||
@ -353,18 +449,18 @@ pub trait Instance {
|
||||
let l: i32 = n;
|
||||
|
||||
/* Effectively, this does:
|
||||
* h = round((duty_cycle * n) / 256)
|
||||
*/
|
||||
* h = round((duty_cycle * n) / 256)
|
||||
*/
|
||||
|
||||
let mut h: i32 = (duty_cycle * n + 127) / 256;
|
||||
if h <= 0 {
|
||||
h = 1;
|
||||
}
|
||||
|
||||
reg_val = (l as u32 - 1) |
|
||||
((h as u32 - 1) << 6) |
|
||||
((n as u32 - 1) << 12) |
|
||||
((pre as u32 - 1) << 18);
|
||||
reg_val = (l as u32 - 1)
|
||||
| ((h as u32 - 1) << 6)
|
||||
| ((n as u32 - 1) << 12)
|
||||
| ((pre as u32 - 1) << 18);
|
||||
}
|
||||
|
||||
self.register_block()
|
||||
@ -450,37 +546,33 @@ pub trait Instance {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write bytes to SPI.
|
||||
///
|
||||
/// Copies the content of `words` in chunks of 64 bytes into the SPI transmission FIFO. If
|
||||
/// `words` is longer than 64 bytes, multiple sequential transfers are performed. This function
|
||||
/// will return before all bytes of the last chunk to transmit have been sent to the wire. If
|
||||
/// you must ensure that the whole messages was written correctly, use
|
||||
/// [`flush`].
|
||||
// FIXME: See below.
|
||||
fn write_bytes(&mut self, words: &[u8]) -> Result<(), Infallible> {
|
||||
let mut words_mut = [0u8; 256];
|
||||
words_mut[0..words.len()].clone_from_slice(&words[0..words.len()]);
|
||||
|
||||
self.transfer(&mut words_mut[0..words.len()])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_bytes(&mut self, words: &[u8]) -> Result<(), Infallible> {
|
||||
let reg_block = self.register_block();
|
||||
let num_chuncks = words.len() / 64;
|
||||
let num_chunks = words.len() / FIFO_SIZE;
|
||||
|
||||
// The fifo has a total of 16 32 bit registers (64 bytes) so the data
|
||||
// must be chunked and then transmitted
|
||||
for (i, chunk) in words.chunks(64).enumerate() {
|
||||
// The fifo has a limited fixed size, so the data must be chunked and then transmitted
|
||||
for (i, chunk) in words.chunks(FIFO_SIZE).enumerate() {
|
||||
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];
|
||||
unsafe {
|
||||
let ptr = u32_as_bytes.as_mut_ptr();
|
||||
ptr.copy_from(chunk.as_ptr(), chunk.len());
|
||||
}
|
||||
let reg_val: u32 = u32::from_le_bytes(u32_as_bytes);
|
||||
|
||||
unsafe {
|
||||
*fifo_ptr = reg_val;
|
||||
fifo_ptr = fifo_ptr.offset(1);
|
||||
};
|
||||
let fifo_ptr = reg_block.w0.as_ptr();
|
||||
unsafe {
|
||||
// It seems that `copy_nonoverlapping` is significantly faster than regular `copy`,
|
||||
// by about 20%... ?
|
||||
core::ptr::copy_nonoverlapping::<u32>(
|
||||
chunk.as_ptr() as *const u32,
|
||||
fifo_ptr as *mut u32,
|
||||
// FIXME: Using any other transfer length **does not work**. I don't understand
|
||||
// why.
|
||||
FIFO_SIZE / 4,
|
||||
);
|
||||
}
|
||||
|
||||
self.update();
|
||||
@ -490,7 +582,7 @@ pub trait Instance {
|
||||
// Wait for all chunks to complete except the last one.
|
||||
// The function is allowed to return before the bus is idle.
|
||||
// see [embedded-hal flushing](https://docs.rs/embedded-hal/1.0.0-alpha.8/embedded_hal/spi/blocking/index.html#flushing)
|
||||
if i < num_chuncks {
|
||||
if i < num_chunks {
|
||||
while reg_block.cmd.read().usr().bit_is_set() {
|
||||
// wait
|
||||
}
|
||||
@ -499,6 +591,54 @@ pub trait Instance {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read bytes from SPI.
|
||||
///
|
||||
/// Sends out a stuffing byte for every byte to read. This function doesn't perform flushing.
|
||||
/// If you want to read the response to something you have written before, consider using
|
||||
/// [`transfer`] instead.
|
||||
fn read_bytes(&mut self, words: &mut [u8]) -> Result<(), Infallible> {
|
||||
let empty_array = [EMPTY_WRITE_PAD; FIFO_SIZE];
|
||||
|
||||
for chunk in words.chunks_mut(FIFO_SIZE) {
|
||||
self.write_bytes(&empty_array[0..chunk.len()])?;
|
||||
self.flush()?;
|
||||
self.read_bytes_from_fifo(chunk)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Read received bytes from SPI FIFO.
|
||||
///
|
||||
/// Copies the contents of the SPI receive FIFO into `words`. This function doesn't perform
|
||||
/// flushing. If you want to read the response to something you have written before, consider
|
||||
/// using [`transfer`] instead.
|
||||
// FIXME: Using something like `core::slice::from_raw_parts` and `copy_from_slice` on the
|
||||
// receive registers works only for the esp32 and esp32c3 varaints. The reason for this is
|
||||
// unknown.
|
||||
fn read_bytes_from_fifo(&mut self, words: &mut [u8]) -> Result<(), Infallible> {
|
||||
let reg_block = self.register_block();
|
||||
|
||||
for chunk in words.chunks_mut(FIFO_SIZE) {
|
||||
self.configure_datalen(chunk.len() as u32 * 8);
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
// Check if the bus is busy and if it is wait for it to be idle
|
||||
fn flush(&mut self) -> Result<(), Infallible> {
|
||||
let reg_block = self.register_block();
|
||||
@ -510,43 +650,10 @@ pub trait Instance {
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
for chunk in words.chunks_mut(FIFO_SIZE) {
|
||||
self.write_bytes(chunk)?;
|
||||
self.flush()?;
|
||||
self.read_bytes_from_fifo(chunk)?;
|
||||
}
|
||||
|
||||
Ok(words)
|
||||
|
@ -53,4 +53,8 @@ vectored = ["esp-hal-common/vectored"]
|
||||
|
||||
[[example]]
|
||||
name = "hello_rgb"
|
||||
required-features = ["smartled"]
|
||||
required-features = ["smartled"]
|
||||
|
||||
[[example]]
|
||||
name = "spi_eh1_loopback"
|
||||
required-features = ["eh1"]
|
||||
|
127
esp32-hal/examples/spi_eh1_loopback.rs
Normal file
127
esp32-hal/examples/spi_eh1_loopback.rs
Normal file
@ -0,0 +1,127 @@
|
||||
//! 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::{
|
||||
clock::ClockControl,
|
||||
gpio::IO,
|
||||
pac::Peripherals,
|
||||
prelude::*,
|
||||
spi::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
Serial,
|
||||
};
|
||||
use panic_halt as _;
|
||||
use xtensa_lx_rt::entry;
|
||||
|
||||
use embedded_hal_1::spi::blocking::SpiBus;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take().unwrap();
|
||||
let mut system = peripherals.DPORT.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 wdt = timer_group0.wdt;
|
||||
let mut serial0 = Serial::new(peripherals.UART0);
|
||||
|
||||
wdt.disable();
|
||||
rtc.rwdt.disable();
|
||||
|
||||
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 = Spi::new(
|
||||
peripherals.SPI2,
|
||||
sclk,
|
||||
mosi,
|
||||
miso,
|
||||
cs,
|
||||
1000u32.kHz(),
|
||||
SpiMode::Mode0,
|
||||
&mut system.peripheral_clock_control,
|
||||
&clocks,
|
||||
);
|
||||
|
||||
let mut delay = Delay::new(&clocks);
|
||||
writeln!(serial0, "=== SPI example with embedded-hal-1 traits ===").unwrap();
|
||||
|
||||
loop {
|
||||
// --- Symmetric transfer (Read as much as we write) ---
|
||||
write!(serial0, "Starting symmetric transfer...").unwrap();
|
||||
let write = [0xde, 0xad, 0xbe, 0xef];
|
||||
let mut read: [u8; 4] = [0x00u8; 4];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Symmetric transfer failed");
|
||||
assert_eq!(write, read);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Asymmetric transfer (Read more than we write) ---
|
||||
write!(serial0, "Starting asymetric transfer (read > write)...").unwrap();
|
||||
let mut read: [u8; 4] = [0x00; 4];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[0..2], &write[..]).expect("Asymmetric transfer failed");
|
||||
assert_eq!(write[0], read[0]);
|
||||
assert_eq!(read[2], 0x00u8);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Symmetric transfer with huge buffer ---
|
||||
// Only your RAM is the limit!
|
||||
write!(serial0, "Starting huge transfer...").unwrap();
|
||||
let mut write = [0x55u8; 4096];
|
||||
for byte in 0..write.len() {
|
||||
write[byte] = byte as u8;
|
||||
}
|
||||
let mut read = [0x00u8; 4096];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Huge transfer failed");
|
||||
assert_eq!(write, read);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Symmetric transfer with huge buffer in-place (No additional allocation needed) ---
|
||||
write!(serial0, "Starting huge transfer (in-place)...").unwrap();
|
||||
let mut write = [0x55u8; 4096];
|
||||
for byte in 0..write.len() {
|
||||
write[byte] = byte as u8;
|
||||
}
|
||||
|
||||
SpiBus::transfer_in_place(&mut spi, &mut write[..]).expect("Huge transfer failed");
|
||||
for byte in 0..write.len() {
|
||||
assert_eq!(write[byte], byte as u8);
|
||||
}
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
}
|
||||
}
|
||||
|
@ -54,3 +54,7 @@ vectored = ["esp-hal-common/vectored"]
|
||||
[[example]]
|
||||
name = "hello_rgb"
|
||||
required-features = ["smartled"]
|
||||
|
||||
[[example]]
|
||||
name = "spi_eh1_loopback"
|
||||
required-features = ["eh1"]
|
||||
|
132
esp32c3-hal/examples/spi_eh1_loopback.rs
Normal file
132
esp32c3-hal/examples/spi_eh1_loopback.rs
Normal file
@ -0,0 +1,132 @@
|
||||
//! 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::{
|
||||
clock::ClockControl,
|
||||
gpio::IO,
|
||||
pac::Peripherals,
|
||||
prelude::*,
|
||||
spi::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
Serial,
|
||||
};
|
||||
use panic_halt as _;
|
||||
use riscv_rt::entry;
|
||||
|
||||
use embedded_hal_1::spi::blocking::SpiBus;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take().unwrap();
|
||||
let mut 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;
|
||||
|
||||
let mut serial0 = Serial::new(peripherals.UART0);
|
||||
|
||||
rtc.swd.disable();
|
||||
rtc.rwdt.disable();
|
||||
wdt0.disable();
|
||||
wdt1.disable();
|
||||
|
||||
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 = Spi::new(
|
||||
peripherals.SPI2,
|
||||
sclk,
|
||||
mosi,
|
||||
miso,
|
||||
cs,
|
||||
1000u32.kHz(),
|
||||
SpiMode::Mode0,
|
||||
&mut system.peripheral_clock_control,
|
||||
&clocks,
|
||||
);
|
||||
|
||||
let mut delay = Delay::new(&clocks);
|
||||
writeln!(serial0, "=== SPI example with embedded-hal-1 traits ===").unwrap();
|
||||
|
||||
loop {
|
||||
// --- Symmetric transfer (Read as much as we write) ---
|
||||
write!(serial0, "Starting symmetric transfer...").unwrap();
|
||||
let write = [0xde, 0xad, 0xbe, 0xef];
|
||||
let mut read: [u8; 4] = [0x00u8; 4];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Symmetric transfer failed");
|
||||
assert_eq!(write, read);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Asymmetric transfer (Read more than we write) ---
|
||||
write!(serial0, "Starting asymetric transfer (read > write)...").unwrap();
|
||||
let mut read: [u8; 4] = [0x00; 4];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[0..2], &write[..]).expect("Asymmetric transfer failed");
|
||||
assert_eq!(write[0], read[0]);
|
||||
assert_eq!(read[2], 0x00u8);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Symmetric transfer with huge buffer ---
|
||||
// Only your RAM is the limit!
|
||||
write!(serial0, "Starting huge transfer...").unwrap();
|
||||
let mut write = [0x55u8; 4096];
|
||||
for byte in 0..write.len() {
|
||||
write[byte] = byte as u8;
|
||||
}
|
||||
let mut read = [0x00u8; 4096];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Huge transfer failed");
|
||||
assert_eq!(write, read);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Symmetric transfer with huge buffer in-place (No additional allocation needed) ---
|
||||
write!(serial0, "Starting huge transfer (in-place)...").unwrap();
|
||||
let mut write = [0x55u8; 4096];
|
||||
for byte in 0..write.len() {
|
||||
write[byte] = byte as u8;
|
||||
}
|
||||
|
||||
SpiBus::transfer_in_place(&mut spi, &mut write[..]).expect("Huge transfer failed");
|
||||
for byte in 0..write.len() {
|
||||
assert_eq!(write[byte], byte as u8);
|
||||
}
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
}
|
||||
}
|
||||
|
@ -52,3 +52,7 @@ vectored = ["esp-hal-common/vectored"]
|
||||
[[example]]
|
||||
name = "hello_rgb"
|
||||
required-features = ["smartled"]
|
||||
|
||||
[[example]]
|
||||
name = "spi_eh1_loopback"
|
||||
required-features = ["eh1"]
|
||||
|
127
esp32s2-hal/examples/spi_eh1_loopback.rs
Normal file
127
esp32s2-hal/examples/spi_eh1_loopback.rs
Normal file
@ -0,0 +1,127 @@
|
||||
//! SPI loopback test
|
||||
//!
|
||||
//! Folowing pins are used:
|
||||
//! SCLK GPIO36
|
||||
//! MISO GPIO37
|
||||
//! MOSI GPIO35
|
||||
//! 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::{
|
||||
clock::ClockControl,
|
||||
gpio::IO,
|
||||
pac::Peripherals,
|
||||
prelude::*,
|
||||
spi::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
Serial,
|
||||
};
|
||||
use panic_halt as _;
|
||||
use xtensa_lx_rt::entry;
|
||||
|
||||
use embedded_hal_1::spi::blocking::SpiBus;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take().unwrap();
|
||||
let mut 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 wdt = timer_group0.wdt;
|
||||
let mut serial0 = Serial::new(peripherals.UART0);
|
||||
|
||||
wdt.disable();
|
||||
rtc.rwdt.disable();
|
||||
|
||||
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 = Spi::new(
|
||||
peripherals.SPI2,
|
||||
sclk,
|
||||
mosi,
|
||||
miso,
|
||||
cs,
|
||||
1000u32.kHz(),
|
||||
SpiMode::Mode0,
|
||||
&mut system.peripheral_clock_control,
|
||||
&clocks,
|
||||
);
|
||||
|
||||
let mut delay = Delay::new(&clocks);
|
||||
writeln!(serial0, "=== SPI example with embedded-hal-1 traits ===").unwrap();
|
||||
|
||||
loop {
|
||||
// --- Symmetric transfer (Read as much as we write) ---
|
||||
write!(serial0, "Starting symmetric transfer...").unwrap();
|
||||
let write = [0xde, 0xad, 0xbe, 0xef];
|
||||
let mut read: [u8; 4] = [0x00u8; 4];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Symmetric transfer failed");
|
||||
assert_eq!(write, read);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Asymmetric transfer (Read more than we write) ---
|
||||
write!(serial0, "Starting asymetric transfer (read > write)...").unwrap();
|
||||
let mut read: [u8; 4] = [0x00; 4];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[0..2], &write[..]).expect("Asymmetric transfer failed");
|
||||
assert_eq!(write[0], read[0]);
|
||||
assert_eq!(read[2], 0x00u8);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Symmetric transfer with huge buffer ---
|
||||
// Only your RAM is the limit!
|
||||
write!(serial0, "Starting huge transfer...").unwrap();
|
||||
let mut write = [0x55u8; 4096];
|
||||
for byte in 0..write.len() {
|
||||
write[byte] = byte as u8;
|
||||
}
|
||||
let mut read = [0x00u8; 4096];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Huge transfer failed");
|
||||
assert_eq!(write, read);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Symmetric transfer with huge buffer in-place (No additional allocation needed) ---
|
||||
write!(serial0, "Starting huge transfer (in-place)...").unwrap();
|
||||
let mut write = [0x55u8; 4096];
|
||||
for byte in 0..write.len() {
|
||||
write[byte] = byte as u8;
|
||||
}
|
||||
|
||||
SpiBus::transfer_in_place(&mut spi, &mut write[..]).expect("Huge transfer failed");
|
||||
for byte in 0..write.len() {
|
||||
assert_eq!(write[byte], byte as u8);
|
||||
}
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
}
|
||||
}
|
||||
|
@ -54,3 +54,7 @@ vectored = ["esp-hal-common/vectored"]
|
||||
[[example]]
|
||||
name = "hello_rgb"
|
||||
required-features = ["smartled"]
|
||||
|
||||
[[example]]
|
||||
name = "spi_eh1_loopback"
|
||||
required-features = ["eh1"]
|
||||
|
127
esp32s3-hal/examples/spi_eh1_loopback.rs
Normal file
127
esp32s3-hal/examples/spi_eh1_loopback.rs
Normal file
@ -0,0 +1,127 @@
|
||||
//! 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::{
|
||||
clock::ClockControl,
|
||||
gpio::IO,
|
||||
pac::Peripherals,
|
||||
prelude::*,
|
||||
spi::{Spi, SpiMode},
|
||||
timer::TimerGroup,
|
||||
Delay,
|
||||
Rtc,
|
||||
Serial,
|
||||
};
|
||||
use panic_halt as _;
|
||||
use xtensa_lx_rt::entry;
|
||||
|
||||
use embedded_hal_1::spi::blocking::SpiBus;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take().unwrap();
|
||||
let mut 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 wdt = timer_group0.wdt;
|
||||
let mut serial0 = Serial::new(peripherals.UART0);
|
||||
|
||||
wdt.disable();
|
||||
rtc.rwdt.disable();
|
||||
|
||||
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 = Spi::new(
|
||||
peripherals.SPI2,
|
||||
sclk,
|
||||
mosi,
|
||||
miso,
|
||||
cs,
|
||||
1000u32.kHz(),
|
||||
SpiMode::Mode0,
|
||||
&mut system.peripheral_clock_control,
|
||||
&clocks,
|
||||
);
|
||||
|
||||
let mut delay = Delay::new(&clocks);
|
||||
writeln!(serial0, "=== SPI example with embedded-hal-1 traits ===").unwrap();
|
||||
|
||||
loop {
|
||||
// --- Symmetric transfer (Read as much as we write) ---
|
||||
write!(serial0, "Starting symmetric transfer...").unwrap();
|
||||
let write = [0xde, 0xad, 0xbe, 0xef];
|
||||
let mut read: [u8; 4] = [0x00u8; 4];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Symmetric transfer failed");
|
||||
assert_eq!(write, read);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Asymmetric transfer (Read more than we write) ---
|
||||
write!(serial0, "Starting asymetric transfer (read > write)...").unwrap();
|
||||
let mut read: [u8; 4] = [0x00; 4];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[0..2], &write[..]).expect("Asymmetric transfer failed");
|
||||
assert_eq!(write[0], read[0]);
|
||||
assert_eq!(read[2], 0x00u8);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Symmetric transfer with huge buffer ---
|
||||
// Only your RAM is the limit!
|
||||
write!(serial0, "Starting huge transfer...").unwrap();
|
||||
let mut write = [0x55u8; 4096];
|
||||
for byte in 0..write.len() {
|
||||
write[byte] = byte as u8;
|
||||
}
|
||||
let mut read = [0x00u8; 4096];
|
||||
|
||||
SpiBus::transfer(&mut spi, &mut read[..], &write[..]).expect("Huge transfer failed");
|
||||
assert_eq!(write, read);
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
|
||||
|
||||
// --- Symmetric transfer with huge buffer in-place (No additional allocation needed) ---
|
||||
write!(serial0, "Starting huge transfer (in-place)...").unwrap();
|
||||
let mut write = [0x55u8; 4096];
|
||||
for byte in 0..write.len() {
|
||||
write[byte] = byte as u8;
|
||||
}
|
||||
|
||||
SpiBus::transfer_in_place(&mut spi, &mut write[..]).expect("Huge transfer failed");
|
||||
for byte in 0..write.len() {
|
||||
assert_eq!(write[byte], byte as u8);
|
||||
}
|
||||
writeln!(serial0, " SUCCESS").unwrap();
|
||||
delay.delay_ms(250u32);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user