2024-11-25 10:00 PM - edited 2024-11-25 10:12 PM
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.
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!