cancel
Showing results for 
Search instead for 
Did you mean: 

Get hardware NSS mode for SPI with DMA in STM32 (F0/F4) working? Or alternatively manually start the according DMA channel (Low Level functions)

Christian Wächter
Associate III

Hello,

I'm trying to get a SPI communication in between two STM32 running with DMA. One is the STM32F051R8T7 (slave), the other ist the STM32F412ZET7 (master). There is nothing else on this SPI-bus. The best would be, if it runs with hardware controlled NSS. I'm facing various problems here and already tried a lot.

The basic problem I'm facing is, that I want to send multiple "messages" via SPI. A message should be any amount of bytes that are filled into one tx-Buffer and then send via SPI by the DMA. After one message, the data in the tx buffer will get modified and send again and so on.

My code is based on the examples of CubeMX, e.g. STM32F072RB-Nucleo\Examples_LL\SPI\SPI_TwoBoards_FullDuplex_DMA. It is working somehow, but I have some independend issues, depending on which way I go from here:

  • When I want to use the hardware NSS, I have to disable the SPI-periphery by LL_SPI_Disable(SPIx) after one message has been send, so that the NSS-signal will be set to high again. but it seems that the NSS-pin is not active pushed to the high-level after sending the LL_SPI_Disable() command, which results in a very slow rising voltage. I assume that the SPI-pins are just set into tristate mode in this case. Pull-Up resistors decrease the time of the rising edges, but are still not perfect. (Yellow = NSS, Cyan = CLK, Purple = MOSI). This is a very crappy behaviour, which means the hardware-NSS function cannot be used this way! Or is there a way to control the NSS-pin when it is in hardware-mode, so that it is pushed to the high-level as it should?0690X00000DYnRuQAL.png

  • An other option would be to always let the SPI-periphery enabled and therefore the NSS-signal set to the low-level. This is fine in this case, because there is nothing else on this SPI-bus execpt the master and slave STM32. But in this case, I'm missing a way to manually initiate the sending of the next message for the DMA. Usually, the tx data is changed after the SPI has been disabled. When enabling the SPI-periphery again, the tx buffer is send imediatelly with DMA, which is fine. But how could I send the data without disabling and again enabling the SPI-periphery? When I would do this, the NSS-level is changed as described above. The functions LL_DMA_EnableStream() or LL_SPI_EnableDMAReq_TX() do not seem to initiate a new transfer for the DMA controller.

Best regards,

Christian

13 REPLIES 13

1. Yes this is the behaviour of NSS, hinted but not quite explained by the manual. Reenable SPI immediately after disable, or simply set NSS as GPIO Out and control it manually.

2. I don't understand what's the problem but maybe that's because I don't use Cube. Simply set up SPI, Rx DMA, then Tx DMA (including enabling) - that's enough for slave to start listening and for master to start the communication.

JW

Christian Wächter
Associate III

Thank you for your answer.

1) It would be ok for me and all applications, that the NSS-signal is pushed to a high-level, when you disable the SPI function block. But the signal is not actively pushed to a high level, like I would expect from the push-pull-output, as you can see in the image of the scope. Reenabling the SPI immediatelly after disabling it will not cause a valid edge. This is what you can see in the image. And because this does not generates a valid edge/pulse, you cannot use it at all because it will always be luck whether or not the slave will actually interpret the NSS-signal as valid high-level.

2) As I already wrote, I can send data. But what I'm missing is a solid or official way, to initiate the sending of new data again and again manually. Currently I'm just diabling the RX and TX DMA Streams with LL_DMA_DisableStream() after the according interrupt occured. Then I'm updating and processing the data. After that, I'm enabling the RX and TX DMA Streams with LL_DMA_EnableStream() again and this causes the new data to be send. This is working, but the question is if this is the intended usage of if this might cause any troubles?

>Reenabling the SPI immediatelly after disabling it will not cause a valid edge.

Did you actually try? If you are concerned about voltage rise during the fraction of us this process should take, use pulldown instead of pullup. But I'd just leave the pin floating, held low by any parasitic capacitance present.

> if this is the intended usage of if this

The intended usage of DMA is, that you set it up and enable and it starts transferring when triggered. The SPI Tx triggers DMA by copy of the signal you can see as TXE bit in status register.

JW

Christian Wächter
Associate III

> Did you actually try?

Please have a look at the image in my first post. The yellow curve is the NSS signal. The command LL_SPI_Disable() has been called a bit to the left of the middle of the screen (where CLK in cyan ends). The command LL_SPI_Enable() was called immediatelly after that! As you can see, this is no edge as you would expect it... Oh and a pull-up (as already written in the first post) does not fix this problem! It will make the slope a bit steeper, but still not as you would expect it...

> The intended usage of DMA is, that you set it up and enable and it starts transferring when triggered. The SPI Tx triggers DMA by copy of the signal you can see as TXE bit in status register.

Could you please clarify this a bit more? What I'm doing is basically this:

__LL_SPI_NSS_HIGH();
LL_SPI_EnableDMAReq_RX();
LL_SPI_EnableDMAReq_TX();
LL_DMA_ConfigAddresses(); // for RX and TX
LL_DMA_SetDataLength(); // for RX and TX
LL_DMA_EnableIT_TC();  // for RX and TX
LL_DMA_EnableStream(); // for RX and TX
__LL_SPI_NSS_LOW();
LL_SPI_Enable(SPI5); // this will start the sending of data
 
// in the DMA interrupt, for each RX and TX
LL_DMA_ClearFlag_TCx();
LL_DMA_DisableStream();
__LL_SPI_NSS_HIGH();
 
// after that, I start the next transmission with theses commands
__LL_SPI_NSS_LOW();
LL_DMA_EnableStream(); // for RX and TX ------> IS THIS THE RIGHT WAY TO INITIATE THE SENDING AGAIN?

Christian Wächter
Associate III

According to the answer of ST to this topic (which is none 😉 ), I assume that this behaviour is well known and will not be changed in the future, not to say "will be ignored"...

This forum is not the official support channel of ST, but a courtesy to their users, providing a platform to help each other and share ideas. ST employees visit here apparently when they have nothing better to do. Like the rest of us.

Back your actual problem, I would just configure the NSS pin as push-pull GPIO output, and toggle the pin directly instead of playing with SPE.

> Back your actual problem, I would just configure the NSS pin as push-pull GPIO output, and toggle the pin directly instead of playing with SPE.

Sorry for the previous confusion - I was under the false impression that NSS is high after SPE is enabled and goes down only with the first transmission.

(To be honest, it never occured to me to even try to use the NSS in this way, ever since I've read the description in the RM, which was clear enough to me to understand that it's unusable for most practical cases. I came to that above incorrect conclusion entirely based on the complaints I've read here over the years... mea culpa)

JW

> Back your actual problem, I would just configure the NSS pin as push-pull GPIO output, and toggle the pin directly instead of playing with SPE.

Well, that's what I actually did. But if you have always to do this in this way, why is the hardware NSS-mode implemented anyway?

The idea - also described in the RM, mind you - is, that in multimaster setting, you wire-OR all NSS togeter, leaving all parties as slaves. Should one of them turn to master, it would pull the line down, generating a frame for the others; after transmission it would then turn back to slave again.

I don't say it's a terribly useful scheme - neither is the per-frame "NSS pulsing" implemented in later incarnations of this module - but the SPI in STM32 are traditionally plagued with half-baked ideas (limited baudrate choice, data packing, hanging busy, incorrectly handled first bit in slave, clock fed back from actual pin in master (leading to erratum), and possibly more)

JW