歪解单片机的时钟系统--关于内外时钟切换及时钟超频测试_16.384m可以做单片机时钟吗-程序员宅基地

技术标签: stm32  嵌入式硬件  嵌入式基础知识  单片机  

  群友问过这种问题,外部接8M晶振和16M晶振有啥区别?
       以我微薄的经验来看,这两个在用的时候差别不大,如果使用ST的固件库(以STM32F103为例),使用8M的晶振会更方便,不用改任何代码,时钟就是72M的全速运行状态。如果用16M晶振,则需要修改代码:
在stm32f10x.h中修改宏定义HSE_VALUE    ((uint32_t)8000000)为HSE_VALUE    ((uint32_t)16000000)。

之后进入system_stm32f10x.c,将RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);改为RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL9);此处是将输入时钟二分频为8M,再进行9倍频到72M,和使用了8M没区别。如果不进行该二分频操作,时钟还是有的,但是会以16M为基准进行9倍频到144M,此时单片机以超频模式运行,也是可以运行的。但是时钟的精准性不能得到保证。

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

复制

RCC_ClocksTypeDef ClockInfo;
 

RCC_GetClocksFreq(&ClockInfo);


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

这里提一下,在使用外部晶振的情况下,ST即使是超频,依旧发挥稳定,不得不夸一下ST的质量。
此时我将我的开发板以8M的基准倍频16倍,得到128M的主频,使用定时器定时10us,示波器测试无误差。串口通信无误。






以72M的主频跑128依旧稳定,赞一个,因为我的外部晶振只有8M最大只能倍频到128,如果使用外部16M,不知继续倍频可以到多少。不过性能还是很好的。
*******************************************
预留测试GD32的效果:
写贴时将GD的GD32E230翻出来进行了同样的测试,因为GD的倍频器倍数较高,我已经倍频到144M(标准72M),测试定时器依旧稳定。

*******************************************
广告很长,你忍一下。
上半场结束,下半场继续:
此处歪解一下时钟的问题,之前有群友很疑惑单片机的低功耗和时钟的关系,疑惑高速的时钟会不会增加MCU的功耗,为啥低功耗要降低时钟速度。这里讲解一下:
可以用用单位时间内执行的指令来看,高速时钟在单位时间内使系统跑了更多的指令,而低速时钟单位时间内跑的少,而单片机是直线结构,内核是不会休息的,功耗就看执行的指令多少。而单片机的低功耗就是降低时钟,让单片机跑慢点。就像人一样,低功耗相当于你不跑了,原地休息,但是你的心跳不会停止,你还是得消耗能量,即使再少还得消耗。
就像人一样,时钟就相当于心跳,只要还活着就得消耗能量,你要想跑得快,心脏就得跳得快,跳得越快能量消耗越高,即使你去睡觉,心跳只要不停止,你还得消耗能量,如果心跳没了,整个人就没了,MCU也就宕机了。所以在处理低功耗时最先解决的就是时钟频率,只有降低了时钟的频率,才能真正降低功耗。关于单片机进入低功耗和唤醒,以及降低整体运行功耗我看能不能在下文讲解,近期刚好做了一个低功耗的项目,这里留悬念吧。

/********************************************************************************************************/
广告结束,正文开始,不好意思,有点喧宾夺主了哈。
回到主题,为了解决时钟切换的问题,才有了这个帖子,上文全属歪楼,为最近开发时的经验总结。
我们在使用STM32103的固件库时,时钟配置在system_stm32f10x.c中,但是只是对外部晶振做了初始化,而对于内部时钟并没有添加代码,如果你的MCU没有外部晶振,当系统运行时是先启动内部时钟,然后会检测外部晶振,如果没有检测到晶振,系统便以内部的8M继续运行,这是不合理的。

这里可以看到,如果外部启动失败,会进入这个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;    

 

问:
如果我的MCU有晶振,但是我不想用外部,就想用内部,如何处理呢?
答:
打一顿就好了,有外部不用干啥用内部呢?
上述纯属恶搞自己,被坑过。。。
因为内部时钟不准!!!测试内部时钟在使用定时器时会有偏差,本人在此吃过亏。此问题在STM32F031和GD32E230中均有体现。但是USART和SPI通信是正常的,即使我用的2.5M波特率的USART和8M的SPI。
解决办法,上述代码不用动,添加如下代码。

通过注释原文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;



    /* Wait till PLL is ready */

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

    {

    }



    /* Select PLL as system clock source */

    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // PLL 做系统时钟



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

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

    {

    }

  }  

}

在STM32F103中,使用内部晶振,最大时钟频率也只能到64M,受倍频因子的影响嘛,最大只能倍频16倍。  但是在STM32F031中,标准使用内部时钟主频只有48M,但是我们仍然可以继续倍频,用内部时钟进行超频达到64M。在我们的产品中就用过内部超频到56M,USART和SPI长时间无问题。
而GD32E230因为其高达32的倍频因子,内部时钟可以倍频到128M。


但是这种几分钟内没有明显发热现象,不敢做长时间测试,现在MCU有点小贵。干费一个就心疼。
总之,无论ST还是国产,其主频更适合在规定的范围内运行,但是跑极限在短时间内也没有很大的问题。这些数据仅供参考。
至此单片机时钟讲解就结束了,没有多少理论性的东西,主要是解决一些时钟使用时的问题,自己也总是忘,留帖一篇作为自省。
此帖中所有代码都经过本人测试,运行无任何问题,但是对于问题的阐述或者一些见解可能有错误,欢迎大佬们批评指正,一定接受各种批评,努力完善。
---------------------
作者:呐咯密密
链接:https://bbs.21ic.com/icview-3182144-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ic2121/article/details/128649397

