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)
- `rtc_cntl::{RtcFastClock, RtcSlowClock, RtcCalSel}` now implement `PartialEq`, `Eq`, `Hash` and `defmt::Format` (#2840)
- 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

View File

@ -552,8 +552,8 @@ impl<'d> Spi<'d, Blocking> {
this.apply_config(&config)?;
let this = this
.with_mosi(NoPin)
.with_miso(NoPin)
.with_sio0(NoPin)
.with_sio1(NoPin)
.with_sck(NoPin)
.with_cs(NoPin);
@ -645,9 +645,21 @@ where
{
/// 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
/// 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);
mosi.enable_output(true, private::Internal);
mosi.enable_input(true, private::Internal);
@ -3123,12 +3135,25 @@ impl Driver {
no_mosi_miso: bool,
data_mode: DataMode,
) -> 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)?;
let reg_block = self.register_block();
reg_block.user().modify(|_, w| {
w.usr_miso_highpart().clear_bit();
w.usr_mosi_highpart().clear_bit();
w.sio().bit(three_wire);
w.doutdin().clear_bit();
w.usr_miso().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
/// implementation.
Unsupported,
/// The given arguments are invalid.
ArgumentsInvalid,
/// An unknown error occurred during SPI communication.
Unknown,
}
@ -98,14 +100,16 @@ pub enum BitOrder {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
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,
/// `Dual` Data Mode - 2 bit, 2 wires
/// `Dual` Data Mode - 2 bits, two data lines. (SIO0, SIO1)
Dual,
/// `Quad` Data Mode - 4 bit, 4 wires
/// `Quad` Data Mode - 4 bit, 4 data lines. (SIO0 .. SIO3)
Quad,
#[cfg(spi_octal)]
/// `Octal` Data Mode - 8 bit, 8 wires
/// `Octal` Data Mode - 8 bit, 8 data lines. (SIO0 .. SIO7)
Octal,
}

View File

@ -231,7 +231,7 @@ mod tests {
let [pin, pin_mirror, _] = ctx.gpios;
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);
}
@ -241,7 +241,7 @@ mod tests {
let [pin, pin_mirror, _] = ctx.gpios;
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);
}
@ -271,7 +271,7 @@ mod tests {
let [pin, pin_mirror, _] = ctx.gpios;
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);
}
@ -324,7 +324,7 @@ mod tests {
.channel0
.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);
}
@ -355,7 +355,7 @@ mod tests {
let spi = ctx
.spi
.with_mosi(mosi)
.with_sio0(mosi)
.with_sio1(gpio)
.with_dma(ctx.dma_channel);
@ -388,7 +388,7 @@ mod tests {
let spi = ctx
.spi
.with_mosi(mosi)
.with_sio0(mosi)
.with_sio2(gpio)
.with_dma(ctx.dma_channel);
@ -421,7 +421,7 @@ mod tests {
let spi = ctx
.spi
.with_mosi(mosi)
.with_sio0(mosi)
.with_sio3(gpio)
.with_dma(ctx.dma_channel);

View File

@ -27,6 +27,81 @@ struct Context {
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)]
#[embedded_test::tests(default_timeout = 3)]
mod tests {
@ -59,7 +134,7 @@ mod tests {
)
.unwrap()
.with_sck(sclk)
.with_mosi(mosi)
.with_sio0(mosi)
.with_dma(dma_channel);
Context {
@ -71,78 +146,21 @@ mod tests {
#[test]
fn test_spi_writes_are_correctly_by_pcnt(ctx: Context) {
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(
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 _);
super::perform_spi_writes_are_correctly_by_pcnt(ctx, DataMode::Single);
}
#[test]
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);
let dma_rx_buf = DmaRxBuf::new(rxd, rx).unwrap();
let dma_tx_buf = DmaTxBuf::new(descriptors, buffer).unwrap();
#[test]
fn test_spi_writes_are_correctly_by_pcnt_tree_wire(ctx: Context) {
super::perform_spi_writes_are_correctly_by_pcnt(ctx, DataMode::SingleThreeWire);
}
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(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 _);
#[test]
fn test_spidmabus_writes_are_correctly_by_pcnt_tree_wire(ctx: Context) {
super::perform_spidmabus_writes_are_correctly_by_pcnt(ctx, DataMode::SingleThreeWire);
}
}

View File

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

View File

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