STM32H7时钟树RCC分析--- HAL库配置

Posted Z小旋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32H7时钟树RCC分析--- HAL库配置相关的知识,希望对你有一定的参考价值。

上一讲我们说了H7时钟树的一些基本概念,现在的话我们来用HAL库和CubeMx配置一下

再次说明,本文耗时较久,如果您想搞懂RCC初始化流程,请认真阅读,刚开始可能看不懂,但是仔细阅读之后绝对会有收获。

启动流程

H7的启动流程我们再说一下:

系统上电复位,进入启动文件 startup_stm32h743xx.s,在这个文件里面执行复位中断服务程序。

  • 在复位中断服务程序里面执行函数 SystemInit,此函数在文件 system_stm32h7xx.c 里面。
  • 之后是调用启动文件__main,最终进入到 main函数

进入到 main 函数就可以开始用户应用程序编程了。在程序运行之前需要:

  • MPU 初始化,需要用到库文件 stm32h7xx_hal_cortex.c 和 stm32h7xx_hal_cortex.h。
  • Cache 初始化,需要用到 core_cm7.h 文件。
  • HAL 库初始化函数 HAL_Init,对Systick滴答定时器初始化,需要用到文件 stm32h7xx_hal.c。
  • 系统时钟初始化,需要用到库文件 stm32h7xx_hal_rcc.c

要注意的是,Hal库的SystemInit函数对于时钟只做了:复位 RCC 时钟配置为默认复位值(默认开启 HSI) 并没有做其他的配置,所以时钟初始化还要用户自己配置。

我们今天关心的就是最后一步的系统时钟初始化

上面的四步,在执行完HAL_Init之后,系统还在用内部高速时钟 HSI,对于 H7 来说,HSI 主频是 64MHz

修改时钟配置

那么我们来看下如何修改时钟的配置:

第 1 步:在 stm32h7xx_hal_conf.h 文件配置 HSE_VALUE
配置的大小要跟板子的实际晶振大小匹配。

我使用的外部晶振是25Mhz的 所以配置为25000000

#if !defined (HSE_VALUE) 
#define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */

第 2 步:然后我们初始化时钟,最主要的就是两个结构体参数和两个函数:

RCC_ClkInitTypeDef RCC_ClkInitStruct;  // 配置时钟源相关参数
RCC_OscInitTypeDef RCC_OscInitStruct;  //配置系统时钟源及各个外设分频系数
 HAL_RCC_OscConfig()  //配置时钟源相关参数
 HAL_RCC_ClockConfig() //配置系统时钟源及各个外设分频系数

HAL_RCC_OscConfig()

HAL_RCC_OscConfig() , 该 函 数 在 HAL 库 关 键 头 文 件stm32h7xx_hal_rcc.h 中声明,在文件 stm32h7xx_hal_rcc.c 中定义。首先我们来看看该函数声明:

__weak HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);

只有一个RCC_OscInitTypeDef 结构体参数,我们来看下这个结构体:

typedef struct

 uint32_t OscillatorType; //需要选择配置的振荡器类型 
 uint32_t HSEState; //HSE 状态 
 uint32_t LSEState; //LSE 状态 
 uint32_t HSIState; //HIS 状态 
 uint32_t HSICalibrationValue; //HIS 校准值
 uint32_t LSIState; //LSI 状态 
uint32_t HSI48State //HSI48 的状态
uint32_t CSIState; //CSI 状态
uint32_t CSICalibrationValue; //CSI 校准值
 RCC_PLLInitTypeDef PLL; //PLL 配置
RCC_OscInitTypeDef;

第一个就是选择振荡器的类型,比如我们要开启 HSE,那么我们会设置 OscillatorType 的值为 RCC_OSCILLATORTYPE_HSE,然后设置 HSEState 的值为 RCC_HSE_ON 开启 HSE。对于其他时钟源 HSI,LSI 和 LSE,配置方法类似

最后有一个 RCC_PLLInitTypeDef PLL; //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
uint32_t PLLR; //PLL 分频系数 R
uint32_t PLLRGE; //PLL1 时钟输入范围
uint32_t PLLVCOSEL; //PLL1 时钟输出范围
uint32_t PLLFRACN; //PLL1 VCO 乘数因子的小数部分
RCC_PLLInitTypeDef;

这里的分频系数,全部都是PLL锁相环内部控制,我们要做的就是明白具体的公式就行

  • Fvco:VCO频率
  • Fsys:系统时钟频率,也是PLL1的p分频输出时钟频率 pll1_p_ck
  • Fpllq:PLL1的q分频输出时钟频率 pll1_q_ck
  • ref1_ck: PLL输入时钟频率,可以是HSI,CSI,HSE等.

  • plln:PLL1倍频系数(PLL倍频),取值范围:4~512.
  • pllm:PLL1预分频系数(进PLL之前的分频),取值范围:2~63.
  • pllp:PLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数)
  • pllq:PLL1的q分频系数(PLL之后的分频),取值范围:1~128.

