Преглед изворни кода

Spi with 16bit framesize (#258)

* separated 8 and 16b dataframe operation

* added conversion from 16b to 8b spi dataframe

* added SpiReadWrite trait to reduce duplicate code

* updated CHANGELOG.md
jonnyando пре 3 година
родитељ
комит
3859f52f13
2 измењених фајлова са 155 додато и 63 уклоњено
  1. 6 1
      CHANGELOG.md
  2. 149 62
      src/spi.rs

+ 6 - 1
CHANGELOG.md

@@ -9,7 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 ### Breaking changes
 
-MonoTimer now takes ownership of the DCB register
+- MonoTimer now takes ownership of the DCB register
+- SPI objects now have a `FrameSize` type field
+
+### Added
+
+- Add 16 bit dataframe size for `SPI`
 
 ### Fixed
 

+ 149 - 62
src/spi.rs

@@ -109,10 +109,11 @@ pins_impl!(
     (MISO, SCK, MOSI), (Miso, Sck, Mosi), (_Miso, _Sck, _Mosi);
 );
 
-pub struct Spi<SPI, REMAP, PINS> {
+pub struct Spi<SPI, REMAP, PINS, FRAMESIZE> {
     spi: SPI,
     pins: PINS,
     _remap: PhantomData<REMAP>,
+    _framesize: PhantomData<FRAMESIZE>,
 }
 
 /// A filler type for when the SCK pin is unnecessary
@@ -147,9 +148,9 @@ remap!(Spi3NoRemap, SPI3, false, PB3, PB4, PB5);
 #[cfg(feature = "connectivity")]
 remap!(Spi3Remap, SPI3, true, PC10, PC11, PC12);
 
-impl<REMAP, PINS> Spi<SPI1, REMAP, PINS> {
+impl<REMAP, PINS> Spi<SPI1, REMAP, PINS, u8> {
     /**
-      Constructs an SPI instance using SPI1.
+      Constructs an SPI instance using SPI1 in 8bit dataframe mode.
 
       The pin parameter tuple (sck, miso, mosi) should be `(PA5, PA6, PA7)` or `(PB3, PB4, PB5)` configured as `(Alternate<PushPull>, Input<Floating>, Alternate<PushPull>)`.
 
@@ -170,13 +171,13 @@ impl<REMAP, PINS> Spi<SPI1, REMAP, PINS> {
         PINS: Pins<REMAP, POS>,
     {
         mapr.modify_mapr(|_, w| w.spi1_remap().bit(REMAP::REMAP));
-        Spi::<SPI1, _, _>::_spi(spi, pins, mode, freq.into(), clocks, apb)
+        Spi::<SPI1, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
     }
 }
 
-impl<REMAP, PINS> Spi<SPI2, REMAP, PINS> {
+impl<REMAP, PINS> Spi<SPI2, REMAP, PINS, u8> {
     /**
-      Constructs an SPI instance using SPI2.
+      Constructs an SPI instance using SPI2 in 8bit dataframe mode.
 
       The pin parameter tuple (sck, miso, mosi) should be `(PB13, PB14, PB15)` configured as `(Alternate<PushPull>, Input<Floating>, Alternate<PushPull>)`.
 
@@ -195,14 +196,14 @@ impl<REMAP, PINS> Spi<SPI2, REMAP, PINS> {
         REMAP: Remap<Periph = SPI2>,
         PINS: Pins<REMAP, POS>,
     {
-        Spi::<SPI2, _, _>::_spi(spi, pins, mode, freq.into(), clocks, apb)
+        Spi::<SPI2, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
     }
 }
 
 #[cfg(any(feature = "high", feature = "connectivity"))]
-impl<REMAP, PINS> Spi<SPI3, REMAP, PINS> {
+impl<REMAP, PINS> Spi<SPI3, REMAP, PINS, u8> {
     /**
-      Constructs an SPI instance using SPI3.
+      Constructs an SPI instance using SPI3 in 8bit dataframe mode.
 
       The pin parameter tuple (sck, miso, mosi) should be `(PB3, PB4, PB5)` or `(PC10, PC11, PC12)` configured as `(Alternate<PushPull>, Input<Floating>, Alternate<PushPull>)`.
 
@@ -221,13 +222,94 @@ impl<REMAP, PINS> Spi<SPI3, REMAP, PINS> {
         REMAP: Remap<Periph = SPI3>,
         PINS: Pins<REMAP, POS>,
     {
-        Spi::<SPI3, _, _>::_spi(spi, pins, mode, freq.into(), clocks, apb)
+        Spi::<SPI3, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
     }
 }
 
 pub type SpiRegisterBlock = crate::pac::spi1::RegisterBlock;
 
-impl<SPI, REMAP, PINS> Spi<SPI, REMAP, PINS>
+pub trait SpiReadWrite<T> {
+    fn read_data_reg(&mut self) -> T;
+    fn write_data_reg(&mut self, data: T);
+    fn spi_write(&mut self, words: &[T]) -> Result<(), Error>;
+}
+
+impl<SPI, REMAP, PINS, FrameSize> SpiReadWrite<FrameSize> for Spi<SPI, REMAP, PINS, FrameSize>
+where
+    SPI: Deref<Target = SpiRegisterBlock>,
+    FrameSize: Copy,
+{
+    fn read_data_reg(&mut self) -> FrameSize {
+        // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows
+        // reading a half-word)
+        return unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const FrameSize) };
+    }
+
+    fn write_data_reg(&mut self, data: FrameSize) {
+        // NOTE(write_volatile) see note above
+        unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut FrameSize, data) }
+    }
+
+    // Implement write as per the "Transmit only procedure" page 712
+    // of RM0008 Rev 20. This is more than twice as fast as the
+    // default Write<> implementation (which reads and drops each
+    // received value)
+    fn spi_write(&mut self, words: &[FrameSize]) -> Result<(), Error> {
+        // Write each word when the tx buffer is empty
+        for word in words {
+            loop {
+                let sr = self.spi.sr.read();
+                if sr.txe().bit_is_set() {
+                    // NOTE(write_volatile) see note above
+                    // unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, *word) }
+                    self.write_data_reg(*word);
+                    if sr.modf().bit_is_set() {
+                        return Err(Error::ModeFault);
+                    }
+                    break;
+                }
+            }
+        }
+        // Wait for final TXE
+        loop {
+            let sr = self.spi.sr.read();
+            if sr.txe().bit_is_set() {
+                break;
+            }
+        }
+        // Wait for final !BSY
+        loop {
+            let sr = self.spi.sr.read();
+            if !sr.bsy().bit_is_set() {
+                break;
+            }
+        }
+        // Clear OVR set due to dropped received values
+        // NOTE(read_volatile) see note above
+        // unsafe {
+        //     let _ = ptr::read_volatile(&self.spi.dr as *const _ as *const u8);
+        // }
+        let _ = self.read_data_reg();
+        let _ = self.spi.sr.read();
+        Ok(())
+    }
+}
+
+impl<SPI, REMAP, PINS, FrameSize> Spi<SPI, REMAP, PINS, FrameSize>
+where
+    SPI: Deref<Target = SpiRegisterBlock>,
+    FrameSize: Copy,
+{
+    #[deprecated(since = "0.6.0", note = "Please use release instead")]
+    pub fn free(self) -> (SPI, PINS) {
+        self.release()
+    }
+    pub fn release(self) -> (SPI, PINS) {
+        (self.spi, self.pins)
+    }
+}
+
+impl<SPI, REMAP, PINS> Spi<SPI, REMAP, PINS, u8>
 where
     SPI: Deref<Target = SpiRegisterBlock> + Enable + Reset,
     SPI::Bus: GetBusFreq,
@@ -300,25 +382,50 @@ where
             spi,
             pins,
             _remap: PhantomData,
+            _framesize: PhantomData,
         }
     }
-
-    #[deprecated(since = "0.6.0", note = "Please use release instead")]
-    pub fn free(self) -> (SPI, PINS) {
-        self.release()
+    /// Converts from 8bit dataframe to 16bit.
+    pub fn frame_size_16bit(self) -> Spi<SPI, REMAP, PINS, u16> {
+        self.spi.cr1.modify(|_, w| w.spe().clear_bit());
+        self.spi.cr1.modify(|_, w| w.dff().set_bit());
+        self.spi.cr1.modify(|_, w| w.spe().set_bit());
+        Spi {
+            spi: self.spi,
+            pins: self.pins,
+            _remap: PhantomData,
+            _framesize: PhantomData,
+        }
     }
-    pub fn release(self) -> (SPI, PINS) {
-        (self.spi, self.pins)
+}
+
+impl<SPI, REMAP, PINS> Spi<SPI, REMAP, PINS, u16>
+where
+    SPI: Deref<Target = SpiRegisterBlock>,
+{
+    /// Converts from 16bit dataframe to 8bit.
+    pub fn frame_size_8bit(self) -> Spi<SPI, REMAP, PINS, u8> {
+        self.spi.cr1.modify(|_, w| w.spe().clear_bit());
+        self.spi.cr1.modify(|_, w| w.dff().clear_bit());
+        self.spi.cr1.modify(|_, w| w.spe().set_bit());
+        Spi {
+            spi: self.spi,
+            pins: self.pins,
+            _remap: PhantomData,
+            _framesize: PhantomData,
+        }
     }
 }
 
-impl<SPI, REMAP, PINS> crate::hal::spi::FullDuplex<u8> for Spi<SPI, REMAP, PINS>
+impl<SPI, REMAP, PINS, FrameSize> crate::hal::spi::FullDuplex<FrameSize>
+    for Spi<SPI, REMAP, PINS, FrameSize>
 where
     SPI: Deref<Target = SpiRegisterBlock>,
+    FrameSize: Copy,
 {
     type Error = Error;
 
-    fn read(&mut self) -> nb::Result<u8, Error> {
+    fn read(&mut self) -> nb::Result<FrameSize, Error> {
         let sr = self.spi.sr.read();
 
         Err(if sr.ovr().bit_is_set() {
@@ -330,13 +437,13 @@ where
         } else if sr.rxne().bit_is_set() {
             // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows
             // reading a half-word)
-            return Ok(unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u8) });
+            return Ok(self.read_data_reg());
         } else {
             nb::Error::WouldBlock
         })
     }
 
-    fn send(&mut self, byte: u8) -> nb::Result<(), Error> {
+    fn send(&mut self, data: FrameSize) -> nb::Result<(), Error> {
         let sr = self.spi.sr.read();
 
         Err(if sr.ovr().bit_is_set() {
@@ -347,7 +454,7 @@ where
             nb::Error::Other(Error::Crc)
         } else if sr.txe().bit_is_set() {
             // NOTE(write_volatile) see note above
-            unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) }
+            self.write_data_reg(data);
             return Ok(());
         } else {
             nb::Error::WouldBlock
@@ -355,12 +462,15 @@ where
     }
 }
 
-impl<SPI, REMAP, PINS> crate::hal::blocking::spi::transfer::Default<u8> for Spi<SPI, REMAP, PINS> where
-    SPI: Deref<Target = SpiRegisterBlock>
+impl<SPI, REMAP, PINS, FrameSize> crate::hal::blocking::spi::transfer::Default<FrameSize>
+    for Spi<SPI, REMAP, PINS, FrameSize>
+where
+    SPI: Deref<Target = SpiRegisterBlock>,
+    FrameSize: Copy,
 {
 }
 
-impl<SPI, REMAP, PINS> crate::hal::blocking::spi::Write<u8> for Spi<SPI, REMAP, PINS>
+impl<SPI, REMAP, PINS> crate::hal::blocking::spi::Write<u8> for Spi<SPI, REMAP, PINS, u8>
 where
     SPI: Deref<Target = SpiRegisterBlock>,
 {
@@ -371,48 +481,25 @@ where
     // default Write<> implementation (which reads and drops each
     // received value)
     fn write(&mut self, words: &[u8]) -> Result<(), Error> {
-        // Write each word when the tx buffer is empty
-        for word in words {
-            loop {
-                let sr = self.spi.sr.read();
-                if sr.txe().bit_is_set() {
-                    // NOTE(write_volatile) see note above
-                    unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, *word) }
-                    if sr.modf().bit_is_set() {
-                        return Err(Error::ModeFault);
-                    }
-                    break;
-                }
-            }
-        }
-        // Wait for final TXE
-        loop {
-            let sr = self.spi.sr.read();
-            if sr.txe().bit_is_set() {
-                break;
-            }
-        }
-        // Wait for final !BSY
-        loop {
-            let sr = self.spi.sr.read();
-            if !sr.bsy().bit_is_set() {
-                break;
-            }
-        }
-        // Clear OVR set due to dropped received values
-        // NOTE(read_volatile) see note above
-        unsafe {
-            let _ = ptr::read_volatile(&self.spi.dr as *const _ as *const u8);
-        }
-        let _ = self.spi.sr.read();
-        Ok(())
+        self.spi_write(words)
+    }
+}
+
+impl<SPI, REMAP, PINS> crate::hal::blocking::spi::Write<u16> for Spi<SPI, REMAP, PINS, u16>
+where
+    SPI: Deref<Target = SpiRegisterBlock>,
+{
+    type Error = Error;
+
+    fn write(&mut self, words: &[u16]) -> Result<(), Error> {
+        self.spi_write(words)
     }
 }
 
 // DMA
 
 pub struct SpiPayload<SPI, REMAP, PINS> {
-    spi: Spi<SPI, REMAP, PINS>,
+    spi: Spi<SPI, REMAP, PINS, u8>,
 }
 
 pub type SpiTxDma<SPI, REMAP, PINS, CHANNEL> = TxDma<SpiPayload<SPI, REMAP, PINS>, CHANNEL>;
@@ -424,7 +511,7 @@ macro_rules! spi_dma {
             type ReceivedWord = u8;
         }
 
-        impl<REMAP, PINS> Spi<$SPIi, REMAP, PINS> {
+        impl<REMAP, PINS> Spi<$SPIi, REMAP, PINS, u8> {
             pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, REMAP, PINS, $TCi> {
                 let payload = SpiPayload { spi: self };
                 SpiTxDma { payload, channel }