>,
+ dma: PeripheralRef<'d, Dma>,
+ config: Config,
+}
+
+impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
+ pub fn new(
+ peri: impl Peripheral + 'd,
+ d0: impl Peripheral
> + 'd,
+ d1: impl Peripheral
> + 'd,
+ d2: impl Peripheral
> + 'd,
+ d3: impl Peripheral
> + 'd,
+ sck: impl Peripheral
> + 'd,
+ nss: impl Peripheral
> + 'd,
+ dma: impl Peripheral
+ 'd,
+ config: Config,
+ ) -> Self {
+ into_ref!(peri, d0, d1, d2, d3, sck, nss);
+
+ unsafe {
+ sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
+ sck.set_speed(crate::gpio::Speed::VeryHigh);
+ nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
+ nss.set_speed(crate::gpio::Speed::VeryHigh);
+ d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
+ d0.set_speed(crate::gpio::Speed::VeryHigh);
+ d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
+ d1.set_speed(crate::gpio::Speed::VeryHigh);
+ d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
+ d2.set_speed(crate::gpio::Speed::VeryHigh);
+ d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
+ d3.set_speed(crate::gpio::Speed::VeryHigh);
+ }
+
+ Self::new_inner(
+ peri,
+ Some(d0.map_into()),
+ Some(d1.map_into()),
+ Some(d2.map_into()),
+ Some(d3.map_into()),
+ Some(sck.map_into()),
+ Some(nss.map_into()),
+ dma,
+ config,
+ )
+ }
+
+ fn new_inner(
+ peri: impl Peripheral
+ 'd,
+ d0: Option>,
+ d1: Option>,
+ d2: Option>,
+ d3: Option>,
+ sck: Option>,
+ nss: Option>,
+ dma: impl Peripheral + 'd,
+ config: Config,
+ ) -> Self {
+ into_ref!(peri, dma);
+
+ T::enable();
+ unsafe {
+ T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
+
+ while T::REGS.sr().read().busy() {}
+
+ T::REGS.cr().write(|w| {
+ w.set_prescaler(config.prescaler);
+ w.set_en(true);
+ });
+ T::REGS.dcr().write(|w| {
+ w.set_fsize(config.memory_size.into());
+ w.set_csht(config.cs_high_time.into());
+ w.set_ckmode(false);
+ });
+ }
+
+ Self {
+ _peri: peri,
+ sck,
+ d0,
+ d1,
+ d2,
+ d3,
+ nss,
+ dma,
+ config,
+ }
+ }
+
+ pub fn command(&mut self, transaction: TransferConfig) {
+ unsafe {
+ T::REGS.cr().modify(|v| v.set_dmaen(false));
+ self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+
+ while !T::REGS.sr().read().tcf() {}
+ T::REGS.fcr().modify(|v| v.set_ctcf(true));
+ }
+ }
+
+ pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
+ unsafe {
+ T::REGS.cr().modify(|v| v.set_dmaen(false));
+ self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+
+ if let Some(len) = transaction.data_len {
+ let current_ar = T::REGS.ar().read().address();
+ T::REGS.ccr().modify(|v| {
+ v.set_fmode(QspiMode::IndirectRead.into());
+ });
+ T::REGS.ar().write(|v| {
+ v.set_address(current_ar);
+ });
+
+ for idx in 0..len {
+ while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
+ buf[idx] = *(T::REGS.dr().ptr() as *mut u8);
+ }
+ }
+
+ while !T::REGS.sr().read().tcf() {}
+ T::REGS.fcr().modify(|v| v.set_ctcf(true));
+ }
+ }
+
+ pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
+ unsafe {
+ T::REGS.cr().modify(|v| v.set_dmaen(false));
+ self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+
+ if let Some(len) = transaction.data_len {
+ T::REGS.ccr().modify(|v| {
+ v.set_fmode(QspiMode::IndirectWrite.into());
+ });
+
+ for idx in 0..len {
+ while !T::REGS.sr().read().ftf() {}
+ *(T::REGS.dr().ptr() as *mut u8) = buf[idx];
+ }
+ }
+
+ while !T::REGS.sr().read().tcf() {}
+ T::REGS.fcr().modify(|v| v.set_ctcf(true));
+ }
+ }
+
+ pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
+ where
+ Dma: QuadDma,
+ {
+ unsafe {
+ self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+
+ let request = self.dma.request();
+ let options = TransferOptions::default();
+
+ T::REGS.ccr().modify(|v| {
+ v.set_fmode(QspiMode::IndirectRead.into());
+ });
+ let current_ar = T::REGS.ar().read().address();
+ T::REGS.ar().write(|v| {
+ v.set_address(current_ar);
+ });
+
+ self.dma
+ .start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options);
+
+ T::REGS.cr().modify(|v| v.set_dmaen(true));
+
+ while self.dma.is_running() {}
+ }
+ }
+
+ pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
+ where
+ Dma: QuadDma,
+ {
+ unsafe {
+ self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+
+ let request = self.dma.request();
+ let options = TransferOptions::default();
+
+ T::REGS.ccr().modify(|v| {
+ v.set_fmode(QspiMode::IndirectWrite.into());
+ });
+
+ self.dma
+ .start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options);
+
+ T::REGS.cr().modify(|v| v.set_dmaen(true));
+
+ while self.dma.is_running() {}
+ }
+ }
+
+ fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
+ unsafe {
+ T::REGS.fcr().modify(|v| {
+ v.set_csmf(true);
+ v.set_ctcf(true);
+ v.set_ctef(true);
+ v.set_ctof(true);
+ });
+
+ while T::REGS.sr().read().busy() {}
+
+ if let Some(len) = transaction.data_len {
+ T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
+ }
+
+ T::REGS.ccr().write(|v| {
+ v.set_fmode(fmode.into());
+ v.set_imode(transaction.iwidth.into());
+ v.set_instruction(transaction.instruction);
+ v.set_admode(transaction.awidth.into());
+ v.set_adsize(self.config.address_size.into());
+ v.set_dmode(transaction.dwidth.into());
+ v.set_abmode(QspiWidth::NONE.into());
+ v.set_dcyc(transaction.dummy.into());
+ });
+
+ if let Some(addr) = transaction.address {
+ T::REGS.ar().write(|v| {
+ v.set_address(addr);
+ });
+ }
+ }
+ }
+}
+
+pub(crate) mod sealed {
+ use super::*;
+
+ pub trait Instance {
+ const REGS: Regs;
+ }
+}
+
+pub trait Instance: Peripheral + sealed::Instance + RccPeripheral {}
+
+pin_trait!(SckPin, Instance);
+pin_trait!(D0Pin, Instance);
+pin_trait!(D1Pin, Instance);
+pin_trait!(D2Pin, Instance);
+pin_trait!(D3Pin, Instance);
+pin_trait!(NSSPin, Instance);
+
+dma_trait!(QuadDma, Instance);
+
+foreach_peripheral!(
+ (quadspi, $inst:ident) => {
+ impl sealed::Instance for peripherals::$inst {
+ const REGS: Regs = crate::pac::$inst;
+ }
+
+ impl Instance for peripherals::$inst {}
+ };
+);
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index a27fcc1ca..cd7d72f91 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -197,6 +197,40 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
.await
}
+ fn inner_blocking_read(&self, buf: &mut [u8]) -> Result {
+ loop {
+ let mut do_pend = false;
+ let mut inner = self.inner.borrow_mut();
+ let n = inner.with(|state| {
+ compiler_fence(Ordering::SeqCst);
+
+ // We have data ready in buffer? Return it.
+ let data = state.rx.pop_buf();
+ if !data.is_empty() {
+ let len = data.len().min(buf.len());
+ buf[..len].copy_from_slice(&data[..len]);
+
+ if state.rx.is_full() {
+ do_pend = true;
+ }
+ state.rx.pop(len);
+
+ return len;
+ }
+
+ 0
+ });
+
+ if do_pend {
+ inner.pend();
+ }
+
+ if n > 0 {
+ return Ok(n);
+ }
+ }
+ }
+
async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result {
poll_fn(move |cx| {
let mut inner = self.inner.borrow_mut();
@@ -236,6 +270,39 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
.await
}
+ fn inner_blocking_write(&self, buf: &[u8]) -> Result {
+ loop {
+ let mut inner = self.inner.borrow_mut();
+ let (n, empty) = inner.with(|state| {
+ let empty = state.tx.is_empty();
+ let tx_buf = state.tx.push_buf();
+ if tx_buf.is_empty() {
+ return (0, empty);
+ }
+
+ let n = core::cmp::min(tx_buf.len(), buf.len());
+ tx_buf[..n].copy_from_slice(&buf[..n]);
+ state.tx.push(n);
+
+ (n, empty)
+ });
+ if empty {
+ inner.pend();
+ }
+ if n != 0 {
+ return Ok(n);
+ }
+ }
+ }
+
+ fn inner_blocking_flush(&self) -> Result<(), Error> {
+ loop {
+ if !self.inner.borrow_mut().with(|state| state.tx.is_empty()) {
+ return Ok(());
+ }
+ }
+ }
+
async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], Error> {
poll_fn(move |cx| {
self.inner.borrow_mut().with(|state| {
@@ -419,3 +486,35 @@ impl<'u, 'd, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'u,
self.inner.inner_flush().await
}
}
+
+impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> {
+ fn read(&mut self, buf: &mut [u8]) -> Result {
+ self.inner_blocking_read(buf)
+ }
+}
+
+impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'u, 'd, T> {
+ fn read(&mut self, buf: &mut [u8]) -> Result {
+ self.inner.inner_blocking_read(buf)
+ }
+}
+
+impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> {
+ fn write(&mut self, buf: &[u8]) -> Result {
+ self.inner_blocking_write(buf)
+ }
+
+ fn flush(&mut self) -> Result<(), Self::Error> {
+ self.inner_blocking_flush()
+ }
+}
+
+impl<'u, 'd, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'u, 'd, T> {
+ fn write(&mut self, buf: &[u8]) -> Result {
+ self.inner.inner_blocking_write(buf)
+ }
+
+ fn flush(&mut self) -> Result<(), Self::Error> {
+ self.inner.inner_blocking_flush()
+ }
+}
diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs
index 1977005fb..ee27cdec8 100644
--- a/embassy-sync/src/pipe.rs
+++ b/embassy-sync/src/pipe.rs
@@ -32,16 +32,16 @@ impl<'p, M, const N: usize> Writer<'p, M, N>
where
M: RawMutex,
{
- /// Writes a value.
+ /// Write some bytes to the pipe.
///
/// See [`Pipe::write()`]
pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> {
self.pipe.write(buf)
}
- /// Attempt to immediately write a message.
+ /// Attempt to immediately write some bytes to the pipe.
///
- /// See [`Pipe::write()`]
+ /// See [`Pipe::try_write()`]
pub fn try_write(&self, buf: &[u8]) -> Result {
self.pipe.try_write(buf)
}
@@ -95,16 +95,16 @@ impl<'p, M, const N: usize> Reader<'p, M, N>
where
M: RawMutex,
{
- /// Reads a value.
+ /// Read some bytes from the pipe.
///
/// See [`Pipe::read()`]
pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> {
self.pipe.read(buf)
}
- /// Attempt to immediately read a message.
+ /// Attempt to immediately read some bytes from the pipe.
///
- /// See [`Pipe::read()`]
+ /// See [`Pipe::try_read()`]
pub fn try_read(&self, buf: &mut [u8]) -> Result {
self.pipe.try_read(buf)
}
@@ -221,12 +221,11 @@ impl PipeState {
}
}
-/// A bounded pipe for communicating between asynchronous tasks
+/// A bounded byte-oriented pipe for communicating between asynchronous tasks
/// with backpressure.
///
-/// The pipe will buffer up to the provided number of messages. Once the
-/// buffer is full, attempts to `write` new messages will wait until a message is
-/// read from the pipe.
+/// The pipe will buffer up to the provided number of bytes. Once the
+/// buffer is full, attempts to `write` new bytes will wait until buffer space is freed up.
///
/// All data written will become available in the same order as it was written.
pub struct Pipe
@@ -277,40 +276,56 @@ where
Reader { pipe: self }
}
- /// Write a value, waiting until there is capacity.
+ /// Write some bytes to the pipe.
///
- /// Writeing completes when the value has been pushed to the pipe's queue.
- /// This doesn't mean the value has been read yet.
+ /// This method writes a nonzero amount of bytes from `buf` into the pipe, and
+ /// returns the amount of bytes written.
+ ///
+ /// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full,
+ /// this method will wait until it is. See [`try_write`](Self::try_write) for a variant that
+ /// returns an error instead of waiting.
+ ///
+ /// It is not guaranteed that all bytes in the buffer are written, even if there's enough
+ /// free space in the pipe buffer for all. In other words, it is possible for `write` to return
+ /// without writing all of `buf` (returning a number less than `buf.len()`) and still leave
+ /// free space in the pipe buffer. You should always `write` in a loop, or use helpers like
+ /// `write_all` from the `embedded-io` crate.
pub fn write<'a>(&'a self, buf: &'a [u8]) -> WriteFuture<'a, M, N> {
WriteFuture { pipe: self, buf }
}
- /// Attempt to immediately write a message.
+ /// Attempt to immediately write some bytes to the pipe.
///
- /// This method differs from [`write`](Pipe::write) by returning immediately if the pipe's
- /// buffer is full, instead of waiting.
- ///
- /// # Errors
- ///
- /// If the pipe capacity has been reached, i.e., the pipe has `n`
- /// buffered values where `n` is the argument passed to [`Pipe`], then an
- /// error is returned.
+ /// This method will either write a nonzero amount of bytes to the pipe immediately,
+ /// or return an error if the pipe is empty. See [`write`](Self::write) for a variant
+ /// that waits instead of returning an error.
pub fn try_write(&self, buf: &[u8]) -> Result {
self.lock(|c| c.try_write(buf))
}
- /// Receive the next value.
+ /// Read some bytes from the pipe.
///
- /// If there are no messages in the pipe's buffer, this method will
- /// wait until a message is written.
+ /// This method reads a nonzero amount of bytes from the pipe into `buf` and
+ /// returns the amount of bytes read.
+ ///
+ /// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty,
+ /// this method will wait until it is. See [`try_read`](Self::try_read) for a variant that
+ /// returns an error instead of waiting.
+ ///
+ /// It is not guaranteed that all bytes in the buffer are read, even if there's enough
+ /// space in `buf` for all. In other words, it is possible for `read` to return
+ /// without filling `buf` (returning a number less than `buf.len()`) and still leave bytes
+ /// in the pipe buffer. You should always `read` in a loop, or use helpers like
+ /// `read_exact` from the `embedded-io` crate.
pub fn read<'a>(&'a self, buf: &'a mut [u8]) -> ReadFuture<'a, M, N> {
ReadFuture { pipe: self, buf }
}
- /// Attempt to immediately read a message.
+ /// Attempt to immediately read some bytes from the pipe.
///
- /// This method will either read a message from the pipe immediately or return an error
- /// if the pipe is empty.
+ /// This method will either read a nonzero amount of bytes from the pipe immediately,
+ /// or return an error if the pipe is empty. See [`read`](Self::read) for a variant
+ /// that waits instead of returning an error.
pub fn try_read(&self, buf: &mut [u8]) -> Result {
self.lock(|c| c.try_read(buf))
}
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index 305dfa02e..6b68bcd7b 100644
--- a/embassy-usb/src/builder.rs
+++ b/embassy-usb/src/builder.rs
@@ -201,6 +201,14 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
self.config_descriptor.end_configuration();
self.bos_descriptor.end_bos();
+ // Log the number of allocator bytes actually used in descriptor buffers
+ info!("USB: device_descriptor used: {}", self.device_descriptor.position());
+ info!("USB: config_descriptor used: {}", self.config_descriptor.position());
+ info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
+ #[cfg(feature = "msos-descriptor")]
+ info!("USB: msos_descriptor used: {}", msos_descriptor.len());
+ info!("USB: control_buf size: {}", self.control_buf.len());
+
UsbDevice::build(
self.driver,
self.config,
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs
index 974268c62..03e4c1dbb 100644
--- a/embassy-usb/src/class/hid.rs
+++ b/embassy-usb/src/class/hid.rs
@@ -458,6 +458,9 @@ impl<'d> Handler for Control<'d> {
return None;
}
+ // This uses a defmt-specific formatter that causes use of the `log`
+ // feature to fail to build, so leave it defmt-specific for now.
+ #[cfg(feature = "defmt")]
trace!("HID control_out {:?} {=[u8]:x}", req, data);
match req.request {
HID_REQ_SET_IDLE => {
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index bfeccd5fe..3016b81cb 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -165,6 +165,25 @@ struct Interface {
num_alt_settings: u8,
}
+/// A report of the used size of the runtime allocated buffers
+#[derive(PartialEq, Eq, Copy, Clone, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct UsbBufferReport {
+ /// Number of device descriptor bytes used
+ pub device_descriptor_used: usize,
+ /// Number of config descriptor bytes used
+ pub config_descriptor_used: usize,
+ /// Number of bos descriptor bytes used
+ pub bos_descriptor_used: usize,
+ /// Number of msos descriptor bytes used
+ ///
+ /// Will be `None` if the "msos-descriptor" feature is not active.
+ /// Otherwise will return Some(bytes).
+ pub msos_descriptor_used: Option,
+ /// Size of the control buffer
+ pub control_buffer_size: usize,
+}
+
/// Main struct for the USB device stack.
pub struct UsbDevice<'d, D: Driver<'d>> {
control_buf: &'d mut [u8],
@@ -239,6 +258,24 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
}
}
+ /// Returns a report of the consumed buffers
+ ///
+ /// Useful for tuning buffer sizes for actual usage
+ pub fn buffer_usage(&self) -> UsbBufferReport {
+ #[cfg(not(feature = "msos-descriptor"))]
+ let mdu = None;
+ #[cfg(feature = "msos-descriptor")]
+ let mdu = Some(self.inner.msos_descriptor.len());
+
+ UsbBufferReport {
+ device_descriptor_used: self.inner.device_descriptor.len(),
+ config_descriptor_used: self.inner.config_descriptor.len(),
+ bos_descriptor_used: self.inner.bos_descriptor.len(),
+ msos_descriptor_used: mdu,
+ control_buffer_size: self.control_buf.len(),
+ }
+ }
+
/// Runs the `UsbDevice` forever.
///
/// This future may leave the bus in an invalid state if it is dropped.
diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs
index b1e0335ee..218d9931a 100644
--- a/embassy-usb/src/msos.rs
+++ b/embassy-usb/src/msos.rs
@@ -32,6 +32,11 @@ impl<'d> MsOsDescriptorSet<'d> {
pub fn is_empty(&self) -> bool {
self.descriptor.is_empty()
}
+
+ /// Returns the length of the descriptor field
+ pub fn len(&self) -> usize {
+ self.descriptor.len()
+ }
}
/// Writes a Microsoft OS 2.0 Descriptor set into a buffer.
diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs
index 6c85ef60a..2e22c9de7 100644
--- a/tests/rp/src/bin/spi_async.rs
+++ b/tests/rp/src/bin/spi_async.rs
@@ -1,3 +1,6 @@
+//! Make sure to connect GPIO pins 3 (`PIN_3`) and 4 (`PIN_4`) together
+//! to run this test.
+//!
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
@@ -18,10 +21,63 @@ async fn main(_spawner: Spawner) {
let mut spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default());
- let tx_buf = [1_u8, 2, 3, 4, 5, 6];
- let mut rx_buf = [0_u8; 6];
- spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
- assert_eq!(rx_buf, tx_buf);
+ // equal rx & tx buffers
+ {
+ let tx_buf = [1_u8, 2, 3, 4, 5, 6];
+ let mut rx_buf = [0_u8; 6];
+ spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
+ assert_eq!(rx_buf, tx_buf);
+ }
+
+ // tx > rx buffer
+ {
+ let tx_buf = [7_u8, 8, 9, 10, 11, 12];
+
+ let mut rx_buf = [0_u8; 3];
+ spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
+ assert_eq!(rx_buf, tx_buf[..3]);
+
+ defmt::info!("tx > rx buffer - OK");
+ }
+
+ // we make sure to that clearing FIFO works after the uneven buffers
+
+ // equal rx & tx buffers
+ {
+ let tx_buf = [13_u8, 14, 15, 16, 17, 18];
+ let mut rx_buf = [0_u8; 6];
+ spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
+ assert_eq!(rx_buf, tx_buf);
+
+ defmt::info!("buffer rx length == tx length - OK");
+ }
+
+ // rx > tx buffer
+ {
+ let tx_buf = [19_u8, 20, 21];
+ let mut rx_buf = [0_u8; 6];
+
+ // we should have written dummy data to tx buffer to sync clock.
+ spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
+
+ assert_eq!(
+ rx_buf[..3],
+ tx_buf,
+ "only the first 3 TX bytes should have been received in the RX buffer"
+ );
+ assert_eq!(rx_buf[3..], [0, 0, 0], "the rest of the RX bytes should be empty");
+ defmt::info!("buffer rx length > tx length - OK");
+ }
+
+ // equal rx & tx buffers
+ {
+ let tx_buf = [22_u8, 23, 24, 25, 26, 27];
+ let mut rx_buf = [0_u8; 6];
+ spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
+
+ assert_eq!(rx_buf, tx_buf);
+ defmt::info!("buffer rx length = tx length - OK");
+ }
info!("Test OK");
cortex_m::asm::bkpt();