Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix double writes #2159

Merged
merged 2 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix i2c embedded-hal transaction (#2028)
- Fix SPI DMA alternating `write` and `read` for ESP32 and ESP32-S2 (#2131)
- Fix I2C ending up in a state when only re-creating the peripheral makes it useable again (#2141)
- Fix `SpiBus::transfer` transferring data twice in some cases (#2159)

### Removed

Expand Down
9 changes: 5 additions & 4 deletions esp-hal/src/spi/master.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2143,9 +2143,9 @@ mod ehal1 {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
// Optimizations
if read.is_empty() {
SpiBus::write(self, write)?;
return SpiBus::write(self, write);
} else if write.is_empty() {
SpiBus::read(self, read)?;
return SpiBus::read(self, read);
}

let mut write_from = 0;
Expand All @@ -2163,6 +2163,8 @@ mod ehal1 {
break;
}

// No need to flush here, `SpiBus::write` will do it for us

if write_to < read_to {
// Read more than we write, must pad writing part with zeros
let mut empty = [EMPTY_WRITE_PAD; FIFO_SIZE];
Expand All @@ -2172,9 +2174,8 @@ mod ehal1 {
SpiBus::write(self, &write[write_from..write_to])?;
}

SpiBus::flush(self)?;

if read_inc > 0 {
SpiBus::flush(self)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gratis technicality: if we read less than we write, we are allowed to write the rest in the background.

self.spi
.read_bytes_from_fifo(&mut read[read_from..read_to])?;
}
Expand Down
63 changes: 61 additions & 2 deletions hil-test/tests/spi_full_duplex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@
#![no_main]

use embedded_hal::spi::SpiBus;
#[cfg(pcnt)]
use esp_hal::{
gpio::interconnect::InputSignal,
pcnt::{channel::EdgeMode, unit::Unit, Pcnt},
};
use esp_hal::{
gpio::Io,
peripherals::SPI2,
prelude::*,
spi::{master::Spi, FullDuplexMode, SpiMode},
};
use hil_test as _;

struct Context {
spi: Spi<'static, esp_hal::peripherals::SPI2, FullDuplexMode>,
spi: Spi<'static, SPI2, FullDuplexMode>,
#[cfg(pcnt)]
pcnt_source: InputSignal,
#[cfg(pcnt)]
pcnt_unit: Unit<'static, 0>,
}

#[cfg(test)]
Expand All @@ -34,12 +44,23 @@ mod tests {
let (_, mosi) = hil_test::common_test_pins!(io);

let mosi_loopback = mosi.peripheral_input();
#[cfg(pcnt)]
let mosi_loopback_pcnt = mosi.peripheral_input();
let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0)
.with_sck(sclk)
.with_mosi(mosi)
.with_miso(mosi_loopback);

Context { spi }
#[cfg(pcnt)]
let pcnt = Pcnt::new(peripherals.PCNT);

Context {
spi,
#[cfg(pcnt)]
pcnt_source: mosi_loopback_pcnt,
#[cfg(pcnt)]
pcnt_unit: pcnt.unit0,
}
}

#[test]
Expand All @@ -65,6 +86,44 @@ mod tests {
assert_eq!(read[2], 0x00u8);
}

#[test]
#[timeout(3)]
#[cfg(pcnt)]
fn test_asymmetric_write(mut ctx: Context) {
let write = [0xde, 0xad, 0xbe, 0xef];

let unit = ctx.pcnt_unit;

unit.channel0.set_edge_signal(ctx.pcnt_source);
unit.channel0
.set_input_mode(EdgeMode::Hold, EdgeMode::Increment);

SpiBus::write(&mut ctx.spi, &write[..]).expect("Asymmetric write failed");
// Flush because we're not reading, so the write may happen in the background
ctx.spi.flush().expect("Flush failed");

assert_eq!(unit.get_value(), 9);
}

#[test]
#[timeout(3)]
#[cfg(pcnt)]
fn test_asymmetric_write_transfer(mut ctx: Context) {
let write = [0xde, 0xad, 0xbe, 0xef];

let unit = ctx.pcnt_unit;

unit.channel0.set_edge_signal(ctx.pcnt_source);
unit.channel0
.set_input_mode(EdgeMode::Hold, EdgeMode::Increment);

SpiBus::transfer(&mut ctx.spi, &mut [], &write[..]).expect("Asymmetric transfer failed");
// Flush because we're not reading, so the write may happen in the background
ctx.spi.flush().expect("Flush failed");

assert_eq!(unit.get_value(), 9);
}

#[test]
#[timeout(3)]
fn test_symmetric_transfer_huge_buffer(mut ctx: Context) {
Expand Down
2 changes: 1 addition & 1 deletion hil-test/tests/spi_full_duplex_dma_pcnt.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! SPI Full Duplex DMA ASYNC Test with PCNT readback.
//! SPI Full Duplex DMA Test with PCNT readback.
//% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3

Expand Down