关于内外时钟切换及时钟超频测试

发布时间:2024-03-19  

前言


近日,有群友困于STM32的时钟系统。这里就详细介绍一下关于内外时钟切换及时钟超频测试,希望对大家能有所帮助


46be059a-75f1-11ed-8abf-dac502259ad0.png


诚然,当使用固件库时,把外部晶振摘掉,系统确实会自动切换到内部时钟,但是只会以8M的默认值运行,显然这是十分不可行的,8M的速度直接让我们的STM32病入膏肓,今天的任务就是让STM32失去外挂(晶振)时,依旧可以激情澎湃。


时钟详解这里不过多介绍,自己也没有别人介绍的好,本文旨在解决现实问题。


此处插播广告:群友问过这种问题,外部接8M晶振和16M晶振有啥区别?


以我微薄的经验来看,这两个在用的时候差别不大,如果使用ST的固件库(以STM32F103为例),使用8M的晶振会更方便,不用改任何代码,时钟就是72M的全速运行状态。如果用16M晶振,则需要修改代码:


在stm32f10x.h中修改宏定义HSE_VALUE ((uint32_t)8000000)为HSE_VALUE ((uint32_t)16000000)。


46e8dcb6-75f1-11ed-8abf-dac502259ad0.png


之后进入system_stm32f10x.c,将RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);改为RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSI_Div2| RCC_CFGR_PLLMULL9);此处是将输入时钟二分频为8M,再进行9倍频到72M,和使用了8M没区别。


如果不进行该二分频操作,时钟还是有的,但是会以16M为基准进行9倍频到144M,此时单片机以超频模式运行,也是可以运行的。但是,时钟的精准性不能得到保证。


470d387c-75f1-11ed-8abf-dac502259ad0.png


系统的时钟可以通过添加代码在debug模式下显示:


RCC_ClocksTypeDef ClockInfo;RCC_GetClocksFreq(&ClockInfo);


4733359a-75f1-11ed-8abf-dac502259ad0.png


通过debug模式下观察ClockInfo的值,便可知道此时系统时钟速度:


4766a934-75f1-11ed-8abf-dac502259ad0.png


4766a934-75f1-11ed-8abf-dac502259ad0.png


这里提一下,在使用外部晶振的情况下,ST即使是超频,依旧发挥稳定,不得不夸一下ST的质量。


此时我将我的开发板以8M的基准倍频16倍,得到128M的主频,使用定时器定时10us,示波器测试无误差。串口通信无误。


47d370dc-75f1-11ed-8abf-dac502259ad0.png


48011564-75f1-11ed-8abf-dac502259ad0.png


48323c2a-75f1-11ed-8abf-dac502259ad0.png


48c47860-75f1-11ed-8abf-dac502259ad0.png


以72M的主频跑128依旧稳定,赞一个,因为我的外部晶振只有8M最大只能倍频到128,如果使用外部16M,不知继续倍频可以到多少。不过性能还是很好的。


预留测试GD32的效果:


写本文时,将GD的GD32E230翻出来进行了同样的测试,因为GD的倍频器倍数较高,我已经倍频到144M(标准72M),测试定时器依旧稳定。


48e53c1c-75f1-11ed-8abf-dac502259ad0.png


广告很长,请忍一下:


上半场结束,下半场继续:


此处歪解一下时钟的问题,之前有群友很疑惑单片机的低功耗和时钟的关系,疑惑高速的时钟会不会增加MCU的功耗,为啥低功耗要降低时钟速度。这里讲解一下:


可以用用单位时间内执行的指令来看,高速时钟在单位时间内使系统跑了更多的指令,而低速时钟单位时间内跑的少,而单片机是直线结构,内核是不会休息的,功耗就看执行的指令多少。而单片机的低功耗就是降低时钟,让单片机跑慢点。就像人一样,低功耗相当于你不跑了,原地休息,但是你的心跳不会停止,你还是得消耗能量,即使再少还得消耗。


就像人一样,时钟就相当于心跳,只要还活着就得消耗能量,你要想跑得快,心脏就得跳得快,跳得越快能量消耗越高,即使你去睡觉,心跳只要不停止,你还得消耗能量,如果心跳没了,整个人就没了,MCU也就宕机了。所以,在处理低功耗时最先解决的就是时钟频率,只有降低了时钟的频率,才能真正降低功耗。关于单片机进入低功耗和唤醒,以及降低整体运行功耗我看能不能在下文讲解,近期刚好做了一个低功耗的项目,这里留悬念吧。


