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的时钟树,很用以理解。
主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。
原文地址:https://www.cnblogs.com/vcan123/p/10424901.html