cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 series spread-spectrum clock generation (SSCG)

Stassen.C
ST Employee

Summary

The main objective of this article is to present the STM32H7 spread-spectrum clock generation (SSCG) feature. STM32H7 differs from other series by using PLL fractional with Timer/DMA.  For an overall introduction to spread-spectrum clock generation, refer to Application note 4850.

 

1. STM32H7 SSCG implementation using PLL fractional with Timer/DMA 

STM32H7 series have a PLL fractional (phase-locked loop) which can be updated from a DMA buffer driven by a timer.

 

Figure 1. STM32H7 PLL fractional block diagram from the reference manuals PLL description chapterFigure 1. STM32H7 PLL fractional block diagram from the reference manuals PLL description chapter

The STM32H7 product series does not have a built-in spread-spectrum clock generation feature.

It uses Timer/DMA as a method instead. The idea is to use a table with predetermined values which will represent the type of modulation needed (for example a triangular waveform). We use this table and inject the values into the sigma delta modulator of the PLL. With the appropriate setup and sequence, the frequency of the clock changes over time. Figure 3 below shows an example.

 

Figure 2. Timer/DMA technique overviewFigure 2. Timer/DMA technique overview

 

Buffer containing the modulating signal declaration:

uint32_t PLLCFGR_Buffer[2]  ={ 
0x70008, /*Value for PLLFCT OFF  */
0x70008  /*Value for PLLFCT ON */
};

uint32_t PLL1FRACR_Buffer[32]  ={
0x0000<<3,0x01FF<<3,0x03FF<<3,0x05FF<<3,0x07FF<<3,0x09FF<<3,0x0BFF<<3,0x0DFF<<3,
0x0FFF<<3,0x11FF<<3,0x13FF<<3,0x15FF<<3,0x17FF<<3,0x19FF<<3,0x1BFF<<3,0x1DFF<<3,                                   	  0x1FFF<<3,0x1DFF<<3,0x1BFF<<3,0x19FF<<3,0x17FF<<3,0x15FF<<3,0x13FF<<3,0x11FF<<3,				  0x0FFF<<3,0x0DFF<<3,0x0BFF<<3,0x09FF<<3,0x07FF<<3,0x05FF<<3,0x03FF<<3,0x01FF<<3
};

Firmware example to start the SSCG with the PLL fractional / timer and DMA peripherals:

  /* Enable BDMA clock */
  __HAL_RCC_BDMA_CLK_ENABLE();

  /* Configure the DMA handler for Transmission process     */
  /* DMA mode is set to circular for an infinite DMA transfer */
  DMA_Handle.Instance                 = BDMA_Channel0;
  DMA_Handle.Init.Request             = BDMA_REQUEST_GENERATOR0;
  DMA_Handle.Init.Direction           = DMA_MEMORY_TO_PERIPH;
  DMA_Handle.Init.PeriphInc           = DMA_PINC_DISABLE;
  DMA_Handle.Init.MemInc              = DMA_MINC_ENABLE;
  DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  DMA_Handle.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
  DMA_Handle.Init.Mode                = DMA_CIRCULAR;
  DMA_Handle.Init.Priority            = DMA_PRIORITY_LOW;
  DMA_Handle.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
  DMA_Handle.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  DMA_Handle.Init.MemBurst            = DMA_MBURST_SINGLE;
  DMA_Handle.Init.PeriphBurst         = DMA_PBURST_SINGLE;

  /* Initialize the DMA with for Transmission process */
  HAL_DMA_Init(&DMA_Handle);

  /* Select Callbacks functions called after Transfer complete and Transfer error */
  HAL_DMA_RegisterCallback(&DMA_Handle, HAL_DMA_XFER_CPLT_CB_ID, NULL);
  HAL_DMA_RegisterCallback(&DMA_Handle, HAL_DMA_XFER_ERROR_CB_ID, HAL_TransferError);

  /* NVIC configuration for DMA transfer complete interrupt*/
  HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn);

  /*##-3- Configure and enable the DMAMUX Request generator  ####################*/
  dmamux_ReqGenParams.SignalID  = HAL_DMAMUX2_REQ_GEN_LPTIM2_OUT; /* External request signal is LPTIM2 signal */
  dmamux_ReqGenParams.Polarity  = HAL_DMAMUX_REQ_GEN_RISING;      /* External request signal edge is Rising  */
  dmamux_ReqGenParams.RequestNumber = 1;                          /* 1 requests on each edge of the external request signal  */

  HAL_DMAEx_ConfigMuxRequestGenerator(&DMA_Handle, &dmamux_ReqGenParams);

  /* NVIC configuration for DMAMUX request generator overrun errors*/
  HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);

  HAL_DMAEx_EnableMuxRequestGenerator (&DMA_Handle);


  DMA_Handle2.Instance                 = BDMA_Channel1;

  DMA_Handle2.Init.Request             = BDMA_REQUEST_GENERATOR1;
  DMA_Handle2.Init.Direction           = DMA_MEMORY_TO_PERIPH;
  DMA_Handle2.Init.PeriphInc           = DMA_PINC_DISABLE;
  DMA_Handle2.Init.MemInc              = DMA_MINC_ENABLE;
  DMA_Handle2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  DMA_Handle2.Init.MemDataAlignment    = DMA_MDATAALIGN_WORD;
  DMA_Handle2.Init.Mode                = DMA_CIRCULAR;
  DMA_Handle2.Init.Priority            = DMA_PRIORITY_LOW;
  DMA_Handle2.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
  DMA_Handle2.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  DMA_Handle2.Init.MemBurst            = DMA_MBURST_SINGLE;
  DMA_Handle2.Init.PeriphBurst         = DMA_PBURST_SINGLE;

  /* Initialize the DMA with for Transmission process */
  HAL_DMA_Init(&DMA_Handle2);

  /* Select Callbacks functions called after Transfer complete and Transfer error */
  HAL_DMA_RegisterCallback(&DMA_Handle2, HAL_DMA_XFER_CPLT_CB_ID, NULL);
  HAL_DMA_RegisterCallback(&DMA_Handle2, HAL_DMA_XFER_ERROR_CB_ID, HAL_TransferError);

  /* NVIC configuration for DMA transfer complete interrupt*/
  HAL_NVIC_SetPriority(BDMA_Channel1_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(BDMA_Channel1_IRQn);

  /*##-3- Configure and enable the DMAMUX Request generator  ####################*/
  dmamux_ReqGenParams.SignalID  = HAL_DMAMUX2_REQ_GEN_LPTIM2_OUT; /* External request signal is LPTIM2 signal */
  dmamux_ReqGenParams.Polarity  = HAL_DMAMUX_REQ_GEN_FALLING;      /* External request signal edge is Rising  */
  dmamux_ReqGenParams.RequestNumber = 2;                          /* 2 requests on each edge of the external request signal  */

  HAL_DMAEx_ConfigMuxRequestGenerator(&DMA_Handle2, &dmamux_ReqGenParams);

  /* NVIC configuration for DMAMUX request generator overrun errors*/
  HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);

  HAL_DMAEx_EnableMuxRequestGenerator (&DMA_Handle2);


  /*##-4- Configure and enable the LPTIM2 used as DMA external request generator signal #####*/
  LPTIM_Config();

  /*##-5- Start the DMA transfer ################################################*/
  /* DMA source buffer is  SRC_BUFFER_LED1_TOGGLE containing values to be written
  to GPIOJ ODR register in order to turn LED1 On/Off each time comes a request from the DMAMUX request generator */
  HAL_DMA_Start_IT(&DMA_Handle, (uint32_t)PLL1FRACR_Buffer, (uint32_t)&RCC->PLL1FRACR, 32);
  HAL_DMA_Start_IT(&DMA_Handle2, (uint32_t)PLLCFGR_Buffer, (uint32_t)&RCC->PLLCFGR, 2);

