在STM32的启动文件中会调用调用固件库函数中的SystemInit(在文件system_stm32f10x.c)来初始化时钟,把时钟初始化为72Mhz,先来看下时钟树的整体图
先看锁相环时钟的设置
我们先看到HSE,什么是HSE,HSE就是High Speed External Clock signal,即高速的外部时钟,它的来源是无源晶振(4-16M),通常使用8M,用RCC_CR时钟控制寄存器的位16:HSEON控制,看到我们的开发板原理图,当使用无源晶振的时候需要用到我们的电容,当使用有源晶振时就只需要IN引脚进入单片机里面
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200308164522381.png
与HSE相对有一个内部的叫HSI,高速的内部时钟,默认为8Mhz,当系统发生故障的时,系统时钟会自动切换到HSI,直到HSE启动成功,用RCC_CR时钟控制及寄存器的位0:HSION控制
看到锁相环,锁相环的作用就是倍频,由倍频因子PLLMUL决定,它的来源可以是内部的HSI的1/2(当使用HSI的时候PLLMUL最大是16,PLLCLK最大只能是64M),也可以是HSE的1/2或者HSE本身(由PLLXTPRE决定),
系统时钟的设置
由图可以直到系统时钟的来源可以是PLLCLK,HSI,HSE,最大为72M,一般的配置是系统时钟等于PLLCLK,经系统时钟出来后会经过AHB预分频器,接着会经过APB1(最大36Mhz),APB2分频器分频后供外设使用(由时钟配置寄存器RCC_CFGR配置)
其中需要理解的是 APB1 和 APB2 的区别, APB1 上面连接的是低速外设,包括电源接口、备份接口、 CAN、 USB、 I2C1、 I2C2、 UART2、 UART3 等等, APB2 上面连接的是高速外设包括 UART1、 SPI1、 Timer1、 ADC1、 ADC2、所有普通 IO 口(PA~PE)、第二功能 IO 口等
引入的一个还有RTC时钟和看门狗时钟
MCO时钟
通过MCO可以给别的芯片提供时钟,节省晶振,节约成本,还能改善EMI,如下图
MCO引脚在F103系列是PA8
特殊点
时钟安全系统(CSS) 时钟安全系统可以通过软件被激活.一旦其被激活,时钟监测器将在HSE振荡器启动延迟后被使能,并在HSE时钟关闭后关闭. 如果HSE时钟发生故障,HSE振荡器被自动关闭,时钟失效事件将被送到高级定时器(TIM1和 TIM8)的刹车输入端(TIMx_BKIN),并产生时钟安全中断CSSI,允许软件完成营救操作.此CSSI中断连接到 Cortex™-M3的NMI中断(不可屏蔽中断)。
注意:
一旦 CSS 被激活,并且 HSE 时钟出现故障,CSS 中断就产生,并且 NMI 也自动产生. NMI 将被不断执行,直到 CSS 中断挂起位被清除.因此,在 NMI 的处理程序中必须通过设置时钟中断寄存器 (RCC_CIR) 里的 CSSC 位来清除 CSS 中断。
如果HSE振荡器被直接或间接地作为系统时钟,(间接的意思是:它被作为PLL输入时钟,并且 PLL时钟被作为系统时钟),时钟故障将导致系统时钟自动切换到HSI振荡器,同时外部HSE振荡 器被关闭.在时钟失效时,如果HSE振荡器时钟(被分频或未被分频)是用作系统时钟的PLL的输 入时钟,PLL也将被关闭.
最后我们来讲解下文件system_stm32f10x.c中的SystemInit函数
void SystemInit (void)
{
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
#ifdef STM32F10X_CL
/* Reset PLL2ON and PLL3ON bits */
RCC->CR &= (uint32_t)0xEBFFFFFF;
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x00FF0000;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;
/* Reset CFGR2 register */
RCC->CFGR2 = 0x00000000;
#else
/* Disable all interrupts and clear pending bits */
RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
/* Configure the Flash Latency cycles and enable prefetch buffer */
SetSysClock();
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
}
可以看出这函数调用了SetSysClock()这个函数,继续看
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
/* If none of the define above is enabled, the HSI is used as System clock
source (default after reset) */
}
用宏作为开关来设置不同的时钟,一般默认定义SYSCLK_FREQ_72MHz,用来输出72Mhz给系统时钟,分析SetSysClockTo72()函数
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* 使能HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待HSE就绪并做超时处理 */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;//如果标志位为1,则HSEStatus为1
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}//再判断一遍
else
{
HSEStatus = (uint32_t)0x00;
}
//HSE启动成功则继续往下执行
if (HSEStatus == (uint32_t)0x01)
{
//使能预处理指令,即一条指令取出另一条指令已经准备好被取
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* 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;
/*#ifdef STM32F10X_CL
/* Configure PLLs ------------------------------------------------------*/
/* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
/* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */
RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
}
/* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9); */
**上面的宏未定义不执行**
#else
/* 配置锁相环: PLLCLK = HSE * 9 = 72 MHz */
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);
#endif /* STM32F10X_CL */
/*使能锁相环 */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL稳定*/
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/*选项PLLCLK作为系统时钟*/
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* 等待PLLCLK切换为系统时钟 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else//启动失败,用户可以在这里定义错误处理代码
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
#endif