lpc8xx_hal/spi/
peripheral.rs

1use core::convert::Infallible;
2
3use embedded_hal::spi::{FullDuplex, Mode, Phase, Polarity};
4
5use crate::{
6    dma::{self, transfer::state::Ready},
7    init_state::{Disabled, Enabled},
8    pac::spi0::cfg::MASTER_A,
9    swm, syscon,
10};
11
12use super::{Clock, ClockSource, Instance, Interrupts, SlaveSelect, Transfer};
13
14/// Interface to a SPI peripheral
15///
16/// Controls the SPI. Use [`Peripherals`] to gain access to an instance of
17/// this struct.
18///
19/// Please refer to the [module documentation] for more information.
20///
21/// # `embedded-hal` traits
22///
23/// - [`embedded_hal::spi::FullDuplex`] for asynchronous transfers
24/// - [`embedded_hal::blocking::spi::Transfer`] for synchronous transfers
25/// - [`embedded_hal::blocking::spi::Write`] for synchronous writes
26///
27/// [`Peripherals`]: ../struct.Peripherals.html
28/// [module documentation]: index.html
29/// [`embedded_hal::spi::FullDuplex`]: #impl-FullDuplex%3Cu8%3E
30/// [`embedded_hal::blocking::spi::Transfer`]: #impl-Transfer%3CW%3E
31/// [`embedded_hal::blocking::spi::Write`]: #impl-Write%3CW%3E
32pub struct SPI<I, State> {
33    spi: I,
34    _state: State,
35}
36
37impl<I> SPI<I, Disabled>
38where
39    I: Instance,
40{
41    pub(crate) fn new(spi: I) -> Self {
42        Self {
43            spi,
44            _state: Disabled,
45        }
46    }
47
48    /// Enable the SPI peripheral in master mode
49    ///
50    /// This method is only available, if `SPI` is in the [`Disabled`] state.
51    /// Code that attempts to call this method when the peripheral is already
52    /// enabled will not compile.
53    ///
54    /// Consumes this instance of `SPI` and returns another instance that has
55    /// its `State` type parameter set to [`Enabled`].
56    ///
57    /// # Examples
58    ///
59    /// Please refer to the [module documentation] for a full example.
60    ///
61    /// [`Disabled`]: ../init_state/struct.Disabled.html
62    /// [`Enabled`]: ../init_state/struct.Enabled.html
63    /// [module documentation]: index.html
64    pub fn enable_as_master<SckPin, MosiPin, MisoPin, CLOCK>(
65        self,
66        clock: &Clock<CLOCK>,
67        syscon: &mut syscon::Handle,
68        mode: Mode,
69        _sck: swm::Function<I::Sck, swm::state::Assigned<SckPin>>,
70        _mosi: swm::Function<I::Mosi, swm::state::Assigned<MosiPin>>,
71        _miso: swm::Function<I::Miso, swm::state::Assigned<MisoPin>>,
72    ) -> SPI<I, Enabled<Master>>
73    where
74        CLOCK: ClockSource,
75    {
76        self.enable::<CLOCK>(syscon);
77
78        self.spi
79            .div
80            .write(|w| unsafe { w.divval().bits(clock.divval) });
81
82        self.configure(mode, MASTER_A::MASTER_MODE);
83
84        SPI {
85            spi: self.spi,
86            _state: Enabled(Master),
87        }
88    }
89
90    /// Enable the SPI peripheral in slave mode
91    ///
92    /// This method is only available, if `SPI` is in the [`Disabled`] state.
93    /// Code that attempts to call this method when the peripheral is already
94    /// enabled will not compile.
95    ///
96    /// Consumes this instance of `SPI` and returns another instance that has
97    /// its `State` type parameter set to [`Enabled`].
98    ///
99    /// [`Disabled`]: ../init_state/struct.Disabled.html
100    /// [`Enabled`]: ../init_state/struct.Enabled.html
101    pub fn enable_as_slave<C, SckPin, MosiPin, MisoPin, Ssel, SselPin>(
102        self,
103        _clock: &C,
104        syscon: &mut syscon::Handle,
105        mode: Mode,
106        _sck: swm::Function<I::Sck, swm::state::Assigned<SckPin>>,
107        _mosi: swm::Function<I::Mosi, swm::state::Assigned<MosiPin>>,
108        _miso: swm::Function<I::Miso, swm::state::Assigned<MisoPin>>,
109        _ssel: swm::Function<Ssel, swm::state::Assigned<SselPin>>,
110    ) -> SPI<I, Enabled<Slave>>
111    where
112        C: ClockSource,
113        Ssel: SlaveSelect<I>,
114    {
115        self.enable::<C>(syscon);
116        self.configure(mode, MASTER_A::SLAVE_MODE);
117
118        SPI {
119            spi: self.spi,
120            _state: Enabled(Slave),
121        }
122    }
123
124    fn enable<C>(&self, syscon: &mut syscon::Handle)
125    where
126        C: ClockSource,
127    {
128        syscon.enable_clock(&self.spi);
129        C::select(&self.spi, syscon);
130    }
131
132    fn configure(&self, mode: Mode, master: MASTER_A) {
133        self.spi.cfg.write(|w| {
134            match mode.polarity {
135                Polarity::IdleHigh => {
136                    w.cpol().high();
137                }
138                Polarity::IdleLow => {
139                    w.cpol().low();
140                }
141            }
142            match mode.phase {
143                Phase::CaptureOnFirstTransition => {
144                    w.cpha().clear_bit();
145                }
146                Phase::CaptureOnSecondTransition => {
147                    w.cpha().set_bit();
148                }
149            }
150            w.master().variant(master);
151            w.enable().enabled();
152            w
153        });
154
155        // Configure word length.
156        self.spi.txctl.write(|w| {
157            // 8 bit length
158            unsafe { w.len().bits(7) }
159        });
160
161        // Configuring the word length via TXCTL has no effect until TXDAT is
162        // written, so we're doing this here. This is not disruptive, as in
163        // master mode, we'll usually overwrite this anyway when starting a
164        // transaction, while in slave mode, we actually need some dummy data in
165        // TXDAT when receiving the first byte, to prevent a TX underrun error.
166        self.spi.txdat.write(|w| unsafe { w.data().bits(0xff) });
167    }
168}
169
170impl<I, Mode> SPI<I, Enabled<Mode>>
171where
172    I: Instance,
173{
174    /// Enable interrupts
175    ///
176    /// Enables all interrupts set to `true` in `interrupts`. Interrupts set to
177    /// `false` are not affected.
178    pub fn enable_interrupts(&mut self, interrupts: Interrupts) {
179        interrupts.enable(&self.spi);
180    }
181
182    /// Disable interrupts
183    ///
184    /// Disables all interrupts set to `true` in `interrupts`. Interrupts set to
185    /// `false` are not affected.
186    pub fn disable_interrupts(&mut self, interrupts: Interrupts) {
187        interrupts.disable(&self.spi);
188    }
189
190    /// Indicates whether the SPI instance is ready to receive
191    ///
192    /// Corresponds to the RXRDY flag in the STAT register.
193    pub fn is_ready_to_receive(&self) -> bool {
194        self.spi.stat.read().rxrdy().bit_is_set()
195    }
196
197    /// Indicates whether the SPI instance is ready to transmit
198    ///
199    /// Corresponds to the TXRDY flag in the STAT register.
200    pub fn is_ready_to_transmit(&self) -> bool {
201        self.spi.stat.read().txrdy().bit_is_set()
202    }
203
204    /// Indicates whether a slave select signal has been asserted
205    ///
206    /// Corresponds to the SSA flag in the STAT register. The flag is cleared
207    /// before this method returns.
208    pub fn is_slave_select_asserted(&self) -> bool {
209        // Can't read field through API. Issue:
210        // https://github.com/lpc-rs/lpc-pac/issues/52
211        let flag = self.spi.stat.read().bits() & (0x1 << 4) != 0;
212        self.spi.stat.write(|w| w.ssa().set_bit());
213        flag
214    }
215
216    /// Indicates whether a slave select signal has been deasserted
217    ///
218    /// Corresponds to the SSD flag in the STAT register. The flag is cleared
219    /// before this method returns.
220    pub fn is_slave_select_deasserted(&self) -> bool {
221        // Can't read field through API. Issue:
222        // https://github.com/lpc-rs/lpc-pac/issues/52
223        let flag = self.spi.stat.read().bits() & (0x1 << 5) != 0;
224        self.spi.stat.write(|w| w.ssd().set_bit());
225        flag
226    }
227
228    /// Indicates whether the master is currently idle
229    ///
230    /// Corresponds to the MSTIDLE flag in the STAT register.
231    pub fn is_master_idle(&self) -> bool {
232        self.spi.stat.read().mstidle().bit_is_set()
233    }
234
235    /// Disable the SPI peripheral
236    ///
237    /// This method is only available, if `SPI` is in the [`Enabled`] state.
238    /// Code that attempts to call this method when the peripheral is already
239    /// disabled will not compile.
240    ///
241    /// Consumes this instance of `SPI` and returns another instance that has
242    /// its `State` type parameter set to [`Disabled`].
243    ///
244    /// [`Enabled`]: ../init_state/struct.Enabled.html
245    /// [`Disabled`]: ../init_state/struct.Disabled.html
246    pub fn disable(self, syscon: &mut syscon::Handle) -> SPI<I, Disabled> {
247        syscon.disable_clock(&self.spi);
248
249        SPI {
250            spi: self.spi,
251            _state: Disabled,
252        }
253    }
254}
255
256impl<I> SPI<I, Enabled<Master>>
257where
258    I: Instance,
259{
260    /// Start an SPI transfer using DMA
261    ///
262    /// Sends all words in the provided buffer, writing the replies back into
263    /// it.
264    ///
265    /// # Panics
266    ///
267    /// Panics, if the length of `buffer` is 0 or larger than 1024.
268    pub fn transfer_all(
269        self,
270        buffer: &'static mut [u8],
271        rx_channel: dma::Channel<I::RxChannel, Enabled>,
272        tx_channel: dma::Channel<I::TxChannel, Enabled>,
273    ) -> Transfer<Ready, I> {
274        Transfer::new(self, buffer, rx_channel, tx_channel)
275    }
276}
277
278impl<I> SPI<I, Enabled<Slave>>
279where
280    I: Instance,
281{
282    /// Receive a word
283    pub fn receive(&mut self) -> nb::Result<u8, RxOverrunError> {
284        let stat = self.spi.stat.read();
285
286        // Can't read field through API. Issue:
287        // https://github.com/lpc-rs/lpc-pac/issues/52
288        if stat.bits() & (0x1 << 2) != 0 {
289            return Err(nb::Error::Other(RxOverrunError));
290        }
291        if stat.rxrdy().bit_is_clear() {
292            return Err(nb::Error::WouldBlock);
293        }
294
295        Ok(self.spi.rxdat.read().rxdat().bits() as u8)
296    }
297
298    /// Transmit a word
299    pub fn transmit(&mut self, word: u8) -> nb::Result<(), TxUnderrunError> {
300        let stat = self.spi.stat.read();
301
302        // Can't read field through API. Issue:
303        // https://github.com/lpc-rs/lpc-pac/issues/52
304        if stat.bits() & (0x1 << 3) != 0 {
305            return Err(nb::Error::Other(TxUnderrunError));
306        }
307        if stat.txrdy().bit_is_clear() {
308            return Err(nb::Error::WouldBlock);
309        }
310
311        self.spi
312            .txdat
313            .write(|w| unsafe { w.data().bits(word as u16) });
314
315        Ok(())
316    }
317}
318
319impl<I, State> SPI<I, State> {
320    /// Return the raw peripheral
321    ///
322    /// This method serves as an escape hatch from the HAL API. It returns the
323    /// raw peripheral, allowing you to do whatever you want with it, without
324    /// limitations imposed by the API.
325    ///
326    /// If you are using this method because a feature you need is missing from
327    /// the HAL API, please [open an issue] or, if an issue for your feature
328    /// request already exists, comment on the existing issue, so we can
329    /// prioritize it accordingly.
330    ///
331    /// [open an issue]: https://github.com/lpc-rs/lpc8xx-hal/issues
332    pub fn free(self) -> I {
333        self.spi
334    }
335}
336
337impl<I: Instance> FullDuplex<u8> for SPI<I, Enabled<Master>> {
338    type Error = Infallible;
339
340    fn read(&mut self) -> nb::Result<u8, Self::Error> {
341        if self.spi.stat.read().rxrdy().bit_is_clear() {
342            return Err(nb::Error::WouldBlock);
343        }
344
345        Ok(self.spi.rxdat.read().rxdat().bits() as u8)
346    }
347
348    fn send(&mut self, word: u8) -> nb::Result<(), Self::Error> {
349        if self.spi.stat.read().txrdy().bit_is_clear() {
350            return Err(nb::Error::WouldBlock);
351        }
352
353        self.spi
354            .txdat
355            .write(|w| unsafe { w.data().bits(word as u16) });
356
357        Ok(())
358    }
359}
360
361impl<I: Instance> embedded_hal::blocking::spi::transfer::Default<u8>
362    for SPI<I, Enabled<Master>>
363{
364}
365
366impl<I: Instance> embedded_hal::blocking::spi::write::Default<u8>
367    for SPI<I, Enabled<Master>>
368{
369}
370
371/// Indicates that SPI is in master mode
372///
373/// Used as a type parameter on [`SPI`].
374///
375/// [`SPI`]: struct.SPI.html
376pub struct Master;
377
378/// Indicates that SPI is in slave mode
379///
380/// Used as a type parameter on [`SPI`].
381///
382/// [`SPI`]: struct.SPI.html
383pub struct Slave;
384
385/// Receiver Overrun Error
386#[derive(Debug)]
387pub struct RxOverrunError;
388
389/// Transmitter Underrun Error
390#[derive(Debug)]
391pub struct TxUnderrunError;