cancel
Showing results for 
Search instead for 
Did you mean: 

[SOLVED] SPI DMA doesnt transmit anything, is my configuration wrong?

indy25
Associate

Im having problems with DMA SPI transmit. Im using stm32f429zi mcu and would like to implement SPI DMA with MOSI only, I dont need MISO. SPI and DMA configuration was generated using CubeMX. Using HAL_SPI_Transmit function SPI works great. But HAL_SPI_Transmit_DMA does not send anything and gets stuck in while loop where it checks HAL_SPI_STATE_READY flag. HAL_SPI_Transmit_DMA returns HAL_OK. I am thinking that I have missed something in configuration but I havent been able to figure it out yet. If I create data to receive buffer and use HAL_SPI_TransmitReceive_DMA it also does not send anything but it doesnt get stuck in loop as spi status is ready.

Im monitoring MOSI and SCK pin with logic analyzer.

Here is my code

PeripheralInit.c:

void MX_SPI5_Init(void)
{
	/* SPI5 parameter configuration*/
	hspi5.Instance = SPI5;
	hspi5.Init.Mode = SPI_MODE_MASTER;
	hspi5.Init.Direction = SPI_DIRECTION_2LINES;
	hspi5.Init.DataSize = SPI_DATASIZE_8BIT;
	hspi5.Init.CLKPolarity = SPI_POLARITY_LOW;
	hspi5.Init.CLKPhase = SPI_PHASE_1EDGE;
	hspi5.Init.NSS = SPI_NSS_SOFT;
	hspi5.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
	hspi5.Init.FirstBit = SPI_FIRSTBIT_LSB; 
	hspi5.Init.TIMode = SPI_TIMODE_DISABLE;
	hspi5.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	hspi5.Init.CRCPolynomial = 10;
	if (HAL_SPI_Init(&hspi5) != HAL_OK)
	{
		Error_Handler();
	}
}
 
void MX_DMA_Init(void)
{
    /* DMA controller clock enable */
    __HAL_RCC_DMA2_CLK_ENABLE();
 
    /* DMA interrupt init */
    /* DMA2_Stream4_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);
}

stm32f4xx_hal_msp.c:

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hspi->Instance==SPI5)
  {
    /* Peripheral clock enable */
    __HAL_RCC_SPI5_CLK_ENABLE();
  
    __HAL_RCC_GPIOF_CLK_ENABLE();
    /**SPI5 GPIO Configuration    
    PF7     ------> SPI5_SCK
    PF9     ------> SPI5_MOSI 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI5;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
 
    /* SPI5 DMA Init */
    /* SPI5_TX Init */
    hdma_spi5_tx.Instance = DMA2_Stream4;
    hdma_spi5_tx.Init.Channel = DMA_CHANNEL_2;
    hdma_spi5_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi5_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi5_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi5_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_spi5_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_spi5_tx.Init.Mode = DMA_NORMAL;
    hdma_spi5_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    hdma_spi5_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi5_tx) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(hspi,hdmatx,hdma_spi5_tx);
  }
}

main.cpp:

uint8_t data_to_send[]={
            0xFF, 0xEE, 0xEE, 0xEE, 0xEE
};
 
