由开发板的原理图可知,开发板使用的外源晶振是8MHZ。接入到OSC_IN和OSC_OUT,同时,并不存在OSC32_IN和OSC32_OUT这俩个引脚,又有XTIN和XTOUT俩个引脚接了6MHZ的晶振。
1.HSE:高速的外部时钟,一般是8M的晶振
RCC时钟控制寄存器的位16,即HSEON:外部高速时钟使能
HSERDY:外部高速时钟就绪标志,当HSEON置1,使HSE使能, HSERDY也会置1,表示HSE已经就绪,可以进行下一步的配置。
2.HSI:高速的内部时钟,一般也是8M的晶振
RCC时钟控制寄存器的位0,即HSEON:外部高速时钟使能
HSERDY:外部高速时钟就绪标志,当HSION置1,使HSI使能, HSIRDY也会置1,表示HSE已经就绪,可以进行下一步的配置。
只有在HSE故障时,系统才会自动切换到HSI,直到HSE重新启动为止。
一般来说,HSE会经倍频之后设置为系统时钟,一般是9倍频,但如果HSE故障,自动切换到HSI时,并不会倍频,此时系统时钟只有8M,系统运行速度大大减缓,几乎瘫痪。
3.PLL_CLK:PLL_CLK一般是HSE在不分频的情况下,经倍频后产生的。由图可知,HSI必须是在2分频的情况下,才能进入PLL_MUL。最大16倍频之后也才4*16=64M,没有达到ST公司设置的最大时钟频率72M,所以,一般会选择HSE进入PLL_SRC,然后经倍频后产生PLL_CLK.
PLL_SRC:由时钟配置寄存器位16 PLLSRC来控制
PLL_MUL由时钟配置寄存器位[21:18] PLLMUL来控制
4.SYS_CLK可以看到,SYS_CLK是可以有3种路径来设置,HSI或HSE或PLL_CLK。一般选择PLL_CLK为系统时钟。
由时钟配置寄存器位[1:0] SW(系统时钟切换)来控制。
同样的也有SWS(系统时钟状态)会被相应的置位,用来读取当前时钟的状态是哪一个。
5.AHB:由图可以看出来,SYS_CLK出来之后是AHB预分频器,AHB预分频器之后是AHB设备,APB1预分频器,APB2预分频器。一般的,AHB不分频,是72M,由钟配置寄存器位[7:4] HPRE进行配置,APB1一般2分频,最大是36M,由钟配置寄存器位[10:8]PPRE1进行控制,APB2,最大是72M,由钟配置寄存器位[13:11]PPRE2进行控制.
值得注意的是,(1)APB2会为ADC提供时钟,会经ADC预分频器分频,ST规定最大只能是14M。但预分频器只是是2、4、6、8分频,因此,72M在分频后最大只能是12M,达不到最大值(2)预分频器之后,会有一个选择预分频系数,如果是1,则频率不变,如果不是1,则频率*2,一般,AHB、APB2的预分频系数为1,APB1为2,如此一来,他们所挂接的定时器的时钟仍然都是最大72M。
6.RTC为芯片内部RTC外设提供时钟。有3个来源。HSE经128分频后得到,或LSE,或LSI,此芯片没有HSE和HSI。
7.MCO微控制器时钟输出,一般是对PA8进行复用,然而开发板原理图中PA8引脚并没有复用,所以应该不存在。
了解了时钟树,接下来就应该了解固件库中怎么配置时钟,固件库中配置时钟的是system_stm32f10x.c文件。在startup_stm32f10x.s中有一句是LDR R0, =SystemInit,启动文件通过调用函数SystemInit()来配置时钟。而system_stm32f10x.c中的函数SystemInit()首先是配置RCC各个寄存器的值,最后会调用函数 SetSysClock()来初始化系统时钟。SetSysClock()中可以看出是根据不同的宏来调用不同的函数,查看宏定义,会发现,系统只定义了宏SYSCLK_FREQ_72MHz,这是因为ST官方定义72M是最大的最合适的时钟。其他的都被注释了,所以这里调用函数SetSysClockTo72(void);
SetSysClockTo72(void)是系统时钟配置函数,其中使用了#ifdef STM32F10X_CL;STM32F10X_CL是指互联型产品,而f103系列是基础性产品,因此,这个宏未定义,相应代码不会执行。
以下是SetSysClockTo72(void)的代码及注释,
//这里有俩句代码需要参考STM32F10xxx闪存编程参考手册
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 的配置 */
/* 使能HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待HSE启动就绪,并做超时处理*/
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));//HSE启动成功,跳出循环
if ((RCC->CR & RCC_CR_HSERDY) != RESET)//再次判断一下HSE是否启动
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)//HSE启动确实成功了
{
/*如果HSE使能,表示系统时钟可以正常工作,那么,如果说时钟是开发板的心脏,指令就是开发板的血液,时钟驱动指令来带动整个开发板程序的正常执行。因此,当时钟使能以后,就需要配置指令的读取方式,指令在f103当中,是存放在Flash中的(f103是哈佛结构),因此,需要配置flash的寄存器*/
//STM32F10xxx闪存编程参考手册-> 第3章 寄存器说明-> ACR寄存器
/*使能 指令预取缓冲区 */
FLASH->ACR |= FLASH_ACR_PRFTBE;//PRFTBE:预取缓冲区使能,是指是否使用指令预取功能
/*设置时延 */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);//LATENCY:时延,这些位表示SYSCLK(系统时钟)周期与闪存访问时间的比例
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; //俩个等待状态
//然后就该配置AHB、APNB1、APB2的预分频因子,
/* HCLK = SYSCLK=72M */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK =72M*/
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK =36M*/
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;//二分频
//这里已经删除了宏STM32F10X_CL部分的代码
/* PLL 锁相环时钟: PLLCLK = HSE * 9 = 72 MHz */
//时钟配置寄存器(RCC_CFGR)的位[21:16]
//这是在把HSE倍频9倍之后变成PLL_CLK,然后选择PLL_CLK成为系统时钟
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
/*使能PLL */
RCC->CR |= RCC_CR_PLLON;
/*循环等待PLL设置稳定下来*/
while((RCC->CR & RCC_CR_PLLRDY) == 0)//时钟控制寄存器(RCC_CR)的位25
{
}
/* 选择PLL作为系统时钟 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/*同样的,也需要等待PLL切换系统时钟变得稳定 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{
/* 如果HSE启动失败,用户自己在这里添加错误处理代码*/
}
}