一、时钟树
A 为输入时钟源,可分为外部时钟源和内部时钟源,字母代表意义如下图,
因此共有四种时钟源,即
HSI(高速内部振荡器)、HSE(高速外部振荡器)、
LSI(低速内部振荡器)、LSE(低速外部振荡器);
B 为锁相环“PLL”;
C 为系统时钟源选择器,此项决定了 MCU 的系统主时钟“SYSCLK”的大小;
D 为Cortex-M 内核系统的时钟和使能单元;
E 为定时器以及其它外设的时钟源 APB1/APB2(Advanced Peripheral Bus);
F 为STM32 的MCO时钟输出功能;
1、时钟源
输入时钟源(Input Clock)主要包括 HSI,HSE,LSI,LSE。
外部时钟源就是从外部通过接晶振的方式获取的时钟源。内部时钟源是由内部 RC 振荡器产生。
芯片上电时默认由内部的 HSI 时钟启动。
2、锁相环PLL
锁相环(Phase-Locked Loop,简称PLL)是一种电子电路,通过反馈控制机制实现输出信号的频率和相位与输入参考信号的同步。
在STM32主控中,锁相环的作用主要有两个:输入时钟净化和倍频。
下图为使用PLL作为系统时钟源时的配置图,
标号①表示的是 PLL 时钟源的选择器,可选HSI 信号,或HSE信号;
标号②表示主PLL输入时钟的分频系数,可选0~63;
标号③表示配置锁相环倍频系数,即选择倍频因子,有三个参数PPL_N、PPL_N、PPL_Q;
3、系统时钟
STM32 的系统时钟是可配置的,它可以为 HSI、PLLCLK、HSE中的一个,通过 CFGR 的位 SW[1:0]设置。AHB(Advanced High-performance Bus)、APB1、APB2(Advanced Peripheral Bus)、内核时钟等时钟通过系统时钟分频得到。
下图为系统时钟配置图,
标号④为系统时钟输入源选择,可选HSI、PLLCLK、HSE中的一个;
标号⑤为AHB预分频器,可选择分频系数:1,2,4,8,16,32,64,128,256,512;
AHB总线时钟直接作为 GPIO(A\B\C\D\E\F\G\H\I\)、以太网、DCMI、FSMC、AHB总线、Cortex内核、存储器和 DMA 的 HCLK时钟,并作为 Cortex 内核自由运行时钟FCLK。
下面介绍一下由AHB(Advanced High-performance Bus)总线时钟得到的时钟:
标号⑥为APB1预分频器,分频因子可以选择 1, 2,4,8,16,
#注意:与APB1低速总线连接的外设有:看门狗定时器、定时器2/3/4/5/6/7、RTC 时钟、USART2/3/4/5、SPI2(I2S2)、SPI3(I2S3)、I2C1~3、CAN 、2个DAC。
标号⑦为APB2预分频器,分频因子可以选择 1, 2,4,8,16,
#注意:与 APB2高速总线连接的外设有:定时器1/8/9/10/11、SPI1、USART1和USART6、3个ADC和SDIO接口。
标号⑧决定了定时器时钟频率,该位由硬件自动设置,分为两种情况:
1、如果APB预分频器为 1,则定时器时钟频率等于APB域的频率;
2、否则,等于APB域的频率的两倍(×2)。
标号⑨是RTC时钟,其时钟源有三个途径:HSE/x(x = 2~31)、LSE 或 LSI。
二、时钟系统配置
STM32F407 默认的情况下,使用的是内部8M的HSI作为时钟源,所以不需要外部晶振也可以下载和运行代码。168MHz是官方推荐使用的最高的稳定时钟频率。
第 1 步:配置 HSE_VALUE
代码中通过使用宏定义的方式来选择 HSE_VALUE 的值是25M或者8M选择定义 HSE_VALUE 的值为 8M。代码如下:
#if !defined (HSE_VALUE)
#define HSE_VALUE 8000000U /*!< Value of the External oscillator in Hz */
#endif
#endif /* HSE_VALUE */
第 2 步:调用 SystemInit 函数
在系统启动之后,程序会先执行 SystemInit 函数,进行一些初始化配置。SystemInit 主要做了如下两个方面工作: 1) 外部存储器配置 2) 中断向量表地址配置。因此,是可以把一些重要的初始化放到 SystemInit 这里,在 main 函数运行前就把重要的一些初始化配置好,使得 main 函数更加简单,但对于初学者,我们暂时不建议这种用法。
HAL 库的 SystemInit 函数并没有任何时钟相关配置,所以后续的初始化步骤,还必须编写自己的时钟配置函数。
第 3 步:在 main 函数里调用用户编写的时钟设置函数
函数 sys_stm32_clock_init 就是用户的时钟系统配置函数。
uint8_t sys_stm32_clock_init(uint32_t plln, uint32_t pllm, uint32_t pllp, uint32_t pllq)
该函数除了配置 PLL 相关参数确定SYSCLK 值之外,还配置了 AHB、APB1 和 APB2 的分频系数,也就是确定了 HCLK,PCLK1 和 PCLK2 的时钟值。
使用 HAL 库配置 STM32F4 时钟系统的一般步骤为:
1)配置时钟源相关参数:调用函数HAL_RCC_OscConfig();
2)配置系统时钟源以及 SYSCLK、AHB、APB1 和APB2 的分频系数:调用函数 HAL_RCC_ClockConfig();
步骤详解:
步骤 1:配置时钟源相关参数。
调用HAL_RCC_OscConfig()函数
HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);
·函数描述
该函数用于配置时钟源。
·函数形参
形参1(RCC_OscInitTypeDef *RCC_OscInitStruct)为结构体 RCC_OscInitTypeDef 类型指针,定义如下:
typedef struct
{
uint32_t OscillatorType; /* 需要选择配置的振荡器类型 */
uint32_t HSEState; /* HSE 状态 */
uint32_t LSEState; /* LSE 状态 */
uint32_t HSIState; /* HSI 状态 */
uint32_t HSICalibrationValue; /* HSI 校准值 */
uint32_t LSIState; /* LSI 状态 */
RCC_PLLInitTypeDef PLL; /* PLL 配置 */
}RCC_OscInitTypeDef;
该结构体前面几个参数主要是用来选择配置的振荡器类型。比如要开启 HSE,那么就要设置OscillatorType的值为RCC_OSCILLATORTYPE_HSE,然后设置 HSEState 的值为 RCC_HSE_ON开启HSE。对于HSI、LSI、LSE,配置方法类似。
RCC_OscInitTypeDef 这个结构体还有一个很重要的成员变量是 PLL,它是结构体 RCC_PLLInitTypeDef 类型。它的作用是配置 PLL 相关参数,它的定义如下:
typedef struct
{
uint32_t PLLState; /* PLL 状态 */
uint32_t PLLSource; /* PLL 时钟源 */
uint32_t PLLM; /* PLL 倍频系数 M */
uint32_t PLLN; /* PLL 倍频系数 N */
uint32_t PLLP; /* PLL 倍频系数 P */
uint32_t PLLQ; /* PLL 倍频系数 Q */
}RCC_PLLInitTypeDef;
该结构体主要用来设置 PLL 时钟源以及相关分频倍频参数,结构体定义的相关内容结合时钟树中红色框的内容更容易理解。
举例:时钟初始化函数 sys_stm32_clock_init 中的实际应用配置内容如下:
/*使能HSE,并选择HSE作为PLL时钟源,配置PLL1,开启USB时钟*/
rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE;
rcc_osc_init.HSEState = RCC_HSE_ON; /* 打开 HSE */
rcc_osc_init.PLL.PLLState = RCC_PLL_ON; /* 打开 PLL */
rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE; /* PLL 时钟源选择 HSE */
rcc_osc_init.PLL.PLLN = plln;
rcc_osc_init.PLL.PLLM = pllm;
rcc_osc_init.PLL.PLLP = pllp;
rcc_osc_init.PLL.PLLQ = pllq;
通过该段程序,我们开启了 HSE 时钟源,同时选择 PLL 时钟源为 HSE,然后把 sys_stm32_clock_init 的形参直接设置作为 PLL 的参数 M 的值,这样就达到了设置 PLL 时钟源 相关参数的目的。
设置好PLL时钟源参数之后,也就确定了PLL的时钟频率,然后可以进行步骤2。
步骤 2:配置系统时钟源,以及 SYSCLK、AHB、APB1 和 APB2 相关参数。
调用HAL_RCC_ClockConfig()函数
HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct,
uint32_t FLatency);
·函数描述
该函数用于配置系统时钟源以及 SYSCLK、AHB、APB1 和 APB2 的分频系数。
·函数形参
形参1(RCC_ClkInitTypeDef *RCC_ClkInitStruct)是结构体 RCC_ClkInitTypeDef 类型指针变量,用于设置SYSCLK时钟源以及SYSCLK、AHB、APB1 和 APB2 的分频系数。
形参2(uint32_t FLatency)用于设置 FLASH 延迟。
RCC_ClkInitTypeDef 结构体类型定义如下:
typedef struct
{
uint32_t ClockType; /* 要配置的时钟 */
uint32_t SYSCLKSource; /* 系统时钟源 */
uint32_t AHBCLKDivider; /* AHB 分频系数 */
uint32_t APB1CLKDivider; /* APB1 分频系数 */
uint32_t APB2CLKDivider; /* APB2 分频系数 */
}RCC_ClkInitTypeDef;
举例:sys_stm32_clock_init 函数中实际应用配置内容如下:
/* 选中 PLL 作为系统时钟源并且配置 HCLK,PCLK1 和 PCLK2*/
rcc_clk_init.ClockType = ( RCC_CLOCKTYPE_SYSCLK \
| RCC_CLOCKTYPE_HCLK \
| RCC_CLOCKTYPE_PCLK1 \
| RCC_CLOCKTYPE_PCLK2);
/* 设置系统时钟时钟源为 PLL */
rcc_clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1; /* AHB 分频系数为 1 */ rcc_clk_init.APB1CLKDivider = RCC_HCLK_DIV4; /* APB1 分频系数为 4 */ rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV2; /* APB2 分频系数为 2 */
sys_stm32_clock_init 函数中的 RCC_ClkInitTypeDef 结构体配置内容:
第一个参数 ClockType 配置表示我们要配置的是 SYSCLK、HCLK、PCLK1 和 PCLK 四个 时钟。
第二个参数 SYSCLKSource 配置选择系统时钟源为 PLLCLK。
第三个参数 AHBCLKDivider 配置 AHB 分频系数为 1。
第四个参数 APB1CLKDivider 配置 APB1 分频系数为 4。
第五个参数 APB2CLKDivider 配置 APB2 分频系数为 2。
至此,mian 函数中便可直接调用sys_stm32_clock_init()函数,并且设置它的四个形参。
举例:假如我们在 mian 函数中调用 sys_stm32_clock_init(336, 8, 2, 7),则可以计算出,PLL 时钟为 PLLCLK = HSE * 336 / 8 / 2 = 168MHz。
总结
1、STM32F407时钟系统系统
2、时钟系统配置:
①宏定义 HSE_VALUE;
②调用HAL_RCC_OscConfig()函数,配置输入时钟源;
③调用HAL_RCC_ClockConfig()函数,配置系统时钟源以及 SYSCLK、AHB、APB1 和 APB2 相关参数;
④在main函数中调用sys_stm32_clock_init()函数并设置形参;
#注意:②和③中的函数均在sys_stm32_clock_init()函数中调用,main函数中只调用sys_stm32_clock_init()函数,且sys_stm32_clock_init()函数的描述在sys.c中,main.c中只配置该函数的四个形参。