SPI LL DMA Communication on STM32H743 Processor

85 views Asked by At

I have been trying to get the SPI periphery working in low level mode because the HAL library was too slow for my use-case.

Below I submit the working code as I know of no working examples available.

I have been using LL_DMA_STREAM_0 for RX and LL_DMA_STREAM_1 for TX on DMA1 with SPI5.

rx_buffer and tx_buffer are defined as:

#define ADC_BUFFER_SIZE (uint8_t)(24)

static uint8_t tx_buffer[ADC_BUFFER_SIZE] = {0};
static uint8_t rx_buffer[ADC_BUFFER_SIZE] = {0};

I operate directly on registers as the inline functions were significantly slower to execute in debug mode without optimization (about 25-30 % slower).

1

There are 1 answers

1
Tomáš Buchta On

This is how you need to configure the DMA:

LL_DMA_ConfigAddresses(DMA1, LL_DMA_STREAM_0, (uint32_t)&SPI5->RXDR, (uint32_t)rx_buffer, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_ConfigAddresses(DMA1, LL_DMA_STREAM_1, (uint32_t)tx_buffer, (uint32_t)&SPI5->TXDR, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

The following function is the callback that runs when the SPI transmission concludes located in stm32h7xx_it.c:

void SPI5_IRQHandler(void)
{
    WRITE_REG(DMA1->LIFCR, DMA_LIFCR_CTCIF0);
    WRITE_REG(DMA1->LIFCR, DMA_LIFCR_CTEIF0);

    WRITE_REG(DMA1->LIFCR, DMA_LIFCR_CTCIF1);
    WRITE_REG(DMA1->LIFCR, DMA_LIFCR_CTEIF1);

    SET_BIT(SPI5->IFCR, SPI_IFCR_EOTC);

    SET_BIT(SPI5->IFCR, SPI_IFCR_TXTFC);

    CLEAR_BIT(SPI5->IER, SPI_IER_EOTIE);

    CLEAR_BIT(((DMA_Stream_TypeDef *)((uint32_t)DMA1 + LL_DMA_STR_OFFSET_TAB[LL_DMA_STREAM_0]))->CR, DMA_SxCR_EN);
    CLEAR_BIT(((DMA_Stream_TypeDef *)((uint32_t)DMA1 + LL_DMA_STR_OFFSET_TAB[LL_DMA_STREAM_1]))->CR, DMA_SxCR_EN);

    CLEAR_BIT(SPI5->CR1, SPI_CR1_SPE);

    CLEAR_BIT(SPI5->CFG1, SPI_CFG1_TXDMAEN);

    CLEAR_BIT(SPI5->CFG1, SPI_CFG1_RXDMAEN);

    ADC_NSS_GPIO_Port->BSRR = ADC_NSS_Pin; // NSS HIGH
}

This is the send/receive function:


static void spi_dma_send_receive(void) {

    ADC_NSS_GPIO_Port->BSRR = ADC_NSS_Pin << 16; // NSS LOW

    WRITE_REG(((DMA_Stream_TypeDef *)((uint32_t)DMA1 + LL_DMA_STR_OFFSET_TAB[LL_DMA_STREAM_0]))->M0AR, (uint32_t)rx_buffer);

    MODIFY_REG(((DMA_Stream_TypeDef *)((uint32_t)DMA1 + LL_DMA_STR_OFFSET_TAB[LL_DMA_STREAM_0]))->NDTR, DMA_SxNDT, ADC_BUFFER_SIZE);

    WRITE_REG(((DMA_Stream_TypeDef *)((uint32_t)DMA1 + LL_DMA_STR_OFFSET_TAB[LL_DMA_STREAM_1]))->M0AR, (uint32_t)tx_buffer);

    MODIFY_REG(((DMA_Stream_TypeDef *)((uint32_t)DMA1 + LL_DMA_STR_OFFSET_TAB[LL_DMA_STREAM_1]))->NDTR, DMA_SxNDT, ADC_BUFFER_SIZE);

    MODIFY_REG(SPI5->CR2, SPI_CR2_TSIZE, ADC_BUFFER_SIZE);

    SET_BIT(SPI5->CFG1, SPI_CFG1_RXDMAEN);

    SET_BIT(((DMA_Stream_TypeDef *)((uint32_t)DMA1 + LL_DMA_STR_OFFSET_TAB[LL_DMA_STREAM_0]))->CR, DMA_SxCR_EN);

    SET_BIT(((DMA_Stream_TypeDef *)((uint32_t)DMA1 + LL_DMA_STR_OFFSET_TAB[LL_DMA_STREAM_1]))->CR, DMA_SxCR_EN);

    SET_BIT(SPI5->CFG1, SPI_CFG1_TXDMAEN);

    SET_BIT(SPI5->CR1, SPI_CR1_SPE);

    SET_BIT(SPI5->CR1, SPI_CR1_CSTART);

    SET_BIT(SPI5->IER, SPI_IER_EOTIE);
}

With this code, I was able to execute a communication window of 24 bytes in roughly 20 microseconds (SPI speed was 15 MHz). The HAL library could only achieve reliable transfers at nearly twice the duration (roughly 24 ksamples in my case).

Hope this helps.