cancel
Showing results for 
Search instead for 
Did you mean: 

Problems with SPI using DMA in slave mode

de Haro Carbonell
Associate III
Posted on November 13, 2017 at 18:04

Hello,

I am using the STM32F429ZIT uC and it is currently communicating with another uC through SPI. The STM32 is slave and the other is the master. The master sends 10 bytes and the slave replies with another 10 bytes in full duplex. Every 150ms.

The communication works fine , attached you can see 'Working_with_all_the_Frames.jpg'. (yellow = CS, green = MISO, white = CLK).

But after a while (randomly 10 min, 1h ...) the communication is corrupted just in the MISO signal , the STM32 sends the 10 bytes frame but instead of sending Byte0 Byte1 Byte2 Byte3 Byte4 Byte5 Byte6 Byte7 Byte8 Byte9 Byte10, (LSB first)

it sends:

Byte10 Byte0 Byte1 Byte2 Byte3 Byte4 Byte5 Byte6 Byte7 Byte8 Byte9, IT IS MOVED TO RIGTH 1 byte!?!?

Attached you can see the capture 'Working.jpg' with byte0 = 0x02 and the rest of the bytes = 0. In the other capture 'NOT_working.jpg' is a capture with the problem. Both uC were working properly for a while and suddenly the STM32 uC started to send this frame all the time (the communication frame is byte = 0x02 and the rest of the bytes = 0 in order to see easily this error).

I have tried the communication in:

'Init.Mode = DMA_NORMAL' and 'DMA_CIRCULAR', and both configuration have the same behaviour.

On the other hand, this problem always happens in debug mode when I click on 'suspend' (pause) and 'resume'(play) as soon as I click on 'resume'  and the processor continues with the program, the MISO signal is shifted forever.

Additionally I am using TIM1, TIM5, TIM2, TIM3 and TIM4 for other things like PWM and interruptions but not related to SPI...

I have tried to solve this problem modifying all the NVIC priorities for all interruptions and so on but the problem get worst.

I am using System Workbench for STM32  latest version.

Any help is appreciate! Thanks in advance and best regards.

Alejandro

Bellow you can see my configuration for SPI and DMA if it can help you:

void MX_DMA_Init(void)

{

  __HAL_RCC_DMA2_CLK_ENABLE();

  HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 2, 0);

  HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);

  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 2, 0);

  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);

}

void MX_SPI6_Init(void)

{

  hspi6.Instance = SPI6;

  hspi6.Init.Mode = SPI_MODE_SLAVE;

  hspi6.Init.Direction = SPI_DIRECTION_2LINES;

  hspi6.Init.DataSize = SPI_DATASIZE_8BIT;

  hspi6.Init.CLKPolarity = SPI_POLARITY_LOW;

  hspi6.Init.CLKPhase = SPI_PHASE_1EDGE;

  hspi6.Init.NSS = SPI_NSS_HARD_INPUT;

  hspi6.Init.FirstBit = SPI_FIRSTBIT_MSB;

  hspi6.Init.TIMode = SPI_TIMODE_DISABLE;

  hspi6.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

  hspi6.Init.CRCPolynomial = 10;

  if (HAL_SPI_Init(&hspi6) != HAL_OK)

  {

    Error_Handler();

  }

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)

{

      GPIO_InitTypeDef GPIO_InitStruct;

      if(hspi->Instance==SPI6)

      {

        __HAL_RCC_SPI6_CLK_ENABLE();

        /**SPI6 GPIO Configuration

        PG8     ------> SPI6_NSS

        PG12     ------> SPI6_MISO

        PG13     ------> SPI6_SCK

        PG14     ------> SPI6_MOSI

        */

        GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14;

        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

        GPIO_InitStruct.Pull = GPIO_NOPULL;

        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;

        GPIO_InitStruct.Alternate = GPIO_AF5_SPI6;

        HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

        hdma_spi6_rx.Instance = DMA2_Stream6;

        hdma_spi6_rx.Init.Channel = DMA_CHANNEL_1;

        hdma_spi6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;

        hdma_spi6_rx.Init.PeriphInc = DMA_PINC_DISABLE;

        hdma_spi6_rx.Init.MemInc = DMA_MINC_ENABLE;

        hdma_spi6_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

        hdma_spi6_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

        hdma_spi6_rx.Init.Mode = DMA_NORMAL;

        hdma_spi6_rx.Init.Priority = DMA_PRIORITY_MEDIUM;

        hdma_spi6_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

        if (HAL_DMA_Init(&hdma_spi6_rx) != HAL_OK)

        {

          Error_Handler();

        }

        __HAL_LINKDMA(hspi,hdmarx,hdma_spi6_rx);

        hdma_spi6_tx.Instance = DMA2_Stream5;

        hdma_spi6_tx.Init.Channel = DMA_CHANNEL_1;

        hdma_spi6_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;

        hdma_spi6_tx.Init.PeriphInc = DMA_PINC_DISABLE;

        hdma_spi6_tx.Init.MemInc = DMA_MINC_ENABLE;

        hdma_spi6_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

        hdma_spi6_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

        hdma_spi6_tx.Init.Mode = DMA_NORMAL;

        hdma_spi6_tx.Init.Priority = DMA_PRIORITY_MEDIUM;

        hdma_spi6_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

        if (HAL_DMA_Init(&hdma_spi6_tx) != HAL_OK)

        {

          Error_Handler();

        }

        __HAL_LINKDMA(hspi,hdmatx,hdma_spi6_tx);

        /* Peripheral interrupt init */

        HAL_NVIC_SetPriority(SPI6_IRQn, 2, 0);

        HAL_NVIC_EnableIRQ(SPI6_IRQn);

      }

}

