cancel
Showing results for 
Search instead for 
Did you mean: 

SPI Rx double buffered DMA triggered from a GPIO

NPato
Associate II

Using an STM32H743Zi I'm trying to receive 32bits of data from two SPI interfaces triggered at the same time from an external GPIO at a fairly high rate e.g. ~200kHz. Trying to use double buffered DMA receive.

I started by trying to get an external GPIO to trigger a peripheral related DMA transaction. I tried using EXTI0 as the DMA synchronisation signal but that only seemed to work for the first time the GPIO was toggled, it never triggered again after that. So next I used a method mentioned else where in the forum which is to use a timer which can take a GPIO into to generate a DMA event, and this works.

I can generate the required 32 clock cycles on each SPI interface at the required time (using a 4 byte circular DMA SPI tx).

The problem I have is reception. I'd assumed I could just start the SPI Rx double buffered DMA and it would just process data as the SPI device indicates that it has Rx data ready. But when I do this the DMA rx starts producing FIFO errors and completing very quickly even without any SPI clock active. It's as if the DMA is reading out of the SPI device without taking any notice of whether there is any data to read or not. The code to start the SPI DMA is based on the HAL_SPI_TransmitReceive_DMA() but modified to handle the double buffered Rx DMA.

Any insight into what I might be doing wrong?

HAL_StatusTypeDef SPI_TxRx_DMA(SPI_HandleTypeDef *hspi, uint8_t * txData, uint16_t txSize, uint8_t * rxData1, uint8_t * rxData2, uint16_t rxSize)
{
    HAL_StatusTypeDef errorcode = HAL_OK;
 
    /* Process locked */
    __HAL_LOCK(hspi);
 
    hspi->State = HAL_SPI_STATE_BUSY_TX_RX;
 
    /* Set the transaction information */
    hspi->ErrorCode = HAL_SPI_ERROR_NONE;
    hspi->pTxBuffPtr = (uint8_t *) txData;
    hspi->TxXferSize = txSize;
    hspi->TxXferCount = txSize;
    hspi->pRxBuffPtr = NULL;
    hspi->RxXferSize = rxSize;
    hspi->RxXferCount = rxSize;
 
    /* Init field not used in handle to zero */
    hspi->RxISR = NULL;
    hspi->TxISR = NULL;
 
    /* Reset the Tx/Rx DMA bits */
    CLEAR_BIT(hspi->Instance->CFG1, SPI_CFG1_TXDMAEN | SPI_CFG1_RXDMAEN);
 
    /* Adjust XferCount according to DMA alignment / Data size */
    if (hspi->Init.DataSize <= SPI_DATASIZE_8BIT)
    {
        if (hspi->hdmatx->Init.MemDataAlignment == DMA_MDATAALIGN_HALFWORD)
        {
            hspi->TxXferCount = (hspi->TxXferCount + (uint16_t) 1UL) >> 1UL;
        }
        if (hspi->hdmatx->Init.MemDataAlignment == DMA_MDATAALIGN_WORD)
        {
            hspi->TxXferCount = (hspi->TxXferCount + (uint16_t) 3UL) >> 2UL;
        }
        if (hspi->hdmarx->Init.MemDataAlignment == DMA_MDATAALIGN_HALFWORD)
        {
            hspi->RxXferCount = (hspi->RxXferCount + (uint16_t) 1UL) >> 1UL;
        }
        if (hspi->hdmarx->Init.MemDataAlignment == DMA_MDATAALIGN_WORD)
        {
            hspi->RxXferCount = (hspi->RxXferCount + (uint16_t) 3UL) >> 2UL;
        }
    }
    else if (hspi->Init.DataSize <= SPI_DATASIZE_16BIT)
    {
        if (hspi->hdmatx->Init.MemDataAlignment == DMA_MDATAALIGN_WORD)
        {
            hspi->TxXferCount = (hspi->TxXferCount + (uint16_t) 1UL) >> 1UL;
        }
        if (hspi->hdmarx->Init.MemDataAlignment == DMA_MDATAALIGN_WORD)
        {
            hspi->RxXferCount = (hspi->RxXferCount + (uint16_t) 1UL) >> 1UL;
        }
    }
    else
    {
        /* Adjustment done */
    }
 
#if 1
    hspi->hdmarx->XferHalfCpltCallback = NULL;
    hspi->hdmarx->XferCpltCallback = NULL; // TBD SPI_DMAReceiveCplt;
    hspi->hdmarx->XferErrorCallback = SPI_DMAError;
    hspi->hdmarx->XferAbortCallback = NULL;
 
    /* Enable the Rx DMA Stream/Channel  */
    if (HAL_OK != HAL_DMAEx_MultiBufferStart_IT(hspi->hdmarx,
                    (uint32_t) &hspi->Instance->RXDR, (uint32_t) rxData1,
                    (uint32_t) rxData2, hspi->RxXferCount))
    {
        /* Update SPI error code */
        SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_DMA);
        errorcode = HAL_ERROR;
        hspi->State = HAL_SPI_STATE_READY;
        return errorcode;
    }
 
    /* Enable Rx DMA Request */
    SET_BIT(hspi->Instance->CFG1, SPI_CFG1_RXDMAEN);
