RFC: Multi CS Spi Implementation (#150)

* initial draft

* working alternative without using i32 id's

* EspSpiDevice + SpiDriverMaster / no global statics

* start splitting implementation in spi & spi_pool

* impl SpiDevice for EspSpiDevice

* spi2_pool draft (spi with software based cs)

* pre master fetch up

* Driver accepts Optional DMA channel + fetch master

* replaced: std with core, mutex with hal's cs

* replaced HashMap with Array

* merged spi2 into spi

* clean up

* replace std with core

* updated loopback example,  spi takes dma directly

* moved criticalsection guard out of spi to spi_pool

* added paragraph to the release.md

* rm fn to add / rm device from bus or update config

* rm stored config from EspSpiDevice

* fmt'd

* grammer + changelog update

* implements inverted chipselect for spi.rs #119

* device with optional cs + guarded soft_cs wrapper

* fmt'd

* add pre / post cs delay functionality for wrapper

* no bus.finish for dev with no cs. partial fix #99

* spi driver renaming

* updated naming + locking with &mut SpiDeviceDriver

* updated naming + locking with &mut SpiDeviceDriver

* clean up

* updated CHANGELOG
This commit is contained in:
Frederick Vollbrecht 2022-11-12 07:54:16 +01:00 committed by GitHub
parent 7220534544
commit 13ba61fb0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 398 additions and 118 deletions

View File

@ -22,6 +22,7 @@ The main themes of the 0.39 release are:
* Support for the `embassy-sync` crate by providing two types of raw mutexes
* Support for the `edge-executor` crate
* Modem and Mac peripherals
* SPI Driver rework
### Major changes elaboration
@ -43,7 +44,7 @@ The main themes of the 0.39 release are:
* Each driver is named `<Peripheral>Driver`, where `<Peripheral>` is the name of the peripheral. E.g. the driver for the `Uart` peripheral is named `UartDriver`, the driver for the `Adc` peripheral is named `AdcDriver` and so on
* The one exception is the GPIO subsystem, where the driver is not named `GpioDriver`, but the simpler `PinDriver` instead, even if the concrete peripheral struct type might be e.g. `Gpio12`
* In case there are multiple drivers per peripheral - as in - say - a master and a slave protocol driver - the driver name contains the `Master` / `Slave` word in its name, as in e.g. `SpiMasterDriver` and `SpiSlaveDriver`
* In case there are multiple drivers per peripheral - as in - say - a master and a slave protocol driver - only the `Slave` contains itself in its name, as in e.g. `SpiDriver` and `SpiSlaveDriver`
* NOTE: We are NOT fond of still using offensive terms like "master" and "slave". Unfortunately, the embedded industry itself has not yet agreed (to our knowledge) on a good replacement for these terms, hence they are still around
### Public API
@ -123,3 +124,15 @@ This is now addressed in that the `esp-idf-hal` crate models two new peripherals
* By implementing both of the above traits, the `modem::Modem` peripheral should be usable by both the Wifi driver in `esp-idf-svc`, as well as the future Bluetooth driver
* `modem::Modem` is splittable into two other peripherals: `modem::WifiModem` (implementing only the `modem::WifiModemPeripheral` trait) and `modem::BluetoothModem` (implementing only the `modem::BluetoothModemPeripheral` trait) so that in future the simultaneous operation of the Wifi and Bluetooth drivers to be possible
* `mac::MAC`: models the EMAC hardware available on the original ESP32 chip. The Ethernet driver implementation - just like the Wifi driver implementation - is still in the `esp-idf-svc` crate which better aligns with the underlying ESP-IDF implementations, yet the new Wifi and Ethernet drivers in `esp-idf-svc` now expect their corresponding peripherals to be supplied during construction time
### SPI Driver rework
The SpiDriver is now split into two parts
* The `SpiDriver` manages access to the underlying SPI hardware
* The new `SpiDeviceDriver` is an abstraction for the "connected Devices" on the given SPI hardware.
This allows for the creation of more than one Devices per SPI hardware. (Up to 6 for the esp32c* variants and 3 for all others).
Creation and Deletion of the SpiDeviceDrivers is independent from SpiDriver. To allow for a more flexible usage an SpiDeviceDrivers can get reference to the SpiDriver by `T`, `&T`, `&mut T`, `Rc(T)`, `Arc(T)` where `T` is SpiDriver.
`SpiDeviceDriver` implements the SpiDevice from `embedded-hal`
A second wrapper implementation is now also provided: `SpiSoftCsDeviceDriver` It allows for more concurrent SpiDevices per SPI Hardware (No hardware limit of 3 / 6). To use it one wrapps an instance of an `SpiDeviceDriver` into an `SpiSharedDeviceDriver`. This shared driver can now be used in an arbitrary number of `SpiSoftCsDeviceDriver`. To easily change an configuration of an `SpiSoftCsDeviceDriver` simply point it to another `SpiSharedDeviceDriver`.

View File

@ -4,7 +4,8 @@
//! SCLK GPIO6
//! SDI GPIO2
//! SDO GPIO7
//! CS GPIO10
//! CS_1 GPIO10
//! CS_2 GPIO3
//!
//! Depending on your target and the board you are using you have to change the pins.
//!
@ -27,20 +28,41 @@ fn main() -> anyhow::Result<()> {
let sclk = peripherals.pins.gpio6;
let serial_in = peripherals.pins.gpio2; // SDI
let serial_out = peripherals.pins.gpio7; // SDO
let cs = peripherals.pins.gpio10;
let cs_1 = peripherals.pins.gpio10;
let cs_2 = peripherals.pins.gpio3;
println!("Starting SPI loopback test");
let config = config::Config::new().baudrate(26.MHz().into());
let mut spi =
SpiMasterDriver::new::<SPI2>(spi, sclk, serial_out, Some(serial_in), Some(cs), &config)?;
let mut spi = SpiDriver::new::<SPI2>(spi, sclk, serial_out, Some(serial_in), Dma::Disabled)?;
let config_1 = config::Config::new().baudrate(26.MHz().into());
let mut device_1 = SpiDeviceDriver::new(&spi, cs_1, &config_2)?;
let config_2 = config::Config::new().baudrate(13.MHz().into());
let mut device_2 = SpiDeviceDriver::new(&spi, cs_2, &config_2)?;
let mut read = [0u8; 4];
let write = [0xde, 0xad, 0xbe, 0xef];
let mut in_place_buf = [0xde, 0xad, 0xbe, 0xef];
loop {
// we are using thread::sleep here to make sure the watchdog isn't triggered
FreeRtos::delay_ms(500);
spi.transfer(&mut read, &write)?;
println!("Wrote {:x?}, read {:x?}", write, read);
device_1.transfer(&mut read, &write)?;
println!(
"Device {:?} : Wrote {:x?}, read {:x?}",
device_1.cs_gpio_number(),
write,
read
);
println!(
"Device {:?} : To Write {:x?} ... ",
device_2.cs_gpio_number(),
in_place_buf
);
device_2.transaction(|bus| bus.transfer_in_place(&mut in_place_buf));
println!("... read {:x?}", in_place_buf);
}
}

View File

@ -1,13 +1,25 @@
//! SPI peripheral control
//!
//! Currently only implements full duplex controller mode support.
//!
//! SPI0 is reserved for accessing flash and sram and therefore not usable for other purposes.
//! SPI1 shares its external pins with SPI0 and therefore has severe restrictions in use.
//!
//! SPI2 & 3 can be used freely.
//!
//! The CS pin is controlled by hardware on esp32 (contrary to the description of embedded_hal).
//! The CS pin can be controlled by hardware on esp32 variants (contrary to the description of embedded_hal).
//!
//! Look at the following table to determine which driver best suits your requirements:
//!
//! | | | SpiDeviceDriver::new | SpiDeviceDriver::new_no_cs | SpiSoftCsDeviceDriver::new | |
//! |---|------------------|----------------------|----------------------------|----------------------------|---|
//! | | managed cs | hardware | - | software triggerd | |
//! | | 1 device | x | x | x | |
//! | | 1-3 devices | x | - | x | |
//! | | 4-6 devices | only on esp32c* | - | x | |
//! | | 6 - infinit | - | - | x | |
//! | | Dma | - | - | - | |
//! | | polling transmit | x | x | x | |
//! | | isr transmit | - | - | - | |
//! | | async ready | - | - | - | |
//!
//! The [Transfer::transfer], [Write::write] and [WriteIter::write_iter] functions lock the
//! APB frequency and therefore the requests are always run at the requested baudrate.
@ -17,20 +29,21 @@
//! # TODO
//! - Quad SPI
//! - DMA
//! - Multiple CS pins
//! - Slave
use core::cell::UnsafeCell;
use core::cmp::{max, min, Ordering};
use core::marker::PhantomData;
use core::ptr;
use core::borrow::Borrow;
use embedded_hal::spi::{SpiBus, SpiBusFlush, SpiBusRead, SpiBusWrite, SpiDevice};
use esp_idf_sys::*;
use crate::delay::BLOCK;
use crate::gpio::{self, InputPin, OutputPin};
use crate::peripheral::Peripheral;
use crate::gpio::{self, AnyOutputPin, InputPin, OutputPin, Pin, PinDriver};
use crate::peripheral::{Peripheral, PeripheralRef};
crate::embedded_hal_error!(
SpiError,
@ -85,7 +98,6 @@ pub type SpiMasterConfig = config::Config;
/// SPI configuration
pub mod config {
use crate::spi::Dma;
use crate::units::*;
use esp_idf_sys::*;
@ -142,7 +154,7 @@ pub mod config {
}
}
/// SPI configuration
/// SPI Device configuration
#[derive(Copy, Clone)]
pub struct Config {
pub baudrate: Hertz,
@ -152,8 +164,8 @@ pub mod config {
/// it will unlock the possibility of using 80Mhz as the bus freq
/// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html#timing-considerations
pub write_only: bool,
pub dma: Dma,
pub duplex: Duplex,
pub cs_active_high: bool,
}
impl Config {
@ -178,13 +190,13 @@ pub mod config {
self
}
pub fn dma(mut self, dma: Dma) -> Self {
self.dma = dma;
pub fn duplex(mut self, duplex: Duplex) -> Self {
self.duplex = duplex;
self
}
pub fn duplex(mut self, duplex: Duplex) -> Self {
self.duplex = duplex;
pub fn cs_active_high(mut self) -> Self {
self.cs_active_high = true;
self
}
}
@ -195,20 +207,21 @@ pub mod config {
baudrate: Hertz(1_000_000),
data_mode: embedded_hal::spi::MODE_0,
write_only: false,
dma: Dma::Disabled,
cs_active_high: false,
duplex: Duplex::Full,
}
}
}
}
pub struct SpiBusMasterDriver<'d> {
pub struct SpiBusDriver<'d> {
handle: spi_device_handle_t,
trans_len: usize,
hardware_cs: bool,
_p: PhantomData<&'d mut ()>,
}
impl<'d> SpiBusMasterDriver<'d> {
impl<'d> SpiBusDriver<'d> {
pub fn read(&mut self, words: &mut [u8]) -> Result<(), EspError> {
for chunk in words.chunks_mut(self.trans_len) {
self.polling_transmit(chunk.as_mut_ptr(), ptr::null(), chunk.len(), chunk.len())?;
@ -284,7 +297,7 @@ impl<'d> SpiBusMasterDriver<'d> {
write,
transaction_length,
rx_length,
true,
self.hardware_cs,
)
}
@ -294,86 +307,93 @@ impl<'d> SpiBusMasterDriver<'d> {
}
}
impl<'d> embedded_hal::spi::ErrorType for SpiBusMasterDriver<'d> {
impl<'d> embedded_hal::spi::ErrorType for SpiBusDriver<'d> {
type Error = SpiError;
}
impl<'d> SpiBusFlush for SpiBusMasterDriver<'d> {
impl<'d> SpiBusFlush for SpiBusDriver<'d> {
fn flush(&mut self) -> Result<(), Self::Error> {
SpiBusMasterDriver::flush(self).map_err(to_spi_err)
SpiBusDriver::flush(self).map_err(to_spi_err)
}
}
impl<'d> SpiBusRead for SpiBusMasterDriver<'d> {
impl<'d> SpiBusRead for SpiBusDriver<'d> {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
SpiBusMasterDriver::read(self, words).map_err(to_spi_err)
SpiBusDriver::read(self, words).map_err(to_spi_err)
}
}
impl<'d> SpiBusWrite for SpiBusMasterDriver<'d> {
impl<'d> SpiBusWrite for SpiBusDriver<'d> {
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
SpiBusMasterDriver::write(self, words).map_err(to_spi_err)
SpiBusDriver::write(self, words).map_err(to_spi_err)
}
}
impl<'d> SpiBus for SpiBusMasterDriver<'d> {
impl<'d> SpiBus for SpiBusDriver<'d> {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
SpiBusMasterDriver::transfer(self, read, write).map_err(to_spi_err)
SpiBusDriver::transfer(self, read, write).map_err(to_spi_err)
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
SpiBusMasterDriver::transfer_in_place(self, words).map_err(to_spi_err)
SpiBusDriver::transfer_in_place(self, words).map_err(to_spi_err)
}
}
/// Master SPI abstraction
pub struct SpiMasterDriver<'d> {
pub struct SpiDriver<'d> {
host: u8,
device: spi_device_handle_t,
max_transfer_size: usize,
_p: PhantomData<&'d mut ()>,
}
impl<'d> SpiMasterDriver<'d> {
impl<'d> SpiDriver<'d> {
/// Create new instance of SPI controller for SPI1
///
/// SPI1 can only use fixed pin for SCLK, SDO and SDI as they are shared with SPI0.
pub fn new_spi1(
spi: impl Peripheral<P = SPI1> + 'd,
pub fn new_spi1<SPI: Spi>(
_spi: impl Peripheral<P = SPI1> + 'd,
sclk: impl Peripheral<P = gpio::Gpio6> + 'd,
sdo: impl Peripheral<P = gpio::Gpio7> + 'd,
sdi: Option<impl Peripheral<P = gpio::Gpio8> + 'd>,
cs: Option<impl Peripheral<P = impl OutputPin> + 'd>,
config: &config::Config,
dma: Dma,
) -> Result<Self, EspError> {
SpiMasterDriver::new_internal(spi, sclk, sdo, sdi, cs, config)
let max_transfer_size = Self::new_internal::<SPI>(sclk, sdo, sdi, dma)?;
Ok(Self {
host: SPI::device() as _,
max_transfer_size,
_p: PhantomData,
})
}
/// Create new instance of SPI controller for all others
pub fn new<SPI: SpiAnyPins>(
spi: impl Peripheral<P = SPI> + 'd,
sclk: impl Peripheral<P = impl OutputPin> + 'd,
sdo: impl Peripheral<P = impl OutputPin> + 'd,
sdi: Option<impl Peripheral<P = impl InputPin + OutputPin> + 'd>,
cs: Option<impl Peripheral<P = impl OutputPin> + 'd>,
config: &config::Config,
) -> Result<Self, EspError> {
SpiMasterDriver::new_internal(spi, sclk, sdo, sdi, cs, config)
}
/// Internal implementation of new shared by all SPI controllers
fn new_internal<SPI: Spi>(
_spi: impl Peripheral<P = SPI> + 'd,
sclk: impl Peripheral<P = impl OutputPin> + 'd,
sdo: impl Peripheral<P = impl OutputPin> + 'd,
sdi: Option<impl Peripheral<P = impl InputPin + OutputPin> + 'd>,
cs: Option<impl Peripheral<P = impl OutputPin> + 'd>,
config: &config::Config,
dma: Dma,
) -> Result<Self, EspError> {
crate::into_ref!(sclk, sdo);
let max_transfer_size = Self::new_internal::<SPI>(sclk, sdo, sdi, dma)?;
Ok(Self {
host: SPI::device() as _,
max_transfer_size,
_p: PhantomData,
})
}
pub fn host(&self) -> spi_host_device_t {
self.host as _
}
fn new_internal<SPI: Spi>(
sclk: impl Peripheral<P = impl OutputPin> + 'd,
sdo: impl Peripheral<P = impl OutputPin> + 'd,
sdi: Option<impl Peripheral<P = impl InputPin + OutputPin> + 'd>,
dma: Dma,
) -> Result<usize, EspError> {
crate::into_ref!(sclk, sdo);
let sdi = sdi.map(|sdi| sdi.into_ref());
let cs = cs.map(|cs| cs.into_ref());
let max_transfer_sz = dma.max_transfer_size();
let dma_chan: spi_dma_chan_t = dma.into();
#[cfg(not(esp_idf_version = "4.3"))]
let bus_config = spi_bus_config_t {
@ -400,7 +420,7 @@ impl<'d> SpiMasterDriver<'d> {
quadhd_io_num: -1,
//data3_io_num: -1,
},
max_transfer_sz: config.dma.max_transfer_size() as i32,
max_transfer_sz: max_transfer_sz as i32,
..Default::default()
};
@ -414,14 +434,75 @@ impl<'d> SpiMasterDriver<'d> {
quadwp_io_num: -1,
quadhd_io_num: -1,
max_transfer_sz: config.dma.max_transfer_size() as i32,
max_transfer_sz: max_transfer_sz as i32,
..Default::default()
};
esp!(unsafe { spi_bus_initialize(SPI::device(), &bus_config, config.dma.into()) })?;
esp!(unsafe { spi_bus_initialize(SPI::device(), &bus_config, dma_chan) })?;
Ok(max_transfer_sz)
}
}
let device_config = spi_device_interface_config_t {
spics_io_num: cs.as_ref().map_or(-1, |p| p.pin()),
impl<'d> Drop for SpiDriver<'d> {
fn drop(&mut self) {
esp!(unsafe { spi_bus_free(self.host()) }).unwrap();
}
}
pub struct SpiDeviceDriver<'d, T> {
handle: spi_device_handle_t,
driver: T,
with_cs_pin: bool,
_p: PhantomData<&'d ()>,
}
impl<'d, T> SpiDeviceDriver<'d, T>
where
T: Borrow<SpiDriver<'d>> + 'd,
{
pub fn new(
driver: T,
cs: impl Peripheral<P = impl InputPin + OutputPin> + 'd,
config: &config::Config,
) -> Result<SpiDeviceDriver<'d, T>, EspError> {
crate::into_ref!(cs);
let cs_ref: PeripheralRef<AnyOutputPin> = cs.into_ref().map_into();
let cs_pin = cs_ref.pin();
let config = Self::create_conf(cs_pin, &config);
let master: &SpiDriver = driver.borrow();
let mut device_handle: spi_device_handle_t = ptr::null_mut();
esp!(unsafe { spi_bus_add_device(master.host(), &config, &mut device_handle as *mut _) })?;
Ok(Self {
handle: device_handle,
driver,
with_cs_pin: true,
_p: PhantomData,
})
}
pub fn new_no_cs(
driver: T,
config: &config::Config,
) -> Result<SpiDeviceDriver<'d, T>, EspError> {
let config = Self::create_conf(-1, &config);
let master: &SpiDriver = driver.borrow();
let mut device_handle: spi_device_handle_t = ptr::null_mut();
esp!(unsafe { spi_bus_add_device(master.host(), &config, &mut device_handle as *mut _) })?;
Ok(Self {
handle: device_handle,
driver,
with_cs_pin: false,
_p: PhantomData,
})
}
fn create_conf(cs: i32, config: &config::Config) -> spi_device_interface_config_t {
spi_device_interface_config_t {
spics_io_num: cs,
clock_speed_hz: config.baudrate.0 as i32,
mode: (((config.data_mode.polarity == embedded_hal::spi::Polarity::IdleHigh) as u8)
<< 1)
@ -432,42 +513,33 @@ impl<'d> SpiMasterDriver<'d> {
SPI_DEVICE_NO_DUMMY
} else {
0_u32
} | if config.cs_active_high {
SPI_DEVICE_POSITIVE_CS
} else {
0_u32
} | config.duplex.as_flags(),
..Default::default()
};
let mut device_handle: spi_device_handle_t = ptr::null_mut();
esp!(unsafe {
spi_bus_add_device(SPI::device(), &device_config, &mut device_handle as *mut _)
})?;
Ok(Self {
host: SPI::device() as _,
device: device_handle,
max_transfer_size: config.dma.max_transfer_size(),
_p: PhantomData,
})
}
}
pub fn device(&self) -> spi_device_handle_t {
self.device
self.handle
}
pub fn host(&self) -> spi_host_device_t {
self.host as _
}
pub fn transaction<R, E>(
&mut self,
f: impl FnOnce(&mut SpiBusMasterDriver<'d>) -> Result<R, E>,
f: impl FnOnce(&mut SpiBusDriver<'d>) -> Result<R, E>,
) -> Result<R, E>
where
E: From<EspError>,
{
let mut bus = SpiBusMasterDriver {
handle: self.device,
trans_len: self.max_transfer_size,
let master: &SpiDriver = self.driver.borrow();
// if DMA used -> get trans length info from master
let trans_len = master.max_transfer_size;
let mut bus = SpiBusDriver {
handle: self.handle,
trans_len,
hardware_cs: self.with_cs_pin,
_p: PhantomData,
};
@ -475,14 +547,18 @@ impl<'d> SpiMasterDriver<'d> {
let trans_result = f(&mut bus);
let finish_result = bus.finish();
// #99 is partially resolved by allowing software CS to ignore this bus.finish() work around
let finish_result = if self.with_cs_pin {
bus.finish()
} else {
Ok(())
};
// Flush whatever is pending.
// Note that this is done even when an error is returned from the transaction.
let flush_result = bus.flush();
core::mem::drop(lock);
let result = trans_result?;
finish_result?;
flush_result?;
@ -490,62 +566,80 @@ impl<'d> SpiMasterDriver<'d> {
Ok(result)
}
fn lock_bus(&mut self) -> Result<Lock, EspError> {
Lock::new(self.device)
pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), EspError> {
self.transaction(|bus| bus.transfer(read, write))
}
pub fn write(&mut self, write: &[u8]) -> Result<(), EspError> {
self.transaction(|bus| bus.write(write))
}
pub fn read(&mut self, read: &mut [u8]) -> Result<(), EspError> {
self.transaction(|bus| bus.read(read))
}
pub fn transfer_in_place(&mut self, buf: &mut [u8]) -> Result<(), EspError> {
self.transaction(|bus| bus.transfer_in_place(buf))
}
fn lock_bus(&self) -> Result<Lock, EspError> {
Lock::new(self.handle)
}
}
impl<'d> Drop for SpiMasterDriver<'d> {
fn drop(&mut self) {
esp!(unsafe { spi_bus_remove_device(self.device) }).unwrap();
esp!(unsafe { spi_bus_free(self.host()) }).unwrap();
}
}
unsafe impl<'d> Send for SpiMasterDriver<'d> {}
impl<'d> embedded_hal::spi::ErrorType for SpiMasterDriver<'d> {
impl<'d, T> embedded_hal::spi::ErrorType for SpiDeviceDriver<'d, T> {
type Error = SpiError;
}
impl<'d> SpiDevice for SpiMasterDriver<'d> {
type Bus = SpiBusMasterDriver<'d>;
impl<'d, T> SpiDevice for SpiDeviceDriver<'d, T>
where
T: Borrow<SpiDriver<'d>> + 'd,
{
type Bus = SpiBusDriver<'d>;
fn transaction<R>(
&mut self,
f: impl FnOnce(&mut Self::Bus) -> Result<R, <Self::Bus as embedded_hal::spi::ErrorType>::Error>,
) -> Result<R, Self::Error> {
SpiMasterDriver::transaction(self, f)
Self::transaction(self, f)
}
}
impl<'d> embedded_hal_0_2::blocking::spi::Transfer<u8> for SpiMasterDriver<'d> {
impl<'d, T> embedded_hal_0_2::blocking::spi::Transfer<u8> for SpiDeviceDriver<'d, T>
where
T: Borrow<SpiDriver<'d>> + 'd,
{
type Error = SpiError;
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
let _lock = self.lock_bus();
let mut chunks = words.chunks_mut(self.max_transfer_size).peekable();
let master: &SpiDriver = self.driver.borrow();
let _lock = self.lock_bus()?;
let mut chunks = words.chunks_mut(master.max_transfer_size).peekable();
while let Some(chunk) = chunks.next() {
let ptr = chunk.as_mut_ptr();
let len = chunk.len();
polling_transmit(self.device, ptr, ptr, len, len, chunks.peek().is_some())?;
polling_transmit(self.handle, ptr, ptr, len, len, chunks.peek().is_some())?;
}
Ok(words)
}
}
impl<'d> embedded_hal_0_2::blocking::spi::Write<u8> for SpiMasterDriver<'d> {
impl<'d, T> embedded_hal_0_2::blocking::spi::Write<u8> for SpiDeviceDriver<'d, T>
where
T: Borrow<SpiDriver<'d>> + 'd,
{
type Error = SpiError;
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
let _lock = self.lock_bus();
let mut chunks = words.chunks(self.max_transfer_size).peekable();
let master: &SpiDriver = self.driver.borrow();
let _lock = self.lock_bus()?;
let mut chunks = words.chunks(master.max_transfer_size).peekable();
while let Some(chunk) = chunks.next() {
polling_transmit(
self.device,
self.handle,
ptr::null_mut(),
chunk.as_ptr(),
chunk.len(),
@ -558,7 +652,10 @@ impl<'d> embedded_hal_0_2::blocking::spi::Write<u8> for SpiMasterDriver<'d> {
}
}
impl<'d> embedded_hal_0_2::blocking::spi::WriteIter<u8> for SpiMasterDriver<'d> {
impl<'d, T> embedded_hal_0_2::blocking::spi::WriteIter<u8> for SpiDeviceDriver<'d, T>
where
T: Borrow<SpiDriver<'d>> + 'd,
{
type Error = SpiError;
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
@ -593,7 +690,10 @@ impl<'d> embedded_hal_0_2::blocking::spi::WriteIter<u8> for SpiMasterDriver<'d>
}
}
impl<'d> embedded_hal_0_2::blocking::spi::Transactional<u8> for SpiMasterDriver<'d> {
impl<'d, T> embedded_hal_0_2::blocking::spi::Transactional<u8> for SpiDeviceDriver<'d, T>
where
T: Borrow<SpiDriver<'d>> + 'd,
{
type Error = SpiError;
fn exec<'a>(
@ -615,6 +715,12 @@ impl<'d> embedded_hal_0_2::blocking::spi::Transactional<u8> for SpiMasterDriver<
}
}
impl<'d, T> Drop for SpiDeviceDriver<'d, T> {
fn drop(&mut self) {
esp!(unsafe { spi_bus_remove_device(self.handle) }).unwrap();
}
}
fn to_spi_err(err: EspError) -> SpiError {
SpiError::other(err)
}
@ -709,3 +815,142 @@ impl_spi!(SPI3: spi_host_device_t_SPI3_HOST);
impl_spi_any_pins!(SPI2);
#[cfg(not(esp32c3))]
impl_spi_any_pins!(SPI3);
use crate::delay::Ets;
use crate::gpio::{Level, Output};
use crate::task::CriticalSection;
pub struct SpiSharedDeviceDriver<'d, DRIVER> {
driver: UnsafeCell<SpiDeviceDriver<'d, DRIVER>>,
mutex: CriticalSection,
}
impl<'d, DRIVER> SpiSharedDeviceDriver<'d, DRIVER>
where
DRIVER: Borrow<SpiDriver<'d>> + 'd,
{
pub fn new(device: SpiDeviceDriver<'d, DRIVER>) -> Self {
Self {
driver: UnsafeCell::new(device),
mutex: CriticalSection::new(),
}
}
pub(crate) fn lock<R>(&self, f: impl FnOnce(&mut SpiDeviceDriver<'d, DRIVER>) -> R) -> R {
let dev = unsafe { &mut *self.driver.get() };
let result = f(dev);
result
}
}
pub struct SpiSoftCsDeviceDriver<'d, SHARED, DRIVER: Borrow<SpiDriver<'d>>> {
shared_device: SHARED,
cs_pin: PinDriver<'d, AnyOutputPin, Output>,
pre_delay_us: Option<u32>,
post_delay_us: Option<u32>,
_p: PhantomData<&'d DRIVER>,
}
impl<'d, SHARED, DRIVER> SpiSoftCsDeviceDriver<'d, SHARED, DRIVER>
where
SHARED: Borrow<SpiSharedDeviceDriver<'d, DRIVER>>,
DRIVER: Borrow<SpiDriver<'d>>,
{
pub fn new(
shared_device: SHARED,
cs: impl Peripheral<P = impl InputPin + OutputPin> + 'd,
cs_level: Level,
) -> Result<Self, EspError> {
let cs_ref: PeripheralRef<AnyOutputPin> = cs.into_ref().map_into();
let mut cs_pin = PinDriver::output(cs_ref)?;
cs_pin.set_level(cs_level)?;
Ok(Self {
shared_device,
cs_pin,
_p: PhantomData,
pre_delay_us: None,
post_delay_us: None,
})
}
/// Add an aditional delay of x in uSeconds before transaction
/// between chip select and first clk out
pub fn cs_pre_delay_us(mut self, delay_us: u32) -> Self {
self.pre_delay_us = Some(delay_us);
self
}
/// Add an aditional delay of x in uSeconds after transaction
/// between last clk out and chip select
pub fn cs_post_delay_us(mut self, delay_us: u32) -> Self {
self.post_delay_us = Some(delay_us);
self
}
pub fn update_shared_driver(&mut self, shared_device: SHARED) {
self.shared_device = shared_device;
}
pub fn transaction<R, E>(
&mut self,
f: impl FnOnce(&mut SpiBusDriver<'d>) -> Result<R, E>,
) -> Result<R, E>
where
E: From<EspError>,
{
let shared_device: &SpiSharedDeviceDriver<'d, DRIVER> = self.shared_device.borrow();
let _lock = shared_device.mutex.enter();
self.cs_pin.toggle()?;
if let Some(delay) = self.pre_delay_us {
Ets::delay_us(delay);
}
let trans_result = shared_device.lock(|driver| driver.transaction(f));
if let Some(delay) = self.post_delay_us {
Ets::delay_us(delay);
}
self.cs_pin.toggle()?;
let result = trans_result?;
Ok(result)
}
pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), EspError> {
self.transaction(|bus| bus.transfer(read, write))
}
pub fn write(&mut self, write: &[u8]) -> Result<(), EspError> {
self.transaction(|bus| bus.write(write))
}
pub fn read(&mut self, read: &mut [u8]) -> Result<(), EspError> {
self.transaction(|bus| bus.read(read))
}
pub fn transfer_in_place(&mut self, buf: &mut [u8]) -> Result<(), EspError> {
self.transaction(|bus| bus.transfer_in_place(buf))
}
pub fn cs_gpio_num(&self) -> i32 {
self.cs_pin.pin()
}
}
impl<'d, DEVICE, DRIVER> embedded_hal::spi::ErrorType for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER>
where
DEVICE: Borrow<SpiSharedDeviceDriver<'d, DRIVER>> + 'd,
DRIVER: Borrow<SpiDriver<'d>> + 'd,
{
type Error = SpiError;
}
impl<'d, DEVICE, DRIVER> SpiDevice for SpiSoftCsDeviceDriver<'d, DEVICE, DRIVER>
where
DEVICE: Borrow<SpiSharedDeviceDriver<'d, DRIVER>> + 'd,
DRIVER: Borrow<SpiDriver<'d>> + 'd,
{
type Bus = SpiBusDriver<'d>;
fn transaction<R>(
&mut self,
f: impl FnOnce(&mut Self::Bus) -> Result<R, <Self::Bus as embedded_hal::spi::ErrorType>::Error>,
) -> Result<R, Self::Error> {
Self::transaction(self, f)
}
}