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}