Skip to content

Commit

Permalink
Implement SpiBus, SpiDevice for LPSPI
Browse files Browse the repository at this point in the history
We implement all I/O in terms of `transact`. We'll manually flush at the
end of eh02 SPI writes. Since the `SpiBus` interface exposes a flush, we
have no need to perform an internal flush when transacting I/O.

Keep the spinning async state machines to manage the TX and FX FIFOs.
We introduce a schedule to eagerly execute transmit operations ahead of
receive operations. Without this ability, we'll stall the LPSPI bus.
(The NOSTALL field can change this behavior, but then we move complexity
into TX FIFO underrun handling.) There's some miri tests to simulate our
hardware utilization.

Our `SpiDevice` impls mimic the embedded-bus design. There's an
"exclusive" device that owns the bus and uses a hardware chip select.
There's a `RefCellDevice` that lets users share the bus and use hardware
chip selects. There's no critical section / mutex device, but it should
be straightforward to add that later.
  • Loading branch information
mciantyre committed Dec 1, 2024
1 parent 130210e commit 1b1b64f
Show file tree
Hide file tree
Showing 5 changed files with 996 additions and 147 deletions.
4 changes: 4 additions & 0 deletions board/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ publish = false
[dependencies.defmt]
version = "0.3"

[dependencies.eh1]
package = "embedded-hal"
version = "1.0"

[dependencies.imxrt-hal]
workspace = true

Expand Down
15 changes: 8 additions & 7 deletions board/src/imxrt1170evk-cm7.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ pub type SpiPcs0 = iomuxc::gpio_ad::GPIO_AD_29;
const SPI_INSTANCE: u8 = 1;

#[cfg(feature = "spi")]
pub type Spi = hal::lpspi::Lpspi<SpiPins, { SPI_INSTANCE }>;
pub type Spi =
hal::lpspi::ExclusiveDevice<SpiPins, SpiPcs0, crate::PanickingDelay, { SPI_INSTANCE }>;
#[cfg(not(feature = "spi"))]
pub type Spi = ();

Expand Down Expand Up @@ -209,15 +210,15 @@ impl Specifics {
sdi: iomuxc.gpio_ad.p31,
sck: iomuxc.gpio_ad.p28,
};
crate::iomuxc::lpspi::prepare({
let pcs0: &mut SpiPcs0 = &mut iomuxc.gpio_ad.p29;
pcs0
});
let mut spi = Spi::new(lpspi1, pins);
let mut spi = hal::lpspi::Lpspi::new(lpspi1, pins);
spi.disabled(|spi| {
spi.set_clock_hz(LPSPI_CLK_FREQUENCY, super::SPI_BAUD_RATE_FREQUENCY);
});
spi
hal::lpspi::ExclusiveDevice::with_pcs0(
spi,
iomuxc.gpio_ad.p29,
crate::PanickingDelay::new(),
)
};
#[cfg(not(feature = "spi"))]
#[allow(clippy::let_unit_value)]
Expand Down
14 changes: 14 additions & 0 deletions board/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,20 @@ pub mod blocking {
}
}

pub struct PanickingDelay(());

impl PanickingDelay {
pub fn new() -> Self {
Self(())
}
}

impl eh1::delay::DelayNs for PanickingDelay {
fn delay_ns(&mut self, _: u32) {
unimplemented!()
}
}

