1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
use core::convert::TryInto;

use embedded_hal::spi;

#[cfg(any(feature = "samd11", feature = "samd21"))]
use crate::pac::sercom0::SPI;
#[cfg(feature = "min-samd51g")]
use crate::pac::sercom0::SPIM;

#[cfg(any(feature = "samd11", feature = "samd21"))]
use crate::pac::sercom0::spi::ctrla::MODE_A;
#[cfg(feature = "min-samd51g")]
use crate::pac::sercom0::spim::ctrla::MODE_A;

use crate::sercom::Sercom;
use crate::time::Hertz;

use super::{BitOrder, DataWidth, Error, Flags, Phase, Polarity, Status};

//==============================================================================
// Registers
//==============================================================================

/// Define a task-focused register interface for SPI peripherals
///
/// This struct acts to define a task-focused, rather than register-focused, API
/// for a SERCOM configured as an SPI peripheral. It also serves to erase the
/// inherent interior mutability of the PAC [`Sercom`] type, which allows it to
/// implement [`Sync`].
pub(super) struct Registers<S: Sercom> {
    pub sercom: S,
}

// Safety: The [`Registers`] struct erases interior mutability, so this is now
// safe.
unsafe impl<S: Sercom> Sync for Registers<S> {}

impl<S: Sercom> Registers<S> {
    #[cfg(any(feature = "samd11", feature = "samd21"))]
    #[inline]
    pub fn spi(&self) -> &SPI {
        self.sercom.spi()
    }

    #[cfg(feature = "min-samd51g")]
    #[inline]
    pub fn spi(&self) -> &SPIM {
        self.sercom.spim()
    }

    /// Reset the SERCOM peripheral
    #[inline]
    pub fn reset(&mut self) {
        self.spi().ctrla.write(|w| w.swrst().set_bit());
        while self.spi().syncbusy.read().swrst().bit_is_set() {}
    }

    #[cfg(feature = "dma")]
    /// Get a pointer to the `DATA` register
    pub fn data_ptr<Z: super::Size>(&self) -> *mut Z::Word {
        self.spi().data.as_ptr() as *mut _
    }

    /// Configure the DIPO and DOPO values
    #[inline]
    pub fn set_dipo_dopo(&mut self, dipo_dopo: (u8, u8)) {
        let (dipo, dopo) = dipo_dopo;
        self.spi().ctrla.modify(|_, w| unsafe {
            w.dipo().bits(dipo);
            w.dopo().bits(dopo)
        });
    }

    /// Configure the SPI operating mode
    ///
    /// For maximum flexibility, this module chooses to always operate in 32-bit
    /// extension mode. The LENGTH counter is used to control the number of byes
    /// in each SPI transaction. Due to a hardware bug, ICSPACE must be at least
    /// one. See the silicon errata for more details.
    #[inline]
    pub fn set_op_mode(&mut self, mode: MODE_A, mssen: bool) {
        self.spi().ctrla.modify(|_, w| w.mode().variant(mode));
        self.spi().ctrlb.modify(|_, w| w.mssen().bit(mssen));
        #[cfg(feature = "min-samd51g")]
        self.spi().ctrlc.write(|w| unsafe {
            w.data32b().data_trans_32bit();
            w.icspace().bits(1)
        });
        while self.spi().syncbusy.read().ctrlb().bit_is_set() {}
    }

    /// Return the current transaction length
    #[cfg(feature = "min-samd51g")]
    #[inline]
    pub fn get_length(&self) -> u8 {
        self.spi().length.read().len().bits()
    }

    /// Set the transaction length
    #[cfg(feature = "min-samd51g")]
    #[inline]
    pub fn set_length(&mut self, length: u8) {
        let length = if length == 0 { 1 } else { length };
        self.spi().length.write(|w| unsafe {
            w.len().bits(length);
            w.lenen().set_bit()
        });
        while self.spi().syncbusy.read().length().bit_is_set() {}
    }

