Showing results for 
Search instead for 
Did you mean: 

SPI Clock rate limitation?

Associate III

TL;DR. What are the limiting factors for incoming clock speed for an SPI peripheral operating in slave mode?

I'm using the STM32U575 MCU with a few peripherals, and am clocking it down to 16MHz for SYSCLK and all related peripherals.  I'm generating a decent amount of data by running the ADC at 20ksps. (TIM2 triggering ADC4, which drops samples into memory using DMA, and have verified output using a 1Mbaud UART.)  However, what I really need is for this data to come out of the SPI peripheral, and more specifically, synced to an external clock.  I figured the SPI in slave mode would be a good fit.

I've been able to get some test byte arrays out of the SPI peripheral when the SCLK line is receiving 200kHz, however, this starts falling apart at 300kHz.  Falling apart means things like the TX complete callback not firing or the clock and data starting to incur some glitches.  

The setup is the U575 Nucleo board, and the clock source is an external signal generator set to 3.3V square wave.  

As an extra data point, I tried increasing the clock of the U575 to 160MHz (10x), and found that it works at 2MHz and stops working at 3MHz (10x jumps respectively).  

Is there something within the SPI peripheral that limits the speed of the incoming clock?

Seth K

DS will contain an exact figure, but for older STM32 the ultimate limit is half the SPI's core clock.

I don't know how can clock glitch in slave mode. If MISO data glitch between bytes (frames) then it's inadequately written software, or internal bus limitation. E.g. you cannot throw some Cube/HAL code on it and expect it to have high performance.


Chief II

from ds :


so limit for clock/slave is 100 M.

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

so limit for clock/slave is 100 M

OK, so clearly it's capable of going faster than I'm finding. So what's actually causing this issue?

I also double checked the conditions listed in the datasheet:

  1. Output speed set to high (I had set this to low previously, as the high drive + oscope probe was causing reflections and double clocking.  I tried all 4 settings, no luck.)
  2. Capacitive load is there, but minimal.  (Not sure how I would reduce this further without ditching the Nucleo for my own board.)
  3. Measurement points (not applicable)
  4. I/O compensation cell: I found that setting and have activated it.  No luck.
  5. HSLV Activated for VDD < 2.7V  (Not applicable, as VDD=3.3V)

Anything else to check?

Seth K
Pavel A.
Evangelist III

without ditching the Nucleo for my own board

So on a Nucleo, with flying wires (?) you want > 3 MHz? Hmm.

If it could be isolated to the fact that it's on a Nucleo board w/ flying wires, then I'd be ok with spending the effort of making my own board.  

However, I can't get it to work at 2MHz with a SYSCLK of 16MHz.  The highest I can get it to work is 200kHz. And I'm able to scale that number from 200kHz to 2MHz by doing a similar jump of 10x in SYSCLK (which then puts me over my power budget). But that makes me think it's a setting issue or a fundamental limitation which I'm not aware of.  

So I'm trying to find that setting and/or fundamental issue. 

Seth K

I don't know how can clock glitch in slave mode. If MISO data glitch between bytes (frames) then it's inadequately written software, or internal bus limitation. E.g. you cannot throw some Cube/HAL code on it and expect it to have high performance.



So, I'm trying to find out what that internal bus limitation is.  Either to change it (if possible) or to work around it.  I'll post code and scope-shots later today.

Seth K

just for info:  i run a spi on H743 (200M core clk) at 48Mbit , scaling down to 16M core -> 4 Mbit should work here.

on you U5 maybe even more, as ds states 100M .

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

Here's what runs in USER CODE block 2 in main.c

void setup(void)
	//Fill this buffer for debug purposes.  Otherwise can be commented out = 0xAA00FFAA; = 0xFF003355;
	int ii=0;
	for (ii=0; ii<TEST_BUF_DATA_LENGTH; ii++){[ii]=ii;
	HAL_ADC_Start_DMA(&hadc4, (uint32_t *)adc_buf.words, ADC_BUF_LEN);
	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);

 And here's what runs in the while(1) loop of main.c in USER CODE WHILE.

void loop(void)
	//Turns on the green LED to show that it's ready for clock.  Debug only.
	LED_GREEN_GPIO_Port->BSRR = (uint32_t)LED_GREEN_Pin;  

	HAL_SPI_Transmit_DMA(&hspi3, test_buf.bytes, TEST_BUF_LENGTH);

I have also enabled the TX complete callback:

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi){
	// Turn off the Green LED, mimicking the enable pin for the clock.
	LED_GREEN_GPIO_Port->BRR = (uint32_t)LED_GREEN_Pin;  //Debug purposes only

 For completeness, here is the test buffer definition:


	struct {
		uint32_t header;
		union {
			uint16_t words[TEST_BUF_DATA_LENGTH];
			uint8_t  bytes[TEST_BUF_DATA_LENGTH*2];
		uint32_t footer;
	uint8_t bytes[TEST_BUF_LENGTH];

SPI Settings:

static void MX_SPI3_Init(void)

  /* USER CODE BEGIN SPI3_Init 0 */

  /* USER CODE END SPI3_Init 0 */

  SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};

  /* USER CODE BEGIN SPI3_Init 1 */

  /* USER CODE END SPI3_Init 1 */
  /* SPI3 parameter configuration*/
  hspi3.Instance = SPI3;
  hspi3.Init.Mode = SPI_MODE_SLAVE;
  hspi3.Init.Direction = SPI_DIRECTION_2LINES_TXONLY;
  hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi3.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi3.Init.NSS = SPI_NSS_SOFT;
  hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi3.Init.CRCPolynomial = 0x7;
  hspi3.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi3.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi3.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi3.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi3.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi3.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi3.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  hspi3.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
  hspi3.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
  if (HAL_SPI_Init(&hspi3) != HAL_OK)
  HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;
  HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP2_LPDMA_CH0_TCF_TRG;
  HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;
  if (HAL_SPIEx_SetConfigAutonomousMode(&hspi3, &HAL_SPI_AutonomousMode_Cfg_Struct) != HAL_OK)
  /* USER CODE BEGIN SPI3_Init 2 */

  /* USER CODE END SPI3_Init 2 */



At 200kHz I get the correct frame out, and the callback triggers, sending channel 3 (PSEUDO_EN aka Green LED) low.

Correct Frame at 200k.png

At 300kHz, I still get the correct frame out, but with 2 noticeable behaviors.  First, channel 3 doesn't go low.  Second, I get an extra high bit(s) after the end of the frame.  

Correct frame, but no CB at 300k.png


Seth K