cancel
Showing results for 
Search instead for 
Did you mean: 

SMPS Clock throttling requirement to initialize

rsoc
Associate III

So, I need to implement SMPS for more efficient power use on my STM32L452 MCU. From what i understand, the clock needs to be brought down to 24 MHz prior to any SMPS actions. I am operating the system at 80 MHz, so I need to bring it down to 24 and then back up to 80 when I am done with the SMPS initializations on startup. I also am running shutdown mode, which cant operate with the SMPS running, so I need to do the same clock manipulations to de-init the SMPS before going into shutdown mode.

I am pretty sure that I implemented the code correctly, because when my code fully executes i see the proper current being pulled by the MCU. However, every so often the code gets hung up on my clock change functions. I have provided the 24 MHz function (giving both took too much space) below. I think it gets stuck on the while loops (I cant fully debg as i am in and out of shutdown mode which kicks out the debugger), where the setting of the requite clock sources does not register correctly. Am I missing something somewhere? Moreover, is my understanding of SMPS setting correct? It seems crazy to me to change the system clock in real time...

/**
  * @brief  System Clock Configuration to 24 MHz
  *         The system Clock is configured as follows :
  *            System Clock source            = PLL (MSI)
  *            SYSCLK(Hz)                     = 24000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 1
  *            APB2 Prescaler                 = 1
  *            MSI Frequency(Hz)              = 4000000
  * @param  None
  * @retval None
  */
