cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 SPI transmit with DMA not working

I am working with a Nucleo-H743ZI on a Makefile C project using STM32CubeMX generated project code (Fiirmware version V1.3.0) to troubleshoot a problem with SPI transmit using DMA.

The project uses SPI1 to transmit to SPI2 on the same board at 25 megabits per second using 4 inch wires for both data and SCLK. The wiring has been proven by running a test in polling mode sending 32 bit random numbers and comparing. The polling works, so the wiring is good.

The end project needs DMA, so I've re-written the code to support DMA. The DMA hardware setup was performed in CubeMX. SPI1 is the DMA transmitter.

The project prints (to virtual com port) messages showing SPI status as it goes long. Once the first DMA operation is started, the program reports that a transmission is ongoing and never ends. If I scope MOSI from SPI1, there is no signal. (There is a signal, however, when the same test is done with polling).

After several days of web search and research, I am here.

I updated STM32CubeMX to Version 5.0.1 STM32CubeMX V1.0. Firmware is V1.3.0

I read that the linker script produced does not not provide a section for the D2 RAM domain, so I searched and found code to do that. I use an __attribute__ to force the TX buffer into D2 domain RAM section. This was verified both by looking at the .map file and by printing the value of the pointer to TXbuf which is 0x30000000. I found the two board example project and saw the cache maintenance statement for transmit and added that to my code. I've also tried turning data cache off altogether. DMA1 stream 0 is used for SPI1 while SPI2 remains in receive polling mode. I've also compared my SPI and DMA setup with the two board project and find no difference.

I found this FAQ:

https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices

and this:

https://community.st.com/s/question/0D50X00009XkX4ZSAV/solved-stm32h7-spi-does-not-seem-to-work-with-dma

and this:

https://community.st.com/s/question/0D50X00009q2XcxSAE/is-there-a-possible-bug-in-function-halspitransmitdma-stm32cubefwh7v

I see solutions relating to Keil, TrueStudio, etc., but nothing for Makefile projects.

The third link shows that there exists a typo in the HAL_SPI_Transmit_DMA() function that improperly uses hdmarx instead of hdmatx handle. This has been corrected, but regardless of what I've tried, the SPI transmit operation never completes and no data ever appears in the SPI2 receive buffer.

So at this point, I have verified the wiring and RAM location of the transmit DMA buffer. I've turned cache off, though I believe this would have more to do with corrupt data rather than no transmit data. I've also tried the 256 baud clock divider to no avail.

I would greatly appreciate clues, pointers, suggestions, etc.

16 REPLIES 16
ST User
Associate III

I encountered the same problem when trying to use the code generated from the CubeMX: The SPI with DMA did not work. I didn't dig enough to figure out why the CubeMX code is bad, but I did get it working, I'll describe the process below:

