cancel
Showing results for 
Search instead for 
Did you mean: 

[SOLVED] STM32F301K8Ux SPI DMA Transfer works only once

Posted on November 29, 2014 at 13:07

Hi,

I have strange problem with making SPI DMA transfers working. It works only one time, the second dma transfer is not performed. I use similar coding for USART and it works perfect, but SPI doesn't. So the issue is to send one byte and receive many bytes. The SPI initialization (using MX lib) looks like following

void MX_SPI3_Init(void)
{
hspi3.Instance = SPI3;
hspi3.Init.Mode = SPI_MODE_MASTER;
hspi3.Init.Direction = SPI_DIRECTION_2LINES;
hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi3.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi3.Init.NSS = SPI_NSS_SOFT;
hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi3.Init.TIMode = SPI_TIMODE_DISABLED;
hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
hspi3.Init.NSSPMode = SPI_NSS_PULSE_DISABLED;
HAL_SPI_Init(&hspi3);
}

The chip select pin is software driven, so two irq are in use

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi3.Instance)
{
mpu9250_read_regs_dma_tc();
}
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi3.Instance)
{
mpu9250_read_regs_dma_tc();
}
}
void mpu9250_read_regs_dma_tc(void)
{
mpu9250_set_cs_state(GPIO_PIN_SET);
dma_regs_read_flag = 0;
}

The routine which is responsible for reading all the registers is like that

void mpu9250_read_regs_dma(uint8_t start_reg_no, uint8_t no_of_bytes, uint8_t * buff)
{
if ( dma_regs_read_flag == 0 ) //it's not perfect but it should not be called more frequently then DMA transfer makes possible
{
dma_regs_read_flag = 1;
mpu9250_set_cs_state(GPIO_PIN_RESET);
memset(spi_tx_buff_dma, 0, no_of_bytes+1);
memset(spi_rx_buff_dma, 0, no_of_bytes+1);
spi_tx_buff_dma[0] = start_reg_no | 0x80; // need to set 0x80 bit for reading operation
// it working but only one time
//HAL_SPI_TransmitReceive_DMA(&hspi3, spi_tx_buff_dma, spi_rx_buff_dma,no_of_bytes+1);
// it does not work either :(
HAL_SPI_Transmit(&hspi3, spi_tx_buff_dma, 1 , MPU9250_TIMEOUT);
HAL_SPI_Receive_DMA(&hspi3, spi_rx_buff_dma,no_of_bytes);
}
}

I have tried two approach, first (commented) where sending and reciving are made by DMA, and second, where DMA is utilized only for receiving data. Both works, but only for first call. Please take a look attached files. You can see that first transfer is ok, but second only sending one byte is ok, but DMA reciving does not work at all (even MISO is going high, I don't know why). The transfer complete interrupt is not called either. I know, that CubeMX is not the best lib, but it's function for starting DMA transfer HAL_DMA_Start_IT (which is in use) looks ok. It disables DMA using CCR register, makes configuration and start DMA again. Any idea? #dma #stm32f301k8ux #spi
13 REPLIES 13
sdim
Associate III
Posted on November 29, 2014 at 14:19

I had the same problem with the UART DMA.

Clear the SPI DMA interrupt flags , you cannot start a second DMA transfer if an interrupt flag is set.

Posted on November 29, 2014 at 19:38

Hi,

Do you mean IFCR register? It's cleared inside

void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
{
...
...
/* Clear the transfer complete flag */
__HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_TC_FLAG_INDEX(hdma));
...
...
}

sdim
Associate III
Posted on November 29, 2014 at 20:12

https://www.google.gr/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CB8QFjAA&url=http://www.st.com/web/en/resource/technical/document/application_note/DM00046011.pdf&ei=QBp6VJAQ6crKA9zfgbAI&usg=AFQjCNEnLMVD_tVPxvCcPlmwANEkSARWsQ&sig2=9_rHM78upkaKVqYy4g1tKQ&bvm=bv.80642063%2cd.bGQ

Using the STM32F2 and STM32F4 DMA controller