/// Configurations for the logger.
///
/// If your board is ready to support the logging infrastructure,
Expand Down
53 changes: 38 additions & 15 deletions examples/rtic_spi_blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,26 +60,26 @@ mod app {
// size and bit order, use this sequence to evaluate how
// the driver packs your transfer elements.
{
use eh02::blocking::spi::Write;
use eh1::spi::SpiDevice;
use hal::lpspi::BitOrder::{self, *};

const BIT_ORDERS: [BitOrder; 2] = [Msb, Lsb];

const U32_WORDS: [u32; 2] = [0xDEADBEEFu32, 0xAD1CAC1D];
for bit_order in BIT_ORDERS {
spi.set_bit_order(bit_order);
spi.bus_mut().set_bit_order(bit_order);
spi.write(&U32_WORDS).unwrap();
}

const U8_WORDS: [u8; 7] = [0xDEu8, 0xAD, 0xBE, 0xEF, 0x12, 0x34, 0x56];
for bit_order in BIT_ORDERS {
spi.set_bit_order(bit_order);
spi.bus_mut().set_bit_order(bit_order);
spi.write(&U8_WORDS).unwrap();
}

const U16_WORDS: [u16; 3] = [0xDEADu16, 0xBEEF, 0x1234];
for bit_order in BIT_ORDERS {
spi.set_bit_order(bit_order);
spi.bus_mut().set_bit_order(bit_order);
spi.write(&U16_WORDS).unwrap();
}

Expand All @@ -88,12 +88,12 @@ mod app {

// Change me to explore bit order behavors in the
// remaining write / loopback transfer tests.
spi.set_bit_order(hal::lpspi::BitOrder::Msb);
spi.bus_mut().set_bit_order(hal::lpspi::BitOrder::Msb);

// Make sure concatenated elements look correct on the wire.
// Make sure we can read those elements.
{
use eh02::blocking::spi::Transfer;
use eh1::spi::SpiDevice;
use hal::lpspi::BitOrder;

macro_rules! transfer_test {
Expand All @@ -103,9 +103,9 @@ mod app {
BitOrder::Lsb => "LSB",
};

spi.set_bit_order($bit_order);
spi.bus_mut().set_bit_order($bit_order);
let mut buffer = $arr;
spi.transfer(&mut buffer).unwrap();
spi.transfer_in_place(&mut buffer).unwrap();
defmt::assert_eq!(buffer, $arr, "Bit Order {}", bit_order_name);
};
}
Expand Down Expand Up @@ -137,12 +137,12 @@ mod app {
transfer_test!([0x01020304u32, 0x05060708, 0x090A0B0C], BitOrder::Msb);
transfer_test!([0x01020304u32, 0x05060708, 0x090A0B0C], BitOrder::Lsb);

spi.set_bit_order(BitOrder::Msb);
spi.bus_mut().set_bit_order(BitOrder::Msb);
delay();
}

{
use eh02::blocking::spi::{Transfer, Write};
use eh1::spi::SpiDevice;

// Change me to test different Elem sizes, buffer sizes,
// bit patterns.
Expand All @@ -153,10 +153,8 @@ mod app {
// Simple loopback transfer. Easy to find with your
// scope.
let mut buffer = BUFFER;
spi.transfer(&mut buffer).unwrap();
if buffer != BUFFER {
defmt::error!("Simple transfer buffer mismatch!");
}
spi.transfer_in_place(&mut buffer).unwrap();
defmt::assert_eq!(buffer, BUFFER);

delay();

Expand All @@ -167,7 +165,7 @@ mod app {
for idx in 0u32..16 {
buffer.fill(SENTINEL.rotate_right(idx));
let expected = buffer;
spi.transfer(&mut buffer).unwrap();
spi.transfer_in_place(&mut buffer).unwrap();
error |= buffer != expected;
}
if error {
Expand All @@ -194,6 +192,31 @@ mod app {

delay();
}

{
use eh1::spi::{
Operation::{Read, TransferInPlace, Write},
SpiDevice,
};

let mut read = [0u8; 7];
let mut xfer = [0u8; 10];
for idx in 0..xfer.len() {
xfer[idx] = idx as u8;
}

spi.transaction(&mut [
TransferInPlace(&mut xfer),
Read(&mut read),
Write(&[0xA5; 13][..]),
])
.unwrap();

assert_eq!(read, [0xff; 7]);
assert_eq!(xfer, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

delay();
}
}
}
}
Loading

0 comments on commit 1b1b64f

Please sign in to comment.