关于嵌入式学习随笔->4《F7系统时钟》

1、STM32有5个时钟源:HSI、HSE、LSI、LSE、PLL。

--》HSI是高速内部时钟,RC振荡器,频率为16MHz,精度不高。可以直接作为系统时钟或者用作PLL时钟输入。

--》HSE是告诉外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~26MHz。

--》LSI是低速内部时钟,RC振荡器,频率为32KHz,提供低功耗时钟。LSI主要可以作为IWDG独立看门狗时钟,LPTimer低功耗定时器时钟以及RTC时钟。

--》LSE是低速外部时钟,接频率为32.768KHz的石英晶体。为RTC提供精准的时钟频率。

--》PLL为锁相环倍频输出。

STM32F767内部时钟最快可达216MHz,这就是用的PLL倍频输出。其实尤其时钟电路可以知道,系统时钟由HSI、HSE、PLL提供,如果HSE出现故障,那么就会由HSI提供系统时钟源,虽然很慢,但是仍然可以是系统正常运行。下边是STM32F767的时钟树,很用以理解。

F767时钟树

   主PLL时钟计算:PLCK = HSE * N / ( M * P ),这个公式可以由电路图导出。(一般用的是外部告诉时钟,xN倍频一般为50~432,R分频系数一般为2~7,Q分频系数一般为2~15)

2、任何一个外设在使用前必须首先要使能相应的时钟

其实任何一个电路的逻辑控制器都是通过一个寄存器来控制的。RCC控制时钟的相关寄存器定义在RCC_TypeDef中。

  1 typedef struct
  2 {
  3   __IO uint32_t CR;            /*!< RCC clock control register,                                  Address offset: 0x00 */
  4   __IO uint32_t PLLCFGR;       /*!< RCC PLL configuration register,                              Address offset: 0x04 */
  5   __IO uint32_t CFGR;          /*!< RCC clock configuration register,                            Address offset: 0x08 */
  6   __IO uint32_t CIR;           /*!< RCC clock interrupt register,                                Address offset: 0x0C */
  7   __IO uint32_t AHB1RSTR;      /*!< RCC AHB1 peripheral reset register,                          Address offset: 0x10 */
  8   __IO uint32_t AHB2RSTR;      /*!< RCC AHB2 peripheral reset register,                          Address offset: 0x14 */
  9   __IO uint32_t AHB3RSTR;      /*!< RCC AHB3 peripheral reset register,                          Address offset: 0x18 */
 10   uint32_t      RESERVED0;     /*!< Reserved, 0x1C                                                                    */
 11   __IO uint32_t APB1RSTR;      /*!< RCC APB1 peripheral reset register,                          Address offset: 0x20 */
 12   __IO uint32_t APB2RSTR;      /*!< RCC APB2 peripheral reset register,                          Address offset: 0x24 */
 13   uint32_t      RESERVED1[2];  /*!< Reserved, 0x28-0x2C                                                               */
 14   __IO uint32_t AHB1ENR;       /*!< RCC AHB1 peripheral clock register,                          Address offset: 0x30 */
 15   __IO uint32_t AHB2ENR;       /*!< RCC AHB2 peripheral clock register,                          Address offset: 0x34 */
 16   __IO uint32_t AHB3ENR;       /*!< RCC AHB3 peripheral clock register,                          Address offset: 0x38 */
 17   uint32_t      RESERVED2;     /*!< Reserved, 0x3C                                                                    */
 18   __IO uint32_t APB1ENR;       /*!< RCC APB1 peripheral clock enable register,                   Address offset: 0x40 */
 19   __IO uint32_t APB2ENR;       /*!< RCC APB2 peripheral clock enable register,                   Address offset: 0x44 */
 20   uint32_t      RESERVED3[2];  /*!< Reserved, 0x48-0x4C                                                               */
 21   __IO uint32_t AHB1LPENR;     /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */
 22   __IO uint32_t AHB2LPENR;     /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */
 23   __IO uint32_t AHB3LPENR;     /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */
 24   uint32_t      RESERVED4;     /*!< Reserved, 0x5C                                                                    */
 25   __IO uint32_t APB1LPENR;     /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */
 26   __IO uint32_t APB2LPENR;     /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */
 27   uint32_t      RESERVED5[2];  /*!< Reserved, 0x68-0x6C                                                               */
 28   __IO uint32_t BDCR;          /*!< RCC Backup domain control register,                          Address offset: 0x70 */
 29   __IO uint32_t CSR;           /*!< RCC clock control & status register,                         Address offset: 0x74 */
 30   uint32_t      RESERVED6[2];  /*!< Reserved, 0x78-0x7C                                                               */
 31   __IO uint32_t SSCGR;         /*!< RCC spread spectrum clock generation register,               Address offset: 0x80 */
 32   __IO uint32_t PLLI2SCFGR;    /*!< RCC PLLI2S configuration register,                           Address offset: 0x84 */
 33   __IO uint32_t PLLSAICFGR;    /*!< RCC PLLSAI configuration register,                           Address offset: 0x88 */
 34   __IO uint32_t DCKCFGR1;      /*!< RCC Dedicated Clocks configuration register1,                 Address offset: 0x8C */
 35   __IO uint32_t DCKCFGR2;      /*!< RCC Dedicated Clocks configuration register 2,               Address offset: 0x90 */
 36 
 37 } RCC_TypeDef;
