STM32H7 clock tree RCC analysis --- HAL library configuration (2)

In the last lecture, we talked about some basic concepts of the H7 clock tree. Now let's use the HAL library and CubeMx to configure it.

Once again, this article takes a long time. If you want to understand the RCC initialization process, please read it carefully. You may not understand it at first, but you will definitely gain something after reading it carefully.

start process

Let's talk about the startup process of H7:

The system is powered on and reset, enter the startup file startup_stm32h743xx.s, and execute the reset interrupt service routine in this file.

  • Execute the function SystemInit in the reset interrupt service routine , which is in the file system_stm32h7xx.c.
  • After that, the startup file __main is called, and finally the main function is entered.

Enter the main function to start user application programming. Before the program can run it needs:

  • MPU initialization requires the library files stm32h7xx_hal_cortex.c and stm32h7xx_hal_cortex.h.
  • Cache initialization requires the core_cm7.h file.
  • The HAL library initialization function HAL_Init , to initialize the Systick tick timer, requires the file stm32h7xx_hal.c.
  • System clock initialization requires the library file stm32h7xx_hal_rcc.c

It should be noted that the SystemInit function of the Hal library only does for the clock: reset the RCC clock configuration to the default reset value (the HSI is enabled by default) and does not do other configurations, so the clock initialization needs to be configured by the user.

What we care about today is the last step of system clock initialization

In the above four steps, after executing HAL_Init, the system is still using the internal high-speed clock HSI. For H7, the main frequency of HSI is 64MHz.

Modify the clock configuration

So let's see how to modify the configuration of the clock:

Step 1:
Configure the size of the HSE_VALUE configuration in the stm32h7xx_hal_conf.h file to match the actual crystal oscillator size of the board.

The external crystal I use is 25Mhz so the configuration is 25000000

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

Step 2: Then we initialize the clock, the main thing is two structure parameters and two functions:

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

HAL_RCC_OscConfig()

HAL_RCC_OscConfig(), this function is declared in the HAL library key header file stm32h7xx_hal_rcc.h and defined in the file stm32h7xx_hal_rcc.c. First let's look at the function declaration:

__weak HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);

There is only one RCC_OscInitTypeDef structure parameter, let's take a look at this structure:

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;

The first is to select the type of oscillator. For example, if we want to turn on HSE, then we will set the value of OscillatorType to RCC_OSCILLATORTYPE_HSE, and then set the value of HSEState to RCC_HSE_ON to turn on HSE. For other clock sources HSI, LSI and LSE, the configuration method is similar

Finally there is a RCC_PLLInitTypeDef PLL; //PLL configuration

This is to configure the phase-locked loop. The structure parameters are as follows

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;

The frequency division coefficients here are all controlled by the PLL phase-locked loop, all we have to do is to understand the specific formula

  • Fvco: VCO frequency
  • Fsys: system clock frequency, which is also the p-divided output clock frequency of PLL1 pll1_p_ck
  • Fpllq: q-divided output clock frequency of PLL1 pll1_q_ck
  • ref1_ck: PLL input clock frequency, can be HSI, CSI, HSE, etc.

  • plln: PLL1 frequency multiplication factor (PLL frequency multiplication), value range: 4~512.
  • pllm: PLL1 prescale coefficient (frequency division before entering PLL), value range: 2~63.
  • pllp: p frequency division coefficient of PLL1 (frequency division after PLL), which is used as system clock after frequency division, value range: 2~128. (And must be a multiple of 2)
  • pllq: q frequency division coefficient of PLL1 (frequency division after PLL), value range: 1~128.

insert image description here

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));

insert image description here
The specific formula is as follows, which is also explained in the previous article
insert image description here

HAL_RCC_ClockConfig()

Next we will look at the HAL_RCC_ClockConfig() function, which is declared as follows:

HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, 
uint32_t FLatency);

There are two parameters: the first entry parameter RCC_ClkInitStruct is the pointer type of the structure RCC_ClkInitTypeDef , which is used to set the SYSCLK clock source and the frequency division coefficients
of SYSCLK, AHB, APB1, APB2, APB3 and APB4 .
The second entry parameter FLatency is used to set the FLASH delay

Let's take a look at the specific location of RCC_ClkInitTypeDef declared in 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;

The first parameter ClockType configuration shows that we want to configure
six clocks of SYSCLK, HCLK, D1PCLK1(PCLK3), PCLK1, PCLK2 and D3PCLK1(PCLK4).
The second parameter SYSCLKSource configuration selects the system clock source as PLL.
The third parameter SYSCLKDivider configures the SYSCLK frequency division coefficient
The fourth parameter AHBCLKDivider configures the AHB frequency division coefficient
The fifth parameter APB1CLKDivider configures the APB1 frequency division coefficient
The sixth parameter APB2CLKDivider configures the APB2 frequency division coefficient The
seventh parameter APB3CLKDivider configures the APB3 frequency division coefficient
The eighth parameter APB4CLKDivider configures the APB4 frequency division factor

This is described in detail below

Then we look at the second parameter: FLatency is used to set the FLASH delay and the voltage regulator VOS

Then you need to know the Flash read operation

The Flash read operation is to read the Flash data every time, and it needs to delay for a certain time to ensure the normal data transmission afterward.

VOS: In STM32H7, the system Flash is affected by the voltage regulator output voltage level selection (VOS) (in the power control register)

In order to get the highest FLASH read speed, we need to set the VOS level to 1 and then set the wait count to 4 to get the highest flash read speed. The ST official routine uses 4 wait states.
insert image description here
STM32H7xx reference manual page 112

The final code is as follows:

annotated



/*
*********************************************************************************************************
*	函 数 名: 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();

}

Main function call:

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

Please add image description
Please add image description

Guess you like

Origin blog.csdn.net/as480133937/article/details/123623820