【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程

时钟系统

  • 前言
  • 1.时钟源
  • 2.时钟的初始化过程
  • 总结


前言

时钟是单片机的心脏,而时钟系统就像是全身的血管,在时钟系统的工作下,单片机各个部位按需求的正常工作。今天写一写我对stm32f4xx时钟系统及初始化的理解。


基于stm32f4xx

1.时钟源

在stm32中系统时钟是通过获得的时钟源经过分频倍频等操作而来的,而时钟源有以下几种:
1.外部高速时钟(HSE):其实就是单片机板子上的晶振电路,用户决定的
2.内部高速时钟(HSI):这是单片机内部自带的振荡器,精度没有石英晶振高,易受温度影响
3.主PLL时钟:其实就是将HSE/HSI倍频后的时钟,可以不选择,直接使用HSE/HSI
4.32 kHz 低速内部 RC (LSI RC),用于驱动独立看门狗,也可选择提供给 RTC 用 于停机/待机模式下的自动唤醒。
5. 32.768 kHz 低速外部晶振(LSE 晶振),用于驱动 RTC 时钟 (RTCCLK)

对于每个时钟源来说,在未使用时都可单独打开或者关闭,以降低功耗。

【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第1张图片
由图可知,LSE /LSI 的用途已经固定了,梯形方框表示选择开关。
对于HSE/HSI如果不选择PLL则可以直接分配给系统时钟及后续外设,但是速度很慢,如果选择了主PLL,则HSE/HSI进入PLL(锁相环)进行分频倍频操作,通过修改M/N/P的值可以配置出想要的时钟速度(最大168Mz)作为系统时钟,在通过分频给下一级外设时钟

	f(sysclk)=(f(HSE)/M)*N/P

除以下时钟外,所有外设时钟均由系统时钟 (SYSCLK) 提供:
(1)来自于特定 PLL 输出 (PLL48CLK) 的 USB OTG FS 时钟 (48 MHz)、基于模拟技术的随机数发生器 (RNG) 时钟 (48 MHz) 和 SDIO 时钟 (48 MHz)。
(2)I2S 时钟
要实现高品质的音频性能,可通过特定的 PLL (PLLI2S) 或映射到 I2S_CKIN 引脚的外 部时钟提供 I2S 时钟。
(3)由外部 PHY 提供的 USB OTG HS (60 MHz) 时钟
(4)由外部 PHY 提供的以太网 MAC 时钟(TX、RX 和 RMII)。当使用以太网时,AHB 时钟频率至少应为 25 MHz。

RCC 向 Cortex 系统定时器 (SysTick) 馈送 8 分频的 AHB 时钟 (HCLK)。SysTick可使用此时钟作为时钟源,也可使用 HCLK 作为时钟源,具体可在 SysTick 控制和状态寄存器 中配置。

● MCO1
用户可通过可配置的预分配器(从 1 到 5)向 MCO1 引脚 (PA8) 输出四个不同的时钟源:
— HSI 时钟
— LSE 时钟
— HSE 时钟
— PLL 时钟
所需的时钟源通过 RCC 时钟配置寄存器 (RCC_CFGR) 中的 MCO1PRE[2:0] 和 MCO1[1:0] 位选择。
● MCO2
用户可通过可配置的预分配器(从 1 到 5)向 MCO2 引脚 (PC9) 输出四个不同的时钟源:
— HSE 时钟
— PLL 时钟
— 系统时钟 (SYSCLK)
— PLLI2S 时钟
所需的时钟源通过 RCC 时钟配置寄存器 (RCC_CFGR) 中的 MCO2PRE[2:0] 和 MCO2 位选择。
对于不同的 MCO 引脚,必须将相应的 GPIO 端口在复用功能模式下进行设置。
MCO 输出时钟不得超过 100 MHz(最大 I/O 速度)。

2.时钟的初始化过程

