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-07 7:36 AM
How are the two MCUs connected? If you're using wires, try shorter ones.
I've seen bit shifting/smearing as low as 8MHz over 6" wires.
2025-07-07 7:50 AM
Thanks for your feedback. But at the moment the wires are short. They are 10 cm or 3.9''
2025-07-07 7:02 PM - edited 2025-07-07 7:03 PM
> 20 MHz - 31MHz
> They are 10 cm or 3.9''
These are not short wires. I would expect data to start dropping out around this frequency. Shorter wires, better shielding, less EF noise, will all help. Termination resistors can help. Possibly, a lower pin frequency can help but check datasheet to see if 31 MHz can still be sent. Differential signals will solve noise issues but impose design requirements.
Since only one bit is shifting, consistently, it's probably cross-talk, possibly from the CS line, getting coupled onto the SCK line.
This is a hardware issue, not a software setup issue.
2025-07-08 1:19 AM
Thanks for your reply. As I am using software NSS (CS) for STM and there is no physical CS line- I would still like to know if that could also cause a cross-talk?
2025-07-08 7:49 AM
If CS signal isn't present, it can't cause cross-talk.
2025-07-09 2:34 AM
I tested with shorter (2-3cm) and longer wires (20cm) the bit shifting still continues. One important thing that I noticed as I increase clock speed from 18 MHz to 25 MHz the delay between the rising edge of MISO and SCK decreases from 15.2 ns to 7.8ns. I am operating in SPI mode 01. I wander if that's the problem which leads to bit shifting. How to resolve it?
2025-07-09 8:25 AM
Can you show a scope trace of the first few bits of the transaction on SCK/MOSI, where the bit shift occurs?
2025-07-09 11:14 PM
At 25 MHz: Green is MISO and blue is SCK
At 18 MHz: Green is MISO and blue is SCK
At 25 MHz I see bit shift while for 18 MHz the data is received correct. In the image: green signal is MISO and blue is SCK
2025-07-10 9:01 PM
Lots of overshoot/ringing on blue but none on green. Signals are also not synchronized. The rising edge of blue should line up with edges in green, but it's not. Green is lagging, and lags more with higher clocks. Don't really know why, but that's likely part of the issue. These are faults of the master SPI.