Skip to content

Commit

Permalink
Support 3-wire-SPI
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoernQ committed Jan 9, 2025
1 parent 0ef0020 commit 4744fa8
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 85 deletions.
31 changes: 28 additions & 3 deletions esp-hal/src/spi/master.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 8 additions & 4 deletions esp-hal/src/spi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down Expand Up @@ -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,
}

Expand Down
14 changes: 7 additions & 7 deletions hil-test/tests/qspi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,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);
}
Expand All @@ -240,7 +240,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);
}
Expand Down Expand Up @@ -270,7 +270,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);
}
Expand Down Expand Up @@ -323,7 +323,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);
}
Expand Down Expand Up @@ -354,7 +354,7 @@ mod tests {

let spi = ctx
.spi
.with_mosi(mosi)
.with_sio0(mosi)
.with_sio1(gpio)
.with_dma(ctx.dma_channel);

Expand Down Expand Up @@ -387,7 +387,7 @@ mod tests {

let spi = ctx
.spi
.with_mosi(mosi)
.with_sio0(mosi)
.with_sio2(gpio)
.with_dma(ctx.dma_channel);

Expand Down Expand Up @@ -420,7 +420,7 @@ mod tests {

let spi = ctx
.spi
.with_mosi(mosi)
.with_sio0(mosi)
.with_sio3(gpio)
.with_dma(ctx.dma_channel);

Expand Down
154 changes: 86 additions & 68 deletions hil-test/tests/spi_half_duplex_write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,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 {
Expand Down Expand Up @@ -58,7 +133,7 @@ mod tests {
)
.unwrap()
.with_sck(sclk)
.with_mosi(mosi)
.with_sio0(mosi)
.with_dma(dma_channel);

Context {
Expand All @@ -70,78 +145,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;

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(DataMode::Single, Command::None, Address::None, 0, &buffer)
.unwrap();

assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _);
super::perform_spidmabus_writes_are_correctly_by_pcnt(ctx, DataMode::Single);
}

spi.half_duplex_write(DataMode::Single, Command::None, Address::None, 0, &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);
}

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);
}
}
2 changes: 1 addition & 1 deletion hil-test/tests/spi_half_duplex_write_psram.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ mod tests {
)
.unwrap()
.with_sck(sclk)
.with_mosi(mosi)
.with_sio0(mosi)
.with_dma(dma_channel);

Context {
Expand Down
4 changes: 2 additions & 2 deletions qa-test/src/bin/spi_halfduplex_read_manufacturer_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 4744fa8

Please sign in to comment.