cancel
Showing results for 
Search instead for 
Did you mean: 

Timer to DMA to SPI STM32H723 using LL

ZacJFrank
Associate

Hi,

I want a GPIO pin to trigger an SPI transfer. The way that I have figured this should work is:
PG3 -> TIM23 -> DMA2 -> SPI1

 

Here is basically what I have so far:

/* Includes --------------------------------------------------------------------------------------------------- */
#include "TypesDefinition.h"
#include "SpiHad.h"

#include <algorithm>

#include <stm32h7xx_ll_spi.h>
#include <stm32h7xx_ll_dma.h>
#include <stm32h7xx_ll_gpio.h>
#include <stm32h7xx_ll_bus.h>
#include <stm32h7xx_ll_system.h>
#include <stm32h7xx_ll_exti.h>
#include <stm32h7xx_ll_tim.h>


namespace mopf::sys
{

/* Static members --------------------------------------------------------------------------------------------- */
static SpiHad* g_SpiHadLocal;
SpiHad* const& g_SpiHad{g_SpiHadLocal};

SpiHad::SpiHad(std::function<void()> SpiCallback, std::function<void()> EotCallback) :
    ISpiHad{},
    m_SpiCallback{SpiCallback},
    m_EotCallback{EotCallback},
    m_Status{},
    m_RxPtr{nullptr},
    m_TransferLength{0U}
{
    g_SpiHadLocal = this;
    GpioConfiguration();
    SpiConfiguration();
}

void SpiHad::StartDmaTransfer(uint8_t const* const TxData, uint8_t* const RxData, uint16_t const Length)
{
    __DSB();
    LL_DMA_ClearFlag_HT0(DMA2); // half transfer flag
    LL_DMA_ClearFlag_TC0(DMA2); // transfer complete flag
    LL_DMA_ClearFlag_HT1(DMA2); // half transfer flag
    LL_DMA_ClearFlag_TC1(DMA2); // transfer complete flag
    LL_SPI_SetTransferSize(SPI1, Length);
    LL_SPI_SetDataWidth(SPI1, LL_SPI_DATAWIDTH_8BIT);

    __DSB();

    LL_SPI_ClearFlag_TXTF(SPI1);
    LL_SPI_ClearFlag_EOT(SPI1);

    __DSB();
    LL_SPI_EnableIT_EOT(SPI1);

    LL_DMA_SetMemoryAddress(DMA2, LL_DMA_STREAM_0, (uint32_t)(TxData));
    LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_0, static_cast<uint16_t>(Length));
    LL_DMA_SetMemoryAddress(DMA2, LL_DMA_STREAM_1, (uint32_t)(RxData));
    LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_1, static_cast<uint16_t>(Length));

    __DSB();
    LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_0);
    LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_1);

    LL_SPI_EnableDMAReq_TX(SPI1);
    LL_SPI_EnableDMAReq_RX(SPI1);
    __DSB();
    LL_SPI_Enable(SPI1);
    __DSB();
    LL_TIM_GenerateEvent_CC1(TIM23); // change later

    // LL_SPI_StartMasterTransfer(SPI1);
}

void SpiHad::DmaTxIsr()
{
    m_Status = Status::OK;
    SpiHad::m_SpiCallback();
}

void SpiHad::DmaRxIsr()
{
    m_Status = Status::OK;
    SpiHad::m_SpiCallback();
}

void SpiHad::SpiIsr()
{
    if (LL_SPI_IsActiveFlag_EOT(SPI1))
    {
        LL_SPI_ClearFlag_EOT(SPI1);
        SpiHad::m_SpiCallback();
    }
}
/* Internal Functions ----------------------------------------------------------------------------------------- */