    /// Set the character size
    #[cfg(any(feature = "samd11", feature = "samd21"))]
    #[inline]
    pub fn set_char_size(&mut self, bits: u8) {
        self.spi()
            .ctrlb
            .modify(|_, w| unsafe { w.chsize().bits(bits) });
    }

    /// Get the clock polarity
    #[inline]
    pub fn get_cpol(&self) -> Polarity {
        let cpol = self.spi().ctrla.read().cpol().bit();
        match cpol {
            false => Polarity::IdleLow,
            true => Polarity::IdleHigh,
        }
    }

    /// Set the clock polarity
    #[inline]
    pub fn set_cpol(&mut self, cpol: Polarity) {
        let cpol = match cpol {
            Polarity::IdleLow => false,
            Polarity::IdleHigh => true,
        };
        self.spi().ctrla.modify(|_, w| w.cpol().bit(cpol));
    }

    /// Get the clock phase
    #[inline]
    pub fn get_cpha(&self) -> Phase {
        let cpha = self.spi().ctrla.read().cpha().bit();
        match cpha {
            false => Phase::CaptureOnFirstTransition,
            true => Phase::CaptureOnSecondTransition,
        }
    }

    /// Set the clock phase
    #[inline]
    pub fn set_cpha(&mut self, cpha: Phase) {
        let cpha = match cpha {
            Phase::CaptureOnFirstTransition => false,
            Phase::CaptureOnSecondTransition => true,
        };
        self.spi().ctrla.modify(|_, w| w.cpha().bit(cpha));
    }

    /// Get the SPI mode (clock polarity & phase)
    #[inline]
    pub fn get_spi_mode(&self) -> spi::Mode {
        let reg = self.spi().ctrla.read();
        let cpol = reg.cpol().bit();
        let cpha = reg.cpha().bit();
        let polarity = match cpol {
            false => Polarity::IdleLow,
            true => Polarity::IdleHigh,
        };
        let phase = match cpha {
            false => Phase::CaptureOnFirstTransition,
            true => Phase::CaptureOnSecondTransition,
        };
        spi::Mode { polarity, phase }
    }

    /// Set the SPI mode (clock polarity & phase)
    #[inline]
    pub fn set_spi_mode(&mut self, mode: spi::Mode) {
        let cpol = match mode.polarity {
            Polarity::IdleLow => false,
            Polarity::IdleHigh => true,
        };
        let cpha = match mode.phase {
            Phase::CaptureOnFirstTransition => false,
            Phase::CaptureOnSecondTransition => true,
        };
        self.spi().ctrla.modify(|_, w| {
            w.cpol().bit(cpol);
            w.cpha().bit(cpha)
        });
    }

    /// Get the bit order of transmission (MSB/LSB first)
    #[inline]
    pub fn get_bit_order(&self) -> BitOrder {
        let order = self.spi().ctrla.read().dord().bits();
        match order {
            false => BitOrder::MsbFirst,
            true => BitOrder::LsbFirst,
        }
    }

    /// Set the bit order of transmission (MSB/LSB first)
    #[inline]
    pub fn set_bit_order(&mut self, order: BitOrder) {
        let order = match order {
            BitOrder::MsbFirst => false,
            BitOrder::LsbFirst => true,
        };
        self.spi().ctrla.modify(|_, w| w.dord().bit(order));
    }

    /// Get the baud rate
    #[inline]
    pub fn get_baud(&mut self, freq: Hertz) -> Hertz {
        let baud = self.spi().baud.read().baud().bits() as u32 + 1;
        Hertz(freq.0 / 2 / baud)
    }

    /// Set the baud rate
    #[inline]
    pub fn set_baud(&mut self, freq: Hertz, baud: impl Into<Hertz>) {
        let baud = baud.into().0;
        let baud = if baud == 0 { 1 } else { baud };
        let bits = (freq.0 / 2 / baud).saturating_sub(1);
        let bits = bits.try_into().unwrap_or(u8::MAX);
        self.spi()
            .baud
            .modify(|_, w| unsafe { w.baud().bits(bits) });
    }