#spi #dma #communication #dma-problem #spi-over-dma #spi-dma
1 ACCEPTED SOLUTION

Accepted Solutions
de Haro Carbonell
Associate III
Posted on November 17, 2017 at 10:32

Hello,

I finally got the solution, I found what the problem was!

Usually, the CS signal goes from 1 to 0, then MISO and MOSI communicates, and once the communication finishes CS signal goes from 0 to 1, and the STM32F429 continues with the rest of the tasks...

This was happening every 150 ms, that's the period of thime both uC are communicating. But the STM32 uC has another tasks with more priority than SPI communication.

When one of this higher priority starts during SPI communication, and once this higher priority is done then the uC continues with the task was doing ( it was SPI),

obviously

this frame is lost and 'HAL_SPI_ErrorCallback' is executed, and then SPI is restarted.If SPI is restarted when CS signal is 1, (spi idle), then there is no problem, SPI is restarted properly and the next frame will be received without problem. BUT if SPI is restarted when CS signal is 0 (STM32 SPI is selected and ready to communicate) then the STM32 is waiting to send and receive an amount of bytes but it will receives less, so an a mismatch of communication bytes is the key of the PROBLEM.

I have solved this issue just adding:

void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)

{

    if(hspi -> Instance == SPI6)

    {

        while(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) != GPIO_PIN_SET) // CS signal

        {

        }

        HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data,10);

    }

}

I have to modify 'WHILE' in order to not stop the processor , but it is the first approximation.

Now the communication is working all the time, but some times a frame is lost (and ' HAL_SPI_ErrorCallback' is called) due to higher priority task. But it is normal, a CRC is implemented to note that.

Thansk JW to help me and support.

I hope this helps to other people.

Best regards.

Alejandro.

View solution in original post

8 REPLIES 8
Posted on November 13, 2017 at 23:09

How do you start the Tx DMA?

What is in the SPI6 interrupt routine?

JW

Posted on November 14, 2017 at 08:59

Thanks for quyck reply.

During initialization code, I configure SPI6 and DMA as it is described before, just after that I enable communication using:

HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data, 10);

Also it were added the following 2 functions related to SPI communication:

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)

{

    if(hspi -> Instance == SPI6)

    {

        HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data, 10);

    }

}

void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)

{

    if(hspi -> Instance == SPI6)

    {

        HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data,10);

    }

}

There is not more functions related to DMA nor SPI. Just the functions related to how to handle the information received and the information to be send. But checking the TX and RX frames using STMStudio, always the uC receives properly the information and the buffer used to send the TX frames are also OK after and before the issue, which means (i suppose) that the problem is related to DMA, because if the uC TX buffer memory is OK, why send them in a wronk byte position???

Another intreresting thing related to STMStudio, is when i check 2 variables: 'DMA_counter_RX' and 'DMA_counter_TX':

        DMA_counter_RX = __HAL_DMA_GET_COUNTER(&hdma_spi6_rx);

        DMA_counter_TX = __HAL_DMA_GET_COUNTER(&hdma_spi6_tx);

Using this '__HAL_DMA_GET_COUNTER' (as far as i understand) I receive the NDTR register value for SPI6 TX and RX.

When the communication is going well DMA_counter_RX = 10 BUT DMA_counter_TX = 9 (i would say that should be 10...) but the comunication goes well... AND as soon as the problem occurs, both variables are = 10 !!! very wird for me...

Thanks for your suport and best regards.

Alejandro

Posted on November 14, 2017 at 10:21

You have enabled the SPI interrupt. If you don't have an explicit ISR ('handler') for it, it may be called repeatedly, flood the processor and cause surprises. Remove the SPI interrupt enable.