上面讲解了时钟系统的结构,那在程序中系统时钟是怎么初始化的呢?
我们打开stm32的启动文件(基于stm32f407):startup_stm32f40_41xxx.s
就会看到:【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第2张图片
在main函数之前,调用了SystemInit();系统初始化,进入这个函数可以看到【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第3张图片
在系统复位后,默认系统时钟为 HSI,通过操作RCC时钟控制寄存器(RCC_CR)开启了HSI时钟使能位(位0),关闭时钟监测器(CSS):位19,关闭HSE时钟使能位(位16)和HSE 时钟旁路(HSE的第二种时钟输入源)(位18)
【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第4张图片
【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第5张图片
【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第6张图片
这里只是对时钟的默认配置,在SystemInit()函数最后调用了SetSysClock();
进入该函数就会发现,第一步它就重新使能了HSE时钟并等待时钟就绪,如果时钟就绪就会将HSEStatus置1,并操作后续的时钟总线分频,如果HSE初始化失败将会使用HSI。
● HCLK = SYSCLK / 1 (SYSCLK一般为最大值168Mhz)
● PCLK2 = HCLK / 2
● PCLK1 = HCLK / 4
注意:时钟总线的分频对F4系列不同MCU会有些许不同,SetSysClock()函数内部有大量的预编译操作,通过定义不同的MCU的宏,选择的配置是不一样的。
例如F407的宏为STM32F40_41xxx
【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第7张图片
总线分频之后配置主PLL参数

RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
               (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);

这里的计算是已经配好的,配置出最大SYSCLK时钟(168Mhz),而这里的PLL_M 其实是对输入的HSE/HSI时钟源进行分频,目的是将其分频为1Mhz方便后面计算出最大时钟,并且F407默认将其定义为25,就是外部HSE时钟的频率。可以打开system_stm32f4xx.c文件夹,也就是SystemInit()函数的最上方可以看到F4xx各系列时钟参数的注释。例如:

              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
   PLLI2S_N                               | NA
   PLLI2S_R                               | NA
   I2S input clock                        | NA
   VDD(V)                                 | 3.3  
   Main regulator output voltage          | Scale1 mode 
   Flash Latency(WS)                      | 5
   Prefetch Buffer                        | ON
   Instruction cache                      | ON
   Data cache                             | ON
   Require 48MHz for USB OTG FS,          | Disabled
   SDIO and RNG clock                     |

配置好主PLL参数后在通过RCC控制寄存器使能主PLL时钟,这样系统时钟就通过主PLL配置好了

!!!这里面有个细节如果是自己买的单片机或画的最小系统板,用的外部晶振可能不是系统默认的,这就会导致经过PLL_M分频后的时钟不是1Mhz,计算出来的系统时钟就不是168Mhz了。虽然对于一般的程序没什么不对劲,看不出哪里不对,但是对于一些需要准确时钟的通信接口:例如串口通信时,实际波特率和我们希望配置的波特率不符,接收和发送的数据就会有误(接收/发送的数据不匹配或失败)
解决方法:假如你的外部晶振是16Mhz的,而系统默认的为25Mhz,我们需要修改两个值:
1.HSE_VALUE:在stm32fxx.h中可以找到它的宏定义
【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第8张图片
修改它的值为你的外部晶振频率,HSE_VALUE的值表示HSE的时钟频率
2.PLL_M:第二个就是修改PLL锁相环的分频值,目的是分频出1Mhz的频率。
所以你的外部晶振是多少就将其修改为多少。可以直接搜索PLL_M找到其定义处(在stm32fxx.h中)
【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第9张图片
细心的朋友可能会发现对于F4不同系列的宏所定义的HSE_VALUE和PLL_M是不同的,它是通过一个全局宏定义来区分的
【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第10张图片
我用的是F407对应的宏是STM32F40_41xxx,不同型号的宏是不一样的,系统文件中有大量的预编译操作,方便F4不同型号的MCU选择,新建的工程是没有这个宏的,所以一定要加上,就在魔术棒C++栏的Define处。
至于宏USE_STDPERIPH_DRIVER则是将stm32f4xx_conf.h(标准外设驱动)
包含到stm32f4xx.h中来,如果不定义该宏程序将会出现大量错误
在这里插入图片描述
这样修改好HSE_VALUE和PLL_M,宏也是正确的,系统时钟就配置完成了(默认最大168Mhz)就不用担心时钟的问题了,可以愉快的玩耍了。
【单片机学习之旅】(2-1)stm32f4时钟系统及初始化过程_第11张图片
在stm32f4xx.h头部的注释中可以具体查看F4个型号对应的宏

总结

以上就是今天要讲的内容,本文仅仅简单介绍了时钟系统和初始化过程,更详细的内容请参考STM32F4xx中文参考手册及库函数系统文件。

你可能感兴趣的:(单片机,stm32,学习)