    /// Get the enable state of the immediate buffer overflow notification
    #[inline]
    pub fn get_ibon(&self) -> bool {
        self.spi().ctrla.read().ibon().bit()
    }

    /// Set the enable state of the immediate buffer overflow notification
    #[inline]
    pub fn set_ibon(&mut self, enabled: bool) {
        self.spi().ctrla.modify(|_, w| w.ibon().bit(enabled));
    }

    /// Get run in standby mode
    #[inline]
    pub fn get_run_in_standby(&self) -> bool {
        self.spi().ctrla.read().runstdby().bit()
    }

    /// Set run in standby mode
    #[inline]
    pub fn set_run_in_standby(&mut self, set: bool) {
        self.spi().ctrla.modify(|_, w| w.runstdby().bit(set));
    }

    /// Enable interrupts for the specified flags
    #[inline]
    pub fn enable_interrupts(&mut self, flags: Flags) {
        self.spi()
            .intenset
            .write(|w| unsafe { w.bits(flags.bits()) });
    }

    /// Disable interrupts for the specified flags
    #[inline]
    pub fn disable_interrupts(&mut self, flags: Flags) {
        self.spi()
            .intenclr
            .write(|w| unsafe { w.bits(flags.bits()) });
    }

    /// Enable the receiver
    #[inline]
    pub fn rx_enable(&mut self) {
        self.spi().ctrlb.modify(|_, w| w.rxen().set_bit());
        while self.spi().syncbusy.read().ctrlb().bit_is_set() {}
    }

    /// Disable the receiver
    #[inline]
    pub fn rx_disable(&mut self) {
        self.spi().ctrlb.modify(|_, w| w.rxen().clear_bit());
        while self.spi().syncbusy.read().ctrlb().bit_is_set() {}
    }

    /// Enable the peripheral
    #[inline]
    pub fn enable(&mut self) {
        self.spi().ctrla.modify(|_, w| w.enable().set_bit());
        while self.spi().syncbusy.read().enable().bit_is_set() {}
    }

    /// Disable the peripheral
    #[inline]
    pub fn disable(&mut self) {
        self.spi().ctrla.modify(|_, w| w.enable().clear_bit());
        while self.spi().syncbusy.read().enable().bit_is_set() {}
    }

    /// Read from the `DATA` register
    #[inline]
    pub fn read_data(&mut self) -> DataWidth {
        self.spi().data.read().data().bits()
    }

    /// Write to the `DATA` register
    #[inline]
    pub fn write_data(&mut self, data: DataWidth) {
        // Safety: All bit patterns are memory safe
        self.spi().data.write(|w| unsafe { w.data().bits(data) })
    }

    /// Read the interrupt flags
    #[inline]
    pub fn read_flags(&self) -> Flags {
        let bits = self.spi().intflag.read().bits();
        Flags::from_bits_truncate(bits)
    }

    /// Clear interrupt flags
    #[inline]
    pub fn clear_flags(&mut self, flags: Flags) {
        unsafe { self.spi().intflag.write(|w| w.bits(flags.bits())) };
    }

    /// Read the error status flags
    #[inline]
    pub fn read_status(&self) -> Status {
        let bits = self.spi().status.read().bits();
        Status::from_bits_truncate(bits)
    }

    /// Clear error status flags
    #[inline]
    pub fn clear_status(&mut self, status: Status) {
        unsafe { self.spi().status.write(|w| w.bits(status.bits())) };
    }

    /// Try to read the interrupt flags, but first check the error status flags.
    #[inline]
    pub fn read_flags_errors(&self) -> Result<Flags, Error> {
        self.read_status().try_into()?;
        Ok(self.read_flags())
    }
}