This commit is contained in:
Björn Quentin 2025-01-09 19:19:18 +01:00 committed by GitHub
commit 92dee4d0cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 134 additions and 86 deletions

View File

@ -55,7 +55,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `adc::{AdcCalSource, Attenuation, Resolution}` now implement `Hash` and `defmt::Format` (#2840) - `adc::{AdcCalSource, Attenuation, Resolution}` now implement `Hash` and `defmt::Format` (#2840)
- `rtc_cntl::{RtcFastClock, RtcSlowClock, RtcCalSel}` now implement `PartialEq`, `Eq`, `Hash` and `defmt::Format` (#2840) - `rtc_cntl::{RtcFastClock, RtcSlowClock, RtcCalSel}` now implement `PartialEq`, `Eq`, `Hash` and `defmt::Format` (#2840)
- Added `tsens::TemperatureSensor` peripheral for ESP32C6 and ESP32C3 (#2875) - Added `tsens::TemperatureSensor` peripheral for ESP32C6 and ESP32C3 (#2875)
- Added `with_rx()` and `with_tx()` methods to Uart, UartRx, and UartTx () - Added `with_rx()` and `with_tx()` methods to Uart, UartRx, and UartTx (#2904)
- SPI: Added support for 3-wire SPI (#2919)
### Changed ### Changed

View File

@ -552,8 +552,8 @@ impl<'d> Spi<'d, Blocking> {
this.apply_config(&config)?; this.apply_config(&config)?;
let this = this let this = this
.with_mosi(NoPin) .with_sio0(NoPin)
.with_miso(NoPin) .with_sio1(NoPin)
.with_sck(NoPin) .with_sck(NoPin)
.with_cs(NoPin); .with_cs(NoPin);
@ -645,9 +645,21 @@ where
{ {
/// Assign the MOSI (Master Out Slave In) pin for the SPI instance. /// Assign the MOSI (Master Out Slave In) pin for the SPI instance.
/// ///
/// Enables output functionality for the pin, and connects it to the MOSI.
pub fn with_mosi<MOSI: PeripheralOutput>(self, mosi: impl Peripheral<P = MOSI> + 'd) -> Self {
crate::into_mapped_ref!(mosi);
mosi.enable_output(false, private::Internal);
self.driver().info.mosi.connect_to(&mut mosi);
self
}
/// Assign the SIO0 pin for the SPI instance.
///
/// Enables both input and output functionality for the pin, and connects it /// Enables both input and output functionality for the pin, and connects it
/// to the MOSI signal and SIO0 input signal. /// to the MOSI signal and SIO0 input signal.
pub fn with_mosi<MOSI: PeripheralOutput>(self, mosi: impl Peripheral<P = MOSI> + 'd) -> Self { pub fn with_sio0<MOSI: PeripheralOutput>(self, mosi: impl Peripheral<P = MOSI> + 'd) -> Self {
crate::into_mapped_ref!(mosi); crate::into_mapped_ref!(mosi);
mosi.enable_output(true, private::Internal); mosi.enable_output(true, private::Internal);
mosi.enable_input(true, private::Internal); mosi.enable_input(true, private::Internal);
@ -3123,12 +3135,25 @@ impl Driver {
no_mosi_miso: bool, no_mosi_miso: bool,
data_mode: DataMode, data_mode: DataMode,
) -> Result<(), Error> { ) -> Result<(), Error> {
let three_wire = cmd.mode() == DataMode::SingleThreeWire
|| address.mode() == DataMode::SingleThreeWire
|| data_mode == DataMode::SingleThreeWire;
if three_wire
&& ((cmd != Command::None && cmd.mode() != DataMode::SingleThreeWire)
|| (address != Address::None && address.mode() != DataMode::SingleThreeWire)
|| data_mode != DataMode::SingleThreeWire)
{
return Err(Error::ArgumentsInvalid);
}
self.init_spi_data_mode(cmd.mode(), address.mode(), data_mode)?; self.init_spi_data_mode(cmd.mode(), address.mode(), data_mode)?;
let reg_block = self.register_block(); let reg_block = self.register_block();
reg_block.user().modify(|_, w| { reg_block.user().modify(|_, w| {
w.usr_miso_highpart().clear_bit(); w.usr_miso_highpart().clear_bit();
w.usr_mosi_highpart().clear_bit(); w.usr_mosi_highpart().clear_bit();
w.sio().bit(three_wire);
w.doutdin().clear_bit(); w.doutdin().clear_bit();
w.usr_miso().bit(!is_write && !no_mosi_miso); w.usr_miso().bit(!is_write && !no_mosi_miso);
w.usr_mosi().bit(is_write && !no_mosi_miso); w.usr_mosi().bit(is_write && !no_mosi_miso);

View File

@ -35,6 +35,8 @@ pub enum Error {
/// Error indicating that the operation is unsupported by the current /// Error indicating that the operation is unsupported by the current
/// implementation. /// implementation.
Unsupported, Unsupported,
/// The given arguments are invalid.
ArgumentsInvalid,
/// An unknown error occurred during SPI communication. /// An unknown error occurred during SPI communication.
Unknown, Unknown,
} }
@ -98,14 +100,16 @@ pub enum BitOrder {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable] #[instability::unstable]
pub enum DataMode { pub enum DataMode {
/// `Single` Data Mode - 1 bit, 2 wires. /// Clock, CS and one data line (SIO0)
SingleThreeWire,
/// `Single` Data Mode - 1 bit, two data lines. (SIO0, SIO1)
Single, Single,
/// `Dual` Data Mode - 2 bit, 2 wires /// `Dual` Data Mode - 2 bits, two data lines. (SIO0, SIO1)
Dual, Dual,
/// `Quad` Data Mode - 4 bit, 4 wires /// `Quad` Data Mode - 4 bit, 4 data lines. (SIO0 .. SIO3)
Quad, Quad,
#[cfg(spi_octal)] #[cfg(spi_octal)]
/// `Octal` Data Mode - 8 bit, 8 wires /// `Octal` Data Mode - 8 bit, 8 data lines. (SIO0 .. SIO7)
Octal, Octal,
} }

View File

@ -231,7 +231,7 @@ mod tests {
let [pin, pin_mirror, _] = ctx.gpios; let [pin, pin_mirror, _] = ctx.gpios;
let pin_mirror = Output::new(pin_mirror, Level::High); let pin_mirror = Output::new(pin_mirror, Level::High);
let spi = ctx.spi.with_mosi(pin).with_dma(ctx.dma_channel); let spi = ctx.spi.with_sio0(pin).with_dma(ctx.dma_channel);
super::execute_read(spi, pin_mirror, 0b0001_0001); super::execute_read(spi, pin_mirror, 0b0001_0001);
} }
@ -241,7 +241,7 @@ mod tests {
let [pin, pin_mirror, _] = ctx.gpios; let [pin, pin_mirror, _] = ctx.gpios;
let pin_mirror = Output::new(pin_mirror, Level::High); let pin_mirror = Output::new(pin_mirror, Level::High);
let spi = ctx.spi.with_miso(pin).with_dma(ctx.dma_channel); let spi = ctx.spi.with_sio1(pin).with_dma(ctx.dma_channel);
super::execute_read(spi, pin_mirror, 0b0010_0010); super::execute_read(spi, pin_mirror, 0b0010_0010);
} }
@ -271,7 +271,7 @@ mod tests {
let [pin, pin_mirror, _] = ctx.gpios; let [pin, pin_mirror, _] = ctx.gpios;
let pin_mirror = Output::new(pin_mirror, Level::High); let pin_mirror = Output::new(pin_mirror, Level::High);
let spi = ctx.spi.with_mosi(pin).with_dma(ctx.dma_channel); let spi = ctx.spi.with_sio0(pin).with_dma(ctx.dma_channel);
super::execute_write_read(spi, pin_mirror, 0b0001_0001); super::execute_write_read(spi, pin_mirror, 0b0001_0001);
} }
@ -324,7 +324,7 @@ mod tests {
.channel0 .channel0
.set_input_mode(EdgeMode::Hold, EdgeMode::Increment); .set_input_mode(EdgeMode::Hold, EdgeMode::Increment);
let spi = ctx.spi.with_mosi(mosi).with_dma(ctx.dma_channel); let spi = ctx.spi.with_sio0(mosi).with_dma(ctx.dma_channel);
super::execute_write(unit0, unit1, spi, 0b0000_0001, false); super::execute_write(unit0, unit1, spi, 0b0000_0001, false);
} }
@ -355,7 +355,7 @@ mod tests {
let spi = ctx let spi = ctx
.spi .spi
.with_mosi(mosi) .with_sio0(mosi)
.with_sio1(gpio) .with_sio1(gpio)
.with_dma(ctx.dma_channel); .with_dma(ctx.dma_channel);
@ -388,7 +388,7 @@ mod tests {
let spi = ctx let spi = ctx
.spi .spi
.with_mosi(mosi) .with_sio0(mosi)
.with_sio2(gpio) .with_sio2(gpio)
.with_dma(ctx.dma_channel); .with_dma(ctx.dma_channel);
@ -421,7 +421,7 @@ mod tests {
let spi = ctx let spi = ctx
.spi .spi
.with_mosi(mosi) .with_sio0(mosi)
.with_sio3(gpio) .with_sio3(gpio)
.with_dma(ctx.dma_channel); .with_dma(ctx.dma_channel);

View File

@ -27,6 +27,81 @@ struct Context {
pcnt_source: InputSignal, pcnt_source: InputSignal,
} }
fn perform_spi_writes_are_correctly_by_pcnt(ctx: Context, mode: DataMode) {
const DMA_BUFFER_SIZE: usize = 4;
let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE);
let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap();
let unit = ctx.pcnt_unit;
let mut spi = ctx.spi;
unit.channel0.set_edge_signal(ctx.pcnt_source);
unit.channel0
.set_input_mode(EdgeMode::Hold, EdgeMode::Increment);
// Fill the buffer where each byte has 3 pos edges.
dma_tx_buf.fill(&[0b0110_1010; DMA_BUFFER_SIZE]);
let transfer = spi
.half_duplex_write(
mode,
Command::None,
Address::None,
0,
dma_tx_buf.len(),
dma_tx_buf,
)
.map_err(|e| e.0)
.unwrap();
(spi, dma_tx_buf) = transfer.wait();
assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _);
let transfer = spi
.half_duplex_write(
mode,
Command::None,
Address::None,
0,
dma_tx_buf.len(),
dma_tx_buf,
)
.map_err(|e| e.0)
.unwrap();
// dropping SPI would make us see an additional edge - so let's keep SPI alive
let (_spi, _) = transfer.wait();
assert_eq!(unit.value(), (6 * DMA_BUFFER_SIZE) as _);
}
fn perform_spidmabus_writes_are_correctly_by_pcnt(ctx: Context, mode: DataMode) {
const DMA_BUFFER_SIZE: usize = 4;
let (rx, rxd, buffer, descriptors) = dma_buffers!(1, DMA_BUFFER_SIZE);
let dma_rx_buf = DmaRxBuf::new(rxd, rx).unwrap();
let dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap();
let unit = ctx.pcnt_unit;
let mut spi = ctx.spi.with_buffers(dma_rx_buf, dma_tx_buf);
unit.channel0.set_edge_signal(ctx.pcnt_source);
unit.channel0
.set_input_mode(EdgeMode::Hold, EdgeMode::Increment);
let buffer = [0b0110_1010; DMA_BUFFER_SIZE];
// Write the buffer where each byte has 3 pos edges.
spi.half_duplex_write(mode, Command::None, Address::None, 0, &buffer)
.unwrap();
assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _);
spi.half_duplex_write(mode, Command::None, Address::None, 0, &buffer)
.unwrap();
assert_eq!(unit.value(), (6 * DMA_BUFFER_SIZE) as _);
}
#[cfg(test)] #[cfg(test)]
#[embedded_test::tests(default_timeout = 3)] #[embedded_test::tests(default_timeout = 3)]
mod tests { mod tests {
@ -59,7 +134,7 @@ mod tests {
) )
.unwrap() .unwrap()
.with_sck(sclk) .with_sck(sclk)
.with_mosi(mosi) .with_sio0(mosi)
.with_dma(dma_channel); .with_dma(dma_channel);
Context { Context {
@ -71,78 +146,21 @@ mod tests {
#[test] #[test]
fn test_spi_writes_are_correctly_by_pcnt(ctx: Context) { fn test_spi_writes_are_correctly_by_pcnt(ctx: Context) {
const DMA_BUFFER_SIZE: usize = 4; super::perform_spi_writes_are_correctly_by_pcnt(ctx, DataMode::Single);
let (_, _, buffer, descriptors) = dma_buffers!(0, DMA_BUFFER_SIZE);
let mut dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap();
let unit = ctx.pcnt_unit;
let mut spi = ctx.spi;
unit.channel0.set_edge_signal(ctx.pcnt_source);
unit.channel0
.set_input_mode(EdgeMode::Hold, EdgeMode::Increment);
// Fill the buffer where each byte has 3 pos edges.
dma_tx_buf.fill(&[0b0110_1010; DMA_BUFFER_SIZE]);
let transfer = spi
.half_duplex_write(
DataMode::Single,
Command::None,
Address::None,
0,
dma_tx_buf.len(),
dma_tx_buf,
)
.map_err(|e| e.0)
.unwrap();
(spi, dma_tx_buf) = transfer.wait();
assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _);
let transfer = spi
.half_duplex_write(
DataMode::Single,
Command::None,
Address::None,
0,
dma_tx_buf.len(),
dma_tx_buf,
)
.map_err(|e| e.0)
.unwrap();
// dropping SPI would make us see an additional edge - so let's keep SPI alive
let (_spi, _) = transfer.wait();
assert_eq!(unit.value(), (6 * DMA_BUFFER_SIZE) as _);
} }
#[test] #[test]
fn test_spidmabus_writes_are_correctly_by_pcnt(ctx: Context) { fn test_spidmabus_writes_are_correctly_by_pcnt(ctx: Context) {
const DMA_BUFFER_SIZE: usize = 4; super::perform_spidmabus_writes_are_correctly_by_pcnt(ctx, DataMode::Single);
}
let (rx, rxd, buffer, descriptors) = dma_buffers!(1, DMA_BUFFER_SIZE); #[test]
let dma_rx_buf = DmaRxBuf::new(rxd, rx).unwrap(); fn test_spi_writes_are_correctly_by_pcnt_tree_wire(ctx: Context) {
let dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap(); super::perform_spi_writes_are_correctly_by_pcnt(ctx, DataMode::SingleThreeWire);
}
let unit = ctx.pcnt_unit; #[test]
let mut spi = ctx.spi.with_buffers(dma_rx_buf, dma_tx_buf); fn test_spidmabus_writes_are_correctly_by_pcnt_tree_wire(ctx: Context) {
super::perform_spidmabus_writes_are_correctly_by_pcnt(ctx, DataMode::SingleThreeWire);
unit.channel0.set_edge_signal(ctx.pcnt_source);
unit.channel0
.set_input_mode(EdgeMode::Hold, EdgeMode::Increment);
let buffer = [0b0110_1010; DMA_BUFFER_SIZE];
// Write the buffer where each byte has 3 pos edges.
spi.half_duplex_write(DataMode::Single, Command::None, Address::None, 0, &buffer)
.unwrap();
assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _);
spi.half_duplex_write(DataMode::Single, Command::None, Address::None, 0, &buffer)
.unwrap();
assert_eq!(unit.value(), (6 * DMA_BUFFER_SIZE) as _);
} }
} }

View File

@ -71,7 +71,7 @@ mod tests {
) )
.unwrap() .unwrap()
.with_sck(sclk) .with_sck(sclk)
.with_mosi(mosi) .with_sio0(mosi)
.with_dma(dma_channel); .with_dma(dma_channel);
Context { Context {

View File

@ -71,8 +71,8 @@ fn main() -> ! {
) )
.unwrap() .unwrap()
.with_sck(sclk) .with_sck(sclk)
.with_mosi(mosi) .with_sio0(mosi)
.with_miso(miso) .with_sio1(miso)
.with_sio2(sio2) .with_sio2(sio2)
.with_sio3(sio3) .with_sio3(sio3)
.with_cs(cs); .with_cs(cs);