广告结束,正文开始,不好意思,有点喧宾夺主了哈!


回到主题,为了解决时钟切换的问题,才有了这个帖子,上文全属歪楼,为最近开发时的经验总结。


我们在使用STM32103的固件库时,时钟配置在system_stm32f10x.c中,但是只是对外部晶振做了初始化,而对于内部时钟并没有添加代码,如果你的MCU没有外部晶振,当系统运行时是先启动内部时钟,然后会检测外部晶振,如果没有检测到晶振,系统便以内部的8M继续运行,这是不合理的。


4907446a-75f1-11ed-8abf-dac502259ad0.png

这里可以看到,如果外部启动失败,会进入这个else,但是这个else中并未添加任何代码,所以只会用8M的内钟执行,我们要做的就是在else中添加外部启动失败的代码:


  /* 开启HSI 即内部晶振时钟 */

  RCC->CR |= (uint32_t)0x00000001; 

  /*选择HSI为PLL的时钟源HSI必须2分频给PLL*/

  RCC->CFGR |= (uint32_t)RCC_CFGR_PLLSRC_HSI_Div2; 

         

  /*PLLCLK=8/2*13=52MHz 设置倍频得到时钟源PLL的频率*/

  RCC->CFGR |= (uint32_t)RCC_CFGR_PLLMULL12;

  /* PLL不分频输出*/

  RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

   

  /* 使能 PLL时钟 */

  RCC->CR |= RCC_CR_PLLON;

  /* 等待PLL时钟就绪*/

  while((RCC->CR & RCC_CR_PLLRDY) == 0)

  {

  }

  /* 选择PLL为系统时钟的时钟源 */

  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));

  RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;  

  /* 等到PLL成为系统时钟的时钟源*/

  while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)

  {

}


该代码填充后如果检测到有外部时钟,便以外部时钟为基准进行时钟的倍频处理,达到用户想要的时钟频率,如果你的MCU没有外部时钟,则会执行else内部的代码,将时钟源切换到内部时钟并进行倍频,如此便达到了自动检测时钟的目的。


问题:这是我根据STM32F031的时钟切换代码演变来的,但是这个只能用于主频小于或等于48M时使用,如果倍频因子超过12,也就是主频超过48M是,就会出现硬件错误,直接卡死。当需要更高的主频时就需要如下配置。


在else里面最开头添加:


  /* Enable Prefetch Buffer */

  FLASH->ACR |= FLASH_ACR_PRFTBE;

  /* Flash 2 wait state */

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

FLASH->ACR|=(uint32_t)FLASH_ACR_LATENCY_2;


492c0bec-75f1-11ed-8abf-dac502259ad0.png


问:如果我的MCU有晶振,但是我不想用外部,就想用内部,如何处理呢?


答:打一顿就好了,有外部不用干啥用内部呢?


上述纯属恶搞自己,被坑过……


因为内部时钟不准!!!测试内部时钟在使用定时器时会有偏差,本人在此吃过亏。此问题在STM32F031和GD32E230中均有体现。但是USART和SPI通信是正常的,即使我用的2.5M波特率的USART和8M的SPI。


解决办法,上述代码不用动,添加如下代码。


4980dbcc-75f1-11ed-8abf-dac502259ad0.png


通过注释原文RCC->CR |= ((uint32_t)RCC_CR_HSEON);并添加RCC->CR &= ~((uint32_t)RCC_CR_HSEON);可默认之以内部时钟方式启动。 注意:在主函数加上


SystemInit();函数哦!!! 最终代码如下:

static void SetSysClockTo72(void)

{



__IO uint32_t StartUpCounter = 0, HSEStatus = 0;






/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/  



/* Enable HSE */  



//RCC->CR |= ((uint32_t)RCC_CR_HSEON);



  /*取消改行注释并注释上文,可默认启动内部时钟*/



  RCC->CR &= ~((uint32_t)RCC_CR_HSEON);



/* Wait till HSE is ready and if Time out is reached exit */



do



{



  HSEStatus = RCC->CR & RCC_CR_HSERDY;



  StartUpCounter++;



} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));







if ((RCC->CR & RCC_CR_HSERDY) != RESET)



{



  HSEStatus = (uint32_t)0x01;



}



else



{



  HSEStatus = (uint32_t)0x00;



}







if (HSEStatus == (uint32_t)0x01)



