mirror of
https://github.com/rust-embedded/embedded-hal.git
synced 2026-03-14 01:47:38 +00:00
spi: add Operation::DelayUs(u32).
This commit is contained in:
parent
30a8190703
commit
d07d39e359
@ -8,6 +8,8 @@ pub use embedded_hal::spi::{
|
||||
Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3,
|
||||
};
|
||||
|
||||
use crate::delay::DelayUs;
|
||||
|
||||
/// SPI device trait
|
||||
///
|
||||
/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected
|
||||
@ -195,19 +197,20 @@ where
|
||||
///
|
||||
/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`],
|
||||
/// ideal for when no sharing is required (only one SPI device is present on the bus).
|
||||
pub struct ExclusiveDevice<BUS, CS> {
|
||||
pub struct ExclusiveDevice<BUS, CS, D> {
|
||||
bus: BUS,
|
||||
cs: CS,
|
||||
delay: D,
|
||||
}
|
||||
|
||||
impl<BUS, CS> ExclusiveDevice<BUS, CS> {
|
||||
impl<BUS, CS, D> ExclusiveDevice<BUS, CS, D> {
|
||||
/// Create a new ExclusiveDevice
|
||||
pub fn new(bus: BUS, cs: CS) -> Self {
|
||||
Self { bus, cs }
|
||||
pub fn new(bus: BUS, cs: CS, delay: D) -> Self {
|
||||
Self { bus, cs, delay }
|
||||
}
|
||||
}
|
||||
|
||||
impl<BUS, CS> ErrorType for ExclusiveDevice<BUS, CS>
|
||||
impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
|
||||
where
|
||||
BUS: ErrorType,
|
||||
CS: OutputPin,
|
||||
@ -215,10 +218,11 @@ where
|
||||
type Error = ExclusiveDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<Word: Copy + 'static, BUS, CS> blocking::SpiDevice<Word> for ExclusiveDevice<BUS, CS>
|
||||
impl<Word: Copy + 'static, BUS, CS, D> blocking::SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
|
||||
where
|
||||
BUS: blocking::SpiBus<Word>,
|
||||
CS: OutputPin,
|
||||
D: embedded_hal::delay::DelayUs,
|
||||
{
|
||||
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
|
||||
self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?;
|
||||
@ -230,6 +234,13 @@ where
|
||||
Operation::Write(buf) => self.bus.write(buf),
|
||||
Operation::Transfer(read, write) => self.bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf),
|
||||
Operation::DelayUs(us) => match self.bus.flush() {
|
||||
Err(e) => Err(e),
|
||||
Ok(()) => {
|
||||
self.delay.delay_us(*us);
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
};
|
||||
if let Err(e) = res {
|
||||
break 'ops Err(e);
|
||||
@ -250,10 +261,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Word: Copy + 'static, BUS, CS> SpiDevice<Word> for ExclusiveDevice<BUS, CS>
|
||||
impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
|
||||
where
|
||||
BUS: SpiBus<Word>,
|
||||
CS: OutputPin,
|
||||
D: DelayUs,
|
||||
{
|
||||
async fn transaction(
|
||||
&mut self,
|
||||
@ -268,6 +280,13 @@ where
|
||||
Operation::Write(buf) => self.bus.write(buf).await,
|
||||
Operation::Transfer(read, write) => self.bus.transfer(read, write).await,
|
||||
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await,
|
||||
Operation::DelayUs(us) => match self.bus.flush().await {
|
||||
Err(e) => Err(e),
|
||||
Ok(()) => {
|
||||
self.delay.delay_us(*us).await;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
};
|
||||
if let Err(e) = res {
|
||||
break 'ops Err(e);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use core::cell::RefCell;
|
||||
use critical_section::Mutex;
|
||||
use embedded_hal::delay::DelayUs;
|
||||
use embedded_hal::digital::OutputPin;
|
||||
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
|
||||
|
||||
@ -15,19 +16,36 @@ use super::DeviceError;
|
||||
/// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely
|
||||
/// negatively impact real-time properties, such as interrupt latency. If you can, prefer using
|
||||
/// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections.
|
||||
pub struct CriticalSectionDevice<'a, BUS, CS> {
|
||||
pub struct CriticalSectionDevice<'a, BUS, CS, D> {
|
||||
bus: &'a Mutex<RefCell<BUS>>,
|
||||
cs: CS,
|
||||
delay: D,
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS> {
|
||||
impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> {
|
||||
/// Create a new ExclusiveDevice
|
||||
pub fn new(bus: &'a Mutex<RefCell<BUS>>, cs: CS) -> Self {
|
||||
Self { bus, cs }
|
||||
pub fn new(bus: &'a Mutex<RefCell<BUS>>, cs: CS, delay: D) -> Self {
|
||||
Self { bus, cs, delay }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS> ErrorType for CriticalSectionDevice<'a, BUS, CS>
|
||||
impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> {
|
||||
/// Create a new CriticalSectionDevice without support for in-transaction delays.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The returned device will panic if you try to execute a transaction
|
||||
/// that contains any operations of type `Operation::DelayUs`.
|
||||
pub fn new_no_delay(bus: &'a Mutex<RefCell<BUS>>, cs: CS) -> Self {
|
||||
Self {
|
||||
bus,
|
||||
cs,
|
||||
delay: super::NoDelay,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS, D> ErrorType for CriticalSectionDevice<'a, BUS, CS, D>
|
||||
where
|
||||
BUS: ErrorType,
|
||||
CS: OutputPin,
|
||||
@ -35,10 +53,11 @@ where
|
||||
type Error = DeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice<Word> for CriticalSectionDevice<'a, BUS, CS>
|
||||
impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for CriticalSectionDevice<'a, BUS, CS, D>
|
||||
where
|
||||
BUS: SpiBus<Word>,
|
||||
CS: OutputPin,
|
||||
D: DelayUs,
|
||||
{
|
||||
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
|
||||
critical_section::with(|cs| {
|
||||
@ -51,6 +70,11 @@ where
|
||||
Operation::Write(buf) => bus.write(buf),
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||
Operation::DelayUs(us) => {
|
||||
bus.flush()?;
|
||||
self.delay.delay_us(*us);
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
//! SPI bus sharing mechanisms.
|
||||
|
||||
use embedded_hal::delay::DelayUs;
|
||||
use embedded_hal::digital::OutputPin;
|
||||
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
|
||||
|
||||
@ -9,19 +10,36 @@ use super::DeviceError;
|
||||
///
|
||||
/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus),
|
||||
/// ideal for when no sharing is required (only one SPI device is present on the bus).
|
||||
pub struct ExclusiveDevice<BUS, CS> {
|
||||
pub struct ExclusiveDevice<BUS, CS, D> {
|
||||
bus: BUS,
|
||||
cs: CS,
|
||||
delay: D,
|
||||
}
|
||||
|
||||
impl<BUS, CS> ExclusiveDevice<BUS, CS> {
|
||||
impl<BUS, CS, D> ExclusiveDevice<BUS, CS, D> {
|
||||
/// Create a new ExclusiveDevice
|
||||
pub fn new(bus: BUS, cs: CS) -> Self {
|
||||
Self { bus, cs }
|
||||
pub fn new(bus: BUS, cs: CS, delay: D) -> Self {
|
||||
Self { bus, cs, delay }
|
||||
}
|
||||
}
|
||||
|
||||
impl<BUS, CS> ErrorType for ExclusiveDevice<BUS, CS>
|
||||
impl<BUS, CS> ExclusiveDevice<BUS, CS, super::NoDelay> {
|
||||
/// Create a new ExclusiveDevice without support for in-transaction delays.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The returned device will panic if you try to execute a transaction
|
||||
/// that contains any operations of type `Operation::DelayUs`.
|
||||
pub fn new_no_delay(bus: BUS, cs: CS) -> Self {
|
||||
Self {
|
||||
bus,
|
||||
cs,
|
||||
delay: super::NoDelay,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<BUS, CS, D> ErrorType for ExclusiveDevice<BUS, CS, D>
|
||||
where
|
||||
BUS: ErrorType,
|
||||
CS: OutputPin,
|
||||
@ -29,10 +47,11 @@ where
|
||||
type Error = DeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<Word: Copy + 'static, BUS, CS> SpiDevice<Word> for ExclusiveDevice<BUS, CS>
|
||||
impl<Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for ExclusiveDevice<BUS, CS, D>
|
||||
where
|
||||
BUS: SpiBus<Word>,
|
||||
CS: OutputPin,
|
||||
D: DelayUs,
|
||||
{
|
||||
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
|
||||
self.cs.set_low().map_err(DeviceError::Cs)?;
|
||||
@ -42,6 +61,11 @@ where
|
||||
Operation::Write(buf) => self.bus.write(buf),
|
||||
Operation::Transfer(read, write) => self.bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf),
|
||||
Operation::DelayUs(us) => {
|
||||
self.bus.flush()?;
|
||||
self.delay.delay_us(*us);
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
|
||||
@ -35,3 +35,12 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dummy `DelayUs` implementation that panics on use.
|
||||
pub struct NoDelay;
|
||||
|
||||
impl embedded_hal::delay::DelayUs for NoDelay {
|
||||
fn delay_us(&mut self, _us: u32) {
|
||||
panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use embedded_hal::delay::DelayUs;
|
||||
use embedded_hal::digital::OutputPin;
|
||||
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
|
||||
use std::sync::Mutex;
|
||||
@ -12,19 +13,36 @@ use super::DeviceError;
|
||||
/// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads,
|
||||
/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is
|
||||
/// it is only available in `std` targets.
|
||||
pub struct MutexDevice<'a, BUS, CS> {
|
||||
pub struct MutexDevice<'a, BUS, CS, D> {
|
||||
bus: &'a Mutex<BUS>,
|
||||
cs: CS,
|
||||
delay: D,
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS> MutexDevice<'a, BUS, CS> {
|
||||
impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> {
|
||||
/// Create a new ExclusiveDevice
|
||||
pub fn new(bus: &'a Mutex<BUS>, cs: CS) -> Self {
|
||||
Self { bus, cs }
|
||||
pub fn new(bus: &'a Mutex<BUS>, cs: CS, delay: D) -> Self {
|
||||
Self { bus, cs, delay }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS> ErrorType for MutexDevice<'a, BUS, CS>
|
||||
impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> {
|
||||
/// Create a new MutexDevice without support for in-transaction delays.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The returned device will panic if you try to execute a transaction
|
||||
/// that contains any operations of type `Operation::DelayUs`.
|
||||
pub fn new_no_delay(bus: &'a Mutex<BUS>, cs: CS) -> Self {
|
||||
Self {
|
||||
bus,
|
||||
cs,
|
||||
delay: super::NoDelay,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS, D> ErrorType for MutexDevice<'a, BUS, CS, D>
|
||||
where
|
||||
BUS: ErrorType,
|
||||
CS: OutputPin,
|
||||
@ -32,10 +50,11 @@ where
|
||||
type Error = DeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice<Word> for MutexDevice<'a, BUS, CS>
|
||||
impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for MutexDevice<'a, BUS, CS, D>
|
||||
where
|
||||
BUS: SpiBus<Word>,
|
||||
CS: OutputPin,
|
||||
D: DelayUs,
|
||||
{
|
||||
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
|
||||
let bus = &mut *self.bus.lock().unwrap();
|
||||
@ -47,6 +66,11 @@ where
|
||||
Operation::Write(buf) => bus.write(buf),
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||
Operation::DelayUs(us) => {
|
||||
bus.flush()?;
|
||||
self.delay.delay_us(*us);
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use core::cell::RefCell;
|
||||
use embedded_hal::delay::DelayUs;
|
||||
use embedded_hal::digital::OutputPin;
|
||||
use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice};
|
||||
|
||||
@ -12,19 +13,36 @@ use super::DeviceError;
|
||||
/// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`,
|
||||
/// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several
|
||||
/// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead.
|
||||
pub struct RefCellDevice<'a, BUS, CS> {
|
||||
pub struct RefCellDevice<'a, BUS, CS, D> {
|
||||
bus: &'a RefCell<BUS>,
|
||||
cs: CS,
|
||||
delay: D,
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS> {
|
||||
impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> {
|
||||
/// Create a new ExclusiveDevice
|
||||
pub fn new(bus: &'a RefCell<BUS>, cs: CS) -> Self {
|
||||
Self { bus, cs }
|
||||
pub fn new(bus: &'a RefCell<BUS>, cs: CS, delay: D) -> Self {
|
||||
Self { bus, cs, delay }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS> ErrorType for RefCellDevice<'a, BUS, CS>
|
||||
impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> {
|
||||
/// Create a new RefCellDevice without support for in-transaction delays.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The returned device will panic if you try to execute a transaction
|
||||
/// that contains any operations of type `Operation::DelayUs`.
|
||||
pub fn new_no_delay(bus: &'a RefCell<BUS>, cs: CS) -> Self {
|
||||
Self {
|
||||
bus,
|
||||
cs,
|
||||
delay: super::NoDelay,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, BUS, CS, D> ErrorType for RefCellDevice<'a, BUS, CS, D>
|
||||
where
|
||||
BUS: ErrorType,
|
||||
CS: OutputPin,
|
||||
@ -32,10 +50,11 @@ where
|
||||
type Error = DeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice<Word> for RefCellDevice<'a, BUS, CS>
|
||||
impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice<Word> for RefCellDevice<'a, BUS, CS, D>
|
||||
where
|
||||
BUS: SpiBus<Word>,
|
||||
CS: OutputPin,
|
||||
D: DelayUs,
|
||||
{
|
||||
fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> {
|
||||
let bus = &mut *self.bus.borrow_mut();
|
||||
@ -47,6 +66,11 @@ where
|
||||
Operation::Write(buf) => bus.write(buf),
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||
Operation::DelayUs(us) => {
|
||||
bus.flush()?;
|
||||
self.delay.delay_us(*us);
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
|
||||
@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- spi: added `Operation::DelayUs(u32)`.
|
||||
|
||||
### Removed
|
||||
- spi: removed read-only and write-only traits.
|
||||
|
||||
|
||||
@ -313,6 +313,8 @@ pub enum Operation<'a, Word: 'static> {
|
||||
///
|
||||
/// Equivalent to [`SpiBus::transfer_in_place`].
|
||||
TransferInPlace(&'a mut [Word]),
|
||||
/// Delay for at least the specified number of microseconds
|
||||
DelayUs(u32),
|
||||
}
|
||||
|
||||
/// SPI device trait
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user