cancel
Showing results for 
Search instead for 
Did you mean: 

SPI communication issue on STM32F103RB (sort of buffer over/underun)

kanserv
Associate

Good day.

I've got an issue when trying to connect two STM32 MCUs via SPI.

STM32F429ZI is master and STM32F103RB is slave.

Master is running at 168MHz and slave runs at 72MHz. SPI clock is the lowest possible - 164.062kHz.

Slave select is hardware controlled.

Clock polarity is 0 and clock phase is 1. Data frame size is 8 bits. Both devices have the same configuration.

Slave device uses DMA in circular mode to receive and transmit. Receive/Transmit is initiated only once on the slave. The firmware is coded with use of HAL.

Master's firmware uses interrupts to read data received via SPI and the source code uses mbed.

Now, master sends 26 bytes to the slave and receives 26 bytes of response. The operation is regular.

Now, the issue I encounter is that the data is sent by slave starting from the second byte, e.g. the buffer provided to HAL_SPI_TransmitReceive_DMA for transmission contains 0x01, 0x02, 0x03... 0x1A and the master receives 0x02, 0x03, 0x04..0x1A, 0x01. Now, the last 0x01 received actually belongs to the next transmission buffer.

UPD: I also see that the transmission from the slave starts with the second byte with help of scope.

Neither of 26-byte packets is lost.

I've modified master's source code to send a byte at a time and checked for the received bytes and verified that the slave only starts to reply with the second byte, i.e. the first response is 0x02, the second response is 0x03, the third one is 0x04 and so on.

This makes me think it's not a buffer underrun as the responses would be all zero or they would be like 0x02, 0x04, and so on.

I'm really thinking to throw away HAL's usage for SPI+DMA but the 164kHz communication speed seems rather low to make the software not fit into timing.

 

What I've checked also:

  1. The edges of SCK, MISO, MOSI and NSS are quite good, no noise present.
  2. The master gets power two seconds after the slave turns on. Master initiates communication in 5 seconds after power on. This is stable and guaranteed behaviour.

    The slave on its power up:

    - sets up HAL and HW config
    - prepares buffer to send
    - calls HAL_SPI_TransmitReceive_DMA with the prepared buffer

    These three steps take quite a little time compared to 2 + 5 seconds for master's communication start.

  3. The data is always available on the slave side. The slave has regular process to fill up the buffer and it uses DMA in circular configuration for SPI communication. The buffer data is substituted once the transmission is over with help of HAL_SPI_RxTxCallback
  4. Slave Select to SCK edge timing issue. Changing SS-SCK latency (with software control of slave select pin) didn't fix the issue. I've increased the delay up to 2 seconds after moving SS GPIO to low to select the slave. Still the issue reproduced.
  5. When I make slave to transmit only a single byte it's transmitted perfectly. Having a buffer of 2, 4, or 8 bytes makes the slave to respond with the second byte on the very first communication cycle

 

Now, I've played with the scope again and noticed some weird timing play. There are three cases:

 

  1. The one depicted earlier. But now I transmit A5, FF and 24 zero bytes from the slave and register FF, 24 zero's, and A5 on the master side. Reminder: the A5 in the end is from the next buffer for transmission on the slave. Yet again, on the slave side two sequential buffer to send will look like this )with respect to time, buffer's index is parenthesized): A5 (0), FF (0), 00 (0) * 24, A5 (1), FF (1), 00 (1) * 24. Master receives FF (0), 00 (0) * 24, A5(1).
    This makes me think that on the slave side DMA is rather fast as it can move 26 bytes within single SPI tick time.
  2. I put the probe of the scope on SCK with respect to GND. Now, master receives a good package: A5 (0), FF (0), 00 (0) * 24. If I remove the probe, the sequence is still good and the issue doesn't reproduce until reset.
  3. I put the probe of the scope on SCK with scope's ground disconnected (i.e. the signal is "measured" with respect to "air"). Now, master receives the following sequence: 00, 00, ..., A5(0), FF(0), 00 (0), 00 (0)... Again, after disconnecting the probe the sequence repeats the same until reset

 

I can see an explanation for the latter case. The probe with respect to "the air" makes a capacitance which should be charged by getting clock high first (remember, CPOL is 0). This makes a great delay on slave's side SCK pin which can be as long as time required for transmitting several bytes.

