@@ -17,29 +17,23 @@ pub struct SpiPeripheral<const RXC: usize, const TXC: usize> {
1717 tx_idx : usize ,
1818 /// How many bytes are loaded into the TX buffer
1919 tx_ready : usize ,
20- /// Has the RX been processed?
20+ /// Has the RX been processed? If so, lie and say we don't have any data
21+ /// (otherwise we process incoming messages multiple times).
2122 is_done : bool ,
22- /// Are we enabled? If not, start and stop won't work.
23- enabled : bool ,
2423}
2524
2625impl < const RXC : usize , const TXC : usize > SpiPeripheral < RXC , TXC > {
2726 pub fn new < SCKPIN , MISOPIN , MOSIPIN > (
2827 dev : pac:: SPI1 ,
2928 pins : ( SCKPIN , MISOPIN , MOSIPIN ) ,
30- speed_hz : u32 ,
3129 rcc : & mut Rcc ,
3230 ) -> SpiPeripheral < RXC , TXC >
3331 where
3432 SCKPIN : stm32f0xx_hal:: spi:: SckPin < pac:: SPI1 > ,
3533 MISOPIN : stm32f0xx_hal:: spi:: MisoPin < pac:: SPI1 > ,
3634 MOSIPIN : stm32f0xx_hal:: spi:: MosiPin < pac:: SPI1 > ,
3735 {
38- defmt:: info!(
39- "pclk = {}, incoming spi_clock = {}" ,
40- rcc. clocks. pclk( ) . 0 ,
41- speed_hz
42- ) ;
36+ defmt:: info!( "pclk = {}" , rcc. clocks. pclk( ) . 0 , ) ;
4337
4438 let mode = embedded_hal:: spi:: MODE_0 ;
4539
@@ -80,29 +74,35 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
8074 w. lsbfirst ( ) . clear_bit ( ) ;
8175 // 2e. Configure the CRCL and CRCEN bits if CRC is needed (it is not)
8276 w. crcen ( ) . disabled ( ) ;
83- // 2f. Turn off soft-slave-management (SSM) and slave-select-internal ( SSI)
84- w. ssm ( ) . disabled ( ) ;
85- w. ssi ( ) . slave_selected ( ) ;
77+ // 2f. Turn on soft-slave-management (SSM) (we control the NSS signal with the SSI bit).
78+ w. ssm ( ) . enabled ( ) ;
79+ w. ssi ( ) . slave_not_selected ( ) ;
8680 // 2g. Set the Master bit low for slave mode
8781 w. mstr ( ) . slave ( ) ;
8882 w
8983 } ) ;
9084
9185 // 3. Write to SPI_CR2 register
9286 dev. cr2 . write ( |w| {
93- // 3a. Configure the DS[3:0] bits to select the data length for the transfer.
87+ // 3a. Configure the DS[3:0] bits to select the data length for the transfer (0b111 = 8-bit words) .
9488 unsafe { w. ds ( ) . bits ( 0b111 ) } ;
95- // 3b. Disable hard-output on the CS pin
89+ // 3b. Disable hard-output on the CS pin (ignored in Master mode)
9690 w. ssoe ( ) . disabled ( ) ;
9791 // 3c. Frame Format
9892 w. frf ( ) . motorola ( ) ;
9993 // 3d. Set NSSP bit if required (we don't want NSS Pulse mode)
10094 w. nssp ( ) . no_pulse ( ) ;
101- // 3e. Configure the FIFO RX Threshold to 1/4 FIFO
95+ // 3e. Configure the FIFO RX Threshold to 1/4 FIFO (8 bits)
10296 w. frxth ( ) . quarter ( ) ;
103- // 3f. LDMA_TX and LDMA_RX for DMA mode - not used
104- // Extra: Turn on RX Not Empty Interrupt Enable
105- w. rxneie ( ) . set_bit ( ) ;
97+ // 3f. Disable DMA mode
98+ w. txdmaen ( ) . disabled ( ) ;
99+ w. rxdmaen ( ) . disabled ( ) ;
100+ // Extra: Turn on RX and Error interrupts, but not TX. The TX
101+ // interrupt is turned off because we deliberately underflow the
102+ // FIFO during the receive phase of the transaction.
103+ w. rxneie ( ) . not_masked ( ) ;
104+ w. txeie ( ) . masked ( ) ;
105+ w. errie ( ) . not_masked ( ) ;
106106 w
107107 } ) ;
108108
@@ -118,34 +118,26 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
118118 tx_idx : 0 ,
119119 tx_ready : 0 ,
120120 is_done : false ,
121- enabled : false ,
122121 } ;
123122
124123 // Empty the receive register
125124 while spi. has_rx_data ( ) {
126125 let _ = spi. raw_read ( ) ;
127126 }
128127
129- spi
130- }
131-
132- /// Allow the [`Self::start`] and [`Self::stop`] functions to actually work.
133- pub fn enable ( & mut self ) {
134- self . enabled = true ;
135- }
128+ // Enable the SPI device
129+ spi . stop ( ) ;
130+ spi . dev . cr1 . write ( |w| {
131+ // Enable the peripheral
132+ w . spe ( ) . enabled ( ) ;
133+ w
134+ } ) ;
136135
137- /// Prevent the [`Self::start`] and [`Self::stop`] functions from working.
138- pub fn disable ( & mut self ) {
139- self . stop ( ) ;
140- self . enabled = false ;
136+ spi
141137 }
142138
143- /// Enable the SPI peripheral (i.e. when CS is low)
139+ /// Enable the SPI peripheral (i.e. when CS goes low)
144140 pub fn start ( & mut self ) {
145- if !self . enabled {
146- return ;
147- }
148-
149141 self . rx_idx = 0 ;
150142 self . tx_idx = 0 ;
151143 self . tx_ready = 0 ;
@@ -155,37 +147,15 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
155147 let _ = self . raw_read ( ) ;
156148 }
157149 self . dev . cr1 . modify ( |_r, w| {
158- w. spe ( ) . enabled ( ) ;
150+ w. ssi ( ) . slave_selected ( ) ;
159151 w
160152 } ) ;
161- // Load our dummy byte (our TX FIFO will send this then repeat it whilst
162- // it underflows during the receive phase).
163- self . raw_write ( 0xFF ) ;
164- // Get an IRQ when there's RX data available
165- self . enable_rxne_irq ( ) ;
166153 }
167154
168- /// Disable the SPI peripheral (i.e. when CS is high)
155+ /// Disable the SPI peripheral (i.e. when CS goes high)
169156 pub fn stop ( & mut self ) {
170- self . disable_rxne_irq ( ) ;
171157 self . dev . cr1 . modify ( |_r, w| {
172- w. spe ( ) . disabled ( ) ;
173- w
174- } ) ;
175- }
176-
177- /// Enable RX Not Empty interrupt
178- fn enable_rxne_irq ( & mut self ) {
179- self . dev . cr2 . modify ( |_r, w| {
180- w. rxneie ( ) . set_bit ( ) ;
181- w
182- } ) ;
183- }
184-
185- /// Disable RX Not Empty interrupt
186- fn disable_rxne_irq ( & mut self ) {
187- self . dev . cr2 . modify ( |_r, w| {
188- w. rxneie ( ) . clear_bit ( ) ;
158+ w. ssi ( ) . slave_not_selected ( ) ;
189159 w
190160 } ) ;
191161 }
@@ -197,13 +167,13 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
197167
198168 fn raw_read ( & mut self ) -> u8 {
199169 // PAC only supports 16-bit read, but that pops two bytes off the FIFO.
200- // So force a 16 -bit read.
170+ // So force an 8 -bit read.
201171 unsafe { core:: ptr:: read_volatile ( & self . dev . dr as * const _ as * const u8 ) }
202172 }
203173
204174 fn raw_write ( & mut self , data : u8 ) {
205- // PAC only supports 16-bit read, but that pops two bytes off the FIFO.
206- // So force a 16 -bit read .
175+ // PAC only supports 16-bit read, but that pushes two bytes onto the FIFO.
176+ // So force an 8 -bit write .
207177 unsafe { core:: ptr:: write_volatile ( & self . dev . dr as * const _ as * mut u8 , data) }
208178 }
209179
@@ -244,14 +214,14 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
244214 /// Call this in the TXEIE interrupt. It will load the SPI FIFO with some
245215 /// data, either from `tx_buffer` or a padding byte.
246216 fn tx_isr ( & mut self ) {
247- if ( self . tx_idx < self . tx_ready ) && ( self . tx_idx < self . tx_buffer . len ( ) ) {
248- // We have some data yet to send
249- let next_tx = self . tx_buffer [ self . tx_idx ] ;
217+ if self . tx_idx < self . tx_ready {
218+ // We have some data yet to send. This is safe as long as we do the
219+ // bounds check when we set `self.tx_ready`.
220+ let next_tx = unsafe { * self . tx_buffer . get_unchecked ( self . tx_idx ) } ;
250221 self . raw_write ( next_tx) ;
251222 self . tx_idx += 1 ;
252223 } else {
253- // No data - send padding
254- self . raw_write ( 0xFF ) ;
224+ // No data - send nothing
255225 }
256226 }
257227
@@ -262,12 +232,15 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
262232 self . tx_ready = 0 ;
263233 self . tx_idx = 0 ;
264234 if data. len ( ) > TXC {
265- // Too much data
235+ // Too much data. This check is important for safety in
236+ // [`Self::tx_isr`].
266237 return Err ( TXC ) ;
267238 }
268239 for ( inc, space) in data. iter ( ) . zip ( self . tx_buffer . iter_mut ( ) ) {
269240 * space = * inc;
270241 }
242+ // We must never set this to be longer than `TXC` as we do an unchecked
243+ // read from `self.tx_buffer` in [`Self::tx_isr`].
271244 self . tx_ready = data. len ( ) ;
272245 Ok ( ( ) )
273246 }
@@ -284,7 +257,9 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
284257
285258 match message. render_to_buffer ( & mut self . tx_buffer ) {
286259 Ok ( n) => {
287- self . tx_ready = n;
260+ // We must never set this to be longer than `TXC` as we do an
261+ // unchecked read from `self.tx_buffer` in [`Self::tx_isr`].
262+ self . tx_ready = n. min ( TXC ) ;
288263 Ok ( ( ) )
289264 }
290265 Err ( _) => Err ( ( ) ) ,
0 commit comments