The Tx DMA places one byte to the transmitter holding register immediately upon enable, but it does not get transmitted until the master starts clocking, that's why you see the Tx DMA advanced by one byte compared to Rx DMA, that's normal .

JW

Posted on November 14, 2017 at 11:05

Hello JW, thanks again for your quick reply.

STM cube mx automatically creates:

void DMA2_Stream5_IRQHandler(void)

{

  /* USER CODE BEGIN DMA2_Stream5_IRQn 0 */

  /* USER CODE END DMA2_Stream5_IRQn 0 */

  HAL_DMA_IRQHandler(&hdma_spi6_tx);

  /* USER CODE BEGIN DMA2_Stream5_IRQn 1 */

  /* USER CODE END DMA2_Stream5_IRQn 1 */

}

/**

* @brief This function handles DMA2 stream6 global interrupt.

*/

void DMA2_Stream6_IRQHandler(void)

{

  /* USER CODE BEGIN DMA2_Stream6_IRQn 0 */

  /* USER CODE END DMA2_Stream6_IRQn 0 */

  HAL_DMA_IRQHandler(&hdma_spi6_rx);

  /* USER CODE BEGIN DMA2_Stream6_IRQn 1 */

  /* USER CODE END DMA2_Stream6_IRQn 1 */

}

void SPI6_IRQHandler(void)

{

  /* USER CODE BEGIN SPI6_IRQn 0 */

  /* USER CODE END SPI6_IRQn 0 */

  HAL_SPI_IRQHandler(&hspi6);

  /* USER CODE BEGIN SPI6_IRQn 1 */

  /* USER CODE END SPI6_IRQn 1 */

}

I didnt modified nothing on them. Is it necessary to modify something? or do you mean other interruption handler?

Many thanks

Alejandro

Posted on November 14, 2017 at 11:52

Just comment out

HAL_NVIC_EnableIRQ(SPI6_IRQn);

compile, test, and report back.

JW

Posted on November 14, 2017 at 12:59

Hello JW, I have tested the code with '//HAL_NVIC_EnableIRQ(SPI6_IRQn);' and at the beginning was working good but after some minutes uC doesnt modify its RX buffer, I mean, just after program and reset the STM uC the communication between both uC is correct but after some minutes the STM doesnt update their RX frame, like if always received the same RX frame.

So , the shifted frames problem is solved but due to comment this interruption , there is a new bug

:(

.

Looks like that is related to interruption....

On the other hand, I have realized that in both SW verision (with HAL_NVIC_EnableIRQ(SPI6_IRQn) commented and uncommented) some times 'void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)' is executed. The execution of this function doent affect:

I have added a counter within this ErrorCallback function and using STM Studio I have seen this counter increasing little by little but the comunication was going well.

Thanks for your support and best regards.

Alejandro

Posted on November 14, 2017 at 15:15

I don't and won't use Cube.

You probably should find you why that callback is called.

JW

de Haro Carbonell
Associate III
Posted on November 17, 2017 at 10:32

Hello,

I finally got the solution, I found what the problem was!

Usually, the CS signal goes from 1 to 0, then MISO and MOSI communicates, and once the communication finishes CS signal goes from 0 to 1, and the STM32F429 continues with the rest of the tasks...

This was happening every 150 ms, that's the period of thime both uC are communicating. But the STM32 uC has another tasks with more priority than SPI communication.

When one of this higher priority starts during SPI communication, and once this higher priority is done then the uC continues with the task was doing ( it was SPI),

obviously

this frame is lost and 'HAL_SPI_ErrorCallback' is executed, and then SPI is restarted.If SPI is restarted when CS signal is 1, (spi idle), then there is no problem, SPI is restarted properly and the next frame will be received without problem. BUT if SPI is restarted when CS signal is 0 (STM32 SPI is selected and ready to communicate) then the STM32 is waiting to send and receive an amount of bytes but it will receives less, so an a mismatch of communication bytes is the key of the PROBLEM.

I have solved this issue just adding:

void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)

{

    if(hspi -> Instance == SPI6)

    {

        while(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_8) != GPIO_PIN_SET) // CS signal

        {

        }

        HAL_SPI_TransmitReceive_DMA(&hspi6, (uint8_t*)HMI_slave_TX_data, (uint8_t*)HMI_slave_RX_data,10);

    }

}

I have to modify 'WHILE' in order to not stop the processor , but it is the first approximation.

Now the communication is working all the time, but some times a frame is lost (and ' HAL_SPI_ErrorCallback' is called) due to higher priority task. But it is normal, a CRC is implemented to note that.

Thansk JW to help me and support.

I hope this helps to other people.

Best regards.

Alejandro.