Skip to content

Commit d04eec8

Browse files
committed
spi: better FIFO prefill
1 parent 23cc395 commit d04eec8

File tree

2 files changed

+53
-24
lines changed

2 files changed

+53
-24
lines changed

examples/spi-hal-one.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,17 @@ fn main() -> ! {
4343
let miso = gpioa.pa6.into_alternate();
4444
let mosi = gpioa.pa7.into_alternate();
4545

46-
// 1/8 SPI/SysClk ratio seems to be the upper limit for continuous transmission one bit at a
47-
// time
46+
// 1/8 SPI/SysClk ratio seems to be the upper limit for continuous transmission
47+
// one byte at a time
48+
// 1/4 works for the first ~5 bytes (+4 prefilled), then we hit cpu limits
4849
let mut spi = dp
4950
.SPI1
5051
.spi((sclk, miso, mosi), spi::MODE_0, 2.MHz(), &mut rcc);
5152
let mut cs = gpioa.pa8.into_push_pull_output();
5253
cs.set_high().unwrap();
5354

5455
// "Hello world!"
55-
const MESSAGE: &[u8] = "Hello world!".as_bytes();
56+
const MESSAGE: &[u8] = "Hello world, but longer!".as_bytes();
5657
let received = &mut [0u8; MESSAGE.len()];
5758

5859
cs.set_low().unwrap();
@@ -64,6 +65,7 @@ fn main() -> ! {
6465
embedded_hal::blocking::spi::Write::write(&mut spi, MESSAGE).unwrap();
6566
cs.set_high().unwrap();
6667
info!("Received {:?}", core::str::from_utf8(received).ok());
68+
assert_eq!(MESSAGE, received);
6769

6870
loop {
6971
cortex_m::asm::nop();

src/spi.rs

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,14 @@ macro_rules! spi {
267267
.cr1
268268
.modify(|_, w| w.bidimode().clear_bit().bidioe().clear_bit());
269269
}
270+
fn fifo_cap(&self) -> u8 {
271+
match self.spi.sr.read().ftlvl().bits() {
272+
0 => 4,
273+
1 => 3,
274+
2 => 2,
275+
_ => 0,
276+
}
277+
}
270278
}
271279

272280
impl SpiExt<$SPIX> for $SPIX {
@@ -286,21 +294,23 @@ macro_rules! spi {
286294
impl<PINS> embedded_hal_one::spi::SpiBus for Spi<$SPIX, PINS> {
287295
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
288296
if words.len() == 0 { return Ok(()) }
289-
// clear tx-only status in the case the previous operation was a write
290-
self.set_bidi();
297+
291298
// prefill write fifo so that the clock doen't stop while fetch the read byte
292-
// one frame should be enough?
293-
nb::block!(self.nb_write(0u8))?;
299+
let prefill = self.fifo_cap() as usize;
300+
for _ in 0..prefill {
301+
nb::block!(self.nb_write(0u8))?;
302+
}
303+
294304
let len = words.len();
295-
for r in words[..len-1].iter_mut() {
305+
for r in words[..len-prefill].iter_mut() {
296306
// TODO: 16 bit frames, bidirectional pins
297307
nb::block!(self.nb_write(0u8))?;
298308
// errors have been checked by the write above
299309
*r = unsafe { nb::block!(self.nb_read_no_err()).unwrap_unchecked() };
300310
}
301-
// safety: length > 0 checked at start of function
302-
*words.last_mut().unwrap() = nb::block!(self.nb_read())?;
303-
Ok(())
311+
Ok(for r in words[len-prefill..].iter_mut() {
312+
*r = nb::block!(self.nb_read())?;
313+
})
304314
}
305315

306316
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
@@ -321,17 +331,28 @@ macro_rules! spi {
321331
return self.read(read)
322332
}
323333

324-
self.set_bidi();
334+
let prefill = self.fifo_cap();
335+
let mut write_iter = write.into_iter();
336+
325337
// same prefill as in read, this time with actual data
326-
nb::block!(self.nb_write(write[0]))?;
338+
let mut prefilled = 0;
339+
for b in write_iter.by_ref().take(prefill as usize) {
340+
nb::block!(self.nb_write(*b))?;
341+
prefilled += 1
342+
}
343+
327344
let common_len = core::cmp::min(read.len(), write.len());
328-
// take 1 less because write skips the first element
329-
let zipped = read.iter_mut().zip(write.into_iter().skip(1)).take(common_len - 1);
345+
// write ahead of reading
346+
let zipped = read.iter_mut().zip(write_iter).take(common_len - prefilled);
330347
for (r, w) in zipped {
331348
nb::block!(self.nb_write(*w))?;
332349
*r = unsafe { nb::block!(self.nb_read_no_err()).unwrap_unchecked() };
333350
}
334-
read[common_len-1] = nb::block!(self.nb_read())?;
351+
352+
// read words left in the fifo
353+
for r in read[common_len-prefilled..common_len].iter_mut() {
354+
*r = nb::block!(self.nb_read())?
355+
}
335356

336357
if read.len() > common_len {
337358
self.read(&mut read[common_len..])
@@ -341,19 +362,25 @@ macro_rules! spi {
341362
}
342363
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
343364
if words.len() == 0 { return Ok(()) }
344-
self.set_bidi();
345-
nb::block!(self.nb_write(words[0]))?;
365+
346366
let cells = core::cell::Cell::from_mut(words).as_slice_of_cells();
367+
let mut write_iter = cells.into_iter();
368+
let mut read_iter = cells.into_iter();
369+
370+
let prefill = self.fifo_cap();
347371

348-
for rw in cells.windows(2) {
349-
let r = &rw[0];
350-
let w = &rw[1];
351-
372+
for w in write_iter.by_ref().take(prefill as usize) {
373+
nb::block!(self.nb_write(w.get()))?;
374+
}
375+
376+
for (r, w) in write_iter.zip(read_iter.by_ref()) {
352377
nb::block!(self.nb_write(w.get()))?;
353378
r.set(unsafe { nb::block!(self.nb_read_no_err()).unwrap_unchecked() });
354379
}
355-
*words.last_mut().unwrap() = nb::block!(self.nb_read())?;
356-
Ok(())
380+
381+
Ok(for r in read_iter {
382+
r.set(nb::block!(self.nb_read())?);
383+
})
357384
}
358385
fn flush(&mut self) -> Result<(), Self::Error> {
359386
let catch = |spi: &mut Self| {

0 commit comments

Comments
 (0)