剖析STM32F103时钟系统
本文所有代码全部使用寄存器方式实现,因为直接操作寄存器更清晰。测试使用的芯片是STM32F103ZET6,硬件LED接在PB5管脚上,芯片外接晶振为8M晶振。
1.STM32F103时钟树,想要了解时钟系统必须看懂时钟树。如下图
PLLSRC:PLL(锁相环)时钟源选择有两个。
1.选择内部RC振荡器2分频输出;
2.选择外部晶振时钟;当选择外部晶振时钟时,通过配置PLLXPTRE 可以是外部晶振不分频直接输出到PLL时钟源,也可以是外部晶振2分频输出到PLL时钟源。
PLLMUL:PLL的倍频系数。
SW:SYSCLK时钟源选择有3种。
1.内部8M的RC振荡器输出到SYSCLK。
2.锁相环输出到SYSCLK。
3.外部晶振输出到SYSCLK。
SYSCLK时钟经过AHP分频器输出到AHB。AHB总线挂有SDIO,FSMC,内核,内存,DMA等高速设备,
AHB时钟经过APB1分频器输出到APB1。APB1总线挂接的都是低速设备,比如TIMER2,3,4,5,6,7。
AHB时钟经过APB2分频器输出到APB2。APB2总线挂接的是高速设备,比如TIMER1,8
SYSCLK输出到AHB,AHB输出到APB1和APB2.
CSS:CSS是时钟安全系统。如果使能了CSS,并且外部高速时钟发生故障,那么系统时钟会自动切换到内部RC振荡器。
代码
#include "stm32f10x.h"
/****STM32时钟初始化****/
void clock_init(void)
{
RCC->CR |= RCC_CR_HSEON;//使能外部高速时钟
while((RCC->CR & RCC_CR_HSERDY) == 0);//等待外部时钟就绪
RCC->CR &= ~RCC_CR_PLLON;//关闭锁相环 进行锁相环相关的配置必须先关闭锁相环
FLASH->ACR |= FLASH_ACR_PRFTBE;
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;//AHB的时钟(HCLK)等于系统时钟(SYSCLK)
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;//APB2的时钟(PCLK2)等于系统时钟(SYSCLK)
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;//APB1的时钟(PCLK1)等于系统时钟/2(SYSCLK/2)
RCC->CFGR |= RCC_CFGR_PLLXTPRE_HSE;//HSE的时钟不分频
RCC->CFGR |= RCC_CFGR_PLLSRC_HSE;//锁相环时钟源选择HSE
RCC->CFGR |= RCC_CFGR_PLLMULL9;//8*9 = 72M 锁相环输出72M
RCC->CR |= RCC_CR_PLLON;//配置完成以后使能锁相环
while((RCC->CR & RCC_CR_PLLRDY) == 0);//等待锁相环时钟稳定
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;//系统时钟选择锁相环时钟 SYSCLK = PLL = 72M
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08);//等待时钟切换完成
SCB->VTOR = FLASH_BASE;
RCC->CFGR |= RCC_CFGR_MCO_SYSCLK;//MCO输出SYSCLK的时钟
}
/****滴答定时器初始化****/
void systick_init(void)
{
SysTick->LOAD = 0;//滴答定时器重载值为
SysTick->VAL = 0;
SysTick->CTRL |= 0x04;//滴答定时器时钟源选择HCLK
SysTick->CTRL &= ~0x01;//禁止计数
}
/****延时函数****/
void systick_delay(uint32_t counter)
{
SysTick->LOAD = counter;
SysTick->VAL = 0;
SysTick->CTRL |= 0x01;//使能计数
while(((SysTick->CTRL)&(0x10000)) == 0);//等待计数完成
}
/****MCO管脚初始化****/
void mco_pin_init(void)
{
//MCO的时钟使用PA8管脚输出
RCC->APB2ENR |= 0x00000004;//使能GPIOA的时钟
GPIOA->CRH |= GPIO_CRH_MODE8_0 |GPIO_CRH_MODE8_1;//50M输出速度
GPIOA->CRH &= ~GPIO_CRH_CNF8_0;//复用推挽输出
GPIOA->CRH |= GPIO_CRH_CNF8_1;
}
void led_init(void)
{
RCC->APB2ENR |= 0x00000008;//使能GPIOB的时钟
GPIOB->CRL &= ~ 0x00f00000;
GPIOB->CRL |= 0x00300000;
}
/****点亮LED(低电平)****/
void led_on(void)
{
GPIOB->ODR &= ~0x00000020;
}
/****关闭LED(高电平)****/
void led_off(void)
{
GPIOB->ODR |= 0x00000020;
}
int main()
{
clock_init();
mco_pin_init();
systick_init();
led_init();
while(1)
{
led_on();
systick_delay(72000);
led_off();
systick_delay(72000);
}
return 0;
}
通过配置寄存器,测试各个时钟,可以通过MCO管脚输出(MCO管脚最高输出50MHZ)。、
我这里是配置PLLXTPRE不分频(8M),PLLSRC选择外部晶振时钟源(8M),PLLMUL选择9倍频(72M),系统时钟选择PLL时钟。那么就是8M*9=72M。
滴答定时器选择HCLK(AHB时钟)。此时HCLK = SYSCLK = PLL = HSE * 9 = 72M
上边程序延时1ms,经过测试没有问题。
为什么倍频偏偏选择9倍频呢?因为ST官方说PLL时钟输出不能超过72M。我们不是乖孩子,我们要超频。配置16倍频,HSE*16 = 128M。超频跑一下,看会怎么样呢,同样用滴答定时器检验。
RCC->CFGR |= RCC_CFGR_PLLMULL16;//8*16 = 128M 锁相环输出128M
systick_delay(128000);
经过测试超频跑也是杠杠滴。
我手上的板子外接晶振是8M,8*16 = 128,最大速度跑到128M。如果外接16M晶振,配置16倍频,能不能跑到256M呢?这个我暂时没法测试(手上没有16M晶振)。