结构体:RCC_TypeDef

3、时钟系统初始化(程序角度HAL库)

在STM32的启动文件中都会由如下代码段(我们称之为引导代码):

  1 Reset_Handler    PROC
  2                  EXPORT  Reset_Handler             [WEAK]
  3         IMPORT  SystemInit
  4         IMPORT  __main
  5 
  6                  LDR     R0, =SystemInit
  7                  BLX     R0
  8                  LDR     R0, =__main
  9                  BX      R0
 10                  ENDP;

     很容易看出,在执行__main函数之前需要先执行SystemInit函数进行系统初始化,而不是我们自认为的会直接到

main函数开始执行(注意__main函数与main函数有区别,详细区别参见之后的STM32之启动文件讲解,暂时未出),总之我们这里只要知道在main函数执行之前系统要先执行SystemInit函数,其定义为:

  1 void SystemInit(void)
  2 {
  3   /* FPU settings ------------------------------------------------------------*/
  4   #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
  5     SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  6   #endif
  7   /* Reset the RCC clock configuration to the default reset state ------------*/
  8   /* Set HSION bit */
  9   RCC->CR |= (uint32_t)0x00000001;
 10 
 11   /* Reset CFGR register */
 12   RCC->CFGR = 0x00000000;
 13 
 14   /* Reset HSEON, CSSON and PLLON bits */
 15   RCC->CR &= (uint32_t)0xFEF6FFFF;
 16 
 17   /* Reset PLLCFGR register */
 18   RCC->PLLCFGR = 0x24003010;
 19 
 20   /* Reset HSEBYP bit */
 21   RCC->CR &= (uint32_t)0xFFFBFFFF;
 22 
 23   /* Disable all interrupts */
 24   RCC->CIR = 0x00000000;
 25 
 26   /* Configure the Vector Table location add offset address ------------------*/
 27 #ifdef VECT_TAB_SRAM
 28   SCB->VTOR = RAMDTCM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
 29 #else
 30   SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
 31 #endif
 32 }

SystemInit函数主要有三个功能:

--》首先是对FPU的设置,关于浮点运算的设置。

--》其次就是对一些寄存器的默认配置,也就是将其设置为初始值。

其中  RCC->CR |= (uint32_t)0x00000001;     /* Set HSION bit */  目的就是设置系统默认时钟为高速内部时钟(HSI),因为在刚开始时系统不确定你是否使用了外部时钟,在这里会进行初试默认值的设置,包括其他寄存器,在这里都有设置开始时的默认值。

--》最后是对中断像量表的地址的设置,包括他的基地址和偏移地址。

HAL库中没有对于系统时钟的设置,只是有个初始值,而在标准库(M3,M4所使用的)中则实在系统初始化函数中对系统时钟进行设置的,不需要用户单独去写时钟配置的函数。

HAL库中系统时钟配置的一般步骤(stm32_clock_init函数):

1—>使能PWR时钟,调用:

  1     /* Enable Power Clock*/
  2     __HAL_RCC_PWR_CLK_ENABLE();

2—>设置调压器输出电压级别,调用:

  1     /* Set Range */
  2     __HAL_PWR_VOLTAGESCALING_CONFIG(VoltageScaling);

3—>选择是否开启Over-Driver功能,调用:

  1 HAL_PWREx_EnableOverDrive(); //开启Over-Driver功能

4—>配置时钟源相关参数,调用:

  1 HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化

5—>配置系统时钟源以及AHB、APB1和APB2的分频系数,调用函数:

  1 HAL_RCC_ClockConfig();

最主要的两个配置函数:

  1 HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);//时钟源配置函数
  2 HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency);//时钟配置函数