Figure 3. Clock spectrum observation on MCO output (narrow carrier)Figure 3. Clock spectrum observation on MCO output (narrow carrier)

 

Figure 4. Clock spectrum observation on MCO output with SSCG enabled (wide carrier)Figure 4. Clock spectrum observation on MCO output with SSCG enabled (wide carrier)

 


Conclusion

Thanks to the STM32H7 PLL fractional, timer, and DMA peripheral it’s simple to implement and shape a spread-spectrum clock generation feature. This allows to reduce the peak energy on the fundamental frequency, as well as its harmonic frequencies, keeping in mind that the generated jitter shape may not be compliant with communication standard like USB.

Related links

 

Comments
Jason615
Associate

I have a couple questions about the operation of this:

 

1. In The STM32H753 reference manual, it states that when updating the FRACN register, that you need to first set the PLLxFRACEN bit to 0, then write the new value, then set the PLLxFRANEN bit to 1. Does using DMA not violate this?

 

2. This might answer question number 1, but what is the writing of the PLLCFGR register used for? The comments don't really describe the process here and I'm wondering if this has something to do with my above question or if there is something else in here that isn't documented? It seems like the values for the PLLCFGR register aren't listed in the code example you provided.

 

3. Are there any limitations on how this works on the overall system? Can I for example have this running on one PLL that is operating the SDRAM and LCD interface, while leaving it off for the rest of the system? Or is this an all or nothing application?

 

4. Are there any timing recommendations for implementing this? Any suggestions for how often the FRACn should be updated or is this something that as an end user we just have to figure out though lab testing? Obviously this can vary greatly based on the application, just asking for some rough guidance.

Thanks for your help

Jason

Stassen.C
ST Employee

Hello Jason,

You are right there is a missing information here :

  1. The timer triggers the first DMA channel to copy a value from a table describing the waveform to the PLL2FRACR.
  2. The timer triggers the second DMA channel to copy two values from memory to the RCC_PLLCFGR (two writes) to toggle the Sigma Delta latch.

The DMA channels are programmed as follows:

  • The first DMA channel operates in memory-to-peripheral mode in a circular manner, pointing to the table describing the waveform and the PLL2FRACR.
  • The second DMA channel operates in memory-to-peripheral mode in a circular manner, pointing to a two-entry table with the values to be written to the RCC_PLLCFGR with the bit reset and then set.

This setup ensures that the sequence of disabling the fractional mode, updating the value, and re-enabling the fractional mode is maintained.

Therefore, using DMA does not inherently violate the procedure as long as the sequence is correctly implemented.

And yes you can use it independently, you have 3 PLL on the STM32H753.

Lastly, we cannot really give a timing recommendation it really depends on the application.

 

Regards,
Stassen

Version history
Last update:
‎2024-12-05 06:22 AM
Updated by: