一、如下图所示为STM32F10XX时钟系统框图
- 上图所示5个蓝色框框为STM32F10xx系列芯片的时钟源,即时钟的来源(STM32F1系列芯片的系统时钟、所有外设时钟等全部来源于这5个时钟源)
- 上图所示SYSCLK系统时钟是最重要的一个时钟,其他的外设时钟全部来自于SYSCLK系统时钟。
- PLL锁相环是用来倍频的。
- 上图所示CSS为时钟监控系统,用来检测一旦HSE外部高速时钟失效,则切换到内部高速时钟作为系统时钟,防止外部晶振不起振导致系统崩溃。
- MCO是STM32F1XX芯片输出内部时钟的一个引脚,对应芯片的PA8引脚。
二、下图所示为RCC相关配置寄存器,红色的为常用的时钟寄存器
- CR寄存器是用来配置时钟源的使能情况的。对于文章开头的时钟系统结构图里蓝色框框里的时钟源,在使用之前都需要使能才能正常工作。使能的瞬间这个时钟还没有稳定,系统需要等待这个时钟源稳定,所以需要判断标志位来判断该时钟源是否输入稳定了。
三、RCC相关头文件和固件库源文件
四、SystemInit时钟系统初始化函数剖析
/**
* @brief Setup the microcontroller system
* Initialize the Embedded Flash Interface, the PLL and update the
* SystemCoreClock variable.
* @note This function should be used only after reset.
* @param None
* @retval None
*/
void SystemInit (void)
{
/* 将RCC时钟配置重置为默认的重置状态(用于调试) */
/* 设置 CR 寄存器的 HSION 位为1 */
RCC->CR |= (uint32_t)0x00000001;
/* 复位SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO 位 */
#ifndef STM32F10X_CL //因为我们用的是STM32大容量芯片,定义的宏是STM32F10X_HD,所以这个#ifdef不成立.
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else //大容量芯片执行这里,配置CFGR寄存器。复位SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO 位为初始化状态。
RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */
/* 复位 CR 寄存器的 HSEON, CSSON and PLLON 位*/
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* 复位 CR HSEBYP 位 */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* 复位 PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE 位 */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
#ifdef STM32F10X_CL //因为我们定义的(STM32F10X_HD),所以下面的部分代码不会执行
/* 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 */
//因为我们宏定义了STM32F10X_HD,所以下面的代码会执行,进入SystemInit_ExtMemCtl()函数
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* 配置系统时钟频率, HCLK, PCLK2 and PCLK1 分频器*/
/* 配置Flash延迟周期并启用预取缓冲区 */
SetSysClock();
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* 在内部SRAM中的向量表重定位. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* 内部FLASH中的向量表重定位. */
#endif
}
/**
* @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.
* @param None
* @retval None
*/
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
/* 如果以上定义都没有启用,则HSI将用作系统时钟源(重置后默认) */
}
/*
配置系统时钟为72MHz
*/
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 和 PCLK1 的配置 ---------------------------*/
/* 使能 外部高速时钟HSE,RCC_CR_HSEON = ((uint32_t)0x00010000)*/
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待 HSE 准备好,如果超时了,就退出 */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* Enable Prefetch Buffer(使能预取缓存器) */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* 因为CPU的速度要比FLASH快得多,所以CPU需要等待几个时钟周期,
具体的寄存器配置我在下面列出了 FLASH_ACR 寄存器的功能,系统时钟为72MHz的时候需要等待2个等待状态*/
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* 设置 HCLK = SYSCLK,由STM32F10XX时钟系统框图可以看到要 HCLK = SYSCLK,AHB预分频器的分频因子要设为1,即不分频 */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK ,PCLK2的时钟频率为72MHz*/
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK/2 ,PCLK1最大的时钟频率为36MHz*/
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
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
//选择HSE为PLL的时钟源,设置倍频系数为9,此时HSE是8MHz,倍频之后为72MHz
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL */
/* 使能PLL时钟 */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL时钟就绪 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* 选择PLL作为系统时钟来源 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /* 如果HSE启动失败,应用程序将有错误的时钟配置。
用户可以在这里添加一些代码来处理这个错误 */
}
}