#endif
 
    // No Tx callbacks
    hspi->hdmatx->XferHalfCpltCallback = NULL;
    hspi->hdmatx->XferCpltCallback = NULL;
    hspi->hdmatx->XferErrorCallback = NULL;
    hspi->hdmatx->XferAbortCallback = NULL;
 
    /* Enable the Tx DMA Stream/Channel  */
    if (HAL_OK != HAL_DMA_Start(hspi->hdmatx, (uint32_t) hspi->pTxBuffPtr,
                    (uint32_t) &hspi->Instance->TXDR, hspi->TxXferCount))
    {
        /* Update SPI error code */
        SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_DMA);
        errorcode = HAL_ERROR;
        hspi->State = HAL_SPI_STATE_READY;
        return errorcode;
    }
 
    // Endless transaction indicated by setting size to 0
    MODIFY_REG(hspi->Instance->CR2, SPI_CR2_TSIZE, 0UL);
 
    /* Enable Tx DMA Request */
    SET_BIT(hspi->Instance->CFG1, SPI_CFG1_TXDMAEN);
 
    /* Enable the SPI Error Interrupt Bit */
    __HAL_SPI_ENABLE_IT(hspi, (/*SPI_IT_OVR |*/SPI_IT_UDR | SPI_IT_FRE | SPI_IT_MODF));
 
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
 
    /* Master transfer start */
    SET_BIT(hspi->Instance->CR1, SPI_CR1_CSTART);
 
    /* Process Unlocked */
    __HAL_UNLOCK(hspi);
    return errorcode;
}

10 REPLIES 10

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

JW

NPato
Associate II

Values of SPI2, DMAMUX1 (stream2) and DMA1 (stream2) attached. These are taken just before the __HAL_SPI_ENABLE(hspi) line. As soon as I step over that the Rx DMA starts writing data to memory even though there is no SPI clock active. I can't see anything obviously wrong in the register settings. The DMAMUX request id is set to 0x27 (39) which is spi2_rx_dma.

0690X00000AQzy4QAD.png

0690X00000AQzxzQAD.png

0690X00000AQzxpQAD.png

I don't use the 'H7. Are you sure it's a good idea to set in SPI DSIZE to 8-bit, FTHLV to 1-data, yet DMA PSIZE to word?

JW

NPato
Associate II

I have the same settings in the tx direction and it works. But that's no guarantee it's a good idea for Rx, the HAL allows it and has code to handle the DSIZE/PSIZE differences. The documentation for FTHLV indicates it is more efficient to use higher values if accessing the data register as 16 or 32 bits but does not say it won't work at '1-data' setting. Setting it to '4-data' does seem to make any difference any way.

I thought more about setting the DMA to transfer bytes. As I've said I don't use the 'H7 with all its blows and whistles, but I'd expect it maintains a certain degree of backward compatibility and that's how you'd do it on the 'F4 - SPI transmit/receive bytes? then DMA transfers bytes.

I don't say that's the root of your problem, just trying.

With *no* clocks you should see *no* DMA anyway.

Without DMA, when you enable SPI by setting SPE, do you see SPI2S_SR.RXP to be set? If no, there's something rotten around the DMA routing (DMAMUX).

JW

NPato
Associate II

Thanks I'll give that a try tomorrow.

I'm not seeing the RXP flag set but then if the DMA is constantly reading from it for some reason that would clear it instantly. This would probably cause an underrun condition if the DMA is actually reading from the SPI, but in SPI master mode there is no flag to indicate this (that I can see).

> I'm not seeing the RXP flag set but then if the DMA is constantly reading from it for some reason that would clear it instantly.

Yes. That's why I wrote above:

>> Without DMA, when you enable SPI by setting SPE, do you see SPI2S_SR.RXP to be set?

Note, that debugger is intrusive; it reading the SPI registers influence the flags in the same way as reading it from software.

> This would probably cause an underrun condition if the DMA is actually reading from the SPI, but in SPI master mode there is no flag to indicate this (that I can see).

The ST's SPI does not have the reputation of a robustly designed IP.

JW

NPato
Associate II

Without Rx DMA on the SPI enabled the RXP flag is not set, so something odd is going on.

But it seems to work if I trigger the Rx DMA from the same trigger as the Tx DMA is using. I've got to verify the data is correct but it looks okay at first glance.

Seems like the Rx DMA gets spurious triggers if active while the SPI device is not 'doing stuff'

gcaif
Associate II

hello, I use spi with dma can`t work in stm32H7,I don`t know where is configure wrong. could you tell me the mpu configure and dma set. thank you.