The STM32CubeH7 software (https://www.st.com/en/embedded-software/stm32cubeh7.html) has a number of example programs, including a SPI with DMA one (in \Projects\STM32H743ZI-Nucleo\Examples\SPI\SPI_FullDuplex_ComDMA).

I started with that example program, and changed the GPIO pins and ports to match those from the CubeMX's generated code. Once I did this, the SPI with DMA worked for me, and I was able to port the code back into my main program. I'd suggest giving this route a try.

Thank you. I've now looked at the example you cited again and in the main.h file, I see the definitions for the pins used. They are, in fact, exactly the same as the ones I am using, that is, PA5 for SCK, PA6 for MISO and PA7 for MOSI with all three using GPIO_AF5_SPI1 which is the same as my project. However, I noticed in the example that SPI1 is using DMA2_stream3 for transmit and I am using DMA1_stream0.

In one of my search hits, someone mentioned that changing the SPI controller to a different one fixed their issue - which seems in line with what you're saying.

A lot of this is very mysterious, so I will see if changing to DMA2_stream3 fixes the problem.

Another odd (and unrelated) thing I've found with this Nucleo board is that the SysTick interrupt timer is disabled due to using pin PB7 as GPIO_output. Not sure why, but it shows that in CubeMX when I look at System Core/SYS - for which SysTick is the timebase - there's a yellow triangle that says "Partly disabled conflict with: PB7 mapped with GPIO_output". Pin PB7 is connected to a board LED (which I like to use). The chip has 144 pins, most of which just go to pads for pins - I can't understand why ST would design this board like that to use THAT pin for a stupid LED which then disables SysTick when they had 143 other pins to choose from. CubeMX happily plops the SysTick functions into the project as if there's nothing wrong. Of course, I just use another timer for what SysTick would do for me, but that's just weird.

I tried DMA2_stream3 - same result, no data transferred and then the SPI1 controller sits there in transmission on-going mode (State = 3). No exception interrupts are triggered, it just won't go.

I'll note that I haven't tried using HAL_SPI_Transmit_DMA(), I used HAL_SPI_TransmitReceive_DMA() in my application where I got it working.

In your SpiHandle setup, do you have Init.Direction set to SPI_DIRECTION_2LINES_TXONLY, or SPI_DIRECTION_1LINE? I'm not sure which would be appropriate, but perhaps you're stalling because you're set up for full duplex mode and only doing communication one way?

I just tried HAL_SPI_TransmitReceive_DMA() with a dummy buffer in DMA RAM section, same thing happens except State is now 5 which is transmit and receive operation ongoing.

SPI1 setup in CubeMX calls SPI1 Full-Duplex Master. Here is the generated code:

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_32BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 7;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi1.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;

Thank you for your help. Going forward, I'll take your advice and begin with the SPI example in the V1.3.0 firmware. I thought I could circumvent that by doing a deeper compare between my project and the example. I'm doing this for one board, so I'll need to use the master code from the example code as a starting point and then add my polling slave SPI2 code and change the way the SPI message is kicked off, but I'll use the "driverware" that's already there. I can then copy the SPI2 code from what CubeMX generated. The bonus is that with the polling project I've already determined that I have the right pins connected and the example project uses the same pins I'm using. Frankenstein-ware to be sure. Hopefully, in the near future, I can post a conclusion.

I was able to merge the example code with my code and it compiles. I started with the example project and added only the SPI2 code to it.

When I run the project, it hangs after the call to HAL_SPI_TransmitReceive_DMA(). The SpiHandle.State value stays at 5 which indicates that a transmit AND receive operation are ongoing - forever. I scoped the SCLK and MISO pins, but I see no data burst or clock burst. The transfer complete callback also is never executed (there are LED indicators) and the wTransferState variable remains set at TRANSFER_WAIT (the callback sets this to another value depending on what happened). From this, I conclude that no transmission happened.

I also fixed the known typo bug in HAL_SPI_Transmit_DMA() (it was referencing the rx buffer instead of the tx buffer) and tried it, but again - no clock burst from SPI1 SCLK pin.

I have a second Nucleo board, so if I have another USB cable, I may resort to testing the project as described.

I just ran the polling test again and it works and I can see the clock and data toggle, so I know the wiring is correct and functional.

Perhaps I should try a different SPI controller? I wouldn't even need to set up a 2 SPI connection, just monitoring the SCLK output for clock bursts should be sufficient.

I seem to remember reading somewhere that the SPI controller won't work unless it's transmitting bytes - not sure about the source reliability, but I can try that as well - that would NOT be ideal, however, since I need to transmit a 24 bit word to the end project device (a DAC). My polling test transmits a 32 bit message, so perhaps what I read was only about using DMA.

I'll take a break for awhile and see if a better idea pops into my head.

I've just finished testing the example. I removed the comment on the define so that it's set for master. Pin PA7 connects to nothing (it is SCLK output from the master), I tried with a connection between PA5 and PA6 and also without the connection. I also removed JP6 which connects pin PA7 to something on the ethernet (which I don't use) just in case it was interfering. In all cases, I push the reset button and the green LED flashes (as it should) and then I push the user button to trigger a send, but I see no clock burst from PA7. I also set the SPI baudrate prescaler to 256 from the default of 8, still no clock burst.

Am I correct in thinking that a single master SPI controller will send a clock burst when a transfer is initiated under these conditions?