2025-11-23 12:43 PM
Using Nucleo-U545RE-Q, SPI2 Full-duplex Master, default GPIO selected by STM32CubeMX v6.15.0 with LL selected for implementation.
Config clock for max 160 MHz and prescale SPI by 8 for 20 Mbps. Add GPDMA Ch2/3 for SPI2 TX/RX.
I double checked the output configuration code generated by the CubeMX against the Cube_FW_H5_V1.5.0 Examples_LL: SPI_TwoBoards_FullDuplex_DMA_Master_Init, and it matches exactly but for peripheral selection.
Jumper-wire PC1-to-PC2 (CN7.36-to-CN7.35) for simple SPI loop-back test.
With this configuration, the loop-back works when I implemented the SPI full-duplex transfer by LL blocking code. But trying to use the GPDMA channels results in no data, no DMA IRQ for TC or DTE, no SPI IRQ for anything. Below is the simple code to try to get a transfer going, there is initialization code that enables all SPI and DMA interrupt sources and handles the IRQ by clearing the flag and setting the local global booleans that you see below. Note that this is setup for Word (32-bit) size, but I've tried with simple 8-bit as well. Besides, the fact that the blocking LL code to make the transfer shows that the 32-bit size is working. I've used much slower bit rate as well just to check, but on a side note, going any faster (e.g. prescale by 4 for 40 Mbps) causes erroneous data RX, so this is a secondary concern as to why because I understood that SPI is good up to system clock DIV by 2. The code below just hangs waiting on any IRQ which never comes to prove a transfer never took place:
#define DMAx GPDMA1
#define SPIx SPI2
#define DMA_RX LL_DMA_CHANNEL_3
#define DMA_TX LL_DMA_CHANNEL_2
void spiXferDMA(void *txBuf, void *rxBuf, uint32_t cnt)
{
uint32_t dmaCnt = cnt * 4; // DMA length configuration is in total bytes even when Data Width is Word
dmaIrq = dmaErr = spiIrq = btnIrq = false;
LL_SPI_ClearFlag_EOT(SPIx);
LL_SPI_ClearFlag_TXTF(SPIx);
LL_SPI_ClearFlag_SUSP(SPIx);
LL_SPI_EnableDMAReq_RX(SPIx);
LL_SPI_EnableDMAReq_TX(SPIx);
LL_SPI_SetTransferSize(SPIx, cnt); // SPI size configuration is in count of Words to transfer
LL_SPI_Enable(SPIx);
LL_DMA_ConfigAddresses(DMAx, DMA_RX, LL_SPI_DMA_GetRxRegAddr(SPIx), (uint32_t)rxBuf);
LL_DMA_ConfigAddresses(DMAx, DMA_TX, (uint32_t)txBuf, LL_SPI_DMA_GetTxRegAddr(SPIx));
LL_DMA_SetBlkDataLength(DMAx, DMA_RX, dmaCnt);
LL_DMA_SetBlkDataLength(DMAx, DMA_TX, dmaCnt);
LL_DMA_EnableChannel(DMAx, DMA_RX);
LL_DMA_EnableChannel(DMAx, DMA_TX);
LL_SPI_StartMasterTransfer(SPIx);
while (!dmaIrq && !dmaErr && !spiIrq && !btnIrq)
{
HAL_GPIO_WritePin(LED_GRN_GPIO_Port, LED_GRN_Pin, GPIO_PIN_SET);
HAL_Delay(30);
HAL_GPIO_WritePin(LED_GRN_GPIO_Port, LED_GRN_Pin, GPIO_PIN_RESET);
HAL_Delay(969);
}
LL_SPI_Disable(SPIx);
LL_SPI_DisableDMAReq_RX(SPIx);
LL_SPI_DisableDMAReq_TX(SPIx);
LL_DMA_DisableChannel(DMAx, DMA_TX);
LL_DMA_DisableChannel(DMAx, DMA_RX);
}