int main(void)
{
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
 
    /* Configure the system clock */
    SystemClock_Config();
 
    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_SPI5_Init();
    MX_DMA_Init();
 
   HAL_SPI_Transmit_DMA(&hspi5,data_to_send, sizeof(data_to_send));
   while(HAL_SPI_GetState(&hspi5) != HAL_SPI_STATE_READY);

1 ACCEPTED SOLUTION

Accepted Solutions
indy25
Associate

Thank you all for help, I got it working - apparently the MX_DMA_Init has to be called before MX_SPI5_Init and not the other way around...

View solution in original post

9 REPLIES 9
S.Ma
Principal

I wouldn't rely on GetState() function.

Gen 2 SPI code extract for me looks like this (just for inspiration):

void SPIP_EnterMasterMode(SPIP_t* pSPIP) {
  // disable NSS EXTI interrupt
  SPIP_DisableNSS_EXTI(pSPIP);
  IO_PinSetHigh(pSPIP->pNSS_Master);
  IO_PinConfigure(pSPIP->pNSS_Master); // NSS output high as starting point
  SPIP_MasterConfigureSpi(pSPIP->hspi); // master/slave status dependent
  SPIP_MasterConfigureDma(pSPIP->hspi);
  SPIP_MasterConfigureSerialMode(pSPIP);
}
 
HAL_StatusTypeDef SPIP_MasterParallelTransferStart_ISR(SPIP_t* pSPIP){
  uint16_t ByteCount = 0; //pSPIP->ParallelByteCount;
  ByteCount = BiggestParallelByteSize();
//  if(ByteCount<128) ByteCount = 128; // 128*8/12 = 85 usec (to give time to slaves to run their rightmost detection during DMA transfer, increase the value if needed
  if(ByteCount & 1) ByteCount++; // as 16 bit SPI, round up to even byte numbers transfer
  if(HAL_SPI_TransmitReceive_DMA(pSPIP->hspi, pSPIP->pParallelTxBuffer, pSPIP->pParallelRxBuffer, ByteCount/2) != HAL_OK) // after we will use bytesize if we want to optimize
     TrapError();
  return HAL_OK;
}
 
HAL_StatusTypeDef SPIP_MasterConfigureSpi(SPI_HandleTypeDef *hspi){
  hspi->Instance               = SPI2;
  hspi->Init.Direction         = SPI_DIRECTION_2LINES;
  hspi->Init.CLKPhase          = SPI_PHASE_1EDGE;
  hspi->Init.CLKPolarity       = SPI_POLARITY_LOW;
  hspi->Init.DataSize          = SPI_DATASIZE_16BIT;
  hspi->Init.FirstBit          = SPI_FIRSTBIT_MSB;
  hspi->Init.TIMode            = SPI_TIMODE_DISABLE;
  hspi->Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;
  hspi->Init.CRCPolynomial     = 7;
  hspi->Init.CRCLength         = SPI_CRC_LENGTH_8BIT;
  hspi->Init.NSS               = SPI_NSS_SOFT;
  hspi->Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;
  hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;//16;
  hspi->Init.Mode = SPI_MODE_MASTER;
  return (HAL_SPI_Init(hspi));
}
 
void SPIP_MasterConfigureDma(SPI_HandleTypeDef *hspi){
  /* Configure the DMA handler for Transmission process */
  pSPIP->hdma_tx->Instance                 = DMA1_Channel2;//DMA1_Channel5;
  pSPIP->hdma_tx->Init.Request             = DMA_REQUEST_SPI2_TX;//DMA_REQUEST_1;
  pSPIP->hdma_tx->Init.Direction           = DMA_MEMORY_TO_PERIPH;
  pSPIP->hdma_tx->Init.PeriphInc           = DMA_PINC_DISABLE;
  pSPIP->hdma_tx->Init.MemInc              = DMA_MINC_ENABLE;
  pSPIP->hdma_tx->Init.PeriphDataAlignment = /*DMA_PDATAALIGN_BYTE;*/ DMA_PDATAALIGN_HALFWORD;
  pSPIP->hdma_tx->Init.MemDataAlignment    = /*DMA_MDATAALIGN_BYTE;*/ DMA_MDATAALIGN_HALFWORD;
  pSPIP->hdma_tx->Init.Mode                = DMA_NORMAL;
  pSPIP->hdma_tx->Init.Priority            = DMA_PRIORITY_HIGH;
  HAL_DMA_Init(pSPIP->hdma_tx);
  /* Associate the initialized DMA handle to the the SPI handle */
  __HAL_LINKDMA(hspi, hdmatx, *(pSPIP->hdma_tx));
 
  /* Configure the DMA handler for Reception process */
  pSPIP->hdma_rx->Instance                 = DMA1_Channel1;//DMA1_Channel4;
  pSPIP->hdma_rx->Init.Request             = DMA_REQUEST_SPI2_RX;//DMA_REQUEST_1;
  pSPIP->hdma_rx->Init.Direction           = DMA_PERIPH_TO_MEMORY;
  pSPIP->hdma_rx->Init.PeriphInc           = DMA_PINC_DISABLE;
  pSPIP->hdma_rx->Init.MemInc              = DMA_MINC_ENABLE;
  pSPIP->hdma_rx->Init.PeriphDataAlignment = /*DMA_PDATAALIGN_BYTE;*/ DMA_PDATAALIGN_HALFWORD;
  pSPIP->hdma_rx->Init.MemDataAlignment    = /*DMA_MDATAALIGN_BYTE;*/ DMA_MDATAALIGN_HALFWORD;
  pSPIP->hdma_rx->Init.Mode                = DMA_NORMAL;
  pSPIP->hdma_rx->Init.Priority            = DMA_PRIORITY_HIGH;
  HAL_DMA_Init(pSPIP->hdma_rx);
  /* Associate the initialized DMA handle to the the SPI handle */
  __HAL_LINKDMA(hspi, hdmarx, *(pSPIP->hdma_rx));
}
 
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi){
  // we only need this in master mode!
  if(pSPIP->Device == IS_SLAVE) return;
  SPIP_InterruptBasedStateMachine(0);
}

Bob S
Principal

You say that HAL_SPI_Transmit_DMA() returns HAL_OK, but your code doesn't show you checking that. You didn't show any of your callback functions. Did you implement any of them? Speciifically the DMA error callback and SPI error callback? And I don't see your code enabling SPI interrupts, which HAL_SPI_Transmit_DMA() uses to catch SPI errors.

Have you set any breakpoints in any of the SPI/DMA interrupt handlers to see if they are ever getting called? Or traced/single-stepped through the HAL_SPI_Transmit_DMA() function? Have you read back the SPI and DMA config registers to see if they are configured how you expect them to be configured?

S.Ma
Principal

TrapError() is a while(1); which stops indefinitely the code if there is trouble (I mostly debug with ST Link).

This is extract of a more complex code which is functional working on boards. (1 STM32 SPI Master to 12 STM32 SPI Slaves)

I don't implement SPI or DMA error callbacks because it's working fine so far.

Yes you can get interrupt breakpoints and you'll discover that HAL enables all interrupt sources: You'll get an interrupt at DMA Half transfer that just slows down the main loop.

Read out and check/post content of SPI and relevant DMA registers.

JW

indy25
Associate

Thank you all for help, I got it working - apparently the MX_DMA_Init has to be called before MX_SPI5_Init and not the other way around...

Even though this is an old thread, I wanted to thank you personally. I have been working with SPI for a STM32F407 processor for a while, building a library for different components.

Next step was to get it to work on STM32F405 as well and for some unknown reason I was unable to communicate with the SD card. Over a week of me pulling my hair before I managed to find this post.

When they were in the wrong order, I was able to transmit data over SPI with DMA but it most often came out as the wrong bytes.

This solved my issue! For some reason CubeMX seems to generate the code in the wrong order for some MCUs

Thanks it solved to me too! I "just" spent 2 hours on this before find this thread!

I can't understand the fix, but can the ST fix this?

I couldn't solve it without solving the same problem. But this solution really works. I can't understand why CubeMX is generating wrong code.

Thank you so much.