Skip to content

Commit 590bbbd

Browse files
committed
Optimize 8-bit SPI transfers and add 16-bit SPI transfers
Signed-off-by: Daniel Egger <[email protected]>
1 parent 0907de0 commit 590bbbd

File tree

3 files changed

+190
-43
lines changed

3 files changed

+190
-43
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Changed
1111

12+
- Optimize SPI implementation
1213
- Use `pac` instead of `stm32` for PAC access and soft-deprecate the former
1314

1415
### Added
1516

17+
- Add 16bit SPI transfers
1618
- Another example resembling a stop watch controlled via serial interface
1719

1820
### Fixed

examples/serial_spi_bridge.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ use panic_halt as _;
66
use stm32f0xx_hal as hal;
77

88
use crate::hal::{
9+
pac,
910
prelude::*,
1011
serial::Serial,
1112
spi::Spi,
1213
spi::{Mode, Phase, Polarity},
13-
pac,
1414
};
1515

1616
use nb::block;
@@ -52,14 +52,12 @@ fn main() -> ! {
5252

5353
let (mut tx, mut rx) = serial.split();
5454

55+
let mut data = [0];
5556
loop {
5657
let serial_received = block!(rx.read()).unwrap();
57-
58-
block!(spi.send(serial_received)).ok();
59-
60-
let spi_received = block!(spi.read()).unwrap();
61-
62-
block!(tx.write(spi_received)).ok();
58+
spi.write(&[serial_received]).ok();
59+
let spi_received = spi.transfer(&mut data).unwrap();
60+
block!(tx.write(spi_received[0])).ok();
6361
}
6462
});
6563
}

src/spi.rs

Lines changed: 183 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@
3030
//! phase: Phase::CaptureOnSecondTransition,
3131
//! }, 1.mhz(), &mut rcc);
3232
//!
33-
//! let mut data = [ 0 ];
33+
//! let mut data = [0];
3434
//! loop {
3535
//! spi.transfer(&mut data).unwrap();
3636
//! }
3737
//! });
3838
//! ```
3939
40+
use core::marker::PhantomData;
4041
use core::{ops::Deref, ptr};
4142

4243
use nb;
@@ -68,6 +69,12 @@ use crate::rcc::{Clocks, Rcc};
6869

6970
use crate::time::Hertz;
7071