VCO频率: Fvco= ref1_ck*(plln/pllm);
pll1_p_ck频率: Fsys=Fvco/pllp= ref1_ck*(plln/(pllm*pllp));
pll1_q_ck频率: Fpllq=Fvco/pllq= ref1_ck*(plln/(pllm*pllq));


具体的公式如下,上一篇也有讲解

HAL_RCC_ClockConfig()

接下来我们就要看 HAL_RCC_ClockConfig()函数,声明如下:

HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, 
uint32_t FLatency);

一共有两个参数:第一个入口参数 RCC_ClkInitStruct 是结构体 RCC_ClkInitTypeDef
指针类型,用来设置 SYSCLK 时钟源以及 SYSCLK、AHB,APB1、APB2、APB3 和 APB4 的分
频系数。第二个入口参数 FLatency 用来设置 FLASH 延迟

我们开看下RCC_ClkInitTypeDef 具体位置在stm32h7xx_hal_rcc.h 中声明

typedef struct

  uint32_t ClockType;             /*!< 要配置的时钟。
                              该参数可以是@ref RCC_System_Clock_Type 的值     */

  uint32_t SYSCLKSource;          /*!< 用作系统时钟的时钟源(SYSCLKS)*/

  uint32_t SYSCLKDivider;         /*!< 系统时钟分频    */

  uint32_t AHBCLKDivider;         /*!< AHB分频 */

  uint32_t APB3CLKDivider;        /*!APB3分频*/

  uint32_t APB1CLKDivider;        /*!APB1分频*/
  uint32_t APB2CLKDivider;        /*!APB2分频*/
  uint32_t APB4CLKDivider;      /*!APB4分频*/
RCC_ClkInitTypeDef;

第一个参数 ClockType 配置说明我们要配置的是 SYSCLK,HCLK、D1PCLK1(PCLK3)、
PCLK1、PCLK2 和 D3PCLK1(PCLK4)六个时钟。
第二个参数 SYSCLKSource 配置选择系统时钟源为 PLL。
第三个参数 SYSCLKDivider 配置 SYSCLK 分频系数
第四个参数 AHBCLKDivider 配置 AHB 分频系数
第五个参数 APB1CLKDivider 配置 APB1 分频系数
第六个参数 APB2CLKDivider 配置 APB2 分频系数
第七个参数 APB3CLKDivider 配置 APB3 分频系数
第八个参数 APB4CLKDivider 配置 APB4 分频系数

这个在下面有详细的介绍

然后我们看第二个参数:FLatency 用来设置 FLASH 延迟和调压器VOS

那就需要知道Flash 读操作

Flash 读操作 是每次读取完Flash数据,需要延时一定时间,确保之后的数据传输正常

VOS:在STM32H7中,系统Flash的受到调压器输出电压级别选择(VOS)的影响(在电源控制寄存器中)

为了得到最高的FLASH读取速度,我们需要设置VOS级别为1 然后等待数设置为4 就可以得到最高的Flash读取速度,ST 官方例程使用的就是 4 个 等待状态数

STM32H7xx参考手册 112页

最终代码如下:

都做了注释