智能推荐

k3安装服务器系统,论如何逗比的在2008R2上安装金蝶K3服务器-程序员宅基地

文章浏览阅读1k次。K3 WISE V13.0在Windows Server 2008 R264位操作系统下的安装配置说明环境:1、Windows Server 2008 R2 64位操作系统;2、K3 WISE V13.0;3、SQLServer2005 32位+SQL SP4;一、Windows Server 2008 R2 64位操作系统配置如下:1、 固定计算机名和手工指定IP地址,共享一个文件夹,赋予ev..._2008r2如何安装13.0k/3wise客户端

ipod 硬件检测_ipod检测-程序员宅基地

文章浏览阅读604次。ipod 硬件检测 操作方法_ipod检测

服务器巡检系统-程序员宅基地

文章浏览阅读1.7k次。实时监控后台录入的网络设备在线情况,断线提醒,历史状态查询,实时监控图表,后台录入数据后自动生成拓扑图。_服务器巡检系统

软件项目管理 2.1.项目立项_软件项目立项流程-程序员宅基地

文章浏览阅读9.5k次。软件项目管理 ——2.1.项目立项 归档于软件项目管理初级学习路线第二章 软件项目确立《初级学习路线合集 》前言大家好,这节我们学习软件项目立项概念一、项目启动背景项目启动的背景大致可以分为四类:1.符合法规,法律或者社会要求2.满足相关方的要求或需求3.执行,变更业务或技术战略4.创造,改进或修复产品,过程或服务二、软件项目启动启动项目是因为客户的需求,然后实现这个要求,最后提交给客户。但是 不是所有的需求都可以被_软件项目立项流程

C# MenuStrip中的item快捷键设置_menustrip快捷键-程序员宅基地

文章浏览阅读4.5k次,点赞5次,收藏8次。基本知识MenuStrip是C#用来做Windows窗口应用程序的一个常用组件,翻译过来就是菜单栏(类似文件、编辑、视图、帮助等等这一栏)。所有经常看到有很多软件的文件-保存选项后面写着Ctrl+S,这就是利用快捷键间接调用点击保存这个事件的功能。实验首先在visual studio中依次点击文件-新建-项目,出现下图按图中数字顺序选择,第三项可以自己设置。确定完成后,可以在解决方案资源管..._menustrip快捷键

6. JavaScript排序算法动画演示效果实现_js快速排序动画演示效果实现-程序员宅基地

文章浏览阅读396次。之前在知乎看到有人在问 自己写了一个冒泡排序算法如何用HTML,CSS,JavaScript展现出来排序过程。   感觉这个问题还挺有意思 。前些时间就来写了一个。这里记录一下实现过程。基本的思想是把排序每一步的时候每个数据的值用DOM结构表达出来。问题一:如何将JavaScript排序的一步步进程展现出来?我试过的几种思..._js快速排序动画演示效果实现

随便推点

达梦数据库基础2-数据库实例(Linux)_linux达梦数据库创建实例-程序员宅基地

达梦数据库基础2-数据库实例(Linux),介绍了在Linux系统中创建和管理达梦数据库实例的方法,包括使用图形界面工具和命令工具来进行操作。摘要长度:88个字符。

几种常规信号(射频)参数测试方法_射频测试s参数-程序员宅基地

文章浏览阅读1w次,点赞14次,收藏119次。最近出了点意外,FPGA的事暂时搁置了。没办法,谁让“老板“”让我做什么,我就得做什么呢!!!这几天调试一块射频板,测试过程中简单总结了几个参数的简单调试方法,在此做个记录,以备以后可能还会用到。当然能够知道这些,也离不开百度,谷歌,还有前辈们的指导,在此表示感谢,话不多说 直接开内容,弄完睡觉。0、测试环境:所有测试激励均为单频信号源。- 1、S参数(1)测试仪器:矢量网络分析仪,电源..._射频测试s参数

手把手教你 3 分钟搞定个人网站 http 免费升级到 https-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏3次。你知道的越多,不知道的就越多,业余的像一棵小草!你来,我们一起精进!你不来,我和你的竞争对手一起精进!编辑:业余草推荐:https://www.xttblog.com/?p=5081手把..._复制下面的网址下载时选择 源文件 (普通下载)我们的用户软件终身免费升级http

七段数码管的显示 滚动显示_数码管滚动显示的摘要-程序员宅基地

文章浏览阅读3.8k次。初级实验_数码管滚动显示的摘要

fifo IP 核使用常见问题汇总_fifo ip核使用读写位宽不一致设置-程序员宅基地

文章浏览阅读6.3k次,点赞7次,收藏50次。fifo 做为FPGA常用的一个模块,每次用的时候都是丢三落四,不是忘这里就是忘哪里。现在记录下来。1--- 关于FIFO 的复位,是高复位。懒得用复位信号的时候,就是把复位信号一直拉低。后来仿真发现,这样fifo是不工作的,因 为仿真时候观察写入和读出数据计数都是0。在虚伪的时候还需要注意,复位信号如果和fifo的时钟不同步,一定要在新时钟下面打两拍。 还有..._fifo ip核使用读写位宽不一致设置

Codeforces的使用-程序员宅基地

文章浏览阅读2.1w次,点赞55次,收藏206次。1.首先,输入codeforces点击进入。2. 进入之后,你将会看到这样的一个页面,先点击左上角,Register(注册)3.emmmmmmm如下,#我的QQ邮箱,懒得删。按照它的提示输入信息,完成之后,Register一下。然后正常登入就好4.你可以输入自己的信息,这里就不多说了5.如图,说的很详细了吧6.选题目点击进入..._codeforces

推荐文章

热门文章

相关标签