72+
/// Typestate for 8-bit transfer size
73+
pub struct EightBit;
74+
75+
/// Typestate for 16-bit transfer size
76+
pub struct SixteenBit;
77+
7178
/// SPI error
7279
#[derive(Debug)]
7380
pub enum Error {
@@ -82,9 +89,10 @@ pub enum Error {
8289
}
8390

8491
/// SPI abstraction
85-
pub struct Spi<SPI, SCKPIN, MISOPIN, MOSIPIN> {
92+
pub struct Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, WIDTH> {
8693
spi: SPI,
8794
pins: (SCKPIN, MISOPIN, MOSIPIN),
95+
_width: PhantomData<WIDTH>,
8896
}
8997

9098
pub trait SckPin<SPI> {}
@@ -204,7 +212,7 @@ spi_pins! {
204212
macro_rules! spi {
205213
($($SPI:ident: ($spi:ident, $spiXen:ident, $spiXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => {
206214
$(
207-
impl<SCKPIN, MISOPIN, MOSIPIN> Spi<$SPI, SCKPIN, MISOPIN, MOSIPIN> {
215+
impl<SCKPIN, MISOPIN, MOSIPIN> Spi<$SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit> {
208216
/// Creates a new spi instance
209217
pub fn $spi<F>(
210218
spi: $SPI,
@@ -226,7 +234,7 @@ macro_rules! spi {
226234
rcc.regs.$apbrstr.modify(|_, w| w.$spiXrst().set_bit());
227235
rcc.regs.$apbrstr.modify(|_, w| w.$spiXrst().clear_bit());
228236

229-
Spi { spi, pins }.spi_init(mode, speed, rcc.clocks)
237+
Spi::<$SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit> { spi, pins, _width: PhantomData }.spi_init(mode, speed, rcc.clocks).into_8bit_width()
230238
}
231239
}
232240
)+
@@ -258,27 +266,17 @@ spi! {
258266
#[allow(dead_code)]
259267
type SpiRegisterBlock = crate::pac::spi1::RegisterBlock;
260268

261-
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> Spi<SPI, SCKPIN, MISOPIN, MOSIPIN>
269+
impl<SPI, SCKPIN, MISOPIN, MOSIPIN, WIDTH> Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, WIDTH>
262270
where
263271
SPI: Deref<Target = SpiRegisterBlock>,
264272
{
265-
fn spi_init<F>(self: Self, mode: Mode, speed: F, clocks: Clocks) -> Self
273+
fn spi_init<F>(self, mode: Mode, speed: F, clocks: Clocks) -> Self
266274
where
267275
F: Into<Hertz>,
268276
{
269277
/* Make sure the SPI unit is disabled so we can configure it */
270278
self.spi.cr1.modify(|_, w| w.spe().clear_bit());
271279

272-
// FRXTH: 8-bit threshold on RX FIFO
273-
// DS: 8-bit data size
274-
// SSOE: cleared to disable SS output
275-
//
276-
// NOTE(unsafe): DS reserved bit patterns are 0b0000, 0b0001, and 0b0010. 0b0111 is valid
277-
// (reference manual, pp 804)
278-
self.spi
279-
.cr2
280-
.write(|w| unsafe { w.frxth().set_bit().ds().bits(0b0111).ssoe().clear_bit() });
281-
282280
let br = match clocks.pclk().0 / speed.into().0 {
283281
0 => unreachable!(),
284282
1..=2 => 0b000,
@@ -323,19 +321,50 @@ where
323321

324322
self
325323
}
326-
pub fn release(self) -> (SPI, (SCKPIN, MISOPIN, MOSIPIN)) {
327-
(self.spi, self.pins)
324+
325+
pub fn into_8bit_width(self) -> Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit> {
326+
// FRXTH: 8-bit threshold on RX FIFO
327+
// DS: 8-bit data size
328+
// SSOE: cleared to disable SS output
329+
self.spi
330+
.cr2
331+
.write(|w| w.frxth().set_bit().ds().eight_bit().ssoe().clear_bit());
332+
333+
Spi {
334+
spi: self.spi,
335+
pins: self.pins,
336+
_width: PhantomData,
337+
}
328338
}
329-
}
330339

331-
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::spi::FullDuplex<u8>
332-
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN>
333-
where
334-
SPI: Deref<Target = SpiRegisterBlock>,
335-
{
336-
type Error = Error;
340+
pub fn into_16bit_width(self) -> Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, SixteenBit> {
341+
// FRXTH: 16-bit threshold on RX FIFO
342+
// DS: 8-bit data size
343+
// SSOE: cleared to disable SS output
344+
self.spi
345+
.cr2
346+
.write(|w| w.frxth().set_bit().ds().sixteen_bit().ssoe().clear_bit());
337347

338-
fn read(&mut self) -> nb::Result<u8, Error> {
348+
Spi {
349+
spi: self.spi,
350+
pins: self.pins,
351+
_width: PhantomData,
352+
}
353+
}
354+
355+
fn set_send_only(&mut self) {
356+
self.spi
357+
.cr1
358+
.modify(|_, w| w.bidimode().set_bit().bidioe().set_bit());
359+
}
360+
361+
fn set_bidi(&mut self) {
362+
self.spi
363+
.cr1
364+
.modify(|_, w| w.bidimode().clear_bit().bidioe().clear_bit());
365+
}
366+
367+
fn check_read(&mut self) -> nb::Result<(), Error> {
339368
let sr = self.spi.sr.read();
340369

341370
Err(if sr.ovr().bit_is_set() {
@@ -345,15 +374,26 @@ where
345374
} else if sr.crcerr().bit_is_set() {
346375
nb::Error::Other(Error::Crc)
347376
} else if sr.rxne().bit_is_set() {
348-
// NOTE(read_volatile) read only 1 byte (the svd2rust API only allows
349-
// reading a half-word)
350-
return Ok(unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u8) });
377+
return Ok(());
351378
} else {
352379
nb::Error::WouldBlock
353380
})
354381
}
355382

356-
fn send(&mut self, byte: u8) -> nb::Result<(), Error> {
383+
fn send_buffer_size(&mut self) -> u8 {
384+
match self.spi.sr.read().ftlvl().bits() {
385+
// FIFO empty
386+
0 => 4,
387+
// FIFO 1/4 full
388+
1 => 3,
389+
// FIFO 1/2 full
390+
2 => 2,
391+
// FIFO full
392+
_ => 0,
393+
}
394+
}
395+
396+
fn check_send(&mut self) -> nb::Result<(), Error> {
357397
let sr = self.spi.sr.read();
358398

359399
Err(if sr.ovr().bit_is_set() {
@@ -363,25 +403,132 @@ where
363403
} else if sr.crcerr().bit_is_set() {
364404
nb::Error::Other(Error::Crc)
365405
} else if sr.txe().bit_is_set() {
366-
// NOTE(write_volatile) see note above
367-
unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) }
368406
return Ok(());
369407
} else {
370408
nb::Error::WouldBlock
371409
})
372410
}
411+
412+
fn read_u8(&mut self) -> u8 {
413+
// NOTE(read_volatile) read only 1 byte (the svd2rust API only allows reading a half-word)
414+
unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u8) }
415+
}
416+
417+
fn send_u8(&mut self, byte: u8) {
418+
// NOTE(write_volatile) see note above
419+
unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) }
420+
}
421+
422+
fn read_u16(&mut self) -> u16 {
423+
// NOTE(read_volatile) read only 2 bytes (the svd2rust API only allows reading a half-word)
424+
unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u16) }
425+
}
426+
427+
fn send_u16(&mut self, byte: u16) {
428+
// NOTE(write_volatile) see note above
429+
unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u16, byte) }
430+
}
431+
432+
pub fn release(self) -> (SPI, (SCKPIN, MISOPIN, MOSIPIN)) {
433+
(self.spi, self.pins)
434+
}
373435
}
374436

375-
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::transfer::Default<u8>
376-
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN>
437+
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::Transfer<u8>
438+
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit>
377439
where
378440
SPI: Deref<Target = SpiRegisterBlock>,
379441
{
442+
type Error = Error;
443+
444+
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
445+
// We want to transfer bidirectionally, make sure we're in the correct mode
446+
self.set_bidi();
447+
448+
for word in words.iter_mut() {
449+
nb::block!(self.check_send())?;
450+
self.send_u8(word.clone());
451+
nb::block!(self.check_read())?;
452+
*word = self.read_u8();
453+
}
454+
455+
Ok(words)
456+
}
380457
}
381458

382-
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::write::Default<u8>
383-
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN>
459+
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::Write<u8>
460+
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit>
384461
where
385462
SPI: Deref<Target = SpiRegisterBlock>,
386463
{
464+
type Error = Error;
465+
466+
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
467+
let mut bufcap: u8 = 0;
468+
469+
// We only want to send, so we don't need to worry about the receive buffer overflowing
470+
self.set_send_only();
471+
472+
// Make sure we don't continue with an error condition
473+
nb::block!(self.check_send())?;
474+
475+
// We have a 32 bit buffer to work with, so let's fill it before checking the status
476+
for word in words {
477+
// Loop as long as our send buffer is full
478+
while bufcap == 0 {
479+
bufcap = self.send_buffer_size();
480+
}
481+
482+
self.send_u8(*word);
483+
bufcap -= 1;
484+
}
485+
486+
// Do one last status register check before continuing
487+
self.check_send().ok();
488+
Ok(())
489+
}
490+
}
491+
492+
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::Transfer<u16>
493+
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, SixteenBit>
494+
where
495+
SPI: Deref<Target = SpiRegisterBlock>,
496+
{
497+
type Error = Error;
498+
499+
fn transfer<'w>(&mut self, words: &'w mut [u16]) -> Result<&'w [u16], Self::Error> {
500+
// We want to transfer bidirectionally, make sure we're in the correct mode
501+
self.set_bidi();
502+
503+
for word in words.iter_mut() {
504+
nb::block!(self.check_send())?;
505+
self.send_u16(*word);
506+
nb::block!(self.check_read())?;
507+
*word = self.read_u16();
508+
}
509+
510+
Ok(words)
511+
}
512+
}
513+
514+
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::Write<u16>
515+
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, SixteenBit>
516+
where
517+
SPI: Deref<Target = SpiRegisterBlock>,
518+
{
519+
type Error = Error;
520+
521+
fn write(&mut self, words: &[u16]) -> Result<(), Self::Error> {
522+
// We only want to send, so we don't need to worry about the receive buffer overflowing
523+
self.set_send_only();
524+
525+
for word in words {
526+
nb::block!(self.check_send())?;
527+
self.send_u16(word.clone());
528+
}
529+
530+
// Do one last status register check before continuing
531+
self.check_send().ok();
532+
Ok(())
533+
}
387534
}

0 commit comments

Comments
 (0)