2025-07-07 7:19 AM - last edited on 2025-07-11 4:21 AM by Andrew Neil
Hello,
I am using STM32H7 as SPI slave with DMA transfer, the communication works fine till 18MHz of clock frequency from the master side however for higher clock speeds of 20 MHz - 31MHz it is noted that the bit shifts by 1 bit for the whole stream of data.
e.g if STM is sending 00100111 00010010 01101101 00000000 then in the master side the received bits are 00010011 10001001 00110110 10000000.
The LSB of first byte shifts to MSB of 2nd byte thus corrupting the whole data. The target is to achieve maximum speed of 31 Mhz. Below I have provided my STM setup code for reference, moreover software NSS is used:
void SPI_Slave_Init(void)
{
/* SPI5 disable */
LL_SPI_Disable(SPI5);
/* USER CODE BEGIN SPI5_Re_Init 0 */
/* USER CODE END SPI5_Re_Init 0 */
LL_SPI_InitTypeDef SPI_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI5);
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOD);
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOF);
LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_GPIOG);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA2);
GPIO_InitStruct.Pin = SPI_SLAVE_TC_PIN;
GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(SPI_SLAVE_TC_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SPI_SLAVE_CLK_PIN | SPI_SLAVE_MISO_PIN | SPI_SLAVE_MOSI_PIN;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(SPI_SLAVE_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SPI_SLAVE_DRDY_PIN;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
LL_GPIO_Init(SPI_SLAVE_DRDY_GPIO_PORT, &GPIO_InitStruct);
LL_GPIO_SetOutputPin(SPI_SLAVE_DRDY_GPIO_PORT, SPI_SLAVE_DRDY_PIN); // Initially high, goes low when data is ready
/* SPI5 DMA Init */
/* SPI5_RX Init */
LL_DMA_SetPeriphRequest(DMA2, LL_DMA_STREAM_4, LL_DMAMUX1_REQ_SPI5_RX);
LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_4, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetStreamPriorityLevel(DMA2, LL_DMA_STREAM_4, LL_DMA_PRIORITY_VERYHIGH);
LL_DMA_SetMode(DMA2, LL_DMA_STREAM_4, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_STREAM_4, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_STREAM_4, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA2, LL_DMA_STREAM_4, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA2, LL_DMA_STREAM_4, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA2, LL_DMA_STREAM_4);
/* DMA2_Stream3_IRQn interrupt configuration */
NVIC_SetPriority(DMA2_Stream4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(DMA2_Stream4_IRQn);
/* SPI5_TX Init */
LL_DMA_SetPeriphRequest(DMA2, LL_DMA_STREAM_5, LL_DMAMUX1_REQ_SPI5_TX);
LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_5, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetStreamPriorityLevel(DMA2, LL_DMA_STREAM_5, LL_DMA_PRIORITY_VERYHIGH);
LL_DMA_SetMode(DMA2, LL_DMA_STREAM_5, LL_DMA_MODE_NORMAL);
LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_STREAM_5, LL_DMA_PERIPH_NOINCREMENT);
LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_STREAM_5, LL_DMA_MEMORY_INCREMENT);
LL_DMA_SetPeriphSize(DMA2, LL_DMA_STREAM_5, LL_DMA_PDATAALIGN_BYTE);
LL_DMA_SetMemorySize(DMA2, LL_DMA_STREAM_5, LL_DMA_MDATAALIGN_BYTE);
LL_DMA_DisableFifoMode(DMA2, LL_DMA_STREAM_5);
/* DMA2_Stream3_IRQn interrupt configuration */
NVIC_SetPriority(DMA2_Stream5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(DMA2_Stream5_IRQn);
/* SPI5 interrupt Init */
NVIC_SetPriority(SPI5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(SPI5_IRQn);
/* USER CODE END SPI5_Re_Init 1 */
/* SPI5 parameter configuration*/
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_SLAVE;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW; //CPOL=0
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE; // CPHA = 1
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 0x1021;
LL_SPI_SetFIFOThreshold(SPI5, LL_SPI_FIFO_TH_04DATA);
LL_SPI_Init(SPI5, &SPI_InitStruct);
LL_SPI_SetStandard(SPI_SLAVE_INSTANCE, LL_SPI_PROTOCOL_MOTOROLA);
}
void SPI5_slave_core_Init(void){
Tx_prepared = false;
// Reinitialize SPI5 (including GPIO setup)
SPI_Slave_Init();
// Disable TX DMA stream and wait for it to stop
xdma_disable_stream(SPI_5_slave.xdma_tx);
while (xdma_check_stream_is_enabled(SPI_5_slave.xdma_tx));
// Disable RX DMA stream and wait for it to stop
xdma_disable_stream(SPI_5_slave.xdma_rx);
while (xdma_check_stream_is_enabled(SPI_5_slave.xdma_rx));
// Setup DMA peripheral and memory addresses
// TX: peripheral address = SPI5 TXDR, memory address = next dataset to send
xdma_set_peripheral_addr(SPI_5_slave.xdma_tx, (uint32_t) &(SPI_5_slave.spi->TXDR));
xdma_set_memory_addr(SPI_5_slave.xdma_tx, (uint32_t)&Raw_Buffer.raw_buffer[Raw_Buffer.raw_buffer_idx_tail]);
// RX: peripheral address = SPI5 RXDR, memory address = RX buffer
xdma_set_peripheral_addr(SPI_5_slave.xdma_rx, (uint32_t) &(SPI_5_slave.spi->RXDR));
xdma_set_memory_addr(SPI_5_slave.xdma_rx, (uint32_t)spi5_rx_dummy_buffer);
const uint32_t data_length_bytes = DATASTORAGE_RAW_BUFFER_LENGTH *sizeof(Raw_Dataset);
xdma_set_data_length(SPI_5_slave.xdma_tx, data_length_bytes);
xdma_set_data_length(SPI_5_slave.xdma_rx, data_length_bytes);
// Clear Flags
SPI_SLAVE_DMA_TX_CLEAR_TC_FLAG;
SPI_SLAVE_DMA_RX_CLEAR_TC_FLAG;
LL_SPI_ClearFlag_EOT(SPI5);
// Enable interrupts for TX DMA (transfer complete and error)
xdma_enable_IT_TC(SPI_5_slave.xdma_tx);
xdma_enable_IT_TE(SPI_5_slave.xdma_tx);
// Enable interrupts for RX DMA (transfer complete and error)
xdma_enable_IT_TC(SPI_5_slave.xdma_rx);
xdma_enable_IT_TE(SPI_5_slave.xdma_rx);
// Set SPI transfer size in number of 32-bit words
LL_SPI_SetTransferSize(SPI_5_slave.spi, data_length_bytes);
// Enable relevant SPI error/status interrupts for monitoring and debugging
LL_SPI_EnableIT_OVR(SPI_5_slave.spi);
LL_SPI_EnableIT_MODF(SPI_5_slave.spi);
LL_SPI_EnableIT_FRE(SPI_5_slave.spi);
LL_SPI_EnableIT_EOT(SPI_5_slave.spi);
//Set NSS low
// CLEAR_BIT(SPI5->CR1, SPI_CR1_SSI);
// Enable DMA streams after configuring everything
xdma_enable_stream(SPI_5_slave.xdma_rx);
xdma_enable_stream(SPI_5_slave.xdma_tx);
// Enable DMA requests on SPI peripheral
SPI_ENABLE_DMA_RX(SPI_5_slave.spi);
SPI_ENABLE_DMA_TX(SPI_5_slave.spi);
//Set NSS low
CLEAR_BIT(SPI5->CR1, SPI_CR1_SSI);
// Enable the SPI peripheral (starts operation)
ENABLE_SPI(SPI_5_slave.spi);
// Activate DRDY only if data available
if(Raw_Buffer.raw_buffer_idx_head !=
Raw_Buffer.raw_buffer_idx_tail) {
LL_GPIO_ResetOutputPin(SPI_SLAVE_DRDY_GPIO_PORT, SPI_SLAVE_DRDY_PIN);
}
Tx_prepared = true;
}
Edited to apply source code formatting - please see How to insert source code for future reference.
2025-07-11 12:08 AM
The rising edge of blue signal(SCK) is not lining up with green rising edge (MISO) as I am operating in SPI mode 01 where the data is sampled at the falling edge of the clock or blue signal. For 18 MHz it samples 15.2 ns prior to falling edge of clock while for 25MHz the timing reduces to 7.8ns. Hope this helps to understand the scope readings better.
2025-07-11 1:52 AM
I am aware it’s in SPI mode 1. Things should still be aligned as stated.
2025-07-11 4:16 AM
I could have been a bit more clear. Here's SPI mode 1:
Note how the rising edge of SCK corresponds to edges on MOSI.
On your scope trace, edges of MOSI are slightly delayed from the rising edge of SCK.
What is the master SPI here?
2025-07-11 4:24 AM
The Master is the PI CM
2025-07-11 4:59 AM
I just checked this delay on oscilloscope and it is quite persistent across all ranges of frequency from 1MHz to 31MHz. The delay is around 10ns and it doesn't change with any increase or decrease in frequency.