时钟源配置函数入口参数时钟源结构体定义:

  1 typedef struct
  2 {
  3   uint32_t OscillatorType;       /*!< The oscillators to be configured.
  4                                       This parameter can be a value of @ref RCC_Oscillator_Type                   */
  5 
  6   uint32_t HSEState;             /*!< The new state of the HSE.
  7                                       This parameter can be a value of @ref RCC_HSE_Config                        */
  8 
  9   uint32_t LSEState;             /*!< The new state of the LSE.
 10                                       This parameter can be a value of @ref RCC_LSE_Config                        */
 11 
 12   uint32_t HSIState;             /*!< The new state of the HSI.
 13                                       This parameter can be a value of @ref RCC_HSI_Config                        */
 14 
 15   uint32_t HSICalibrationValue;   /*!< The HSI calibration trimming value (default is RCC_HSICALIBRATION_DEFAULT).
 16                                        This parameter must be a number between Min_Data = 0x00 and Max_Data = 0x1F */
 17 
 18   uint32_t LSIState;             /*!< The new state of the LSI.
 19                                       This parameter can be a value of @ref RCC_LSI_Config                        */
 20 
 21   RCC_PLLInitTypeDef PLL;        /*!< PLL structure parameters                                                    */
 22 
 23 }RCC_OscInitTypeDef;

     刚好有5个时钟源:HSI、HSE、LSI、LSE、PLL,其中在时钟源配置函数中要先对时钟源类型,在进行相应的时钟源配置,而不同的时钟原配置所需要的操作是不同的,因此这里就要先用时钟源类型来进行判断。要设置主PLL参数就又需要一个结构体来实现(RCC_PLLInitTypeDef ):

  1 typedef struct
  2 {
  3   uint32_t PLLState;   /*!< The new state of the PLL.
  4                             This parameter can be a value of @ref RCC_PLL_Config                      */
  5 
  6   uint32_t PLLSource;  /*!< RCC_PLLSource: PLL entry clock source.
  7                             This parameter must be a value of @ref RCC_PLL_Clock_Source               */
  8 
  9   uint32_t PLLM;       /*!< PLLM: Division factor for PLL VCO input clock.
 10                             This parameter must be a number between Min_Data = 2 and Max_Data = 63    */
 11 
 12   uint32_t PLLN;       /*!< PLLN: Multiplication factor for PLL VCO output clock.
 13                             This parameter must be a number between Min_Data = 50 and Max_Data = 432  */
 14 
 15   uint32_t PLLP;       /*!< PLLP: Division factor for main system clock (SYSCLK).
 16                             This parameter must be a value of @ref RCC_PLLP_Clock_Divider             */
 17 
 18   uint32_t PLLQ;       /*!< PLLQ: Division factor for OTG FS, SDMMC and RNG clocks.
 19                             This parameter must be a number between Min_Data = 2 and Max_Data = 15    */
 20 #if defined (STM32F765xx) || defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx) || defined (STM32F779xx)
 21   uint32_t PLLR;       /*!< PLLR: Division factor for DSI clock.
 22                             This parameter must be a number between Min_Data = 2 and Max_Data = 7    */
 23 #endif /* STM32F767xx || STM32F769xx || STM32F777xx || STM32F779xx */
 24 
 25 }RCC_PLLInitTypeDef;

     这些程序代码结合最上边的时钟树就可以很容易的理解了,其中PLLM、PLLN、PLLP、PLLQ、PLLR与时钟树中主PLL中参数M、N、P、Q、R一一对应。

以下代码为时钟源配置相关代码(HAL_RCC_OscConfig函数配置方法):

  1     RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE;    //时钟源为HSE
  2     RCC_OscInitStructure.HSEState=RCC_HSE_ON;                      //打开HSE
  3     RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;				   //打开PLL
  4     RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE;          //PLL时钟源选择HSE
  5     RCC_OscInitStructure.PLL.PLLM=pllm;	//主PLL和音频PLL分频系数(PLL之前的分频)
  6     RCC_OscInitStructure.PLL.PLLN=plln; //主PLL倍频系数(PLL倍频)
  7     RCC_OscInitStructure.PLL.PLLP=pllp; //系统时钟的主PLL分频系数(PLL之后的分频)
  8     RCC_OscInitStructure.PLL.PLLQ=pllq; //USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频)
  9     ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化

时钟配置函数相关代码(HAL_RCC_ClockConfig函数配置方法):

  1     //选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
  2     RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
  3     RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;//设置系统时钟时钟源为PLL
  4     RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;//AHB分频系数为1
  5     RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV4;//APB1分频系数为4
  6     RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV2;//APB2分频系数为2
  7     ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_7);//同时设置FLASH延时周期为7WS,也就是8个CPU周期。

这样就将系统时钟配置好了,注意与时钟树相结合,更容易理解。

     而要想达到最大时钟频率216MHz那么就需要我们开启Over-Driver的功能,还要设置调压器的级别为1。这样还没有设置完全,还有一个等待周期的设置,一般电压范围确定后等待周期也就固定了,这个参数的配置其实就是时钟入口参数的第二个值FLatency。

猜你喜欢

转载自www.cnblogs.com/vcan123/p/10424901.html
F7