@@ -12,15 +12,16 @@ pub struct SpiPeripheral<const RXC: usize, const TXC: usize> {
1212 rx_buffer : [ u8 ; RXC ] ,
1313 /// How many bytes have been received?
1414 rx_idx : usize ,
15+ /// How many bytes do we want?
16+ rx_want : usize ,
1517 /// A space for data we're about to send
1618 tx_buffer : [ u8 ; TXC ] ,
1719 /// How many bytes have been played from the TX buffer
1820 tx_idx : usize ,
1921 /// How many bytes are loaded into the TX buffer
2022 tx_ready : usize ,
21- /// Has the RX been processed? If so, lie and say we don't have any data
22- /// (otherwise we process incoming messages multiple times).
23- is_done : bool ,
23+ /// The in-progress RX CRC
24+ rx_crc : neotron_bmc_protocol:: CrcCalc ,
2425}
2526
2627impl < const RXC : usize , const TXC : usize > SpiPeripheral < RXC , TXC > {
@@ -50,10 +51,11 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
5051 dev,
5152 rx_buffer : [ 0u8 ; RXC ] ,
5253 rx_idx : 0 ,
54+ rx_want : 0 ,
5355 tx_buffer : [ 0u8 ; TXC ] ,
5456 tx_idx : 0 ,
5557 tx_ready : 0 ,
56- is_done : false ,
58+ rx_crc : neotron_bmc_protocol :: CrcCalc :: new ( ) ,
5759 } ;
5860
5961 spi. config ( Self :: MODE ) ;
@@ -130,12 +132,11 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
130132 // 3f. Disable DMA mode
131133 w. txdmaen ( ) . disabled ( ) ;
132134 w. rxdmaen ( ) . disabled ( ) ;
133- // Extra: Turn on RX and Error interrupts, but not TX. The TX
134- // interrupt is turned off because we deliberately underflow the
135- // FIFO during the receive phase of the transaction.
136- w. rxneie ( ) . not_masked ( ) ;
135+ // Extra: Turn on RX and Error interrupts, but not TX. We swap
136+ // interrupts once the read phase is complete.
137+ w. rxneie ( ) . masked ( ) ;
137138 w. txeie ( ) . masked ( ) ;
138- w. errie ( ) . not_masked ( ) ;
139+ w. errie ( ) . masked ( ) ;
139140 w
140141 } ) ;
141142
@@ -144,16 +145,30 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
144145 // 5. DMA registers - not required
145146 }
146147
147- /// Enable the SPI peripheral (i.e. when CS goes low)
148- pub fn start ( & mut self ) {
148+ /// Enable the SPI peripheral (i.e. when CS goes low).
149+ ///
150+ /// We tell it how many bytes we are expecting, so it knows when to update
151+ /// the main thread.
152+ pub fn start ( & mut self , num_bytes : usize ) {
153+ if num_bytes > RXC {
154+ panic ! ( "Read too large" ) ;
155+ }
149156 self . rx_idx = 0 ;
157+ self . rx_want = num_bytes as usize ;
150158 self . tx_idx = 0 ;
151159 self . tx_ready = 0 ;
152- self . is_done = false ;
160+ self . rx_crc . reset ( ) ;
153161 // Empty the receive register
154162 while self . has_rx_data ( ) {
155163 let _ = self . raw_read ( ) ;
156164 }
165+ // Turn on RX interrupt; turn off TX interrupt
166+ self . dev . cr2 . write ( |w| {
167+ w. rxneie ( ) . not_masked ( ) ;
168+ w. txeie ( ) . masked ( ) ;
169+ w
170+ } ) ;
171+ // Tell the SPI engine it has a chip-select
157172 self . dev . cr1 . modify ( |_r, w| {
158173 w. ssi ( ) . slave_selected ( ) ;
159174 w
@@ -221,37 +236,46 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
221236 }
222237
223238 /// Get a slice of data received so far.
224- pub fn get_received ( & self ) -> Option < & [ u8 ] > {
225- if !self . is_done {
226- Some ( & self . rx_buffer [ 0 ..self . rx_idx ] )
227- } else {
228- None
229- }
230- }
231-
232- /// Mark the RX as processed, so we don't do it again (until the SPI is
233- /// disabled and re-enabled by the next chip select).
234- pub fn mark_done ( & mut self ) {
235- self . is_done = true ;
239+ pub fn get_received ( & self ) -> Option < ( & [ u8 ] , u8 ) > {
240+ Some ( ( ( & self . rx_buffer [ 0 ..self . rx_idx ] ) , self . rx_crc . get ( ) ) )
236241 }
237242
238- pub fn handle_isr ( & mut self ) {
243+ /// Call this when the SPI peripheral interrupt fires.
244+ ///
245+ /// It will handle incoming bytes and/or outgoing bytes, depending on what
246+ /// phase we are in.
247+ pub fn handle_isr ( & mut self ) -> bool {
248+ let mut have_packet = false ;
239249 let irq_status = self . dev . sr . read ( ) ;
240250 if irq_status. rxne ( ) . is_not_empty ( ) {
241- self . rx_isr ( ) ;
251+ if self . rx_isr ( ) {
252+ // We've got enough, turn the RX interrupt off. Everything else
253+ // we receive is going to be garbage.
254+ self . dev . cr2 . write ( |w| {
255+ w. rxneie ( ) . masked ( ) ;
256+ w
257+ } ) ;
258+ have_packet = true ;
259+ }
242260 }
243261 if irq_status. txe ( ) . is_empty ( ) {
244262 self . tx_isr ( ) ;
245263 }
264+ have_packet
246265 }
247266
248267 /// Try and read from the SPI FIFO
249- fn rx_isr ( & mut self ) {
250- let cmd = self . raw_read ( ) ;
268+ fn rx_isr ( & mut self ) -> bool {
269+ let byte = self . raw_read ( ) ;
270+ if self . rx_want == 0 {
271+ panic ! ( "unwanted data 0x{:02x}" , byte) ;
272+ }
251273 if self . rx_idx < self . rx_buffer . len ( ) {
252- self . rx_buffer [ self . rx_idx ] = cmd;
274+ self . rx_buffer [ self . rx_idx ] = byte;
275+ self . rx_crc . add ( byte) ;
253276 self . rx_idx += 1 ;
254277 }
278+ self . rx_idx == self . rx_want
255279 }
256280
257281 /// Call this in the TXEIE interrupt. It will load the SPI FIFO with some
@@ -264,7 +288,8 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
264288 self . raw_write ( next_tx) ;
265289 self . tx_idx += 1 ;
266290 } else {
267- // No data - send nothing
291+ // No data - send 0x00
292+ self . raw_write ( 0x00 ) ;
268293 }
269294 }
270295
@@ -285,6 +310,11 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
285310 // We must never set this to be longer than `TXC` as we do an unchecked
286311 // read from `self.tx_buffer` in [`Self::tx_isr`].
287312 self . tx_ready = data. len ( ) ;
313+ // Turn on the TX interrupt
314+ self . dev . cr2 . write ( |w| {
315+ w. txeie ( ) . not_masked ( ) ;
316+ w
317+ } ) ;
288318 Ok ( ( ) )
289319 }
290320
@@ -303,6 +333,11 @@ impl<const RXC: usize, const TXC: usize> SpiPeripheral<RXC, TXC> {
303333 // We must never set this to be longer than `TXC` as we do an
304334 // unchecked read from `self.tx_buffer` in [`Self::tx_isr`].
305335 self . tx_ready = n. min ( TXC ) ;
336+ // Turn on the TX interrupt
337+ self . dev . cr2 . write ( |w| {
338+ w. txeie ( ) . not_masked ( ) ;
339+ w
340+ } ) ;
306341 Ok ( ( ) )
307342 }
308343 Err ( _) => Err ( ( ) ) ,
0 commit comments