2014-11-29 04:07 AM
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 followingvoid 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
2014-11-29 05:19 AM
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.2014-11-29 10:38 AM
Hi,
Do you mean IFCR register? It's cleared insidevoid HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
{
...
...
/* Clear the transfer complete flag */
__HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_TC_FLAG_INDEX(hdma));
...
...
}
2014-11-29 11:12 AM
Using the STM32F2 and STM32F4 DMA controller''2. DMA flag management before enabling a new transferBefore 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''
2014-11-29 12:41 PM
> 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. JW2014-11-29 01:13 PM
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.
2014-11-29 01:16 PM
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.2014-11-29 02:55 PM
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 Krzysiek2015-11-26 06:22 AM
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!2018-02-28 10:29 PM
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