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;