cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 SPI slave mode with DMA - transfer issues above 18MHz clock

Amit_RS
Associate

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.

14 REPLIES 14
bmckenney
Associate III

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.

Thanks for your feedback. But at the moment the wires are short. They are 10 cm or 3.9''

TDK
Super User

>  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.

If you feel a post has answered your question, please click "Accept as Solution".

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?

If CS signal isn't present, it can't cause cross-talk.

If you feel a post has answered your question, please click "Accept as Solution".

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?

TDK
Super User

Can you show a scope trace of the first few bits of the transaction on SCK/MOSI, where the bit shift occurs?

If you feel a post has answered your question, please click "Accept as Solution".

At 25 MHz: Green is MISO and blue is SCKAt 25 MHz: Green is MISO and blue is SCK

 

At 18 MHz: Green is MISO and blue is SCKAt 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

TDK
Super User

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.

If you feel a post has answered your question, please click "Accept as Solution".