HSE 是高速外部时钟信号,可以由有源晶振或者无源晶振提供,频率为4~16MHz。当使用有源晶振时,时钟从OSC_IN引脚进入,OSC_OUT引脚悬空;当使用无源晶振时,时钟从OSC_IN和OSC_OUT进入,并且要配谐振电容。
HSE最常使用的是8MHz的无源晶振。当确定PLL时钟来源的时候,HSE 可以不分频或者2分频,这个由时钟配置寄存器CFGR的位17,即PLLXTRE设置。
PLL时钟来源可以有两个:一个是HSE,另一个是HSI/2。由时钟配置寄存器CFGR的位16,即PLLSRC设置具体用哪个。HSI是内部高速的时钟信号,频率为8MHz,这里我们选择HSE作为PLL的时钟来源。
通过设置PLL的倍频因子,可以对PLL的时钟来源进行倍频,倍频因子可以是2至16,由时钟配置寄存器CFGR的位21~18,即PLLMUL[3:0]设置,这里设置为9倍频,因为上一步设置PLL的时钟来源为HSE=8MHz,所以PLLCLK=8M*9=72M。72M是ST官方推荐的稳定运行时钟,可以增大倍频因子实现超频,最大为128MHz。
由图可知系统时钟的来源可以是HSI,PLLCLK,HSE,具体由时钟配置寄存器CFGR的为1~0,即SW[1:0]设置。这里设置系统时钟:SYSCLK=PLLCLK=72MHz。
系统时钟SYSCLK经过AHB预分频器分频之后得到的时钟叫APB总线时钟,即HCLK,分频因子可以是[1,2,4,8,16,64,128,256,512],具体由时钟配置寄存器CFGR的位7~4,即HPRE[3:0]设置。这里设置为1分频,即HCLK=SYSCLK=72MHz。
APB1总线时钟PCLK1由HCLK经过低速APB2预分频器得到,分频因子可以是[1,2,4,8,16],具体由时钟配置寄存器CFGR的位10~8,即PRRE1[2:0]设置。HCLK1属于低速的总线时钟,最高为36MHz。片上低速外设就挂载到这条总线上,比如USART2/3/4/5,SPI2/3,IIC1/IIC2等。这里设置2分频,即PCLK1=HCLK/2=36MHz。
APB2总线时钟PCLK2由HCLK经过高速APB2预分频器得到,分频因子可以是[1,2,4,8,16],具体由时钟配置寄存器CFGR的位13~11,即PPRE2[2:0]设置。HCLK2属于高速的总线时钟,片上高速外设就挂载到这条总线上,比如全部GPIO,USART1,SPI1等。这里设置1分频,即PCLK2=HCLK=72MHz。
上面的7个步骤对应的设置系统时钟的库函数,该函数截取自system_stm32f10x.c。
由STM32时钟树可知,USB时钟是由 PLLCLK经过USB预分频器得到分频因子可以是1或1.5,具体由时钟配置寄存器CFGR的位22,即USBPRE配置。USB的时钟最高是48MHz,所以PLLCLK只能是48MHz或者72MHz。PLLCLK只能是由HSE倍频得到,不能使用HSI倍频。
Cortex系统时钟由HCLK 8 分频得到,等于9MHz,Cortex系统时钟用来驱动内核的系统定时器SysTick,SysTick一般用于操作系统的时钟节拍,或普通定时。
ADC时钟由PCLK2经过预分频得到,分频因子可以是[2,4,6,8],具体由时钟配置寄存器CFGR的位15~14,即ADCPRE[1:0]决定。
RTC时钟可由HSE/128分频得到,也可由低速外部时钟信号LSE提供,频率为32.768kHz,也可由低速内部时钟信号HSI提供,由备份域控制寄存器BDCR的位9~8,即RTCSEL[1:0]配置。独立看门狗的时钟只能由LSI提供,LSI是低速的内部时钟信号,频率为30至60kHz,一般取40kHz。
MCO是Microcontrolller Clock Output 的缩写,是微控制器时钟输出引脚,在STM32F1系列中由PA8复用所得,主要作用是对外提供时钟,相当于有源晶振。MCO的时钟来源可以是:PLLCLK/2,HSI,HSE,SYSCLK,具体选哪一个由时钟配置寄存器CFGR的位26~24,即MCO[2:0]决定。
void HSE_SetSysClock(uint32_t pllmul)
{
__IO uint32_t StartUpCounter = 0, HSEStartUpStatus = 0;
//把RCC外设初始化成复位状态
RCC_DeInit();
//使能HSE,开启外部晶振,
RCC_HSEConfig(RCC_HSE_ON);
//等待SHE启动稳定
HSEStartUpStatus = RCC_WaitForHSEStartUp();
//当HSE稳定之后继续往下执行
if(HSEStartUpStatus == SUCCESS)
{
//使能Flash预存取缓存区
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//设置SYSCLK周期与Flash访问时间的比例,这里统一设置成2
//0: 0
FLASH_SetLatency(FLASH_Latency_2);
//AHB预分频因子设置为1,HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//APB2预分频因子设置为1,PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
//APB1预分频因子设置为2,PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PLLConfig(RCC_PLLSource_HSI_Div2,pllmul);
//开启PLL
RCC_PLLCmd(ENABLE);
//等待PLL稳定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
//当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//读取时钟切换状态位,确保PLLCLK被选为系统时钟
while (RCC_GetSYSCLKSource()!=0x08)
{
}
}
else
{
while(1)
{
}
}
}
这个函数使用库函数编写,有个形参pllmul,用来设置PLL的倍频因子。
函数调用举例:HSE_SetSysClock(RCC_PLLMul_9); 设置时钟为:8MHz * 9 = 72MHz
HSE_SetSysClock(RCC_PLLMul_16); 设置时钟为:8MHz * 16 = 128MHz
void HSI_SetSysClock(uint32_t pllmul)
{
__IO uint32_t HSIStartUpStatus = 0;
//把RCC外设初始化成复位状态
RCC_DeInit();
//使能HSI
RCC_HSICmd(ENABLE);
//等待SHI启动稳定
HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
//当HSI稳定之后继续往下执行
if(HSIStartUpStatus == RCC_CR_HSIRDY)
{
//使能Flash预存取缓存区
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//设置SYSCLK周期与Flash访问时间的比例,这里统一设置成2
//0: 0
FLASH_SetLatency(FLASH_Latency_2);
//AHB预分频因子设置为1,HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//APB2预分频因子设置为1,PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
//APB1预分频因子设置为2,PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PLLConfig(RCC_PLLSource_HSI_Div2,pllmul);
//开启PLL
RCC_PLLCmd(ENABLE);
//等待PLL稳定
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
//当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//读取时钟切换状态位,确保PLLCLK被选为系统时钟
while (RCC_GetSYSCLKSource()!=0x08)
{
}
}
else
{
while(1)
{
}
}
}
HSI设置系统时钟函数与SHE设置系统时钟函数原理上一样,但HSI必须2分频之后才能作为PLL的时钟来源。所以使用HSI时,最大系统时钟SYSCL只能是HSI/216=416=64MHz.函数调用举例:HSI_SetSysClock(RCC_PLLMul_9);设置系统时钟为:4MHz * 9=36MHz。
void Delay(__IO uint32_t nCount)
{
for(;nCount !=0; nCount--);
}
在STM32F103系列中,PA8可以复用为MCO引脚,对外提供时钟输出。
void MCO_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//开启GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//选择GPIO8引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
//设置为复用推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//设置IO的反转速率为50MHz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//初始化GPIOA8
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
//MCO引脚输出可以是HSE,HSI,PLLCL/2,SYSCLK
//RCC_MCOConfig(RCC_MCO_HSE);
//RCC_MCOConfig(RCC_MCO_HSI);
//RCC_MCOConfig(RCC_MCO_PLLCLK_Div2);
RCC_MCOConfig(RCC_MCO_SYSCLK);
int main(void)
{
//程序来到main函数之前,启动文件statup_stm32f10x_hd.s已经调用
//SystemInit()函数把系统时钟初始化成72MHz
//SystemInit()在system_stm32f10x.c中定义
//重新设置系统时钟,这时候可以选择是使用HSE还是HSI
//使用HSE时,SYSCLK = 8MHz * RCC_PLLMul_x,x为2~16,最高是128MHZ
HSE_SetSysClock(RCC_PLLMul_9);
//使用HSI时,SYSCLK = 4MHz * RCC_PLLMul_x,x为2~16,最高是64MHZ
//HSI_SetSysClock(RCC_PLLMul_16);
//MCO引脚初始化
MCO_GPIO_Config();
RCC_MCOConfig(RCC_MCO_SYSCLK);
//LED端口初始化
LED_GPIO_Config();
while(1)
{
LED1(ON);
Delay(0x0FFFFF);
LED1(OFF);
Delay(0x0FFFFF);
}
}