2018-04-11 01:57 PM
Hello, I am wanting to change the clock speed during runtime. I have an area of the code where I want to enter a lower power mode, shut down all peripherals, and wait for an external interrupt to occur.
The specific chip I am using is the STM32F303CCT6 and the STM32F301C6, and
I am using STM32CubeMX to generate the projects. Here are my current clock settings from STM32CubeMX:And here is the code it generated in the project:
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_PeriphCLKInitTypeDef PeriphClkInit;/**Initializes the CPU, AHB and APB busses clocks
*/ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = 16; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); }/**Initializes the CPU, AHB and APB busses clocks
*/ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{ _Error_Handler(__FILE__, __LINE__); }PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_TIM16|RCC_PERIPHCLK_TIM17
|RCC_PERIPHCLK_ADC1; PeriphClkInit.Tim16ClockSelection = RCC_TIM16CLK_HCLK; PeriphClkInit.Tim17ClockSelection = RCC_TIM17CLK_HCLK; PeriphClkInit.Adc1ClockSelection = RCC_ADC1PLLCLK_DIV1;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{ _Error_Handler(__FILE__, __LINE__); }/**Configure the Systick interrupt time
*/ HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);/**Configure the Systick
*/ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);}I was hoping I would be able to change the clock settings in STM32CubeMX by just typing in a ''1'' in ''HCLK(MHz)'', generate the code, then copy the clock settings, then revert back to 64MHZ. Then paste the clock settings in a new function and call that, thus switching to the 1MHZ clock setting. It doesn't seem to be that easy though?
What would be the best way to change back and forth from 64MHz and 1MHz? Any help or advice is greatly appreciated, thanks!
2018-08-07 10:58 AM
Hello, checking back in on this. Can anyone offer me any advice?
2018-08-07 12:08 PM
Use the RCC SW bits to select the HSI as the primary source, from the PLL
Wait for that to shift via SWS bits
Turn off the PLL
Drop the flash latency to 0
Set the AHB Divider to 8
Disable clocks on peripherals
Set the flash latency to 2
Set the AHB Divider to 1
Turn on the PLL
Wait for that to spin up
Select the PLL clock as the primary source
Wait for that to shift
Enable clocks on peripherals
2018-08-07 01:22 PM
Hi @Community member , thanks for the info! Wow that sounds complicated just to switch clock speeds. You don't by chance have any examples or code for this do you? I'm using the HAL libraries, I have never referenced any registers before, only HAL functions.
2018-08-07 02:13 PM
The HAL hides a lot of complexity. Jamming values into the configuration structure might accomplish the same, but I suspect it will be a lot more circuitous internally.
You'd still need to realign all the peripherals to the new clocks.
2018-08-07 02:54 PM
@Community member Is there an easier way to just change the PLL multiplyer from x16 to x2? I'm not understanding what you have been mentioning.
2018-08-07 03:49 PM
It has to turn the PLL off to change frequency, and synchronous machine needs a clock, so the HAL code will need to dance around behind the API. The HSI is running at 8 MHz, just saying you can switch to that.
You can also just change the AHB divider.
The thing that eats the most power is a while(1) loop doing nothing useful. Consider using __WFI() in these loops so it suspends operation until the next interrupt comes along.
2018-08-08 02:21 PM
@Community member , I think I got it working, however, my timers don't work anymore, even after re-initializing and re-starting them. Do you have any pointers for that? Thanks again for your help, and here is what I did that *appears to be working:
void Slow_Down_Clock()
{
uint16_t timeout;
/* Enable HSI clock */
RCC->CR |= RCC_CR_HSION;
/* Wait till HSI is ready */
timeout = 0xFFFF;
while (!(RCC->CR & RCC_CR_HSIRDY) && timeout--);
/* Select HSI clock as main clock */
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_SW)) | RCC_CFGR_SW_HSI;
/* Disable PLL */
RCC->CR &= ~RCC_CR_PLLON;
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV4;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_TIM16|RCC_PERIPHCLK_TIM17
|RCC_PERIPHCLK_ADC1;
PeriphClkInit.Tim16ClockSelection = RCC_TIM16CLK_HCLK;
PeriphClkInit.Tim17ClockSelection = RCC_TIM17CLK_HCLK;
PeriphClkInit.Adc1ClockSelection = RCC_ADC1PLLCLK_DIV1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
Speed clock back up:
void Speed_Up_Clock()
{
uint16_t timeout;
/* Enable HSI clock */
RCC->CR |= RCC_CR_HSION;
/* Wait till HSI is ready */
timeout = 0xFFFF;
while (!(RCC->CR & RCC_CR_HSIRDY) && timeout--);
/* Select HSI clock as main clock */
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_SW)) | RCC_CFGR_SW_HSI;
/* Disable PLL */
RCC->CR &= ~RCC_CR_PLLON;
SystemClock_Config(); //This is the STM32 generated function to configure the clock originally at program start
}
I did some printf functions before slowing it down, and after speeding it back up, and the UART still dumps characters to the screen, so I think this part is working? However, I can't get the timers to start back up? I am also using other peripherals such as ADC, DAC, external interrupts, etc.