2025-07-07 7:19 AM
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;
}
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''