打印
[应用相关]

STM32F4+DP83848以太网通信指南系列(二):系统时钟

[复制链接]
920|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

本章为系列指南第二章,主要是介绍一下STM32F4的时钟配置。时钟是一个嵌入式产品从零开始开发的基石,一切逻辑都在时钟的节奏中安静地弹奏着,时钟为整个电路带来了欢快的「心跳」。开发者如果对时钟没有控制能力,就会把脉不准整个旋律的节奏,从而导致诸如通信波特率、通信时序、延时操作等关键功能全都紊乱,系统的构建也就无从谈起。

时钟如此重要,那么普通开发者,需要对时钟有多深的认知呢?STM32F4的时钟配置到底复不复杂?几行代码能搞定?

不要着急,我下面将用最简单的白话文来剖析STM32的时钟系统。不过在这之前,我们应该先吃一颗定心丸,因为在STM32中配置时钟是非常简单的,简单到我们甚至不需要写一行代码就能配置好,因为从标准库3.5版本以后,SystemInit()这一重要函数,已经默认帮你在上电后执行了,都不需要你手动调用就帮你配置好时钟了。


使用特权

评论回复
沙发
keaibukelian|  楼主 | 2019-6-18 14:12 | 只看该作者
一切根据原理图

刚才提到,SystemInit()这一配置系统时钟的重要函数,不需要我们手动调用,STM32会帮我们上电后,嵌入到main函数开头部分自动执行,将我们的系统时钟配置到168MHz。那为什么我们还要来学习配置时钟呢?

因为不同的原理图使用的器件,引脚接法不一样,所以如果我们直接使用默认的SystemInit()函数,可能并不能按照我们预期的频率配置系统时钟,这时候就需要我们对STM32的时钟概念做一个了解,然后根据我们实际使用的电路原理图来修改系统默认的SystemInit()函数,从而达到一切可控,一切按预期的配置。

下图是我手上这块开发板的原理图局部特写,特写位置为23、24引脚:


使用特权

评论回复
板凳
keaibukelian|  楼主 | 2019-6-18 14:12 | 只看该作者

图中,标明23、24脚为OSC_IN,OSC_OUT,OSC是oscillation的缩写,中文是「振荡器」的意思,表示这两个引脚接外部晶振。闲来无事,我们去ST官网下载编号为DS8626的STM32F40x的datasheet求证一下看看。

可以看到,我们的原理图上,外部晶振使用的是8MHz的,问题来了,STM32官方默认的外部晶振是25MHz的,也就是说,如果我们完全不改配置文件,系统默认调用了SystemInit()函数,直接就会导致系统的「心律失常」。


使用特权

评论回复
地板
keaibukelian|  楼主 | 2019-6-18 14:12 | 只看该作者
时钟树

关于时钟树,我个人认为对于新手来说有些复杂了,但几乎用不到其中的很多功能,这里我就懒得贴出完整的时钟树图片了,我们只需要了解下面几个知识点:

每个芯片都有一个自己的最大频率,这是一个芯片的固有属性,这个最大频率代表着这颗芯片处理能力的速度上限,比如STM32F407一般是168MHz。我们可以通过不同的方案配制出这个最大频率(没有人愿意自己的芯片降频使用吧)。最最最常用的方式就是使用外部晶振提供一个基础的频率,然后在这个频率上进行加工,最终得到一个168MHz的时钟出来。给系统提供基础频率的外部晶振,一般是MHz级别的,我们称之为高速外部晶振(HSE),这里我们开发板上配置的是一颗8MHz的晶振。此外如果系统需要用到RTC时钟,还需要一个低速外部晶振(LSE),一般是额定频率32.768KHz,这是后话,此处不表。从8MHz的HSE提高变成168MHz最大频率的过程,我们叫做「倍频」,相反从一个高速频率降成低速频率的过程,称之为「分频」。STM32F4内部通过锁相环进行倍频,其原理我没高兴去了解,我们知道有一种叫锁相环的东西可以倍频就行。倍频时涉及到三个常量需要定义,分别是:PLL_M, PLL_N, PLL_P,最终得到系统频率的公式请牢记:SYSCLOCK = HSE / PPL_M * PPL_N / PPL_P,这个公式对于具体的系统来说,这么用:168MHz = 8MHz / PPL_M * PPL_N / PPL_P,因此一个合理的配置就是:



  • PPL_M = 8;



  • PPL_N = 336;



  • PPL_P = 2;


如果有另一块开发板,系统原理图上标明OSC_IN和OSC_OUT处接的是25MHz晶振,聪明的你一定知道把上面的PPL_M值修改为25就可以了。时钟配置时,还有一个宏定义需要定义:PPL_Q,这个是用来配置USB、SD卡读写时的频率的,计算公式是USBClock = HSE / PPL_M * PPL_N /PLL_Q,一般约定USB时钟频率小于等于48MHz,因此PPL_Q的范围也很好确定,如果项目中没有用到USB和SD卡读写,一般这个宏可以忽略,保持默认值(事实上,默认值是7,336/7=48,刚刚好可以把USB频率设置为48MHz)。


使用特权

评论回复
5
keaibukelian|  楼主 | 2019-6-18 14:13 | 只看该作者
倍频宏定义