{



  /* Enable Prefetch Buffer */



  FLASH->ACR |= FLASH_ACR_PRFTBE;







  /* Flash 2 wait state */



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



  FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;  











  /* HCLK = SYSCLK */



  RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;



  



  /* PCLK2 = HCLK */



  RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;



  



  /* PCLK1 = HCLK */



  RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;







#ifdef STM32F10X_CL



  /* Configure PLLs ------------------------------------------------------*/



  /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */



  /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */



  



  RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |



          RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);



  RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |



         RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);






  /* Enable PLL2 */



  RCC->CR |= RCC_CR_PLL2ON;



  /* Wait till PLL2 is ready */



  while((RCC->CR & RCC_CR_PLL2RDY) == 0)



  {



  }



  



 



  /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ 



  RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);



  RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | 



          RCC_CFGR_PLLMULL9); 



#else  



  /*PLL configuration: PLLCLK = HSE * 9 = 72 MHz */



  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |



              RCC_CFGR_PLLMULL));



  RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL16);



#endif /* STM32F10X_CL */







  /* Enable PLL */



  RCC->CR |= RCC_CR_PLLON;







  /* Wait till PLL is ready */



  while((RCC->CR & RCC_CR_PLLRDY) == 0)



  {



  }



  



  /* Select PLL as system clock source */



  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));



  RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;  







  /* Wait till PLL is used as system clock source */



  while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)



  {



  }



}



else





  /* Enable Prefetch Buffer */



  FLASH->ACR |= FLASH_ACR_PRFTBE;



  /* Flash 2 wait state */



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



  FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    



   /* 开启HSI 即内部晶振时钟 */



  RCC->CR |= (uint32_t)0x00000001; 







  /*选择HSI为PLL的时钟源HSI必须2分频给PLL*/



  RCC->CFGR |= (uint32_t)RCC_CFGR_PLLSRC_HSI_Div2; 







         



  /*PLLCLK=8/2*13=52MHz 设置倍频得到时钟源PLL的频率*/



  RCC->CFGR |= (uint32_t)RCC_CFGR_PLLMULL16;







  /* PLL不分频输出*/



  RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;



   



  /* 使能 PLL时钟 */



  RCC->CR |= RCC_CR_PLLON;







  /* 等待PLL时钟就绪*/



  while((RCC->CR & RCC_CR_PLLRDY) == 0)



  {



  }











  /* 选择PLL为系统时钟的时钟源 */



  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));



  RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;  







  /* 等到PLL成为系统时钟的时钟源*/



  while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)



  {







  }



}



}

在STM32F030或者STM32F031中,同样可以做类似操作:


复制

static void SetSysClock(void)

{



__IO uint32_t StartUpCounter = 0, HSEStatus = 0;






/* SYSCLK, HCLK, PCLK configuration ----------------------------------------*/



/* Enable HSE */ 







RCC->CR |= ((uint32_t)RCC_CR_HSEON);



  //修改为内部晶振  



//  RCC->CR &= ~((uint32_t)RCC_CR_HSEON);



 



/* Wait till HSE is ready and if Time out is reached exit */



do



{



  HSEStatus = RCC->CR & RCC_CR_HSERDY;



  StartUpCounter++;



} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));







if ((RCC->CR & RCC_CR_HSERDY) != RESET)



{



  HSEStatus = (uint32_t)0x01;



}



else



{



  HSEStatus = (uint32_t)0x00;



}







if (HSEStatus == (uint32_t)0x01)



{



  /* Enable Prefetch Buffer and set Flash Latency */



  FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;



 



  /* HCLK = SYSCLK */



  RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;



  



  /* PCLK = HCLK */



  RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;







  /* PLL configuration = HSE * 6 = 48 MHz */



  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));



  RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL7);



    



  /* Enable PLL */



  RCC->CR |= RCC_CR_PLLON;







  /* Wait till PLL is ready */



  while((RCC->CR & RCC_CR_PLLRDY) == 0)



  {



  }







  /* Select PLL as system clock source */



  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));



  RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;  







  /* Wait till PLL is used as system clock source */



  while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)



  {



  }



}



else



{ /* If HSE fails to start-up, the application will have wrong clock 



   configuration. User can add here some code to deal with this error */



       // HSI 内部时钟做为PLL时钟源并配置PLL 56M做为系统时钟



  /* Enable Prefetch Buffer and set Flash Latency */



  FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;







  /* HCLK = SYSCLK */



  RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;







  /* PCLK = HCLK */



  RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;







  // PLL configuration = (HSI/2) * 12 = 48 MHz



  RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_14); // 8M/2 * 14 = 56M







  /* Enable PLL */



  RCC->CR |= RCC_CR_PLLON;

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>