stm32f7xx_hal/
spi.rs

1//! Interface to the SPI peripheral
2//!
3//! See chapter 32 in the STM32F746 Reference Manual.
4
5pub use embedded_hal::spi::{Mode, Phase, Polarity};
6
7use core::{fmt, marker::PhantomData, ops::DerefMut, pin::Pin, ptr};
8
9use as_slice::{AsMutSlice, AsSlice as _};
10use embedded_hal::{
11    blocking::spi::{transfer, write, write_iter},
12    spi::FullDuplex,
13};
14
15use crate::{
16    gpio::{self, Alternate},
17    pac::{self, spi1::cr2},
18    rcc::{BusClock, Clocks, Enable, RccBus},
19    state,
20};
21
22use crate::dma;
23use fugit::HertzU32 as Hertz;
24
25/// Entry point to the SPI API
26pub struct Spi<I, P, State> {
27    spi: I,
28    pins: P,
29    _state: State,
30}
31
32impl<I, P> Spi<I, P, state::Disabled>
33where
34    I: Instance + Enable + BusClock,
35    P: Pins<I>,
36{
37    /// Create a new instance of the SPI API
38    pub fn new(instance: I, pins: P) -> Self {
39        Self {
40            spi: instance,
41            pins,
42            _state: state::Disabled,
43        }
44    }
45
46    /// Initialize the SPI peripheral
47    pub fn enable<Word>(
48        self,
49        mode: Mode,
50        freq: Hertz,
51        clocks: &Clocks,
52        apb: &mut <I as RccBus>::Bus,
53    ) -> Spi<I, P, Enabled<Word>>
54    where
55        Word: SupportedWordSize,
56    {
57        I::enable(apb);
58        let cpol = mode.polarity == Polarity::IdleHigh;
59        let cpha = mode.phase == Phase::CaptureOnSecondTransition;
60
61        let br = match I::clock(clocks) / freq {
62            0 => unreachable!(),
63            1..=2 => 0b000,
64            3..=5 => 0b001,
65            6..=11 => 0b010,
66            12..=23 => 0b011,
67            24..=47 => 0b100,
68            48..=95 => 0b101,
69            96..=191 => 0b110,
70            _ => 0b111,
71        };
72        self.spi.configure::<Word>(br, cpol, cpha);
73
74        Spi {
75            spi: self.spi,
76            pins: self.pins,
77            _state: Enabled(PhantomData),
78        }
79    }
80}
81
82impl<I, P, Word> Spi<I, P, Enabled<Word>>
83where
84    I: Instance,
85    P: Pins<I>,
86    Word: SupportedWordSize,
87{
88    /// Start an SPI transfer using DMA
89    ///
90    /// Sends the data in `buffer` and writes the received data into buffer
91    /// right after. Returns a [`Transfer`], to represent the ongoing SPI
92    /// transfer.
93    ///
94    /// Please note that the word "transfer" is used with two different meanings
95    /// here:
96    /// - An SPI transfer, as in an SPI transaction that involves both sending
97    ///   and receiving data. The method name refers to this kind of transfer.
98    /// - A DMA transfer, as in an ongoing DMA operation. The name of the return
99    ///   type refers to this kind of transfer.
100    ///
101    /// This method, as well as all other DMA-related methods in this module,
102    /// requires references to two DMA handles, one each for the RX and TX
103    /// streams. This will actually always be the same handle, as each SPI
104    /// instance uses the same DMA instance for both sending and receiving. It
105    /// would be nice to simplify that, but I believe that requires an equality
106    /// constraint in the where clause, which is not supported yet by the
107    /// compiler.
108    pub fn transfer_all<B>(
109        self,
110        buffer: Pin<B>,
111        dma_rx: &dma::Handle<<Rx<I> as dma::Target>::Instance, state::Enabled>,
112        dma_tx: &dma::Handle<<Tx<I> as dma::Target>::Instance, state::Enabled>,
113        rx: <Rx<I> as dma::Target>::Stream,
114        tx: <Tx<I> as dma::Target>::Stream,
115    ) -> Transfer<Word, I, P, B, Rx<I>, Tx<I>, dma::Ready>
116    where
117        Rx<I>: dma::Target,
118        Tx<I>: dma::Target,
119        B: DerefMut + 'static,
120        B::Target: AsMutSlice<Element = Word>,
121    {
122        // Create the RX/TX tokens for the transfer. Those must only exist once,
123        // otherwise it would be possible to create multiple transfers trying to
124        // use the same hardware resources.
125        //
126        // We guarantee that they only exist once by only creating them where we
127        // have access to `self`, moving `self` into the `Transfer` while they
128        // are in use, and dropping them when returning `self` from the
129        // transfer.
130        let rx_token = Rx(PhantomData);
131        let tx_token = Tx(PhantomData);
132
133        // We need to move a buffer into each of the `dma::Transfer` instances,
134        // while keeping the original buffer around to return to the caller
135        // later, when the transfer is finished.
136        //
137        // Here we create two `Buffer` from raw pointers acquired from `buffer`.
138        let rx_buffer = dma::PtrBuffer {
139            ptr: buffer.as_slice().as_ptr(),
140            len: buffer.as_slice().len(),
141        };
142        let tx_buffer = dma::PtrBuffer {
143            ptr: buffer.as_slice().as_ptr(),
144            len: buffer.as_slice().len(),
145        };
146
147        // Create the two DMA transfers. This is safe, for the following
148        // reasons:
149        // 1. The trait bounds on this method guarantee that `buffer`, which we
150        //    created the two buffer instances from, can be safely read from and
151        //    written to.
152        // 2. The semantics of the SPI peripheral guarantee that the buffer
153        //    reads/writes are synchronized, preventing race conditions.
154        let rx_transfer = unsafe {
155            dma::Transfer::new(
156                dma_rx,
157                rx,
158                Pin::new(rx_buffer),
159                rx_token,
160                self.spi.dr_address(),
161                dma::Direction::PeripheralToMemory,
162            )
163        };
164        let tx_transfer = unsafe {
165            dma::Transfer::new(
166                dma_tx,
167                tx,
168                Pin::new(tx_buffer),
169                tx_token,
170                self.spi.dr_address(),
171                dma::Direction::MemoryToPeripheral,
172            )
173        };
174
175        Transfer {
176            buffer,
177            target: self,
178
179            rx: rx_transfer,
180            tx: tx_transfer,
181
182            _state: dma::Ready,
183        }
184    }
185}
186
187impl<I, P, Word> FullDuplex<Word> for Spi<I, P, Enabled<Word>>
188where
189    I: Instance,
190    P: Pins<I>,
191    Word: SupportedWordSize,
192{
193    type Error = Error;
194
195    fn read(&mut self) -> nb::Result<Word, Self::Error> {
196        self.spi.read()
197    }
198
199    fn send(&mut self, word: Word) -> nb::Result<(), Self::Error> {
200        self.spi.send(word)
201    }
202}
203
204impl<I, P, Word> transfer::Default<Word> for Spi<I, P, Enabled<Word>>
205where
206    I: Instance,
207    P: Pins<I>,
208    Word: SupportedWordSize,
209{
210}
211
212impl<I, P, Word> write::Default<Word> for Spi<I, P, Enabled<Word>>
213where
214    I: Instance,
215    P: Pins<I>,
216    Word: SupportedWordSize,
217{
218}
219
220impl<I, P, Word> write_iter::Default<Word> for Spi<I, P, Enabled<Word>>
221where
222    I: Instance,
223    P: Pins<I>,
224    Word: SupportedWordSize,
225{
226}
227
228impl<I, P, State> Spi<I, P, State>
229where
230    I: Instance,
231    P: Pins<I>,
232{
233    /// Destroy the peripheral API and return a raw SPI peripheral instance
234    pub fn free(self) -> (I, P) {
235        (self.spi, self.pins)
236    }
237}
238
239/// Implemented for all instances of the SPI peripheral
240///
241/// Users of this crate should not implement this trait.
242pub trait Instance {
243    fn configure<Word>(&self, br: u8, cpol: bool, cpha: bool)
244    where
245        Word: SupportedWordSize;
246    fn read<Word>(&self) -> nb::Result<Word, Error>
247    where
248        Word: SupportedWordSize;
249    fn send<Word>(&self, word: Word) -> nb::Result<(), Error>
250    where
251        Word: SupportedWordSize;
252    fn dr_address(&self) -> u32;
253}
254
255/// Implemented for all tuples that contain a full set of valid SPI pins
256pub trait Pins<I> {}
257
258impl<I, SCK, MISO, MOSI> Pins<I> for (SCK, MISO, MOSI)
259where
260    SCK: Sck<I>,
261    MISO: Miso<I>,
262    MOSI: Mosi<I>,
263{
264}
265
266/// Implemented for all pins that can function as the SCK pin
267///
268/// Users of this crate should not implement this trait.
269pub trait Sck<I> {}
270
271/// Implemented for all pins that can function as the MISO pin
272///
273/// Users of this crate should not implement this trait.
274pub trait Miso<I> {}
275
276/// Implemented for all pins that can function as the MOSI pin
277///
278/// Users of this crate should not implement this trait.
279pub trait Mosi<I> {}
280
281macro_rules! impl_instance {
282    (
283        $(
284            $name:ty {
285                regs: ($bus:ident, $reset:ident, $enable:ident),
286                pins: {
287                    SCK: [$($sck:ty,)*],
288                    MISO: [$($miso:ty,)*],
289                    MOSI: [$($mosi:ty,)*],
290                }
291            }
292        )*
293    ) => {
294        $(
295            impl Instance for $name {
296
297                // I don't like putting this much code into the macro, but I
298                // have to: There are two different SPI variants in the PAC, and
299                // while I haven't found any actual differences between them,
300                // they're still using different sets of types, and I need to
301                // generate different methods to interface with them, even
302                // though these methods end up looking identical.
303                //
304                // Maybe this is a problem in the SVD file that can be fixed
305                // there.
306
307                fn configure<Word>(&self, br: u8, cpol: bool, cpha: bool)
308                    where Word: SupportedWordSize
309                {
310                    self.cr2.write(|w| {
311                        // Data size
312                        //
313                        // This is safe, as `Word::ds` returns an enum which can
314                        // only encode valid variants for this field.
315                        let w = unsafe { w.ds().bits(Word::ds().into()) };
316
317                        w
318                            // FIFO reception threshold.
319                            .frxth().bit(Word::frxth().into())
320                            // Disable TX buffer empty interrupt
321                            .txeie().masked()
322                            // Disable RX buffer not empty interrupt
323                            .rxneie().masked()
324                            // Disable error interrupt
325                            .errie().masked()
326                            // Frame format
327                            .frf().motorola()
328                            // NSS pulse management
329                            .nssp().no_pulse()
330                            // SS output
331                            .ssoe().disabled()
332                            // Enable DMA support
333                            .txdmaen().enabled()
334                            .rxdmaen().enabled()
335                    });
336
337                    self.cr1.write(|w|
338                        w
339                            // Use two lines for MISO/MOSI
340                            .bidimode().unidirectional()
341                            // Disable hardware CRC calculation
342                            .crcen().disabled()
343                            // Enable full-duplex mode
344                            .rxonly().full_duplex()
345                            // Manage slave select pin manually
346                            .ssm().enabled()
347                            .ssi().set_bit()
348                            // Transmit most significant bit first
349                            .lsbfirst().msbfirst()
350                            // Set baud rate value
351                            .br().bits(br)
352                            // Select master mode
353                            .mstr().master()
354                            // Select clock polarity
355                            .cpol().bit(cpol)
356                            // Select clock phase
357                            .cpha().bit(cpha)
358                            // Enable SPI
359                            .spe().enabled()
360                    );
361                }
362
363                fn read<Word>(&self) -> nb::Result<Word, Error> {
364                    let sr = self.sr.read();
365
366                    // Check for errors
367                    //
368                    // This whole code should live in a method in `Error`, but
369                    // this wouldn't be straight-forward, due to the different
370                    // SPI types in the PAC, explained in more detail in
371                    // another comment.
372                    if sr.fre().is_error() {
373                        return Err(nb::Error::Other(Error::FrameFormat));
374                    }
375                    if sr.ovr().is_overrun() {
376                        return Err(nb::Error::Other(Error::Overrun));
377                    }
378                    if sr.modf().is_fault() {
379                        return Err(nb::Error::Other(Error::ModeFault));
380                    }
381
382                    // Did we receive something?
383                    if sr.rxne().is_not_empty() {
384                        // It makes a difference whether we read this register
385                        // as a `u8` or `u16`, so we can't use the standard way
386                        // to access it. This is safe, as `&self.dr` is a
387                        // memory-mapped register.
388                        let value = unsafe {
389                            ptr::read_volatile(self.dr_address() as *mut _)
390                        };
391
392                        return Ok(value);
393                    }
394
395                    Err(nb::Error::WouldBlock)
396                }
397
398                fn send<Word>(&self, word: Word) -> nb::Result<(), Error> {
399                    let sr = self.sr.read();
400
401                    // Check for errors
402                    //
403                    // This whole code should live in a method in `Error`, but
404                    // this wouldn't be straight-forward, due to the different
405                    // SPI types in the PAC, explained in more detail in
406                    // another comment.
407                    if sr.fre().is_error() {
408                        return Err(nb::Error::Other(Error::FrameFormat));
409                    }
410                    if sr.ovr().is_overrun() {
411                        return Err(nb::Error::Other(Error::Overrun));
412                    }
413                    if sr.modf().is_fault() {
414                        return Err(nb::Error::Other(Error::ModeFault));
415                    }
416
417                    // Can we write to the transmit buffer?
418                    if sr.txe().is_empty() {
419                        // It makes a difference whether we write a `u8` or
420                        // `u16` to this register, so we can't use the standard
421                        // way to access it. This is safe, as `&self.dr` is a
422                        // memory-mapped register.
423                        unsafe {
424                            ptr::write_volatile(
425                                self.dr_address() as *mut _,
426                                word,
427                            );
428                        }
429
430                        return Ok(())
431                    }
432
433                    Err(nb::Error::WouldBlock)
434                }
435
436                fn dr_address(&self) -> u32 {
437                    core::ptr::addr_of!(self.dr) as u32
438                }
439            }
440
441            $(
442                impl Sck<$name> for $sck {}
443            )*
444
445            $(
446                impl Miso<$name> for $miso {}
447            )*
448
449            $(
450                impl Mosi<$name> for $mosi {}
451            )*
452        )*
453    }
454}
455
456impl_instance!(
457    pac::SPI1 {
458        regs: (apb2, spi1rst, spi1en),
459        pins: {
460            SCK: [
461                gpio::PA5<Alternate<5>>,
462                gpio::PB3<Alternate<5>>,
463                gpio::PG11<Alternate<5>>,
464            ],
465            MISO: [
466                gpio::PA6<Alternate<5>>,
467                gpio::PB4<Alternate<5>>,
468                gpio::PG9<Alternate<5>>,
469            ],
470            MOSI: [
471                gpio::PA7<Alternate<5>>,
472                gpio::PB5<Alternate<5>>,
473                gpio::PD7<Alternate<5>>,
474            ],
475        }
476    }
477    pac::SPI2 {
478        regs: (apb1, spi2rst, spi2en),
479        pins: {
480            SCK: [
481                gpio::PA9<Alternate<5>>,
482                gpio::PA12<Alternate<5>>,
483                gpio::PB10<Alternate<5>>,
484                gpio::PB13<Alternate<5>>,
485                gpio::PD3<Alternate<5>>,
486                gpio::PI1<Alternate<5>>,
487            ],
488            MISO: [
489                gpio::PB14<Alternate<5>>,
490                gpio::PC2<Alternate<5>>,
491                gpio::PI2<Alternate<5>>,
492            ],
493            MOSI: [
494                gpio::PB15<Alternate<5>>,
495                gpio::PC1<Alternate<5>>,
496                gpio::PC3<Alternate<5>>,
497                gpio::PI3<Alternate<5>>,
498            ],
499        }
500    }
501    pac::SPI3 {
502        regs: (apb1, spi3rst, spi3en),
503        pins: {
504            SCK: [
505                gpio::PB3<Alternate<6>>,
506                gpio::PC10<Alternate<6>>,
507            ],
508            MISO: [
509                gpio::PB4<Alternate<6>>,
510                gpio::PC11<Alternate<6>>,
511            ],
512            MOSI: [
513                gpio::PB2<Alternate<7>>,
514                gpio::PB5<Alternate<6>>,
515                gpio::PC12<Alternate<6>>,
516                gpio::PD6<Alternate<5>>,
517            ],
518        }
519    }
520    pac::SPI4 {
521        regs: (apb2, spi4rst, spi4en),
522        pins: {
523            SCK: [
524                gpio::PE2<Alternate<5>>,
525                gpio::PE12<Alternate<5>>,
526            ],
527            MISO: [
528                gpio::PE5<Alternate<5>>,
529                gpio::PE13<Alternate<5>>,
530            ],
531            MOSI: [
532                gpio::PE6<Alternate<5>>,
533                gpio::PE14<Alternate<5>>,
534            ],
535        }
536    }
537    pac::SPI5 {
538        regs: (apb2, spi5rst, spi5en),
539        pins: {
540            SCK: [
541                gpio::PF7<Alternate<5>>,
542                gpio::PH6<Alternate<5>>,
543            ],
544            MISO: [
545                gpio::PF8<Alternate<5>>,
546                gpio::PH7<Alternate<5>>,
547            ],
548            MOSI: [
549                gpio::PF9<Alternate<5>>,
550                gpio::PF11<Alternate<5>>,
551            ],
552        }
553    }
554);
555
556#[cfg(any(
557    feature = "stm32f745",
558    feature = "stm32f746",
559    feature = "stm32f756",
560    feature = "stm32f765",
561    feature = "stm32f767",
562    feature = "stm32f769",
563    feature = "stm32f777",
564    feature = "stm32f778",
565    feature = "stm32f779",
566))]
567impl_instance!(
568    pac::SPI6 {
569        regs: (apb2, spi6rst, spi6en),
570        pins: {
571            SCK: [
572                gpio::PG13<Alternate<5>>,
573            ],
574            MISO: [
575                gpio::PG12<Alternate<5>>,
576            ],
577            MOSI: [
578                gpio::PG14<Alternate<5>>,
579            ],
580        }
581    }
582);
583
584/// Placeholder for a pin when no SCK pin is required
585pub struct NoSck;
586impl<I> Sck<I> for NoSck {}
587
588/// Placeholder for a pin when no MISO pin is required
589pub struct NoMiso;
590impl<I> Miso<I> for NoMiso {}
591
592/// Placeholder for a pin when no MOSI pin is required
593pub struct NoMosi;
594impl<I> Mosi<I> for NoMosi {}
595
596#[derive(Debug)]
597pub enum Error {
598    FrameFormat,
599    Overrun,
600    ModeFault,
601}
602
603/// RX token used for DMA transfers
604pub struct Rx<I>(PhantomData<I>);
605
606/// TX token used for DMA transfers
607pub struct Tx<I>(PhantomData<I>);
608
609/// A DMA transfer of the SPI peripheral
610///
611/// Since DMA can send and receive at the same time, using two DMA transfers and
612/// two DMA streams, we need this type to represent this operation and wrap the
613/// underlying [`dma::Transfer`] instances.
614pub struct Transfer<Word: SupportedWordSize, I, P, Buffer, Rx: dma::Target, Tx: dma::Target, State>
615{
616    buffer: Pin<Buffer>,
617    target: Spi<I, P, Enabled<Word>>,
618    rx: dma::Transfer<Rx, dma::PtrBuffer<Word>, State>,
619    tx: dma::Transfer<Tx, dma::PtrBuffer<Word>, State>,
620    _state: State,
621}
622
623impl<Word, I, P, Buffer, Rx, Tx> Transfer<Word, I, P, Buffer, Rx, Tx, dma::Ready>
624where
625    Rx: dma::Target,
626    Tx: dma::Target,
627    Word: SupportedWordSize,
628{
629    /// Enables the given interrupts for this DMA transfer
630    ///
631    /// These interrupts are only enabled for this transfer. The settings
632    /// doesn't affect other transfers, nor subsequent transfers using the same
633    /// DMA streams.
634    pub fn enable_interrupts(
635        &mut self,
636        rx_handle: &dma::Handle<Rx::Instance, state::Enabled>,
637        tx_handle: &dma::Handle<Tx::Instance, state::Enabled>,
638        interrupts: dma::Interrupts,
639    ) {
640        self.rx.enable_interrupts(rx_handle, interrupts);
641        self.tx.enable_interrupts(tx_handle, interrupts);
642    }
643
644    /// Start the DMA transfer
645    ///
646    /// Consumes this instance of `Transfer` and returns another instance with
647    /// its type state set to indicate the transfer has been started.
648    pub fn start(
649        self,
650        rx_handle: &dma::Handle<Rx::Instance, state::Enabled>,
651        tx_handle: &dma::Handle<Tx::Instance, state::Enabled>,
652    ) -> Transfer<Word, I, P, Buffer, Rx, Tx, dma::Started> {
653        Transfer {
654            buffer: self.buffer,
655            target: self.target,
656            rx: self.rx.start(rx_handle),
657            tx: self.tx.start(tx_handle),
658            _state: dma::Started,
659        }
660    }
661}
662
663impl<Word, I, P, Buffer, Rx, Tx> Transfer<Word, I, P, Buffer, Rx, Tx, dma::Started>
664where
665    Rx: dma::Target,
666    Tx: dma::Target,
667    Word: SupportedWordSize,
668{
669    /// Checks whether the transfer is still ongoing
670    pub fn is_active(
671        &self,
672        rx_handle: &dma::Handle<Rx::Instance, state::Enabled>,
673        tx_handle: &dma::Handle<Tx::Instance, state::Enabled>,
674    ) -> bool {
675        self.rx.is_active(rx_handle) || self.tx.is_active(tx_handle)
676    }
677
678    /// Waits for the transfer to end
679    ///
680    /// This method will block if the transfer is still ongoing. If you want
681    /// this method to return immediately, first check whether the transfer is
682    /// still ongoing by calling `is_active`.
683    ///
684    /// An ongoing transfer needs exlusive access to some resources, namely the
685    /// data buffer, the DMA stream, and the peripheral. Those have been moved
686    /// into the `Transfer` instance to prevent concurrent access to them. This
687    /// method returns those resources, so they can be used again.
688    pub fn wait(
689        self,
690        rx_handle: &dma::Handle<Rx::Instance, state::Enabled>,
691        tx_handle: &dma::Handle<Tx::Instance, state::Enabled>,
692    ) -> WaitResult<Word, I, P, Rx, Tx, Buffer> {
693        let (rx_res, rx_err) = match self.rx.wait(rx_handle) {
694            Ok(res) => (res, None),
695            Err((res, err)) => (res, Some(err)),
696        };
697        let (tx_res, tx_err) = match self.tx.wait(tx_handle) {
698            Ok(res) => (res, None),
699            Err((res, err)) => (res, Some(err)),
700        };
701
702        let res = TransferResources {
703            rx_stream: rx_res.stream,
704            tx_stream: tx_res.stream,
705            target: self.target,
706            buffer: self.buffer,
707        };
708
709        if let Some(err) = rx_err {
710            return Err((res, err));
711        }
712        if let Some(err) = tx_err {
713            return Err((res, err));
714        }
715
716        Ok(res)
717    }
718}
719
720/// Returned by [`Transfer::wait`]
721pub type WaitResult<Word, I, P, Rx, Tx, Buffer> = Result<
722    TransferResources<Word, I, P, Rx, Tx, Buffer>,
723    (TransferResources<Word, I, P, Rx, Tx, Buffer>, dma::Error),
724>;
725
726/// The resources that an ongoing transfer needs exclusive access to
727pub struct TransferResources<Word, I, P, Rx: dma::Target, Tx: dma::Target, Buffer> {
728    pub rx_stream: Rx::Stream,
729    pub tx_stream: Tx::Stream,
730    pub target: Spi<I, P, Enabled<Word>>,
731    pub buffer: Pin<Buffer>,
732}
733
734// As `TransferResources` is used in the error variant of `Result`, it needs a
735// `Debug` implementation to enable stuff like `unwrap` and `expect`. This can't
736// be derived without putting requirements on the type arguments.
737impl<Word, I, P, Rx, Tx, Buffer> fmt::Debug for TransferResources<Word, I, P, Rx, Tx, Buffer>
738where
739    Rx: dma::Target,
740    Tx: dma::Target,
741{
742    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
743        write!(f, "TransferResources {{ .. }}")
744    }
745}
746
747/// Indicates that the SPI peripheral is enabled
748///
749/// The `Word` type parameter indicates which word size the peripheral is
750/// configured for.
751pub struct Enabled<Word>(PhantomData<Word>);
752
753pub trait SupportedWordSize: dma::SupportedWordSize + private::Sealed {
754    fn frxth() -> cr2::FRXTH_A;
755    fn ds() -> cr2::DS_A;
756}
757
758impl private::Sealed for u8 {}
759impl SupportedWordSize for u8 {
760    fn frxth() -> cr2::FRXTH_A {
761        cr2::FRXTH_A::Quarter
762    }
763
764    fn ds() -> cr2::DS_A {
765        cr2::DS_A::EightBit
766    }
767}
768
769impl private::Sealed for u16 {}
770impl SupportedWordSize for u16 {
771    fn frxth() -> cr2::FRXTH_A {
772        cr2::FRXTH_A::Half
773    }
774
775    fn ds() -> cr2::DS_A {
776        cr2::DS_A::SixteenBit
777    }
778}
779
780mod private {
781    /// Prevents code outside of the parent module from implementing traits
782    ///
783    /// This trait is located in a module that is not accessible outside of the
784    /// parent module. This means that any trait that requires `Sealed` cannot
785    /// be implemented only in the parent module.
786    pub trait Sealed {}
787}