''2. DMA flag management before enabling a new transfer

Before enabling a new transfer, the user must ensure that the Transfer Complete Interrupt Flag (TCIF) in DMA_LISR or DMA_HISR is cleared.

As a general recommendation, it is advised to clear all flags in the DMA_LIFCR and DMA_HIFCR registers before starting a new transfer''

Posted on November 29, 2014 at 21:41

> So the issue is to send one byte and receive many bytes.

In SPI, you can't. To receive as a master, you need the clock clocking, and to achieve that, you need to transmit.

JW

Posted on November 29, 2014 at 22:13

As i wrote, the flags are cleared by macro during TC irq

#define __HAL_DMA_CLEAR_FLAG(__HANDLE__, __FLAG__) (DMA1->IFCR |= (__FLAG__))

Just to be sure, I put

DMA1->IFCR = 0x0FFFFFFF; 

At the end of tc irq routine, and before starting DMA transfer but it did not change anything.
Posted on November 29, 2014 at 22:16

It just I did, see attached files.

There are tx and rx buffer. The tx buffer contain at first position the byte which will be send. The rest of the buffer contain 0. The RX buffer will be filled starting from 2nd byte by revived data.

It works, but only one time.

Posted on November 29, 2014 at 23:55

Hi,

It's working 🙂 The problem was related to not enabled DMA tx irq. It has to be enabled even if only rx dma is in use. In fact, both dma should be used same time, there is no way to use only TX dma if SPI is configured in bidirectional mode.

Regards

Krzysiek

florent23
Associate II
Posted on November 26, 2015 at 15:22

Hi,

this post is rather old but I stumbled upon it while searching for a solution to a problem I was having, and since I found what was causing the problem, I'm posting the answer here in case someone stumbles on this discussion the same way I did.

So the problem I had was that when I used HAL_SPI_TransmitReceive more than once, the first transfer worked fine, but the second one didn't: I never got the transfer complete interrupt.

It turns out that for some reason when the first transfer completes, only the rx interrupt gets called (with HAL_DMA_IRQHandler(&hdma_spi1_rx)), not the tx interrupt.

Unfortunately inside the HAL_SPI_TransmitReceive function, there is a call to HAL_DMA_Start_IT for hdma_spi1_tx (and for hdma_spi1_rx) which locks hdma_spi1_tx. But since the tx interrupt never comes, hdma_spi1_tx never gets unlocked after the first transfer (HAL_DMA_IRQHandler(&hdma_spi1_tx) unlocks it). So when HAL_SPI_TransmitReceive is called a second time, it calls HAL_DMA_Start_IT(hdma_spi1_tx), which tries to lock hdma_spi1_tx, which is already locked, so it returns HAL_BUSY, but HAL_SPI_TransmitReceive doesn't check the return value of HAL_DMA_Start_IT so it seems that everything went ok with the call when in fact it didn't, and the transfer never happens.

To fix it, I have to unlock hdma_spi1_tx manually inside HAL_SPI_TxRxCpltCallback so that the second time I call HAL_SPI_TransmitReceive, hdma_spi1_tx can be locked once more.

The weird thing is that from the second transfer on, both rx and tx interrupts get called at the end of the transfer so the manual unlocking has to happen only once. I still don't know why the first transfer behaves differently than the next ones, but now the problem is fixed.

Hope this helps someone!

Manish Sharma
Associate III
Posted on March 01, 2018 at 07:29

Hi All,

Recently, I faced the same issue and found the solution.

Note: SPI_CR2 register says that 'Endless Transaction is initialized when  CSTART is set while zero value stored at TSIZE'

I debugged it and added condition for NORMAL mode. It is working fine on my STM32H753I Eval Board. Hope, It will work on yours

too if you see the same issue while transferring data using SPI DMA.

HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)

{

    if(hspi->Init.Mode == DMA_NORMAL) //Check Normal Mode

      MODIFY_REG(hspi->Instance->CR2, SPI_CR2_TSIZE, Size);

}

Regards,

Manish