根据我们对原理图,以及对时钟树的认知,下面我们就要着手来配置系统时钟了。之前已经说过,SystemInit()不需要开发者手动调用,那么时钟树上的那些参数怎么配置入参呢。原来是通过宏定义的方式实现的,文件名为:system_stm32f4xx.c,呃。。。如果你不知道这个文件,那可得去看一看Keil5创建STM32工程的步骤了,这个文件在Std标准库的样例模板中,具体的路径为:\STM32F4xx_DSP_StdPeriph_Lib_V1.4.0\Project\STM32F4xx_StdPeriph_Templates,我们一般在Keil中创建STM32的工程,都是复制这个模板工程,标准库下载地址:http://www.stmicroelectronics.com.cn/en/embedded-software/stsw-stm32065.html。OK,我们打开这个文件,看看里面都有啥:



  • * 5. This file configures the system clock as follows:



  •   *=============================================================================



  •   *=============================================================================



  •   *                    Supported STM32F40xxx/41xxx devices



  •   *-----------------------------------------------------------------------------



  •   *        System Clock source                    | PLL (HSE)



  •   *-----------------------------------------------------------------------------



  •   *        SYSCLK(Hz)                             | 168000000



  •   *-----------------------------------------------------------------------------



  •   *        HCLK(Hz)                               | 168000000



  •   *-----------------------------------------------------------------------------



  •   *        AHB Prescaler                          | 1



  •   *-----------------------------------------------------------------------------



  •   *        APB1 Prescaler                         | 4



  •   *-----------------------------------------------------------------------------



  •   *        APB2 Prescaler                         | 2



  •   *-----------------------------------------------------------------------------



  •   *        HSE Frequency(Hz)                      | 25000000



  •   *-----------------------------------------------------------------------------



  •   *        PLL_M                                  | 25



  •   *-----------------------------------------------------------------------------



  •   *        PLL_N                                  | 336



  •   *-----------------------------------------------------------------------------



  •   *        PLL_P                                  | 2



  •   *-----------------------------------------------------------------------------



  •   *        PLL_Q                                  | 7



  •   *-----------------------------------------------------------------------------



使用特权

评论回复
6
keaibukelian|  楼主 | 2019-6-18 14:13 | 只看该作者

看,ST写的注释非常漂亮是不是,同时我们也注意到官方使用默认HSE确实是25MHz的,如果我们的开发板不做任何配置,直接使用这个配置,时钟就乱掉了。接下来,我们就不管这些注释了,直接去修改对应的宏定义吧(搜索关键字,重新定义宏):



  • #define HSE_BYPASS_INPUT_FREQUENCY   8000000







  • /************************* PLL Parameters *************************************/



  • #if defined (STM32F40_41xxx) || defined (STM32F427_437xx) || defined (STM32F429_439xx) || defined (STM32F401xx)



  • /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */



  • #define PLL_M      8  //HSE是8MHz晶振,PLL分频系数设置为8



  • #else /* STM32F411xE */



  • #if defined (USE_HSE_BYPASS)



  • #define PLL_M      8   



  • #else /* STM32F411xE */   



  • #define PLL_M      16



  • #endif /* USE_HSE_BYPASS */



  • #endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx */  







  • /* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */



  • #define PLL_Q      7







  • #if defined (STM32F40_41xxx)



  • #define PLL_N      336  



  • //倍频系数,SYSCLOCK = HSE / PPLM * PPLN / PPLP = 8MHz / 8 * 336 / 2 = 168MHz



  • /* SYSCLK = PLL_VCO / PLL_P */



  • #define PLL_P      2



  • #endif /* STM32F40_41xxx */




使用特权

评论回复
7
keaibukelian|  楼主 | 2019-6-18 14:14 | 只看该作者
简单测试

OK,经过以上的步骤,我们已经将系统时钟调整为168MHz了,但是成功了吗?确实是168MHz吗?我们来做个实验就可以了,实验非常简单,弄个GPIO控制LED小灯,系统里设置每隔1秒钟交替闪烁,观测小灯亮灭的间隙是否差不多为1秒钟。因为是简单测试,延时功能我们就不使用定时器或者SysTick了,我们直接用步进法来做延时测试,弄个vu32类型的变量,从0一直累加,直到达到68,000,000,这期间的过程就是1秒钟。伪代码如下:



  • vu32 i = 0;



  • int main(){



  •     GPIOInit()...



  •     while(1){



  •         LED = 1;



  •         i = 0;



  •         while(i < 168000000)



  •             i++;



  •         LED = 0;



  •         i = 0;



  •         while(i < 168000000)



  •             i++;



  •     }



  • }


如果LED小灯能预期按照大概1秒的间隙亮灭的话,就说明我们对STM32的时钟配置正确了。


使用特权

评论回复
8
643757107| | 2019-6-18 22:52 | 只看该作者
32的时钟非常好用。

使用特权

评论回复
9
wakayi| | 2019-7-9 11:05 | 只看该作者
非常不错的帖子

使用特权

评论回复
10
keaibukelian|  楼主 | 2019-8-6 11:15 | 只看该作者
非常感谢支持

使用特权

评论回复
11
nongfuxu| | 2020-6-5 08:15 | 只看该作者
学习了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

4146

帖子

5

粉丝