Skip to main content
Visitor II
November 26, 2024
Question

STM32H743 SPI DMA delay

  • November 26, 2024
  • 2 replies
  • 1013 views

Hello.

I have a TIM3 configured to generate a PWM signal with frequency 1 MHz and duration 200 ns using CC1 channel. TIM3 also generates a DMA request on CC1.

DMA1 is configured to transfer value 0xFFFF from AXI SRAM to SPI2_TXDR register, triggering transfer from TIM3_CH1.

The problem is that there is a delay of 70 ns between the end of the PWM signal and the CLK signal of SPI2. Here's the image illustrating it.

DS2_2024112683659.png

Yellow channel is TIM3 PWM signal. Blue channel is SPI_CLK signal. DMA transfer is triggered on TIM_CH1 (at the moment of the falling edge of the yellow signal). As you can see there's a delay of 70 ns, which is a lot and not acceptable in my application.

Does anyone know the reason for this delay?

MCU: STM32H743, revision V

System clock: 480 MHz

AHB, APBs clocks: 240 MHz

SPI clock: 80 MHz

 

DMA1 configuration:

 

 

 

 

 // Disable stream
 DMA1_Stream1->CR &= ~DMA_SxCR_EN;
 
 // DMA is the flow controller
 DMA1_Stream1->CR &= ~DMA_SxCR_PFCTRL;
 // Direction: memory to peripheral
 LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_STREAM_1, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
 // Circular mode enabled
 DMA1_Stream1->CR |= DMA_SxCR_CIRC;
 // Peripheral increment disabled
 DMA1_Stream1->CR &= ~DMA_SxCR_PINC; 
 // Memory increment disabled
 DMA1_Stream1->CR &= ~DMA_SxCR_MINC;
 // Peripheral size: half-word
 LL_DMA_SetPeriphSize(DMA1, LL_DMA_STREAM_1, LL_DMA_PDATAALIGN_HALFWORD);
 // Memory size: half-word
 LL_DMA_SetMemorySize(DMA1, LL_DMA_STREAM_1, LL_DMA_MDATAALIGN_HALFWORD); 
 // Priority: very high
 LL_DMA_SetStreamPriorityLevel(DMA1, LL_DMA_STREAM_1, LL_DMA_PRIORITY_VERYHIGH); 
 // Double buffered mode disabled
 DMA1_Stream1->CR &= ~DMA_SxCR_DBM; 
 // Disable bufferable transfers (required for USART, read ERRATA)
 DMA1_Stream1->CR &= ~DMA_SxCR_TRBUFF;
 
 // Peripheral burst mode: single transfer (forced to single in Direct mode)
 LL_DMA_SetPeriphBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_PBURST_SINGLE);
 // Memory burst mode: single transfer (forced to single in Direct mode)
 LL_DMA_SetMemoryBurstxfer(DMA1, LL_DMA_STREAM_1, LL_DMA_MBURST_SINGLE); 
 
 // Direct mode enabled (note: direct mode is not allowed for mem-mem transfers)
 // The opposite of Direct mode is using FIFO and burst transfers.
 DMA1_Stream1->FCR &= ~DMA_SxFCR_DMDIS;
 
 // Peripheral address
 DMA1_Stream1->PAR = (uint32_t) &(SPI2->TXDR);
 
 // Memory address
 DMA1_Stream1->M0AR = (uint32_t) &(ad400x.mosiValue);
 
 // Data count (auto-reloads in circular mode)
 DMA1_Stream1->NDTR = 1; 
 
 // Disable Transfer Complete Interrupt
 DMA1_Stream1->CR &= ~DMA_SxCR_TCIE;
 
 // Set peripheral request type: TIM3_CH1
 LL_DMA_SetPeriphRequest(DMA1, LL_DMA_STREAM_1, LL_DMAMUX1_REQ_TIM3_CH1); 

 // Enable stream
 DMA1_Stream1->CR |= DMA_SxCR_EN;

 

 

 

 

SPI2 configuration:

 

 

 

 

 // Disable SPI2
 SPI2->CR1 &= ~SPI_CR1_SPE; 
 
 // Select SPI1,2,3 kernel clock (spi_ker_ck): PLL1Q (160 MHz)
 LL_RCC_SetSPIClockSource(LL_RCC_SPI123_CLKSOURCE_PLL1Q); 
 
 // BaudRate: Fspi = PLL1Q / 2 = 80 MHz
 LL_SPI_SetBaudRatePrescaler(SPI2, LL_SPI_BAUDRATEPRESCALER_DIV2);
 
 // Data frame size: 16 bits (data is 16-bits when reading the result of the 
 // conversion and when in register access mode)
 LL_SPI_SetDataWidth(SPI2, LL_SPI_DATAWIDTH_16BIT);
 
 // Number of data frames in one data packet: 1
 LL_SPI_SetFIFOThreshold(SPI2, LL_SPI_FIFO_TH_01DATA);
 
 // Disable RX DMA
 SPI2->CFG1 &= ~SPI_CFG1_RXDMAEN;
 
 // Disable TX DMA
 SPI2->CFG1 &= ~SPI_CFG1_TXDMAEN; 
 
 // Number of idle cycles between two consecutive data frames
 LL_SPI_SetInterDataIdleness(SPI2, LL_SPI_ID_IDLENESS_00CYCLE);
 
 // Don't swap MOSI and MISO pins
 SPI2->CFG2 &= ~SPI_CFG2_IOSWP;
 
 // Transfer direction: full duplex
 LL_SPI_SetTransferDirection(SPI2, LL_SPI_FULL_DUPLEX);
 
 // Data format: MSB first
 SPI2->CFG2 &= ~SPI_CFG2_LSBFRST;
 
 // SPI Mode 1: CPOL = 0, CPHA = 0
 SPI2->CFG2 &= ~SPI_CFG2_CPOL;
 SPI2->CFG2 &= ~SPI_CFG2_CPHA;
 
 // Software slave management & select slave
 SPI2->CFG2 |= SPI_CFG2_SSM; 
 SPI2->CR1 |= SPI_CR1_SSI; 
 
 // SPI takes no control of its GPIOs while it is disabled
 SPI2->CFG2 &= ~SPI_CFG2_AFCNTR;
 
 // Master mode
 SPI2->CFG2 |= SPI_CFG2_MASTER; 
 
 // Enable SPI2
 SPI2->CR1 |= SPI_CR1_SPE;
 
 // TSIZE = 0, TSER = 0 (Transaction size in units of Data Packets). If set to 0,
 // then CSTART is never cleared and EOT events won't occur.
 SPI2->CR2 = 0;
 
 // Start Master Transfer (CSTART = 1)
 LL_SPI_StartMasterTransfer(SPI2);

 

 

 

 

 

Thank you!

 

    This topic has been closed for replies.

    2 replies

    ST Employee
    November 27, 2024

    Hello @Valeev.Kamil,

    An internal ticket has been created:

    Ticket Number: 197135

    We will respond to you as soon as possible.

    Thanks and Best Regards,

    Dor_RH

    ST Employee
    December 4, 2024

    Hello @Valeev.Kamil,

    Could you please share your project files (.ioc, main.c ...) so we can analyze and assist you more effectively?

    Thanks,

    Dor_RH