void SpiHad::SpiConfiguration()
{
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
    LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA2);
    LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_TIM23);

    /* Common initialization */
    LL_GPIO_InitTypeDef GPIO_InitStruct{
        {},
        LL_GPIO_MODE_ALTERNATE,
        LL_GPIO_SPEED_FREQ_HIGH,
        LL_GPIO_OUTPUT_PUSHPULL,
        LL_GPIO_PULL_NO,
        LL_GPIO_AF_5,
    };

    GPIO_InitStruct.Pin = LL_GPIO_PIN_10; // Chip Select -> PG10
    LL_GPIO_Init(GPIOG, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_11; // Clk  -> PG11
    LL_GPIO_Init(GPIOG, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_9; // MISO -> PG9
    LL_GPIO_Init(GPIOG, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = LL_GPIO_PIN_7; // MOSI -> PD7
    LL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    //lint -restore

    /* Configure the SPI1 parameters */
    LL_SPI_InitTypeDef SPI_InitStruct{};
    SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
    SPI_InitStruct.Mode              = LL_SPI_MODE_MASTER;
    SPI_InitStruct.DataWidth         = LL_SPI_DATAWIDTH_8BIT;
    SPI_InitStruct.ClockPolarity     = LL_SPI_POLARITY_LOW;
    SPI_InitStruct.ClockPhase        = LL_SPI_PHASE_1EDGE;
    SPI_InitStruct.NSS               = LL_SPI_NSS_HARD_OUTPUT;
    SPI_InitStruct.BaudRate          = LL_SPI_BAUDRATEPRESCALER_DIV4;
    SPI_InitStruct.BitOrder          = LL_SPI_MSB_FIRST;
    SPI_InitStruct.CRCCalculation    = LL_SPI_CRCCALCULATION_DISABLE;
    SPI_InitStruct.CRCPoly           = 0x0;

    LL_SPI_Init(SPI1, &SPI_InitStruct);

    LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);   // todo check
    LL_SPI_SetFIFOThreshold(SPI1, LL_SPI_FIFO_TH_03DATA); // todo check
    LL_SPI_DisableNSSPulseMgt(SPI1);


    LL_DMA_SetPeriphRequest(DMA2, LL_DMA_STREAM_0, LL_DMAMUX1_REQ_TIM23_CH1);
    LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_0, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
    LL_DMA_SetStreamPriorityLevel(DMA2, LL_DMA_STREAM_0, LL_DMA_PRIORITY_HIGH);
    LL_DMA_SetMode(DMA2, LL_DMA_STREAM_0, LL_DMA_MODE_NORMAL);
    LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_STREAM_0, LL_DMA_PERIPH_NOINCREMENT);
    LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_STREAM_0, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetPeriphSize(DMA2, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_SetMemorySize(DMA2, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_DisableFifoMode(DMA2, LL_DMA_STREAM_0);
    LL_DMA_SetFIFOThreshold(DMA2, LL_DMA_STREAM_0, LL_DMA_FIFOTHRESHOLD_1_4);
    // HalDmaInit();

    LL_DMA_SetPeriphAddress(DMA2, LL_DMA_STREAM_0, LL_SPI_DMA_GetTxRegAddr(SPI1));


    LL_DMA_SetPeriphRequest(DMA2, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_SPI1_RX);
    LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
    LL_DMA_SetStreamPriorityLevel(DMA2, LL_DMA_STREAM_1, LL_DMA_PRIORITY_HIGH);
    LL_DMA_SetMode(DMA2, LL_DMA_STREAM_1, LL_DMA_MODE_NORMAL);
    LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_STREAM_1, LL_DMA_PERIPH_NOINCREMENT);
    LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_STREAM_1, LL_DMA_MEMORY_INCREMENT);
    LL_DMA_SetPeriphSize(DMA2, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_SetMemorySize(DMA2, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_BYTE);
    LL_DMA_DisableFifoMode(DMA2, LL_DMA_STREAM_1);

    LL_DMA_SetFIFOThreshold(DMA2, LL_DMA_STREAM_1, LL_DMA_FIFOTHRESHOLD_3_4);
    LL_DMA_SetPeriphAddress(DMA2, LL_DMA_STREAM_1, LL_SPI_DMA_GetRxRegAddr(SPI1));

    // Enable DMA requests for TX and RX
    LL_SPI_EnableDMAReq_TX(SPI1);
    LL_SPI_EnableDMAReq_RX(SPI1);

    /* Lock GPIO for master to avoid glitches on the clock output */
    LL_SPI_EnableGPIOControl(SPI1);
    LL_SPI_DisableMasterRxAutoSuspend(SPI1);

    HAL_NVIC_SetPriority(SPI1_IRQn, 7U, 0U);
    NVIC_ClearPendingIRQ(SPI1_IRQn);
    HAL_NVIC_EnableIRQ(SPI1_IRQn);

    constexpr uint32_t period{80U}; // Set the period for TIM23
    // Enable clocks for TIM23
    // Configure TIM23 for one-shot mode
    LL_TIM_SetPrescaler(TIM23, 15 - 1);
    LL_TIM_SetCounterMode(TIM23, LL_TIM_COUNTERMODE_UP);
    LL_TIM_SetAutoReload(TIM23, period - 1);                   // Set the period
    LL_TIM_SetOnePulseMode(TIM23, LL_TIM_ONEPULSEMODE_SINGLE); // One-shot mode
    // Set the output polarity to active high (non-inverted)
    LL_TIM_OC_SetPolarity(TIM23, LL_TIM_CHANNEL_CH4, LL_TIM_OCPOLARITY_LOW);

    // Configure TRGO to be generated on enable event
    LL_TIM_EnableDMAReq_CC1(TIM23);
    // LL_TIM_SetTriggerOutput(TIM23, LL_TIM_TRGO_CC1IF); // change back to ENABLE or UPDATE

    // Configure TIM23_CH4 to output to PF3
    LL_TIM_OC_SetMode(TIM23, LL_TIM_CHANNEL_CH4, LL_TIM_OCMODE_PWM1);
    LL_TIM_OC_SetCompareCH4(TIM23, period - 10); //
    LL_TIM_OC_EnablePreload(TIM23, LL_TIM_CHANNEL_CH4);
    LL_TIM_CC_EnableChannel(TIM23, LL_TIM_CHANNEL_CH4);

    // Generate an update event to apply the configuration
    LL_TIM_GenerateEvent_UPDATE(TIM23);
}

void SpiHad::GpioConfiguration()
{
    /* Common initialization */

    LL_GPIO_InitTypeDef GPIO_InitStruct{
        {},
        LL_GPIO_MODE_OUTPUT,
        LL_GPIO_SPEED_FREQ_HIGH,
        LL_GPIO_OUTPUT_PUSHPULL,
        LL_GPIO_PULL_NO,
        LL_GPIO_AF_0,
    };

    GPIO_InitStruct.Pin = LL_GPIO_PIN_12;
    LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_12);

    GPIO_InitStruct.Mode      = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Alternate = LL_GPIO_AF_13;
    GPIO_InitStruct.Pin       = LL_GPIO_PIN_3;
    LL_GPIO_Init(GPIOF, &GPIO_InitStruct);

    // Configure PC14 as input with interrupt
    GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
    GPIO_InitStruct.Pin  = LL_GPIO_PIN_14;
    GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; // Adjust pull-up/down as needed
    LL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    // Configure PG3 as input with link to start TIM23
    GPIO_InitStruct.Mode      = LL_GPIO_MODE_ALTERNATE;
    GPIO_InitStruct.Pin       = LL_GPIO_PIN_3;
    GPIO_InitStruct.Pull      = LL_GPIO_PULL_NO; // Adjust pull-up/down as needed
    GPIO_InitStruct.Alternate = LL_GPIO_AF_13;   // TIM23_ETR
    LL_GPIO_Init(GPIOG, &GPIO_InitStruct);

    // Clear any pending interrupts and enable the NVIC IRQ
    __HAL_GPIO_EXTI_CLEAR_IT(LL_GPIO_PIN_14);
    HAL_NVIC_SetPriority(EXTI15_10_IRQn, 7U, 0);
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}


}

