cancel
Showing results for 
Search instead for 
Did you mean: 

SPI sends old bytes on MISO when no data available

c_shearer_cooper
Associate III

I'm creating some SPI slave code using an STM32 on a G474RE board.  The protocol is that the master sends two command bytes, and then the slave sends back a single byte response.  My SPI is a little rusty, but as I recall the way you do this is to have the master send a third byte (just 0x00) and the slave response will show up in MISO.

That's working just fine, what I'm curious about is the data that shows up on MISO while the master is sending his two command bytes on MOSI.  It seems like the STM32 just picks a random byte it sent sometime in the past and sends it again.  It's not a big deal, I really only care about the third byte, but it makes me a little nervous to have random data wandering around on SPI.

Am I doing something wrong?  Is this just the way the STM32's SPI works?  Is there a way to tell the STM32 "if you have nothing to send, send a zero"?

Thanks,

Chris

 

8 REPLIES 8
AScha.3
Chief

So you want to get the "answer" in the 3. spi transmission, using ..TransmitReceive(..) - right ?

Do you have some delay before the slave should answer ? (It needs some time to get the answer ready...)

To send zero, if no new data - is up to you . The cpu cannot know, whats useful or nonsense information.

If you feel a post has answered your question, please click "Accept as Solution".
c_shearer_cooper
Associate III

I am writing my own ISR; during the second RXNE interrupt, the slave code realizes what is being requested, and writes the answer into pspi->Instance->DR.  I didn't think I had enough time for all the overhead that HAL causes.

It would be great if the slave could tell the master "hold on, I don't have your answer yet" but I don't see how to do that in SPI.

So here's what I see: the master sends 0x0B 0x01 0x00 (0x0B means read a value, 0x01 means value at address 1, 0x00 to hear the slave's response).  The slave sends his response (0xDE) on MISO during that third byte by writing it into pspi->Instance->DR,

Then the master wants to know the value at address 0x02 so it sends 0x0B 0x02 0x00.  Sure enough, during that third byte, it gets the expected response, what's weird is that during the first two bytes (0x0B and 0x02), the STM32 is sending 0xDE again, even though I didn't tell it to send anything.  

Ok, but you have to insert some delay for the slave , to have the answer ready and write it to spi-> DR .

If your code is fast (not much to do for the slave), i would just make the spi sequence some bytes longer:

..TransmitReceive(..)  : 0x0B 0x01 0x00 0x00 0x00 ; then the 3. 0x00 is containing the data.

So slave has ( 2 bytes x spi speed ) time, to write the result to spi-> DR .

Adjust your spi clock + the "dummy" bytes , to get a useful delay. (or just try some combinations.)

If you feel a post has answered your question, please click "Accept as Solution".

Unfortunately, in this case I have no control over the master, so whatever it's expecting, I have to deal with.

So back to my original question, why is the STM32 sending "old" bytes even though I didn't tell it to?

But you know, how spi is working ??

AScha3_0-1712680257117.png

So when master sending, content of slave register coming in as received data - useful or nonsense.

This is up to you - to always (impossible without some delay) have the ( slave ) data there, it will send on next transmisson.

If no new data, old will be shifted through the loop.

AScha3_1-1712680631854.png

 

If you feel a post has answered your question, please click "Accept as Solution".
c_shearer_cooper
Associate III

Notice that your two diagrams present different answers to my question.

In Figure 349, the slave has a "Tx shift register" and an "Rx shift register".  In this scenario, once a byte has been written into the Rx shift register and sent out over MISO, there will be nothing left in the Rx shift register and it will send zeroes on subsequent clock cycles.  That's not what's happening so this figure is not accurate for the STM32.

In Figure 348, there is a single "Shift register" and it pulls data out of a "Tx FIFO" and into the Shift register so it has something to send over MISO.  This fits with what I'm seeing, if the STM32 simply recycles the contents of the Tx FIFO instead of clearing it out.   That makes sense, the designers of the STM32 had two choices:

1) When the Tx FIFO is empty, send zeroes

2) Don't check whether the Tx FIFO is empty or not, just send the next byte

Option 2 is less silicon so probably a better choice.

So for my program, if I write a 0x00 into pspi->Instance->DR every time I don't have something else to send, it seems like that will zero out the Tx FIFO so the STM32 will not send old bytes.

For anyone looking at this post in the future - writing extra zeroes into pspi->Instance->DR doesn't work.

The documentation for the STM32G4 says it's got "Two 32-bit embedded Rx and Tx FIFOs with DMA capability" (I think they should replace "two" with "separate" in that sentence).  Anyway, the definition for DR in stm32g474xx.h is "__IO uint32_t DR;" but the documentation shows that the DR register is only 16-bit.

So as a test, instead of the slave just writing one byte into DR when it got a register read request, I had it write "0xFFFFFF00 | outValue", and what I saw was this (I'm obviously guessing at the contents of the 32-bit TxFIFO):

MOSI0x0B0x000x000x0B0x010x000x0B0x020x00
Tx FIFO0x000000000x00000000Write 0xFFAD, get 0x0000FFAD0xAD0000FF0xFFAD0000Write 0xFF1D, get 0xFFADFF1D0x1DFFADFF0xFF1DFFADWrite 0xFFF2, get 0xFF1DFFF2
MISO0x000x000xAD0xFF0x000x1D0xFF0xAD0xF2

Bottom line - it looks like the STM32 is doing a rotate right on the contents of the FIFO, where I would prefer it do a shift right, but there's no option for controlling that.

https://community.st.com/t5/stm32-mcus-products/spi-in-slave-mode-please-clarify-mosi-i-e-tx-behaviour-when-dr/m-p/442759 :

 

If SPI peripheral has a TxFIFO (e.g. STM32F0, STM32F3, STM32L4), the behavior is the same with following exception: the "underrun pattern" comes from the oldest value stored at TxFIFO.

JW