cancel
Showing results for 
Search instead for 
Did you mean: 

SPI dma reads start at index 1 after first read

skip_shane
Associate II

I'm trying to read and write SPI bytes from an STM32F446RE chip with DMA.  The STM32f4 is the slave and a RPi is the master.  The first SPI read works fine (data read into buffer starting at index 0), but subsequent reads have the data starting at index 1 instead of index 0.  I tried resetting the SPI peripheral with this bit of code I found on the forums:

 

#define RESETSPI {\
	HAL_SPI_Abort(&hspi2);\
	__HAL_RCC_SPI2_FORCE_RESET();\
	__HAL_RCC_SPI2_RELEASE_RESET();\
}

 

 This fixes the reading off by 1, but it results in the last byte sent back to the PI to be wrong.

 

Does anyone know what might be happening or how I might address it?

1 ACCEPTED SOLUTION

Accepted Solutions

SPI is inherently duplex, you can't transmit without receiving. If you transmit and don't pick the received data from the data register (either reading it by processor, or by DMA), they just overflow, but one byte remains in the receiver.

If you then switch on Rx DMA, that one byte gets transferred immediately into the memory, before really receiving data.

(In slave setting, it's not just active transmission when data are arriving into the receiver, but whenever clocks arrive from the master.)

So, the solution may be to read out the SPI_DR register by processor, immediately before enabling reception. Another (and in theory better) solution is to have the Rx DMA on all the time (circular), but I admit it may can get tricky to determine, where the freshly received data exactly are in such circular buffer.

If you reset SPI in the Tx DMA transfer-complete callback, that may be too soon, it may be before the entire byte has been actually shifted out by SPI - that's why it gets corrupted.

JW

View solution in original post

5 REPLIES 5

That's too little context to be able to judge. DMA is set up how, circular, single-shot? If latter, how it is started, in relationship to NSS/clock incoming from master? "Read" is which direction? NSS is handled how? The "extra" datum is identical to the previous "correct" byte, or not?

JW

Thanks for your comment.  To answer your questions:

  • DMA is set to one shot for rx and tx
  • The reads on the STM32 side are off by 1 if I don't reset the SPI peripheral with the code above.  The reads on the RPi side look to be fine with no peripheral reset
  • If I do the SPI peripheral reset in `HAL_SPI_TxCpltCallback()` then the reads the on the STM32 side are as expected, without extra 0 padding.  But the reads on the RPi side have a corrupted last byte.
  • Read is started by ` HAL_SPI_Receive_DMA(&hspi2, spi_rx_buf,SPI_MSG_MAX_LEN);`
  • Write is started by ` HAL_SPI_Transmit_DMA(&hspi2, dBuffer,MEDULLA_RSP_LEN);`
  • NSS is handled by hardware
  • The extra data is always at position 0 and always a value of 0

 

I got the firmware working by reseting the STM32 SPI peripheral after every write, then on the Pi side I only look at 3 of the 4 crc bytes.  But it would still be nice to figure out what I'm doing wrong here.

Why are the DMAs two different lengths (SPI_MSG_MAX_LEN and MEDULLA_RSP_LEN)? How much they are exactly? How many bytes transfers master, exactly? Do you have waveform captures to show?

JW

The Pi sends 4k bytes to the STM32, then the STM32 responds with 6 bytes.  I'll try to get some waveform captures to post here.

SPI is inherently duplex, you can't transmit without receiving. If you transmit and don't pick the received data from the data register (either reading it by processor, or by DMA), they just overflow, but one byte remains in the receiver.

If you then switch on Rx DMA, that one byte gets transferred immediately into the memory, before really receiving data.

(In slave setting, it's not just active transmission when data are arriving into the receiver, but whenever clocks arrive from the master.)

So, the solution may be to read out the SPI_DR register by processor, immediately before enabling reception. Another (and in theory better) solution is to have the Rx DMA on all the time (circular), but I admit it may can get tricky to determine, where the freshly received data exactly are in such circular buffer.

If you reset SPI in the Tx DMA transfer-complete callback, that may be too soon, it may be before the entire byte has been actually shifted out by SPI - that's why it gets corrupted.

JW