时钟介绍
众所周知,要让单片机的各个设备(如ADC、定时器TIMx……)正常工作,必须要为其提供一定的时钟频率,因此,为了能进行更多、更灵活的时钟配置,stm32单片机提供了了多个时钟源,通过对时钟源进行简单配置(如倍频、分频),然后提供给各个需要使用的设备,使其正常工作。
时钟树
图中的梯形符号可以理解为一个多入单出的开关,即从多个输入中选择一个输出
时钟源
从上图可以看出,stm32有5个时钟源:HSI、HSE、LSI、LSE、PLL(其实只有四个,因为PLL时钟并不能算“源”,因为它也是来源于HSI或者HSE,从时钟树中也能看出来)
说明:时钟源的字母"H"代表高速时钟,"L"代表低速时钟,"I"代表内部时钟,"E"代表外部时钟,外部时钟都有2个引脚引出去,用来接外部晶振。
高速内部时钟(HSI):由内部RC振荡器产生,频率为8MHz,但不稳定。
高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz,一般采用8MHz的晶振。
低速内部时钟(LSI):由内部RC振荡器产生,也主要提供给实时时钟模块,频率大约为40KHz。
低速外部时钟(LSE):以外部晶振作时钟源,主要提供给实时时钟模块,所以一般采用32.768KHz。
高速、低速时钟
高速时钟(HSE和HSI)提供给芯片主体的主时钟
低速时钟(LSE和LSI)只是提供给芯片中的RTC(实时时钟)及独立看门狗使用
从图中可以看出高速时钟也可以提供给RTC。
外部、内部时钟
内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默认使用内部高速时钟。
外部时钟信号是由外部的晶振输入的,在精度和稳定性上都有很大优势,所以上电之后我们再通过软件配置,转而采用外部时钟信号。
外部高速时钟HSE
- 从外部晶振(8MHz)接到OSC_OUT和OSC_IN引脚开始
- 经过第一个分频器PLLXTPRE(不分频或者分频——频率除以2)
- 遇到开关PLLSRC(PLL entry clock source),我们可以选择其输出,输出为外部高速时钟(HSE)或是内部高速时钟(HSI)。这里选择输出为HSE
- 接着遇到锁相环PLL,具有倍频作用,在这里我们可以输入倍频因子PLLMUL(可以是
2,3,4,...,16
),建议不要超过72MHz,但要是想超频,就得在这个寄存器上做手脚啦。
PLLCLK与SYSCLK时钟输出到外设
系统时钟SYSCLK经过一系列配置后提供给相应外设。
-
经过PLL的时钟称为PLLCLK。分频因子选1,即不分频;倍频因子我们设定为9倍频。也就是说,经过PLL之后,我们的时钟从原来HSE的8MHz变为72MHz的PLLCLK(8M/1 * 9=72M)
-
紧接着又遇到了一个开关SW,通过这个开关,可以切换SYSCLK的时钟源,可以选择为HSI、PLLCLK、HSE;经过这个开关之后就是STM32的系统时钟SYSCLK了。
-
我们选择PLLCLK时钟,所以得到SYSCLK为72MHz。PLLCLK在输入到SW前,还流向了USB预分频器,这个分频器输出为USB外设的时钟(USBCLK)
-
SYSCLK经过AHB预分频器,分频后再通过一些其它配置(如APB1,APB2分频等)输出到其它外设,给这些外设提供时钟
经过AHB预分频的时钟可以成为HCLK时钟
,经过APB1预分频的时钟称为PCLK1时钟
,经过APB2预分频的时钟称为PCLK2时钟
-
GPIO外设挂载在APB2总线上,所以把APB2预分频器设置为不分频,就可以得到GPIO外设的时钟也等于HCLK,为72MHz了
总结:
SYSCLK:系统时钟,STM32大部分器件的时钟来源。主要由AHB预分频器分配到各个部件,一般最大配置为72MHz(虽然可以超频,但不建议,因为会不稳定)
HCLK:由AHB预分频器直接输出得到,它是高速总线AHB的时钟信号,提供给存储器,DMA及cortex内核,是cortex内核运行的时钟,cpu主频就是这个信号,它的大小与STM32运算速度,数据存取速度密切相关。
FCLK:同样由AHB预分频器输出得到,是内核的“自由运行时钟”。“自由”表现在它不来自时钟 HCLK,因此在HCLK时钟停止时 FCLK 也继续运行。它的存在,可以保证在处理器休眠时,也能够采样和到中断和跟踪休眠事件 ,它与HCLK互相同步。
PCLK1:外设时钟,由APB1预分频器输出得到,最大频率为36MHz,提供给挂载在APB1总线上的外设
PCLK2:外设时钟,由APB2预分频器输出得到,最大72MHz,提供给挂载在APB2总线上的外设如GPIO
查看APBx总线挂载的外设
【在头文件 stm32f10x_rcc.h
头文件中指出了各个外设挂载在哪个总线上】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
#define RCC_APB2Periph_AFIO ((uint32_t)0x00000001) #define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004) #define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008) #define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010) #define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020) #define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040) #define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080) #define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100) #define RCC_APB2Periph_ADC1 ((uint32_t)0x00000200) #define RCC_APB2Periph_ADC2 ((uint32_t)0x00000400) #define RCC_APB2Periph_TIM1 ((uint32_t)0x00000800) #define RCC_APB2Periph_SPI1 ((uint32_t)0x00001000) #define RCC_APB2Periph_TIM8 ((uint32_t)0x00002000) #define RCC_APB2Periph_USART1 ((uint32_t)0x00004000) #define RCC_APB2Periph_ADC3 ((uint32_t)0x00008000) #define RCC_APB2Periph_TIM15 ((uint32_t)0x00010000) #define RCC_APB2Periph_TIM16 ((uint32_t)0x00020000) #define RCC_APB2Periph_TIM17 ((uint32_t)0x00040000) #define RCC_APB2Periph_TIM9 ((uint32_t)0x00080000) #define RCC_APB2Periph_TIM10 ((uint32_t)0x00100000) #define RCC_APB2Periph_TIM11 ((uint32_t)0x00200000)
#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00))
#define RCC_APB1Periph_TIM2 ((uint32_t)0x00000001) #define RCC_APB1Periph_TIM3 ((uint32_t)0x00000002) #define RCC_APB1Periph_TIM4 ((uint32_t)0x00000004) #define RCC_APB1Periph_TIM5 ((uint32_t)0x00000008) #define RCC_APB1Periph_TIM6 ((uint32_t)0x00000010) #define RCC_APB1Periph_TIM7 ((uint32_t)0x00000020) #define RCC_APB1Periph_TIM12 ((uint32_t)0x00000040) #define RCC_APB1Periph_TIM13 ((uint32_t)0x00000080) #define RCC_APB1Periph_TIM14 ((uint32_t)0x00000100) #define RCC_APB1Periph_WWDG ((uint32_t)0x00000800) #define RCC_APB1Periph_SPI2 ((uint32_t)0x00004000) #define RCC_APB1Periph_SPI3 ((uint32_t)0x00008000) #define RCC_APB1Periph_USART2 ((uint32_t)0x00020000) #define RCC_APB1Periph_USART3 ((uint32_t)0x00040000) #define RCC_APB1Periph_UART4 ((uint32_t)0x00080000) #define RCC_APB1Periph_UART5 ((uint32_t)0x00100000) #define RCC_APB1Periph_I2C1 ((uint32_t)0x00200000) #define RCC_APB1Periph_I2C2 ((uint32_t)0x00400000) #define RCC_APB1Periph_USB ((uint32_t)0x00800000) #define RCC_APB1Periph_CAN1 ((uint32_t)0x02000000) #define RCC_APB1Periph_CAN2 ((uint32_t)0x04000000) #define RCC_APB1Periph_BKP ((uint32_t)0x08000000) #define RCC_APB1Periph_PWR ((uint32_t)0x10000000) #define RCC_APB1Periph_DAC ((uint32_t)0x20000000) #define RCC_APB1Periph_CEC ((uint32_t)0x40000000)
|
时钟配置
系统初始化函数
系统在复位后,会首先调用系统初始化函数SystemInit()
进行初始化配置,包括一些时钟初始化配置等,之后才会调用 main 函数进行执行。
在执行 SystemInit()
函数后,配置的时钟如下所述:
SYSCLK(系统时钟) = 72MHZ
AHB总线时钟HCLK = 72MHZ
APB1总线时钟PCLK1 = 36MHZ
APB2总线时钟PCLK2 = 72MHZ
PLL主时钟PLLCLK = 72MHZ
(PLL2时钟 = 40MHZ)
系统时钟配置
配置顺序 HSE->PLL->SYSCLK
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void RCC_HSE_Config(u32 div,u32 pllm) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); if(RCC_WaitForHSEStartUp()==SUCCESS) { RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); RCC_PCLK2Config(RCC_HCLK_Div1); RCC_PLLConfig(div,pllm); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource()!=0x08); } }
|
RCC_PLLConfig(div,pllm)
就是配置从 HSE->PLL的过程中分频、倍频因子的值,如下图:
示例程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include "stm32f10x.h" #include "led.h"
void delay(u32 i){ while(i--); }
void RCC_HSE_Config(u32 div,u32 pllm) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); if(RCC_WaitForHSEStartUp()==SUCCESS) { RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); RCC_PCLK2Config(RCC_HCLK_Div1); RCC_PLLConfig(div,pllm); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource()!=0x08); } }
int main() { RCC_HSE_Config(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9); LED_Init(); while(1) { GPIO_ResetBits(LED1_PORT,LED1_PIN); delay(6000000); GPIO_SetBits(LED1_PORT,LED1_PIN); delay(6000000); } }
|