uint32_t SystemClock_Config_24MHz(void)
{
  uint16_t init_cycles = 0;
  uint32_t c_SystemCoreClock = 0;
  ////////////////////////////////////////////////////////////////////////////
  // Configure the system clock, which touches 
  // * FLASH->ACR
  // * RCC->CR
  // * RCC->PLLCFGR
  // * RCC->CFGR
  ////////////////////////////////////////////////////////////////////////////
  
  /////////////////////////////////////////////
  // Set flash latency to 4 wait states
  // [RMM0394, 3.7.1]
  /////////////////////////////////////////////
      
  FLASH->ACR &= ~(FLASH_ACR_LATENCY);
  FLASH->ACR |= (FLASH_ACR_LATENCY_4WS);   
 
  /////////////////////////////////////////////
  // turn on the MSI (multi-speed internal) clock
  // and wait for it to enable
  // [RMM0394, 6.4.1]
  /////////////////////////////////////////////
  
  RCC->CR |= RCC_CR_MSION;   // was: LL_RCC_MSI_Enable();
  
  while((RCC->CR & RCC_CR_MSION) != RCC_CR_MSION)
  {
    RCC->CR |= RCC_CR_MSION;
    init_cycles++;
  }
  
  /////////////////////////////////////////////
  // configure the PLL [RMM0394, 6.4.4]
  // * PLL source is MSI clock
  // * PLLM=1 (main division factor)
  // * PLLN=24 (main PLL multiplication factor for VCO)
  // * PLLR=4 (Main PLL division factor for PLLCLK (system clock)) 
  // was: LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_MSI, LL_RCC_PLLM_DIV_1, 24, LL_RCC_PLLR_DIV_2);
  /////////////////////////////////////////////
      
  uint32_t tmp_PLLCFGR = RCC->PLLCFGR;
  
  tmp_PLLCFGR &= ~(0x3 << 0);    // clear bits 1:0
  tmp_PLLCFGR |= (1 << 0);       // 01 in bits 1:0 makes the source MSI clock
  
  tmp_PLLCFGR &= ~(0x7 << 4);    // clear bits 6:4, this makes the main division factor PLLM=1 
 
  tmp_PLLCFGR &= ~(0x7F << 8);   // clear bits 14:8
  tmp_PLLCFGR |= (24 << 8);      // 24 in bits 14:8 makes main PLL multiplication factor for VCO, PLLN=24
  
  tmp_PLLCFGR &= ~(0x3 << 25);   // clear bits 26:25, this makes the Main PLL division factor for PLLCLK (system clock), PLLR = 2
  tmp_PLLCFGR |= (1 << 25);   // set bit 25 to 1, this makes the Main PLL division factor for PLLCLK (system clock), PLLR = 4
  
  RCC->PLLCFGR = tmp_PLLCFGR;
      
  /////////////////////////////////////////////
  // set PLLON to enable the main PLL [RMM0394, 6.4.1]
  // was: LL_RCC_PLL_Enable();
  /////////////////////////////////////////////
  
  RCC->CR |= (1 << 24);
  
  /////////////////////////////////////////////
  // turn on the Main PLL PLLCLK output enable
  // and wait for RCC->CR.PLLRDY to indicate it is ready
  // [RMM0394, 6.4.4]
  // was: LL_RCC_PLL_EnableDomain_SYS
  /////////////////////////////////////////////
  
  RCC->PLLCFGR |= (0x1 << 24);
  
  while((RCC->CR & (0x1 << 25)) != (0x1 << 25))
  {
    init_cycles++;
  }
  
  /////////////////////////////////////////////
  // Set AHB prescaler (RCC->CFGR.HPR) to no divisor from SYSCLK [RMM0394, 6.4.3]
  // was:LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
  /////////////////////////////////////////////   
 
  RCC->CFGR &= ~(0xF << 4);    // clearing bits 7:4 makes SYSCLK not divided (divisor=1)
 
  /////////////////////////////////////////////   
  // make the PLL the new SYSCLK source [RMM0394, 6.4.3]
  // and wait for the SWS report to indicate the switch has been made
  // was: LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
  /////////////////////////////////////////////   
  
  RCC->CFGR &= ~(0x3 << 0);    // clear bits 1:0
  RCC->CFGR |= (0x3 << 0);       // 11 in bits 1:0 makes the source the PLL
  
  while((RCC->CFGR & (0x3 << 2)) != (0x3<<2))  // bits 3:2 is the SWS, system clock status; will be 3 when PLL is active
  {
    init_cycles++;
  }
  
  /////////////////////////////////////////////   
  // Set APB1 & APB2 prescaler [RMM0394, 6.4.3]
  // was: LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1), LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
  /////////////////////////////////////////////   
 
  RCC->CFGR &= ~(0x7 << 8);    // clear bits 10:8, PPRE1,which makes HCLK not divided for APB1 clock (PCLK1)
  RCC->CFGR &= ~(0x7 << 11);    // clear bits 13:11, PPRE2,which makes HCLK not divided for APB2 clock (PCLK2)
  
  /////////////////////////////////////////////   
  // so, our MSI is used as the source for the PLL
  // this defaults to 4mhz
  // this is divided by M (1)
  // multiplied by N (24)
  // and then divided by R (4)
  // to result in a PLLCLK of 24mhz
  // and PLLCLK is selected as the SYSCLK
  /////////////////////////////////////////////   
  
  c_SystemCoreClock = 24000000;  // 24MHz
  
  ///////////////////////////////////////////////////////
  // initialize the system tick
  // Configure the source of time base considering new system clocks settings
  ///////////////////////////////////////////////////////    
  
  uint32_t ticks = c_SystemCoreClock / 1000;        
  SysTick->LOAD  = (uint32_t)(ticks - 1UL); // set reload register    
  SysTick->VAL   = 0UL;   //  Load the SysTick Counter Value 
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; 
 
  return c_SystemCoreClock;
}

2 REPLIES 2

Are you calling this function while runing at 80MHz, i.e. from PLL?

> FLASH->ACR &= ~(FLASH_ACR_LATENCY);

After this, the FLASH read latency is 0 WS. If you enter this routine with clock running fast, it may fail because of this.

Also, you start to change PLL without first switching system clock to some known running stable clock, and switching PLL off. I am not sure this would work.

> I cant fully debg as i am in and out of shutdown mode which kicks out the debugger)

So use the good old wiggling-pin-observed-by-oscilloscope-or-LA method.

JW

rsoc
Associate III

I call FLASH->ACR &= ~(FLASH_ACR_LATENCY) while running at 80 MHz yes. But immediately after setting the latency to zero, i set it to four. So that shouldn't be it right?

As for PLL. i initialize the clock to a stable 80 MHz before doing the SMPS jazz, so is that not starting it from a stable perspective?

Sorry for the delay in response, I got caught up in other bugs. Appreciate any insight you have