kea_hal/spi.rs
1//! # SPI Peripheral
2//!
3//! ## Pins
4//!
5//! The SPI0 peripheral has two choices for its I/O pins (SCK, MOSI, MISO, CS),
6//! PTB2:5 or PTE0:3. SPI1 only uses one set of pins (PTD0:3).
7//!
8//! At this time there is no method implemented to return Pins from the SPI
9//! peripherals. Keep in mind that once the pins given to the SPI they can not
10//! be returned.
11//!
12//! ### High Drive Current GPIO
13//!
14//! The SPI peripheral, like other peripherals in this family of MCUs will
15//! reconfigure the GPIO ports as needed when the peripheral is activated.
16//! However, the SPI peripherals do not modify the High Drive Current
17//! peripheral's settings. This provides stronger drive to the SPI bus lines in
18//! order to increase the slew rate for the output signal.
19//!
20//! Note that only the MOSI (sdo in controller mode) pin for SPI0 and SPI1 with
21//! the default pins and MISO (sdo in peripheral mode) for SPI0 with alternate
22//! pins have the high current drive peripheral implemented.
23//!
24//! See the spi-talking-to-myself example in the source repo.
25//!
26//! ## Interrupts
27//!
28//! SPI peripherals have one interrupt vector, 4 flags, and 3 masks each.
29//! Once in the interrupt you will have to check the flag bits to determine
30//! which flag triggered the interrupt in order to respond appropriately.
31
32use crate::hal::spi;
33use crate::{pac::SPI0, pac::SPI1, HALExt};
34use core::marker::PhantomData;
35use embedded_time::rate::*;
36
37/// Spi Peripheral Interface
38// @TODO - Work out how to store pins so we can give them back
39pub struct Spi<SPI, Disabled, Pins> {
40 peripheral: SPI,
41 _state: PhantomData<Disabled>,
42 _pins: PhantomData<Pins>,
43}
44
45/// Peripheral disabled
46pub struct Disabled;
47/// Peripheral enabled
48pub struct Enabled<T> {
49 _state: PhantomData<T>,
50}
51/// Peripheral ignores this sub-state at this time
52pub struct DontCare;
53
54/// SPI Peripheral in Controller mode
55pub struct Controller;
56///SPI Peripheral in Peripheral mode
57pub struct Peripheral;
58
59/// Peripheral uses default pins (or only has one set of pins available)
60pub struct DefaultPins;
61/// Peripheral is using alternate pins
62pub struct AltPins;
63
64// // Roughing work on Pin Storage so we can return the pins one day
65// struct DefaultPinsSpi0<T2, T3, T4, T5> {
66// clock: PTB2<T2>,
67// copi: PTB3<T3>,
68// cipo: PTB4<T4>,
69// cs: Option<PTB5<T5>>,
70// }
71//
72// impl<T2, T3, T4, T5> Spi0Pins for DefaultPinsSpi0<T2, T3, T4, T5> {}
73
74// impl<T2, T3, T4, T5> Default for DefaultPinsSpi0<T2, T3, T4, T5> {
75// fn default() -> Self {
76// Self {
77// clock: None,
78// copi: None,
79// cipo: None,
80// cs: None,
81// }
82// }
83// }
84//
85// struct AltPinsSpi0<T0, T1, T2, T3> {
86// clock: Option<PTE0<T0>>,
87// copi: Option<PTE1<T1>>,
88// cipo: Option<PTE2<T2>>,
89// cs: Option<PTE3<T3>>,
90// }
91//
92// impl<T0, T1, T2, T3> Spi0Pins for AltPinsSpi0<T0, T1, T2, T3> {}
93//
94// impl<T0, T1, T2, T3> Default for AltPinsSpi0<T0, T1, T2, T3> {
95// fn default() -> Self {
96// Self {
97// clock: None,
98// copi: None,
99// cipo: None,
100// cs: None,
101// }
102// }
103// }
104
105use crate::gpio::gpioa::{PTB2, PTB3, PTB4, PTB5};
106use crate::gpio::gpiob::{PTE0, PTE1, PTE2, PTE3};
107impl HALExt for SPI0 {
108 type T = Spi<SPI0, Disabled, DefaultPins>;
109 fn split(self) -> Self::T {
110 Spi {
111 peripheral: self,
112 _state: PhantomData,
113 _pins: PhantomData,
114 }
115 }
116}
117
118use crate::gpio::gpioa::{PTD0, PTD1, PTD2, PTD3};
119impl HALExt for SPI1 {
120 type T = Spi<SPI1, Disabled, DefaultPins>;
121 fn split(self) -> Spi<SPI1, Disabled, DefaultPins> {
122 Spi {
123 peripheral: self,
124 _state: PhantomData,
125 _pins: PhantomData,
126 }
127 }
128}
129
130impl Spi<SPI0, Disabled, DefaultPins> {
131 /// Change to alternate pins
132 pub fn into_alt_pins(self) -> Spi<SPI0, Disabled, AltPins> {
133 Spi {
134 peripheral: self.peripheral,
135 _state: PhantomData,
136 _pins: PhantomData,
137 }
138 }
139
140 /// Enable SPI0 with default pins
141 pub fn enable_as_controller<T2, T3, T4, T5>(
142 self,
143 clock: PTB2<T2>,
144 sdo: PTB3<T3>,
145 // sdi: Option<PTB4<T4>>, // Bidirectional mode needs own Mode type
146 sdi: PTB4<T4>,
147 cs: Option<PTB5<T5>>,
148 manage_cs: bool,
149 mode: spi::Mode,
150 ) -> Spi<SPI0, Controller, DefaultPins> {
151 // Select PTB2:5 for SPI0
152 let sim = unsafe { &(*pac::SIM::ptr()) };
153 // Select PTE0:3 for SPI0
154 sim.pinsel.modify(|_, w| w.spi0ps()._0());
155 // Enable busclock to SPI0 peripheral before touching it
156 sim.scgc.modify(|_, w| w.spi0()._1());
157 self.enable_spi(true, false, cs.is_some(), manage_cs, mode);
158 let _ = (clock, sdo, sdi, cs);
159 Spi {
160 peripheral: self.peripheral,
161 _state: PhantomData,
162 _pins: PhantomData,
163 }
164 }
165
166 /// Enable SPI0 in peripheral mode with Alternate Pins
167 pub fn enable_as_peripheral<T2, T3, T4, T5>(
168 self,
169 clock: PTB2<T2>,
170 sdi: PTB3<T3>,
171 sdo: PTB4<T4>,
172 cs: PTB5<T5>,
173 mode: spi::Mode,
174 ) -> Spi<SPI0, Enabled<Peripheral>, DefaultPins> {
175 let sim = unsafe { &(*pac::SIM::ptr()) };
176 // Select PTE0:3 for SPI0
177 sim.pinsel.modify(|_, w| w.spi0ps()._0());
178 // Enable busclock to SPI0 peripheral before touching it
179 sim.scgc.modify(|_, w| w.spi0()._1());
180
181 // Peripheral mode always uses cs, and manage_cs has no effect
182 self.enable_spi(false, false, true, true, mode);
183
184 let _ = (clock, sdo, sdi, cs);
185 Spi {
186 peripheral: self.peripheral,
187 _state: PhantomData,
188 _pins: PhantomData,
189 }
190 }
191}
192
193impl Spi<SPI0, Disabled, AltPins> {
194 /// Enable SPI0 in controller mode with Alternate Pins
195 pub fn enable_as_controller<T0, T1, T2, T3>(
196 self,
197 clock: PTE0<T0>,
198 sdo: PTE1<T1>,
199 // sdi: Option<PTE2<T2>>, // Bidirectional mode needs own Mode type
200 sdi: PTE2<T2>,
201 cs: Option<PTE3<T3>>,
202 manage_cs: bool,
203 mode: spi::Mode,
204 ) -> Spi<SPI0, Controller, AltPins> {
205 let sim = unsafe { &(*pac::SIM::ptr()) };
206 // Select PTE0:3 for SPI0
207 sim.pinsel.modify(|_, w| w.spi0ps()._1());
208 // Enable busclock to SPI0 peripheral before touching it
209 sim.scgc.modify(|_, w| w.spi0()._1());
210
211 // bidirectional controller with mode fault enabled will auto switch to
212 // peripheral mode when modefault occurs. Current impl does not handle
213 // that
214 self.enable_spi(true, false, cs.is_some(), manage_cs, mode);
215 let _ = (clock, sdo, sdi, cs);
216 Spi {
217 peripheral: self.peripheral,
218 _state: PhantomData,
219 _pins: PhantomData,
220 }
221 }
222
223 /// Enable SPI0 in peripheral mode with Alternate Pins
224 pub fn enable_as_peripheral<T0, T1, T2, T3>(
225 self,
226 clock: PTE0<T0>,
227 // sdi: Option<PTE1<T1>>, // Bidirectional mode needs own mode type
228 sdi: PTE1<T1>,
229 sdo: PTE2<T2>,
230 cs: PTE3<T3>,
231 mode: spi::Mode,
232 ) -> Spi<SPI0, Enabled<Peripheral>, AltPins> {
233 let sim = unsafe { &(*pac::SIM::ptr()) };
234 // Select PTE0:3 for SPI0
235 sim.pinsel.modify(|_, w| w.spi0ps()._1());
236 // Enable busclock to SPI0 peripheral before touching it
237 sim.scgc.modify(|_, w| w.spi0()._1());
238
239 // Peripheral mode always uses cs, and manage_cs has no effect
240 self.enable_spi(false, false, true, true, mode);
241
242 let _ = (clock, sdi, sdo, cs);
243 Spi {
244 peripheral: self.peripheral,
245 _state: PhantomData,
246 _pins: PhantomData,
247 }
248 }
249}
250
251impl Spi<SPI1, Disabled, DefaultPins> {
252 /// Enable SPI1 as the controller with default pins
253 ///
254 /// When manage_cs is set (and the cs pin has been provided) SPI1 will
255 /// automatically drive the CS pin when doing SPI transfers. If manage_cs
256 /// is not set, then this pin is used to detect if there is another
257 /// Controller on the bus, which throws the Master Mode Fault flag
258 pub fn enable_as_controller<T0, T1, T2, T3>(
259 self,
260 clock: PTD0<T0>,
261 sdo: PTD1<T1>,
262 // sdi: Option<PTD2<T2>>, // Bidirectional mode needs own Mode type
263 sdi: PTD2<T2>,
264 cs: Option<PTD3<T3>>,
265 manage_cs: bool,
266 mode: spi::Mode,
267 ) -> Spi<SPI1, Enabled<Controller>, DefaultPins> {
268 // Enable bus clock to SPI1 (needed before writing anything to the SPI
269 // peripheral
270 unsafe { (*pac::SIM::ptr()).scgc.modify(|_, w| w.spi1()._1()) };
271
272 // bidirectional controller with mode fault enabled will auto switch to
273 // peripheral mode when modefault occurs. Current impl does not handle
274 // that
275
276 self.enable_spi(true, false, cs.is_some(), manage_cs, mode);
277
278 let _ = (clock, sdo, sdi, cs);
279 Spi {
280 peripheral: self.peripheral,
281 _state: PhantomData,
282 _pins: PhantomData,
283 }
284 }
285
286 /// Enable SPI1 as peripheral with default pins
287 pub fn enable_as_peripheral<T0, T1, T2, T3>(
288 self,
289 clock: PTD0<T0>,
290 //sdi: Option<PTD1<T1>>, // Bidirectional mode needs own Mode type
291 sdi: PTD1<T1>,
292 sdo: PTD2<T2>,
293 cs: PTD3<T3>,
294 mode: spi::Mode,
295 ) -> Spi<SPI1, Enabled<Peripheral>, DefaultPins> {
296 // Enable bus clock to SPI1 (needed before writing anything to the SPI
297 // peripheral
298 unsafe { (*pac::SIM::ptr()).scgc.modify(|_, w| w.spi1()._1()) };
299
300 // Peripheral mode always uses cs, and manage_cs has no effect
301 self.enable_spi(false, false, true, true, mode);
302 let _ = (clock, sdi, sdo, cs);
303 Spi {
304 peripheral: self.peripheral,
305 _state: PhantomData,
306 _pins: PhantomData,
307 }
308 }
309}
310
311macro_rules! spi_builder {
312 ( $($SpiRegister:ident,)+ ) => {
313 $(
314 impl<Pins> Spi<$SpiRegister, Disabled, Pins> {
315 /// Do the low level work of enabling the SPI. try for DRY.
316 fn enable_spi(
317 &self,
318 is_controller: bool,
319 is_bidirectional: bool,
320 use_cs: bool,
321 manage_cs: bool,
322 mode: spi::Mode
323 ) {
324 self.peripheral.c1.write(|w| {
325 w.lsbfe()._0() // MSB is transfered first
326 .ssoe()
327 .bit(use_cs && manage_cs)
328 .cpha()
329 // was using variant, but using bit works for both SPI0/1
330 .bit(match mode.phase {
331 spi::Phase::CaptureOnFirstTransition => false,
332 spi::Phase::CaptureOnSecondTransition => true,
333 })
334 .cpol()
335 // was using variant, but using bit works for both SPI0/1
336 .bit(match mode.polarity {
337 spi::Polarity::IdleLow => false,
338 spi::Polarity::IdleHigh => true,
339 })
340 .mstr()
341 .bit(is_controller)
342 .sptie()._0() // No interrupts implemented yet
343 .spe()
344 ._1()
345 .spie()._0() // No interrupts implemented yet
346 });
347
348 // Cannot just write to C2 because of reserve bits
349 // using bidirectional mode trashes FullDuplex?
350 self.peripheral.c2.modify(|_, w| {
351 w.spc0().bit(is_bidirectional)
352 .spiswai()._0() // default Spi active in WAIT.
353 .bidiroe().bit(is_controller) // if bidir, start in right mode
354 .modfen().bit(use_cs)
355 .spmie()._0() // No interrupts implemented yet
356 });
357 }
358 }
359
360 impl<Pins, Mode> Spi<$SpiRegister, Enabled<Mode>, Pins> {
361 /// Set the baud rate of transmission
362 ///
363 /// This is only used when the MCU is the bus Controller. This relies on
364 /// accurately inputting the bus frequency until a way to share the current
365 /// bus frequency is worked out.
366 pub fn set_baudrate(&self, baudrate: Hertz, bus_freq: Hertz) {
367 let divisor = bus_to_baudrate_divisor(bus_freq.integer(), baudrate.integer());
368 self.set_baudrate_divisor(&divisor);
369 }
370
371 /// Set the baud rate by directly setting the divisor
372 pub fn set_baudrate_divisor(&self, divisor: &BaudrateDivisor) {
373 self.peripheral
374 .br
375 .write(|w| unsafe { w.sppr().bits(divisor.scale).spr().bits(divisor.power) });
376 }
377
378 /// Get the current baudrate divisor
379 pub fn baudrate_divisor(&self) -> BaudrateDivisor {
380 let reader = self.peripheral.br.read();
381 BaudrateDivisor {
382 scale: reader.sppr().bits(),
383 power: reader.spr().bits(),
384 }
385 }
386
387 /// Check if mode fault occured
388 pub fn mode_fault(&self) -> bool {
389 self.peripheral.s.read().modf().bit()
390 }
391 }
392
393 impl<Mode, Pins> Spi<$SpiRegister, Mode, Pins> {
394 /// Check if read buffer full (ready to read)
395 pub fn read_ready(&self) -> bool {
396 self.peripheral.s.read().sprf().bit()
397 }
398 /// Check if read matches value in match register
399 pub fn read_matches(&self) -> bool {
400 self.peripheral.s.read().spmf().bit()
401 }
402 /// Check if transmit buffer empty (ready to send)
403 pub fn send_ready(&self) -> bool {
404 self.peripheral.s.read().sptef().bit()
405 }
406 /// Set the value for hardware read match
407 pub fn set_hw_match(&self, value: u8) {
408 self.peripheral.m.write(|w| unsafe { w.bits(value) });
409 }
410 }
411
412 impl<Pins, Mode> spi::FullDuplex<u8> for Spi<$SpiRegister, Enabled<Mode>, Pins> {
413 type Error = core::convert::Infallible;
414
415 fn read(&mut self) -> nb::Result<u8, Self::Error> {
416 if !self.read_ready() {
417 return Err(nb::Error::WouldBlock);
418 }
419
420 // Bidirectional mode not implemented
421 // // Set direction for bidirectional mode (no effect on normal mode)
422 // self.peripheral.c2.modify(|_, w| w.bidiroe()._0());
423
424 Ok(self.peripheral.d.read().bits())
425 }
426
427 fn send(&mut self, word: u8) -> nb::Result<(), Self::Error> {
428 if !self.send_ready() {
429 return Err(nb::Error::WouldBlock);
430 }
431
432 // Bidirectional mode not implemented
433 // // Set direction for bidirectional mode (no effect on normal mode)
434 // self.peripheral.c2.modify(|_, w| w.bidiroe()._1());
435
436 self.peripheral.d.write(|w| unsafe { w.bits(word) });
437 Ok(())
438 }
439 }
440
441 )+
442 };
443}
444spi_builder!(SPI0, SPI1,);
445
446/// Holds the parameters used to calculate the divisor used to derive the SPI
447/// baudrate from the bus clock
448pub struct BaudrateDivisor {
449 /// Linearly scale the bus clock, value must be <= 7
450 pub scale: u8,
451 /// Scale the bus clock divisor by a power of 2, value must be <= 8
452 pub power: u8,
453}
454impl BaudrateDivisor {
455 /// The transfer function for computing the baud rate divisor.
456 /// was throwing errors about converting between time rates and u32, so now in
457 /// u32
458 pub const fn divisor(&self) -> Result<u32, ()> {
459 if (self.scale > 7) || (self.power > 8) {
460 return Err(());
461 }
462 Ok((self.scale as u32 + 1) << (self.power + 1))
463 //let result = bus_freq / divisor;
464 }
465}
466const fn bus_to_baudrate_divisor(bus_freq: u32, baudrate: u32) -> BaudrateDivisor {
467 // yolo on rounding errors
468 let target: u32 = bus_freq / baudrate;
469 divisor_to_baudrate_divisor(target)
470}
471
472// Replace with something other than stupid brute force eventually
473// In the mean-time it's not that bad. 72 loops done on the user's host
474// computer is practically instantaneous. Even on target it isn't the end of
475// the world.
476// @TODO check NXP's examples to see if they do better so I don't have to think
477const fn divisor_to_baudrate_divisor(divisor: u32) -> BaudrateDivisor {
478 let mut best: BaudrateDivisor = BaudrateDivisor { scale: 7, power: 9 };
479 let mut scale: u8 = 0;
480 let mut power: u8 = 0;
481 let mut old_error: u32 = u32::max_value();
482 while scale <= 7 {
483 while power <= 8 {
484 let new = BaudrateDivisor { scale, power };
485 let new_div = match new.divisor() {
486 Ok(f) => f,
487 Err(_) => 8 << 9,
488 };
489 let error: u32 = (new_div as i32 - divisor as i32).unsigned_abs();
490 if error <= old_error {
491 old_error = error;
492 best.scale = scale;
493 best.power = power;
494 }
495 power += 1;
496 }
497 power = 0;
498 scale += 1;
499 }
500 best
501}
502
503/// Errors used in Result types in this module
504pub enum Errors {
505 /// The baudrate divisor requested is out of range
506 DivsorOutOfRange,
507 /// One of the inputs is out of range
508 BadInput,
509}