Skip to content

Commit 63f64e4

Browse files
Merge #68
68: Allow arbitrary SPI configurations r=richardeoin a=ryan-summers This PR adds a new `spi::Config` structure that can be used to provide arbitrary device-specific configurations for the SPI peripheral as discussed in #64. Because of the ability to configure the frame size, I have added a template parameter to the `read()` and `send()` API for `FullDuplex` implementation, but this is not necessarily the best approach (ref #61). The existing API is unchanged due to the implementation of `From<spi::Mode>` for the new `spi::Config` structure, so applications will not need to be updated. @richardeoin Let me know what you think of this implementation - it's only preliminary at this point, but is the general idea I had in #64 Co-authored-by: Ryan Summers <[email protected]>
2 parents b56ca8d + a311dbb commit 63f64e4

File tree

1 file changed

+122
-21
lines changed

1 file changed

+122
-21
lines changed

src/spi.rs

Lines changed: 122 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub use crate::hal::spi::{
6161
use crate::stm32;
6262
use crate::stm32::rcc::{d2ccip1r, d3ccipr};
6363
use crate::stm32::spi1::cfg1::MBR_A as MBR;
64+
use core::convert::From;
6465
use core::marker::PhantomData;
6566
use core::ptr;
6667
use nb;
@@ -111,6 +112,76 @@ where
111112
{
112113
}
113114

115+
/// A structure for specifying SPI configuration.
116+
///
117+
/// This structure uses builder semantics to generate the configuration.
118+
///
119+
/// `Example`
120+
/// ```
121+
/// use embedded_hal::spi::Mode;
122+
///
123+
/// let config = Config::new(Mode::MODE_0)
124+
/// .manage_cs()
125+
/// ```
126+
#[derive(Copy, Clone)]
127+
pub struct Config {
128+
mode: Mode,
129+
swap_miso_mosi: bool,
130+
cs_delay: f32,
131+
managed_cs: bool,
132+
}
133+
134+
impl Config {
135+
/// Create a default configuration for the SPI interface.
136+
///
137+
/// Arguments:
138+
/// * `mode` - The SPI mode to configure.
139+
pub fn new(mode: Mode) -> Self {
140+
Config {
141+
mode: mode,
142+
swap_miso_mosi: false,
143+
cs_delay: 0.0,
144+
managed_cs: false,
145+
}
146+
}
147+
148+
/// Specify that the SPI MISO/MOSI lines are swapped.
149+
///
150+
/// Note:
151+
/// * This function updates the HAL peripheral to treat the pin provided in the MISO parameter
152+
/// as the MOSI pin and the pin provided in the MOSI parameter as the MISO pin.
153+
pub fn swap_mosi_miso(mut self) -> Self {
154+
self.swap_miso_mosi = true;
155+
self
156+
}
157+
158+
/// Specify a delay between CS assertion and the beginning of the SPI transaction.
159+
///
160+
/// Note:
161+
/// * This function introduces a delay on SCK from the initiation of the transaction. The delay
162+
/// is specified as a number of SCK cycles, so the actual delay may vary.
163+
///
164+
/// Arguments:
165+
/// * `delay` - The delay between CS assertion and the start of the transaction in seconds.
166+
/// register for the output pin.
167+
pub fn cs_delay(mut self, delay: f32) -> Self {
168+
self.cs_delay = delay;
169+
self
170+
}
171+
172+
/// CS pin is automatically managed by the SPI peripheral.
173+
pub fn manage_cs(mut self) -> Self {
174+
self.managed_cs = true;
175+
self
176+
}
177+
}
178+
179+
impl From<Mode> for Config {
180+
fn from(mode: Mode) -> Self {
181+
Self::new(mode)
182+
}
183+
}
184+
114185
/// A filler type for when the SCK pin is unnecessary
115186
pub struct NoSck;
116187
/// A filler type for when the Miso pin is unnecessary
@@ -270,27 +341,29 @@ pub struct Spi<SPI, WORD = u8> {
270341
pub trait SpiExt<SPI, WORD>: Sized {
271342
type Rec: ResetEnable;
272343

273-
fn spi<PINS, T>(
344+
fn spi<PINS, T, CONFIG>(
274345
self,
275346
_pins: PINS,
276-
mode: Mode,
347+
config: CONFIG,
277348
freq: T,
278349
prec: Self::Rec,
279350
clocks: &CoreClocks,
280351
) -> Spi<SPI, WORD>
281352
where
282353
PINS: Pins<SPI>,
283-
T: Into<Hertz>;
354+
T: Into<Hertz>,
355+
CONFIG: Into<Config>;
284356

285-
fn spi_unchecked<T>(
357+
fn spi_unchecked<T, CONFIG>(
286358
self,
287-
mode: Mode,
359+
config: CONFIG,
288360
freq: T,
289361
prec: Self::Rec,
290362
clocks: &CoreClocks,
291363
) -> Spi<SPI, WORD>
292364
where
293-
T: Into<Hertz>;
365+
T: Into<Hertz>,
366+
CONFIG: Into<Config>;
294367
}
295368

296369
macro_rules! spi {
@@ -312,22 +385,25 @@ macro_rules! spi {
312385
// For each $TY
313386
$(
314387
impl Spi<$SPIX, $TY> {
315-
pub fn $spiX<T>(
388+
pub fn $spiX<T, CONFIG>(
316389
spi: $SPIX,
317-
mode: Mode,
390+
config: CONFIG,
318391
freq: T,
319392
prec: rec::$Rec,
320393
clocks: &CoreClocks,
321394
) -> Self
322395
where
323396
T: Into<Hertz>,
397+
CONFIG: Into<Config>,
324398
{
325399
// Enable clock for SPI
326400
prec.enable();
327401

328402
// Disable SS output
329403
spi.cfg2.write(|w| w.ssoe().disabled());
330404

405+
let config: Config = config.into();
406+
331407
let spi_freq = freq.into().0;
332408
let spi_ker_ck = match Self::kernel_clk(clocks) {
333409
Some(ker_hz) => ker_hz.0,
@@ -353,23 +429,46 @@ macro_rules! spi {
353429
// ssi: select slave = master mode
354430
spi.cr1.write(|w| w.ssi().slave_not_selected());
355431

432+
// Calculate the CS->transaction cycle delay bits.
433+
let cycle_delay: u8 = {
434+
let mut delay: u32 = (config.cs_delay * spi_freq as f32) as u32;
435+
436+
// If the cs-delay is specified as non-zero, add 1 to the delay cycles
437+
// before truncation to an integer to ensure that we have at least as
438+
// many cycles as required.
439+
if config.cs_delay > 0.0_f32 {
440+
delay = delay + 1;
441+
}
442+
443+
if delay > 0xF {
444+
delay = 0xF;
445+
}
446+
447+
delay as u8
448+
};
449+
450+
// The calculated cycle delay may not be more than 4 bits wide for the
451+
// configuration register.
452+
356453
// mstr: master configuration
357454
// lsbfrst: MSB first
358-
// ssm: enable software slave management (NSS pin
359-
// free for other uses)
360455
// comm: full-duplex
361456
spi.cfg2.write(|w| {
362457
w.cpha()
363-
.bit(mode.phase ==
458+
.bit(config.mode.phase ==
364459
Phase::CaptureOnSecondTransition)
365460
.cpol()
366-
.bit(mode.polarity == Polarity::IdleHigh)
461+
.bit(config.mode.polarity == Polarity::IdleHigh)
367462
.master()
368463
.master()
369464
.lsbfrst()
370465
.msbfirst()
371466
.ssm()
372-
.enabled()
467+
.bit(config.managed_cs == false)
468+
.mssi()
469+
.bits(cycle_delay)
470+
.ioswp()
471+
.bit(config.swap_miso_mosi == true)
373472
.comm()
374473
.full_duplex()
375474
});
@@ -460,28 +559,30 @@ macro_rules! spi {
460559
impl SpiExt<$SPIX, $TY> for $SPIX {
461560
type Rec = rec::$Rec;
462561

463-
fn spi<PINS, T>(self,
562+
fn spi<PINS, T, CONFIG>(self,
464563
_pins: PINS,
465-
mode: Mode,
564+
config: CONFIG,
466565
freq: T,
467566
prec: rec::$Rec,
468567
clocks: &CoreClocks) -> Spi<$SPIX, $TY>
469568
where
470569
PINS: Pins<$SPIX>,
471-
T: Into<Hertz>
570+
T: Into<Hertz>,
571+
CONFIG: Into<Config>,
472572
{
473-
Spi::<$SPIX, $TY>::$spiX(self, mode, freq, prec, clocks)
573+
Spi::<$SPIX, $TY>::$spiX(self, config, freq, prec, clocks)
474574
}
475575

476-
fn spi_unchecked<T>(self,
477-
mode: Mode,
576+
fn spi_unchecked<T, CONFIG>(self,
577+
config: CONFIG,
478578
freq: T,
479579
prec: rec::$Rec,
480580
clocks: &CoreClocks) -> Spi<$SPIX, $TY>
481581
where
482-
T: Into<Hertz>
582+
T: Into<Hertz>,
583+
CONFIG: Into<Config>,
483584
{
484-
Spi::<$SPIX, $TY>::$spiX(self, mode, freq, prec, clocks)
585+
Spi::<$SPIX, $TY>::$spiX(self, config, freq, prec, clocks)
485586
}
486587
}
487588

0 commit comments

Comments
 (0)