zoukankan      html  css  js  c++  java
  • 【ARM】---关于STM32F407启动后的系统时钟频率问题

      玩STM32的时间也比较久了,最早的一直玩的是STD标准库的103系列,但是ST公司也是“与时俱进”,舍弃了当年的标准库,转而推广HAL库,反正无论怎么样把,对于STM32的使用也仅仅停留在使用阶段,底层涉入不神,我一直觉得真正的大牛们,都是趴在最底层不愿意起来的那一群,唉……底层难啊。近来由于课设要求,重新捡起来F407的板子,继续ST进阶之路。长时间不玩,对于STM32陌生了好多,这次玩板子,看的更深了点,花了一下午,终于解决了一个问题:关于板子上电以后系统时钟频率的设置问题。  

      以前最先用103的STD库做开发,后来搞过一段时间寄存器开发,一直都认为STM32的主流时钟频率比如72M,168M等是在启动文件里,main()函数之前SystemInit函数中设置,后来才发现目前的代码都没有对他进行设置。本人喜好寄存器版的简单粗暴,不需要添加过多的文件,直接建个工程,弄个main函数就开始跑程序了,那么问题就来了,这时候的系统时钟频率到底是多少呢,SystemInit没有对他设置,那么到底是在哪里设置了他呢,不然程序怎么能跑起来?带着这些问题,搞了一下午,也算是了解了!

      OK,。下面,以我自己的STM32F407的板子为例进行测试说明,HSE晶振为8M,HSI为16M

      下面是:启动文件里面的原版代码,粘来是方便大家看下,查找源码可以发现,SystemInit的代码的第一行是开启HSI时钟源,其他的基本都是复位操作,还有一些内存SRAM的设置。我们建立工程之后,直接在main中写代码,程序是可以运行的,那么这时候我的407板子的时钟是多少M呢?

     1 //启动代码中的函数
     2 Reset_Handler PROC
     3 EXPORT Reset_Handler [WEAK]
     4 IMPORT SystemInit
     5 IMPORT __main
     6 
     7 LDR R0, =SystemInit
     8 BLX R0
     9 LDR R0, =__main
    10 BX R0
    11 ENDP

      我分了几个部分对这个进行测试,

      一、确保程序可以运行,

    1 int main(void)
    2 {
    3     u32 fre1;
    4         
    5     fre1 = 2;
    6 }

      建立完工程,编译无错误之后,写入上面的代码,编译通过后,load到板子里,然后开启debug,调试,通过watch窗口,查看到fre1变量的值会变成2,如下图,说明程序可以运行,fre2,fre3是测试程序时添加的变量,这里不用在意。

      二、OK,测试程序可以跑,那么接下来开始思考,这时系统的主频是多少,我采用了,HAL库提供的一个函数HAL_RCC_GetSysClockFreq(),通过它可以得到系统主频的值。代码也是非常简单,

    1 int main(void)
    2 {
    3     u32 fre1;
    4     fre1 = 2;
    5     fre1 = HAL_RCC_GetSysClockFreq();
    6 }

      将上面的程序load到板子后,继续开启debug模式,查看系统主频,结果如下,可以看到fre1最后的值是0x00F42400,通过计算器换算之后发现该值其实是16M,那也就是说此时的系统时钟频率是16M,这就让我比较奇怪了,怎么会是16M!

      为了查看为什么此时系统时钟的值是16M,我查看了以下此时RCC中关于时钟配置的寄存器的值,如下图

      

      先来说CFGR寄存器,因为要查看的是系统时钟,那么就要搞清楚目前系统时钟的来源是谁,看最低两位的SW1和SW0,手册上是这样描述的!

    由上图可以看到,该两位都是0,根据手册,此时的系统时钟来源是HSI,就没有HSE和PLL啥事,查阅407板载资料,HSI的时钟源是16MHZ,至此我明白了,原来此时的系统时钟,是由系统时钟HSI时钟源提供的,那么到底是不是呢,为了验证我的想法,我继续查看了CR寄存器,此时,他的值如下,可以看到此时HSION是被置位的,也就是此时开启了,HSI的时钟,那应该就没啥错了!

     

      不过还有一个疑问,就是到底是谁开启了HSI时钟呢,又是在哪段代码里开启了呢,这可以说是一个不算问题的问题,但是笔者还真的试图去源码当中找过,也真的是太不认真了,后来才发现这个CR寄存器的复位值,就直接将HSION位置1了。这句是直接从手册中截图来的,也就是每一次的复位,系统硬件都会把HSION位置1,表明此时开启了HSI时钟源!

      而且刚刚还忽略了一个严重的问题,在SystemInit()函数中,第一条代码,就是开启HSI时钟,我尝试将该行代码注释掉,发现并没有任何影响,看来确实是由硬件置位的。看到这里,问题基本被解决了一大半,此时系统的主时钟基本是有寄存器初始复位值所决定,原来搞了这么久都竟然没发现这个问题,唉,实在是太不应该,如果有像笔者一样的菜鸟的话,也在此给各位提个醒。

      三、问题基本被搞清楚了,那么我就想,既然现在是HSI时钟源,那么晚我想将系统时钟变为其他的频率,可以吗?答案是当然,我没有直接去设置168M,而是用了简单的三行代码,将时钟源选择改为了HSE,大家看过时钟树图的都应该清楚,系统时钟的来源可以是HSE,也可以是PLL,而如果要设为高速时钟的话,肯定就需要PLL,不过我的目的仅仅是测试下修改时钟,因此也就不那么麻烦了,我添加了几行代码,如下:

     1 int main(void)
     2 {
     3     u32 fre1;
     4     fre1 = 2;
     5     fre1 = HAL_RCC_GetSysClockFreq();
     6     
     7     /*选择时钟源为HSE*/
     8     RCC->CR |= 0X1<<16;              //开启HSE时钟
     9     while((RCC->CR & 0X1<<16) == 1);  //等待HSERDY就绪
    10
    11     RCC->CFGR |= 0X1;                //时钟源选择为HSE
    12     
    13     fre1 = HAL_RCC_GetSysClockFreq();
    14

      根据以上的代码,大家应该能猜到fre1的值的变化情况把 ,第一次为2,第二次是16M,那么第三次…………,没错就应该是8M了,结果如下:0x007a1200,经过换算,没错确实是8M,一切成功。

      四、设置时钟主频为168M

      玩过407的人应该都知道,407推荐主频为168M比较好,那么我们就在这里设置下他的系统主频,168M是一个高速时钟,那么我的时钟源肯定要不能直接使用HSE了,而应该使用PLL(其实PLL的源是HSE),而系统主频的源是PLL,这三者之间的关系大家要搞清楚。在这里我们先不做大的改动,直接将PLL时钟源开启,然后将系统时钟的源选择为PLL,测试一下时钟频率是多少,代码如下:

     1 int main(void)
     2 {
     3     u32 fre1 = 0;
     4     
     5     /*选择PLL的时钟源是HSE*/
     6     RCC->PLLCFGR |= 0X1<<22;  //必需在开启PLL和HSE之前设置
     7     
     8     /*开启HSE*/
     9     RCC->CR |= 0X1<<16;    
    10     while((RCC->CR & 0X1<<16) == 1);
    11     
    12     /*开启PLL*/
    13     RCC->CR |= 0X1<<24;
    14     while((RCC->CR & 0X1<<24) == 1);//等待PLLRDY准备就绪
    15     
    16     /*选择PLL为系统的时钟源*/
    17     RCC->CFGR |= 0X2;
    18     
    19     FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;  //设置FALSH预取等待时间
    20     
    21     fre1 = HAL_RCC_GetSysClockFreq();
    22 }

      使用PLL的源得到的系统时钟经过测试为48M,结果如下图:

      

      明明没有设置过RCC_PLLCFGR的值,但是竟然还是得到了48M的时钟,原因是什么呢,跟前面一样,同样这里RCC_PLLCFGR的寄存器有个RESET值,有心的朋友可以对照着手册查看一下,该寄存器的RESET值最终能够得到pll_m = 16,pll_p = 2,pll_n = 192,计算之后同样能够的得到这个48M的时钟频率,那么如果你想要设置主频时钟为168M,那么在笔者的代码基础上修改一下PLL_CFGR寄存器的值,应该就可以解决所有的问题了,着重提醒一下,当你的时钟设置为168M后,必须要设置FLASH预取等待时间,否则会有问题!

      由于主要目的是为了测试,因此代码着实有点粗鄙不堪。不够严谨,还望各位能够见谅,这里也只是希望能够给各位分享一下,上电之后系统主频的变化。从最底层的角度来帮助分析,这样才更加清楚明了,当你底层都会了,那么无论再来什么的库都不是问题。相反,你只是停留在库的层面,这个库你会了,下次换个库,你又得花时间整!

      个人观点,不喜勿喷,希望能给予大家帮助。

  • 相关阅读:
    在main函数中使用django模型(附django正反向的外键关联查询)
    使用正则表达式替换文本内容
    spring 上下文和spring mvc上下文和web应用上下文servletContext之间的关系
    idea快捷键
    Spring MVC的jar包版本问题
    Spring MVC的参数类型转换
    HiddenHttpMethodFilter进行请求过滤,实现Rest风格的url
    Spring MVC的异常处理
    Spring MVC 的拦截器
    @ResponseBody&@RequestBody
  • 原文地址:https://www.cnblogs.com/szhb-5251/p/STN32.html
Copyright © 2011-2022 走看看