On the other hand, I can't see any valid explanation for the second case as the probe adds capacitance between SCK and GND, which should've led to another slight delay, something like fractions of a bit time.

 

I can't see any valid reasoning behind this sort of behaviour. The sending from the second byte appears to happen when using SPI+DMA, SPI in non-blocking mode (interrupt), blocking SPI mode when the buffer size is larger than 1 byte.

 

Do you have any thoughts on reasons of this happening?

Thanks in advance.

Sorry for my broken English

 

I've already checked these (and other) links but to no avail:

https://stackoverflow.com/questions/47306571/stm32f-spi-with-dma-errorcallback-and-frames-shifted

https://community.st.com/t5/stm32-mcus-products/spi-overrun/td-p/436527

https://community.st.com/t5/stm32-mcus-products/spi-overrun-when-using-the-dma/td-p/394935

https://community.st.com/t5/stm32-mcus-products/spi-underrun-error/td-p/67765

https://community.st.com/t5/stm32-mcus-products/spi-overrun-when-using-the-dma/td-p/394935

https://electronics.stackexchange.com/questions/509826/spi-data-arrives-at-every-2nd-send-and-in-the-wrong-sequence

 

UPD: Slave uses SPI2 interface.

UPD: I've checked DMA 1 Channel 5 (SPI2 TX) registers on slave side before master power on and after slave setup finished. CNDTR (data address = 0x4002 0000 + 0x0C + 0x14 * (5 - 1)) contains value 0x19 which is a unit less than the requested 0x1A. I guess it's due the very first copy to SPI2->DR upon enabling of the DMA channel. Also, CMAR points to the start of buffer to transmit (0xA5 byte is located at address stored in CMAR) which confuses me a bit.

The channel DMA1 Channel 5 is configured in Very High priority. DMA 1 Channel 4 for SPI2 RX is configured for High priority. Other than that only DMA for ADC is used with Normal priority.

1 ACCEPTED SOLUTION

Accepted Solutions
gbm
Lead III

I wrote it many times on this forum in last few weeks: its very hard to implement SPI slave in a microcontroller unless it cooperates with a carefully tweaked master, tuned for this cooperation. In my opinion - not worth the effort. To connect two microcontrollers, use UART and your problems will disappear. You will also save two signals.

Also, it's hard to help without seeing a single line of source code. I suspect that slave may be sending a frame s a result of signals changing during the master peripheral configuration. You may try to activate pullups on all the relevant inputs, NSS in particular.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

View solution in original post

4 REPLIES 4
gbm
Lead III

I wrote it many times on this forum in last few weeks: its very hard to implement SPI slave in a microcontroller unless it cooperates with a carefully tweaked master, tuned for this cooperation. In my opinion - not worth the effort. To connect two microcontrollers, use UART and your problems will disappear. You will also save two signals.

Also, it's hard to help without seeing a single line of source code. I suspect that slave may be sending a frame s a result of signals changing during the master peripheral configuration. You may try to activate pullups on all the relevant inputs, NSS in particular.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
Ozone
Lead II

If I understand you correctly ...

> Now, the issue I encounter is that the data is sent by slave starting from the second byte,  ...

This is how SPI works.
A transfer is always initiated by the master device (in your case the STM32F429). The slave device only realizes this access after the first byte has arrived (STM32F103), and an interrupt occurs.
The value transfered synchronously from the slave with this initial transmission is the value the DR register contained at that time. Which is usually the reset value of this register, 0x00. Unless your slave firmware is able to predict what comes next ...

To get a response value (from dedicated SPI slave devices), you would usually need to discard this first byte, and clock another byte out (often a dummy value) to read the response.

In case of a MCU/code driven SPI slave, you would need to take processing time delays of the slave into account.
E.g. if the master sends an initial command byte to read a certain value, the slave device needs to fetch it before, and stuff it into DR.
For dedicated SPI slaves (like e.g. MEMS sensors or ADCs), the functionality is hardwired, and thus basically immediately.

In my case, the slave doesn't send zero byte first and then the second byte. It sends the second byte as if it was the first one, i.e. as if the whole buffer was circularly rotated one byte to the left.

It is not necessarily zero, but the value DR contains when the master initiates the transfer.