cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 - DMA with SPI in slave mode doubles the first byte loaded into DMA

sergey239955
Associate III

We are migrating from STM32F103 to STM32L4. The code architecture is the same, as the product basically is the same. We just upgrade the micro.

We have an SPI that is configured in slave mode, and a device that supplies clock to the SPI. DMA is used to feed the SPI with data. What we do is we first init SPI, then we init and load DMA with data, then we enable DMA request and a few milliseconds later we enable the clock source. Everything is very basic and works absolutely fine on STM32F103.

We found that the STM32L4 with practically the same code (save for specifics of peripheral configuration) doubles the very first byte loaded into DMA. Say, if you load a string of 5 bytes 0x1, 0x2, 0x3,0x4 and 0x5, the SPI will output 0x1, 0x1, 0x2, 3, 4, and 5. Whatever the first byte is, it will be doubled (we suspect by SPI) because the DMA count is correct. Meaning that the DMA will stop after 5 bytes, while the SPI will output 6 bytes.

Anyone saw anything similar to it? We are positive that it is not our bug. It is almost impossible to do even if we wanted to. And the same code works fine in STM32F103.

26 REPLIES 26

>I can read and print all registers (DMA and SPI) before the transfer

>starts, but I am not quite sure which field/bit to look at .

I don't know either, so post them all.

It would be nice to see waveforms from the LA, too.

JW

Review SPI Data transmission and reception procedures / Communication using DMA sections in the reference manual. It's a bit hard to follow your code, because register names are obfuscated by the LL_ wrappers, but I suspect that your code does not follow the recommended procedure, where the very last steps should be setting

SPI3->CR2 |= SPI_CR2_TXDMAEN;
SPI3->CR1 |= SPI_CR1_SPE;

sergey239955
Associate III

Hi Berendi,

the LL_ wrappers are from the ST low level library for STM32L4xx,

but LL_SPI_EnableDMAReq_TX(SPI3)  sets the TXDMAEN bit in CR2 and LL_SPI_Enable(SPI3) sets the SPE bit.

I moved the SPE enable right after setting TXDMAEN, but the result is the same.

sergey239955
Associate III

I read the DMA and SPI registers after I initialize them for the transfer. Here are the values, but I see nothing there that doesn't match the configuration in the code that I provided, and nothing suspicious

SPI CR1   0x000002C0
SPI CR2   0x00000702
SPI SR    0x00001800
DMA CCR   0x00003091
DMA CNDTR 0x00000020
DMA CPAR  0x40003C0C
DMA CMAR  0x2001D1C0

Any ideas?

As I said, the strangest thing is the behavior changes with the frequency of the external clock for the slave SPI.

What was the value loaded into NDTR? It's obviously not 16 as in the example you gave above. And SPI TxFIFO is already full (as expected).

What is the state of SCK at this moment, does it match CPOL? Are there any transitions of SCK since the SPI was enabled? Is the idle state of SCK depending on the data rate you are mentioning?

Could you try to have the internal NSS at 1 while enabling SPI, and then perform a 1-to-0 transition on the internal NSS (using SPIx_CR1.SSI) after you've enabled the SPI, and having ensured SCK matches CPOL?

JW

sergey239955
Associate III

No, of course not 16. In this register capture the NDTR was set to 0x20, I believe.

Since I suspect that DMA has nothing to do with the SPI behavior, I tested SPI without DMA.

Since the SPI has 32 bit FIFO, I loaded 4 bytes. Later the clock starts. I would expect all 4 bytes output correctly. But I, in fact, get 5 bytes, the first being sent twice.

Here is the code:

  LL_SPI_StructInit(&SPI_Init_ToFromRadio);
// change non-defaults to what we need 
  SPI_Init_ToFromRadio.NSS = LL_SPI_NSS_SOFT;
  SPI_Init_ToFromRadio.BitOrder = LL_SPI_LSB_FIRST;
 
  LL_SPI_DeInit(SPI3);
  LL_SPI_Init(SPI3, &SPI_Init_ToFromRadio);
  LL_SPI_Enable(SPI3);
 
  LL_SPI_TransmitData8(SPI3, 0x33);
  LL_SPI_TransmitData8(SPI3, 0x55);
  LL_SPI_TransmitData8(SPI3, 0);
  LL_SPI_TransmitData8(SPI3, 0xff);

The SPI is doing it, not DMA

Here is the logic analyzer trace of what happens on the SPI data out line, and the clock

0690X00000By2BeQAJ.jpg

It was captured using the code above. You can clearly see that the first 0x33 comes out doubled, followed by 0x55, 0x0 and 0xff.

Since I do not stop the clock after that (being the test code), the SPI continues outputting 0x33, I guess it is the SPI behavior when in underflow condition.

What is important is that the SPI peripheral is deInit and re-init every time when I call this code. IMHO it points to a racing condition in the SPI IP becase different clock rates behave differently with the same code.

What do you think?

sergey239955
Associate III

to answer your question:

No, the idle state doesn't depend on the clock.

I can quickly change the rate and capture the trace again to confirm that there are no glitches of any sort.

> I guess it is the SPI behavior when in underflow condition.

Yes, I've been explicitly assured here by ST that this is the case, can't find the thread at the moment.

> No, the idle state doesn't depend on the clock.

But obviously it's not 0 while you set CPOL to 0.

Note this line in RM:

The SCK signal must be settled at an idle state level corresponding to the selected polarity before the SPI slave is enabled.''

JW

sergey239955
Associate III

Well, it appears I was wrong, because I made an assumption (wrongly) that the idle state of the clock doesn't depend on the rate. It does.

Below is the correct behavior of SPI at a different rate.

Looks like the transceiver is acting differently at different rates, and the SPI responds differently.

0690X00000By2rBQAR.jpg

Okay, so problem solved?

JW