/*
*********************************************************************************************************
*	函 数 名: SystemClock_Config
*	功能说明: 初始化系统时钟
*            	System Clock source            = PLL (HSE)
*            	SYSCLK(Hz)                     = 400000000 (CPU Clock)
*           	HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
*            	AHB Prescaler                  = 2
*            	D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
*            	D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
*            	D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
*            	D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
*            	HSE Frequency(Hz)              = 25000000
*            	VDD(V)                         = 3.3
*            	Flash Latency(WS)              = 4
*	形    参: 
*           plln:PLL1倍频系数(PLL倍频),取值范围:4~512.
*			pllm:PLL1预分频系数(进PLL之前的分频),取值范围:2~63.
*			pllp:PLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数)
*			pllq:PLL1的q分频系数(PLL之后的分频),取值范围:1~128.
*
*
*            Fvco:VCO频率
*            Fsys:系统时钟频率,也是PLL1的p分频输出时钟频率 pll1_p_ck
*            Fpllq:PLL1的q分频输出时钟频率  pll1_q_ck
*            ref1_ck: PLL输入时钟频率,可以是HSI,CSI,HSE等.
*
*
*            plln:PLL1倍频系数(PLL倍频),取值范围:4~512.
*			 pllm:PLL1预分频系数(进PLL之前的分频),取值范围:2~63.
*		     pllp:PLL1的p分频系数(PLL之后的分频),分频后作为系统时钟,取值范围:2~128.(且必须是2的倍数)
*			 pllq:PLL1的q分频系数(PLL之后的分频),取值范围:1~128.
*
*
*
*			 VCO频率: Fvco= ref1_ck*(plln/pllm);
*			 pll1_p_ck频率: Fsys=Fvco/pllp= ref1_ck*(plln/(pllm*pllp));
*			 pll1_q_ck频率: Fpllq=Fvco/pllq= ref1_ck*(plln/(pllm*pllq));
*
*			CPU频率(rcc_c_ck)=pll1_p_ck频率=400Mhz 
*			rcc_aclk=rcc_hclk3=200Mhz
*			AHB1/2/3/4(rcc_hclk1/2/3/4)=200Mhz  
*			APB1/2/3/4(rcc_pclk1/2/3/4)=100Mhz  
*			FMC时钟频率=pll2_r_ck=((25/25)*512/2)=256Mhz
*
*
*          外部晶振为25M的时候,推荐值:plln=160,pllm=5,pllp=2,pllq=2.
*          得到:Fvco=25*(160/5)=800Mhz
*          CPU频率Fsys= pll1_p_ck频率=800/2=400Mhz
*          pll1_q_ck频率=800/2=400Mhz
*	返 回 值: 无

*********************************************************************************************************
*/
void SystemClock_Config(u32 plln,u32 pllm,u32 pllp,u32 pllq)

	HAL_StatusTypeDef ret=HAL_OK;
	RCC_ClkInitTypeDef RCC_ClkInitStruct;
	RCC_OscInitTypeDef RCC_OscInitStruct;
	 /*使能供电配置更新 */
	MODIFY_REG(PWR->CR3,PWR_CR3_SCUEN, 0);
	
	/* 
      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
         详情看参考手册的Table 12的表格。
      2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    */
	__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

	while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY) 
  
		
		/* 使能HSE,并选择HSE作为PLL时钟源 */
	RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_HSE;
	RCC_OscInitStruct.HSEState=RCC_HSE_ON;
	RCC_OscInitStruct.HSIState=RCC_HSI_OFF;
	RCC_OscInitStruct.CSIState=RCC_CSI_OFF;
	RCC_OscInitStruct.PLL.PLLState=RCC_PLL_ON;
	RCC_OscInitStruct.PLL.PLLSource=RCC_PLLSOURCE_HSE;

	RCC_OscInitStruct.PLL.PLLN=plln;
	RCC_OscInitStruct.PLL.PLLM=pllm;
	RCC_OscInitStruct.PLL.PLLP=pllp;
	RCC_OscInitStruct.PLL.PLLQ=pllq;

	RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
	RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
	ret=HAL_RCC_OscConfig(&RCC_OscInitStruct);
	if(ret!=HAL_OK) while(1);
  
		
		
		/* 
       选择PLL的输出作为系统时钟
       配置RCC_CLOCKTYPE_SYSCLK系统时钟
       配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
       配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
       配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
       配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
       配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
		AHB 分频系数为 2,故其频率为HCLK=SYSCLK/2=200MHz。
		APB1 分频系数为 2,故其频率为 PCLK1=HCLK/2=100MHz。
		APB2分频系数为 2,故其频率为 PCLK2=HCLK/2=200/2=100MHz,
		APB3 分频系数为 2,故其频率PCLK3=HCLK/2=200/2=100MHz,
		APB4 的分频系数为 2,故其频率 PLCK4=HCLK/2=200/2=100MHz 
    */
		
	RCC_ClkInitStruct.ClockType=(RCC_CLOCKTYPE_SYSCLK|\\
                                RCC_CLOCKTYPE_HCLK |\\
                                RCC_CLOCKTYPE_D1PCLK1 |\\
                                RCC_CLOCKTYPE_PCLK1 |\\
                                RCC_CLOCKTYPE_PCLK2 |\\
                                RCC_CLOCKTYPE_D3PCLK1);

	RCC_ClkInitStruct.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;
	RCC_ClkInitStruct.SYSCLKDivider=RCC_SYSCLK_DIV1;
	RCC_ClkInitStruct.AHBCLKDivider=RCC_HCLK_DIV2;
	RCC_ClkInitStruct.APB1CLKDivider=RCC_APB1_DIV2; 
	RCC_ClkInitStruct.APB2CLKDivider=RCC_APB2_DIV2; 
	RCC_ClkInitStruct.APB3CLKDivider=RCC_APB3_DIV2;  
	RCC_ClkInitStruct.APB4CLKDivider=RCC_APB4_DIV2; 
	
	/* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
	ret=HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
	if(ret!=HAL_OK) while(1);



   /*   使用IO的高速模式,要使能IO补偿,即调用下面三个函数 
      (1)使能CSI clock
      (2)使能SYSCFG clock
      (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    */
	__HAL_RCC_CSI_ENABLE() ;

	__HAL_RCC_SYSCFG_CLK_ENABLE() ;

	HAL_EnableCompensationCell();



主函数调用:

	SystemClock_Config(160,5,2,4);            //设置时钟400Mhz 


以上是关于STM32H7时钟树RCC分析--- HAL库配置的主要内容,如果未能解决你的问题,请参考以下文章

STM32H7时钟树RCC分析--- CubeMx配置

STM32H7时钟树RCC分析--- CubeMx配置

STM32H7时钟树RCC分析---原理讲解

STM32H7时钟树RCC分析---原理讲解

STM32H7教程第91章 STM32H7的FDCAN总线基础知识和HAL库API

STM32H7教程第36章 STM32H7的LPTIM低功耗定时器基础知识和HAL库API