I initially got the SPI working with DMA by directly by having LL_DMAMUX1_REQ_SPI1_RX as the peripheral request for both streams and starting the transfer with LL_SPI_StartMasterTransfer(SPI1);

So the SPI and DMA work in general. With the current setup, I'm skipping the pin triggering the timer, and just generating a TIM23_CH1 event manually. Once that works I'll get to triggering the timer.

When I call the function LL_TIM_GenerateEvent_CC1(TIM23), the value of DMA2->S0NDTR (for the TxSpi) decrements by one. I would  expect it to transfer all bytes (4 in the first case). The SPI does nothing. DMA2->S1NDTR (for the RxSpi) doesn't budge. If I call LL_TIM_GenerateEvent_CC1 a few more times, it continues decrementing, and the TC flags are set, but again, nothing happens with the SPI.

I played around with the peripheral source (I had the RxDma (Stream1) as coming from LL_DMAMUX1_REQ_TIM23_CH1 as well (as the TxDma does)), but it didn't seem to help.

 

Any ideas? Cheers

 

1 REPLY 1
ZacJFrank
Associate

Ok, apparently I still needed to call LL_SPI_StartMasterTransfer(SPI1);

 

Now the SPI at least sends out the one byte it receives. But the DMA still only transfers 1 byte per DMA request. I want it to send as many bytes as in NDTR. I tried playing with the burst feature but it doesn't seem to want to do that.