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

You need to specify the value of CR2.TSIZE even in 'no DMA' mode.

The above description of the problem, was very helpful.

The solution that I used was different and may help someone else. However the symptoms my system exhibited were the same. hdma_spi_rx when handled before hdma_spi_tx, and using HAL_SPI_TxRxCpltCallback to start sending the next set of data would lock data transmission. Tx was locked because handling of hdma_spi_rx triggers the callback to HAL_SPI_TxRxCpltCallback.

Changing the DMA interrupt for the RX DMA to be a lower priority than TX DMA fixed the problem.

DMA2_Stream3 was setup to handle hdma_spi1_tx, and DMA2_Stream0 was setup to handle hdma_spi1_rx.

  /* DMA2_Stream3_IRQn interrupt configuration */

  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0, 0);

  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);

  /* DMA2_Stream0_IRQn interrupt configuration */

  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 1, 0);

  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

Thanks, I had the same issue.. now it is solved...

EDeme.1
Associate II

One more thing that might cause the same behaviour is when DMA buffer is located in some place in RAM (e.g. in CCMRAM or AXI) where the given DMA does not have direct access.