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}