玩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呢?

 //启动代码中的函数
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

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

  一、确保程序可以运行,

 int main(void)
{
u32 fre1; fre1 = ;
}

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

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

 int main(void)
{
u32 fre1;
fre1 = ;
fre1 = HAL_RCC_GetSysClockFreq();
}

  将上面的程序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,不过我的目的仅仅是测试下修改时钟,因此也就不那么麻烦了,我添加了几行代码,如下:

 int main(void)
{
u32 fre1;
fre1 = ;
fre1 = HAL_RCC_GetSysClockFreq(); /*选择时钟源为HSE*/
RCC->CR |= 0X1<<;      //开启HSE时钟
while((RCC->CR & 0X1<<16) == 1);  //等待HSERDY就绪 RCC->CFGR |= 0X1;      //时钟源选择为HSE fre1 = HAL_RCC_GetSysClockFreq();

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

  四、设置时钟主频为168M

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

 int main(void)
{
u32 fre1 = ; /*选择PLL的时钟源是HSE*/
RCC->PLLCFGR |= 0X1<<; //必需在开启PLL和HSE之前设置 /*开启HSE*/
RCC->CR |= 0X1<<;
while((RCC->CR & 0X1<<) == ); /*开启PLL*/
RCC->CR |= 0X1<<;
while((RCC->CR & 0X1<<) == );//等待PLLRDY准备就绪 /*选择PLL为系统的时钟源*/
RCC->CFGR |= 0X2; FLASH->ACR = FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; //设置FALSH预取等待时间 fre1 = HAL_RCC_GetSysClockFreq();
}

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

  

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

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

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

关于STM32F407启动后的系统时钟频率问题的更多相关文章

  1. 关于mysql8启动后又停止(windows10系统),忘记密码以及密码过期等坑解决办法总结!

    一 我遇到的问题 1 mysql连接不了,mysql服务启动后又马上关闭 2 忘记密码或者重装服务后提示安装的随机密码过期 一个一个来,先看第一个: 1 出现这个情况很大原因是mysql安装目录有多余 ...

  2. 简单的股票信息查询系统 1 程序启动后,给用户提供查询接口,允许用户重复查股票行情信息(用到循环) 2 允许用户通过模糊查询股票名,比如输入“啤酒”, 就把所有股票名称中包含“啤酒”的信息打印出来 3 允许按股票价格、涨跌幅、换手率这几列来筛选信息, 比如输入“价格>50”则把价格大于50的股票都打印,输入“市盈率<50“,则把市盈率小于50的股票都打印,不用判断等于。

    '''需求:1 程序启动后,给用户提供查询接口,允许用户重复查股票行情信息(用到循环)2 允许用户通过模糊查询股票名,比如输入“啤酒”, 就把所有股票名称中包含“啤酒”的信息打印出来3 允许按股票价格 ...

  3. MySQL 安装和启动服务,“本地计算机 上的 MySQL 服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。”

    MySQL 安装和启动服务,以及遇到的问题 MySQL版本: mysql-5.7.13-winx64.zip (免安装,解压放到程序文件夹即可,比如 C:\Program Files\mysql-5. ...

  4. Hbase HRegionServer启动后自动关闭

    突然发现HBASE无法使用了. 然后看到在分布式的情况下,节点上的HRegionServer启动后自动关闭. 同步时间就能解决这个问题. 网上同步时间 1.  安装ntpdate工具 sudo apt ...

  5. linux启动后自动登录并运行自定义图形界面程序

    在<Ubuntu CTRL+ALT+F1~F6 进入命令模式后不支持中文显示的解决办法>一文中提到linux启动在以后运行一个独占显示器的图形程序的两种办法. 1.不启动xserver,使 ...

  6. 绿色版Tomcat 启动 + 停止 + 随系统自动启动 - - 博客频道 - CSDN.NET

    body { font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI ...

  7. 通过 U 盘启动重装 macOS 系统

    重装系统是工作和生活中经常需要做的事情,作为一名开发人员,学会该技能你才是一名合格的程序猿!以后再也不会遇到"程旭元你会装系统吗?"的尴尬了!本文主要介绍怎样通过U盘启动重新安装 ...

  8. 让windows 2003启动后直接进入桌面

    windows 2003启动后进入桌面需要解决的几个问题 1.如何去除掉 ctrl+alt+del的提示界面 2.如何设置自动登录的用户名密码 3.在异常启动时会出现关闭事件跟踪程序 也会导致不能直接 ...

  9. Android Bug分析系列:第三方平台安装app启动后,home键回到桌面后点击app启动时会再次启动入口类bug的原因剖析

    前言 前些天,测试MM发现了一个比较奇怪的bug. 具体表现是: 1.将app包通过电脑QQ传送到手机QQ上面,点击安装,安装后选择打开app (此间的应用逻辑应该是要触发 [闪屏页Activity] ...

随机推荐

  1. strcmp与strncmp的区别

    ==================  strcmp与strncmp都是用来比较字符串的,区别在于能否比较指定长度字符串. strcmp C/C++函数,比较两个字符串  设这两个字符串为str1,s ...

  2. C# 实现快捷键几种方法

    本文讲解了三种方法实现C# button快捷键,如Alt + *(按钮快捷键),Ctrl+*及其他组合键等. 一. C# button快捷键之第一种:Alt + *(按钮快捷键) 在大家给button ...

  3. PD中设置外键约束名称生成规则

    选择Database—>Edit Current DBMS选择Scripts->Objects->Reference->ConstName可以发现右侧的Value为: FK_% ...

  4. 基于C++求两个数的最大公约数最小公倍数

    求x,y最大公约数的函数如下: int gys(int x,int y) { int temp; while(x) {temp=x; x=y%x; y=temp;} return y; } x=y的时 ...

  5. SWT简介

    --------------siwuxie095                         SWT 简介:     SWT(Standard Widget Toolkit) 也是基于一个对等体实 ...

  6. 面试题: 1天的java面试题 已看1

    1,自我介绍下,我直接说的项目经历,(哪年在哪个公司呆过) 2,问是否有带过团队的经历,我说去年带过一次. 3,Struts是单例模式还是多例模式?我先说单例模式,后说多例模式. Struts1是单例 ...

  7. JS中的引用类型

    JS的数据类型可以分为两类:一类是原始类型(比如数字.布尔值.字符串.undefined.null),另外就是对象类型.我们通常将对象类型称为引用类型.对象值都是引用.举个例子来说明,下如下的代码: ...

  8. 算法Sedgewick第四版-第1章基础-013一用stack实现自动补全表达式括号

    package algorithms.exercise; import algorithms.ADT.Stack; import algorithms.util.StdIn; import algor ...

  9. 高性能MySQL笔记-第5章Indexing for High Performance-004怎样用索引才高效

    一.怎样用索引才高效 1.隔离索引列 MySQL generally can’t use indexes on columns unless the columns are isolated in t ...

  10. C语言实现文件实时更新

    一.简介 在linux或者unix操作系统中在系统引导的时候会开启很多服务,这些服务就叫做守护进程. 守护进程脱离了终端并且在后台运行:守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显 ...