2024-11-27 08:28 PM - edited 2024-11-27 10:05 PM
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:
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.
Now, I've played with the scope again and noticed some weird timing play. There are three cases:
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
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.
Solved! Go to Solution.
2024-11-28 12:16 AM
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.
2024-11-28 12:16 AM
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.
2024-11-28 12:39 AM
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.
2024-11-28 01:16 AM
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.
2024-11-29 08:06 AM
It is not necessarily zero, but the value DR contains when the master initiates the transfer.