@@ -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,33 @@ 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, TX and Error interrupts
101+ w. rxneie ( ) . not_masked ( ) ;
102+ w. txeie ( ) . not_masked ( ) ;
103+ w. errie ( ) . not_masked ( ) ;
106104 w
107105 } ) ;
108106
@@ -118,7 +116,6 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
118116 tx_idx : 0 ,
119117 tx_ready : 0 ,
120118 is_done : false ,
121- enabled : false ,
122119 } ;
123120
124121 // Empty the receive register
@@ -129,23 +126,8 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
129126 spi
130127 }
131128
132- /// Allow the [`Self::start`] and [`Self::stop`] functions to actually work.
133- pub fn enable ( & mut self ) {
134- self . enabled = true ;
135- }
136-
137- /// Prevent the [`Self::start`] and [`Self::stop`] functions from working.
138- pub fn disable ( & mut self ) {
139- self . stop ( ) ;
140- self . enabled = false ;
141- }
142-
143- /// Enable the SPI peripheral (i.e. when CS is low)
129+ /// Enable the SPI peripheral (i.e. when CS goes low)
144130 pub fn start ( & mut self ) {
145- if !self . enabled {
146- return ;
147- }
148-
149131 self . rx_idx = 0 ;
150132 self . tx_idx = 0 ;
151133 self . tx_ready = 0 ;
@@ -155,37 +137,18 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
155137 let _ = self . raw_read ( ) ;
156138 }
157139 self . dev . cr1 . modify ( |_r, w| {
158- w. spe ( ) . enabled ( ) ;
140+ w. ssi ( ) . slave_selected ( ) ;
159141 w
160142 } ) ;
161143 // Load our dummy byte (our TX FIFO will send this then repeat it whilst
162144 // it underflows during the receive phase).
163145 self . raw_write ( 0xFF ) ;
164- // Get an IRQ when there's RX data available
165- self . enable_rxne_irq ( ) ;
166146 }
167147
168- /// Disable the SPI peripheral (i.e. when CS is high)
148+ /// Disable the SPI peripheral (i.e. when CS goes high)
169149 pub fn stop ( & mut self ) {
170- self . disable_rxne_irq ( ) ;
171150 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 ( ) ;
151+ w. ssi ( ) . slave_not_selected ( ) ;
189152 w
190153 } ) ;
191154 }
@@ -197,13 +160,13 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
197160
198161 fn raw_read ( & mut self ) -> u8 {
199162 // PAC only supports 16-bit read, but that pops two bytes off the FIFO.
200- // So force a 16 -bit read.
163+ // So force an 8 -bit read.
201164 unsafe { core:: ptr:: read_volatile ( & self . dev . dr as * const _ as * const u8 ) }
202165 }
203166
204167 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 .
168+ // PAC only supports 16-bit read, but that pushes two bytes onto the FIFO.
169+ // So force an 8 -bit write .
207170 unsafe { core:: ptr:: write_volatile ( & self . dev . dr as * const _ as * mut u8 , data) }
208171 }
209172
@@ -224,6 +187,10 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
224187
225188 pub fn handle_isr ( & mut self ) {
226189 let irq_status = self . dev . sr . read ( ) ;
190+ if irq_status. fre ( ) . is_error ( ) || irq_status. ovr ( ) . is_overrun ( ) {
191+ // Handle errors!?
192+ defmt:: info!( "SPI sr=0b{:08b}" , irq_status. bits( ) ) ;
193+ }
227194 if irq_status. rxne ( ) . is_not_empty ( ) {
228195 self . rx_isr ( ) ;
229196 }
@@ -244,9 +211,10 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
244211 /// Call this in the TXEIE interrupt. It will load the SPI FIFO with some
245212 /// data, either from `tx_buffer` or a padding byte.
246213 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 ] ;
214+ if self . tx_idx < self . tx_ready {
215+ // We have some data yet to send. This is safe as long as we do the
216+ // bounds check when we set `self.tx_ready`.
217+ let next_tx = unsafe { * self . tx_buffer . get_unchecked ( self . tx_idx ) } ;
250218 self . raw_write ( next_tx) ;
251219 self . tx_idx += 1 ;
252220 } else {
@@ -262,12 +230,15 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
262230 self . tx_ready = 0 ;
263231 self . tx_idx = 0 ;
264232 if data. len ( ) > TXC {
265- // Too much data
233+ // Too much data. This check is important for safety in
234+ // [`Self::tx_isr`].
266235 return Err ( TXC ) ;
267236 }
268237 for ( inc, space) in data. iter ( ) . zip ( self . tx_buffer . iter_mut ( ) ) {
269238 * space = * inc;
270239 }
240+ // We must never set this to be longer than `TXC` as we do an unchecked
241+ // read from `self.tx_buffer` in [`Self::tx_isr`].
271242 self . tx_ready = data. len ( ) ;
272243 Ok ( ( ) )
273244 }
@@ -284,7 +255,9 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
284255
285256 match message. render_to_buffer ( & mut self . tx_buffer ) {
286257 Ok ( n) => {
287- self . tx_ready = n;
258+ // We must never set this to be longer than `TXC` as we do an
259+ // unchecked read from `self.tx_buffer` in [`Self::tx_isr`].
260+ self . tx_ready = n. min ( TXC ) ;
288261 Ok ( ( ) )
289262 }
290263 Err ( _) => Err ( ( ) ) ,
0 commit comments