19集 STM32 时钟

蓝色方框为时钟源:HSI RC、HSE OSC、PLL、LSE OSC、LSI RC
梯形紫色为选择器:就像一个切换开关。
可以从两步分析该时钟图:
1、从SYSCLK开始看,由谁可以提供时钟给SYSCLK,逐一分析。
2、从RTCCLK、IWDGCLK开始分析,由谁可以提供时钟给RTCCLK\IWDGCLK。
一、时钟源:内部时钟的精度都比较差(《STM32中文参考手册 V10》 ---- 57页)
HSI RC: 高速内部时钟(约8M),当被用作PLL时钟的输入时,系统时钟能得到的最大频率为64MHz
HSE OSC: 高速外部时钟,一般采用晶振(可选4~16M,一般选8M)
PLL: 锁相环(2~16倍),用于倍频时钟,可选来源 HSI 二分频、HSE、HSE二分频。
PLL的设置必须在其激活前完成,一旦PLL被激活,这些参数都无法被改动,如果发现PLL的 设置无效,就可能在设置时PLL已经完成激活,如果需要使用USB接口,PLL必须被设置为输 出48MHz或者72MHz时钟,用于提供48MHz的USBCLK时钟。
LSE OSC: 低速外部时钟,一般采用晶振(32.768KHz)用于提供RTC时钟
LSI RC: 低速内部时钟(约40KHz),用于提供独立看门口作为时钟源
二、SYSCLK:(最重要的时钟)
从上图中可以看到,SYSCLK为最重要的时钟,几乎大部分的外设时钟都是从SYSCLK得到时钟信号。所以可以从SYSCLK开始分析。
SYSCLK可以选择从HSI RC、PLL、HSE OSC取其中一个时钟信号,而PLL则可以从HSI RC的二分频、HSE OSC、HSE OSC 二分频取得时钟信号。
系统复位后,HSI为默认的系统时钟来源。在做时钟源切换时,目标时钟源稳定就绪后才会发生切换。
1、CSS 时钟安全系统:(当系统运行速度莫名其妙的下降、或者USB不工作,可以留意这里)
因STM32内部时钟是利用电容产生的时钟,并不是太稳定,所以一般项目中都会采用外部晶振通过HSE OSC提供稳定的时钟给系统,当外部晶振出现问题即HSE失效时,CSS就会自动将时钟源切换至HSI RC。
CSS可以通过软件被激活,一旦其被激活,CSS将在HSE振荡器启动延迟后被使能,并在HSE时钟关闭后关闭。
如果HSE时钟发生故障,HSE振荡器被自动关闭,时钟失效事件将被送到高级定时器(TIM1和TIM8)的刹车输入端,并产生时钟安全中断CSSI,允许软件完成营救操作。此CSSI中断连接到Cortex-M3的NMI中断。
一旦CSS被激活,并且HSE时钟出现故障,CSS中断就产生,并且NMI也自动产生。NMI将不断执行,直到CSS中断挂起位被清除。因此,在NMI的处理程序中必须通过设置时钟中断寄存器(RCC_CIR)里的CSSC位来清除CSS中断。
如果HSE振荡器被直接或间接地作为系统时钟,时钟故障将导致系统时钟自动切换到HSI振荡器,同时外部HSE振荡器被关闭。在时钟失效时,如果HSE振荡器时钟是用做系统时钟的PLL的输入时钟,PLL也将关闭。(问题来了,那么USBCLK是不是也就失效了吧?)
2、每个时钟都可以独立开启或关闭,这样可以关闭不需要的时钟从而降低功耗。
从下图可以看出,每次需要使用某个外设时都要开启相应的外设时钟。

(参考STM32中文参考手册 V10 56页)
三、RTCCLK(RTC时钟)
RTCCLK可以选择从HSE OSC的128分频、LSE OSC、LSI RC中取时钟信号。
这里没有CSS,那如果采用HSE的时钟信号而其又失效,岂不是RTC也就失效了?
四、IWDGCLK(看门狗时钟)
看门口时钟只能从 LSI RC中取得时钟源。
五、MCO 输出(PA8引脚)
STM32还可以对外输出时钟,时钟源可以从SYSCLK、HSI、HSE、PLLCLK的二分频取得信号。
六、重要的时钟
SYSCLK系统时钟
AHB总线时钟(高速)
APB1总线时钟(低速):速度最高36MHz 连接低速外设
APB2总线时钟(高速):速度最高72MHz 连接高速外设
PLL时钟:用于倍频
七、寄存器相关(具体参阅 《STM32中文参考手册 V10》 60~75页)
typedef struct
{
__IO uint32_t CR; // HSI、HSE、CSS、PLL等的使能和就绪标志位
__IO uint32_t CFGR; // PLL等的时钟源选择、分频系数设定
__IO uint32_t CIR; // 清除、使能时钟就绪中断
__IO uint32_t APB2RSTR; // APB2线上所有外设复位寄存器
__IO uint32_t APB1RSTR; // APB1线上所有外设复位寄存器
__IO uint32_t AHBENR; // DMA、SDIO等时钟使能
__IO uint32_t APB2ENR; // APB2线上所有外设时钟使能
__IO uint32_t APB1ENR; // APB1线上所有外设时钟使能
__IO uint32_t BDCR; // 备份域控制寄存器
__IO uint32_t CSR; // 控制状态寄存器
} RCC_TypeDef;
0x4002 1000 - 0x4002 13FF 复位和时钟控制(RCC)
#define PERIPH_BASE ((uint32_t)0x40000000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
#define RCC ((RCC_TypeDef *) RCC_BASE)
八、库函数类型(system_stm32f10x.c -- SystemInit() 函数中通过直接配置寄存器配置,并未采用库函数)
1、时钟使能配置:
RCC_LSEConfig() 、RCC_HSEConfig()、
RCC_HSICmd() 、 RCC_LSICmd() 、 RCC_PLLCmd() …
2、时钟源相关配置:
RCC_PLLConfig ()、 RCC_SYSCLKConfig() 、
RCC_RTCCLKConfig() …
3、分频系数选择配置:
RCC_HCLKConfig() 、 RCC_PCLK1Config() 、 RCC_PCLK2Config()…
4、外设时钟使能:
RCC_APB1PeriphClockCmd(): //APB1线上外设时钟使能
RCC_APB2PeriphClockCmd(); //APB2线上外设时钟使能
RCC_AHBPeriphClockCmd(); //AHB线上外设时钟使能
5. 其他外设时钟配置:
RCC_ADCCLKConfig (); RCC_RTCCLKConfig();
6、状态参数获取参数:
RCC_GetClocksFreq();
RCC_GetSYSCLKSource();
RCC_GetFlagStatus()
7、RCC中断相关函数 :
RCC_ITConfig() 、 RCC_GetITStatus() 、 RCC_ClearITPendingBit()…
20集 SystemInit()
SystemInit()函数在 startup_stm32f10x_hd.s 文件里面先于main函数调用。
也就是说我们在写mian函数时,不必去调用SystemInit()函数。
一、代码分析 (需要参考 《STM32中文参考手册 V10.pdf》、《STM32F10xxx闪存编程参考手册.pdf》)
void SystemInit (void)
{
// 开启HSI内部8MHz高速时钟 1
RCC->CR |= (uint32_t)0x00000001;
// 设置HSI做为系统时钟 1:0
// SYSCLK不分频 7:4
// APB1\APB2 不分频 13:8
// ADC预分频 PCLK2 2分频后作为ADC时钟 15:14
// MCO引脚不输出时钟 26:24
RCC->CFGR &= (uint32_t)0xF8FF0000;
// HSE振荡器关闭 16
// CSS时钟监视关闭 19
// PLL关闭 24
RCC->CR &= (uint32_t)0xFEF6FFFF;
// 设置外部4-16MHz振荡器没有旁路 18
RCC->CR &= (uint32_t)0xFFFBFFFF;
// 设置HSI振荡器时钟经2分频后作为PLL输入时钟 16
// 设置HSE不分频 17
// 设置PLL 2倍频输出 21:18
// 设置USB预分频 PLL时钟1.5倍分频作为USB时钟 22
RCC->CFGR &= (uint32_t)0xFF80FFFF;
// 设置LSI、LSE、HSI、HSE、PLL就绪中断关闭 12:8
// 清除LSI、LSE、HSI、HSE、PLL就绪中断标志位 20:16
// 清楚CSSF时钟安全系统中断标志位 23
RCC->CIR = 0x009F0000;
/* 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
}
#define SYSCLK_FREQ_72MHz 72000000
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
}
#define RCC_CR_HSEON ((uint32_t)0x00010000)
#define RCC_CR_HSERDY ((uint32_t)0x00020000)
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)
#define FLASH_ACR_PRFTBE ((uint8_t)0x10)
#define FLASH_ACR_LATENCY ((uint8_t)0x03)
#define FLASH_ACR_LATENCY_2 ((uint8_t)0x02)
#define RCC_CFGR_HPRE_DIV1 ((uint32_t)0x00000000)
#define RCC_CFGR_PPRE2_DIV1 ((uint32_t)0x00000000)
#define RCC_CFGR_PPRE1_DIV2 ((uint32_t)0x00000400)
#define RCC_CFGR_PLLSRC ((uint32_t)0x00010000)
#define RCC_CFGR_PLLXTPRE ((uint32_t)0x00020000)
#define RCC_CFGR_PLLMULL ((uint32_t)0x003C0000)
#define RCC_CFGR_PLLSRC_HSE ((uint32_t)0x00010000)
#define RCC_CFGR_PLLMULL9 ((uint32_t)0x001C0000)
#define RCC_CR_PLLON ((uint32_t)0x01000000)
#define RCC_CR_PLLRDY ((uint32_t)0x02000000)
#define RCC_CFGR_SW ((uint32_t)0x00000003)
#define RCC_CFGR_SW_PLL ((uint32_t)0x00000002)
#define RCC_CFGR_SWS ((uint32_t)0x0000000C)
typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
/* 要设置 72MHz 需要使用HSE外部时钟 */
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
// 开启HSE时钟 16
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; // HSE时钟就绪
}
else
{
HSEStatus = (uint32_t)0x00; // HSE时钟未就绪
}
// 如果HSE时钟就绪
if (HSEStatus == (uint32_t)0x01)
{
// 启用预取缓冲区 4
FLASH->ACR |= FLASH_ACR_PRFTBE;
// 48MHz < SYSCLK <= 72MHz 设置两个等待周期 2:0
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/*** SYSCLK -> HCLK -> PLCK1\PCLK2 */
/* HCLK = SYSCLK */
// 设置AHB分频系数为 1 即不分频,等于SYSCLK的频率 即72MHz
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
// 设置APB2分频系数为 1 即不分频,等于HCLK的频率 即72MHz
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK/2 */
// 设置APB1分频系数为 2 即二分频,等于HCLK/2的频率 即36MHz
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
// 这里主要是清零操作 21:16
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
// 设置HSE作为PLL输入时钟 16
// 设置PLL 9 倍频输出 (HSE为8M晶振输入:SYSCLK = 8MHz * 9 = 72MHz)
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
/* Enable PLL */
// 开启PLL
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
// 等待PLL就绪标志位就绪 25
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
// 清零操作
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
// 设置PLL为SYSCLK时钟源
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未就绪
{ /* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
二、总结
SystemInit():
设置SYSCLK、APB1、APB2不分频,MCO不输出时钟
SYSCLK由PLL提供时钟、PLL则由HSI经2分频后提供时钟
设置USB时钟为PLL输出时钟的1.5倍
关闭所有时钟源的中断、清除所有时钟源的中断标志位
SetSysClockTo72():
该函数首先是开启HSE时钟,然后在一定的时间等待其稳定。
稳定之后,设置FLASH开启预取缓冲区、设置两个等待周期。
接着设置AHB、APB2、APB1分配系数来配置HCLK、PCLK2、PCLK1的时钟频率。
再设置HSE作为PLL时钟源,并设置PLL为9倍频,再开启PLL,等待PLL就绪。
三、注意
SystemCoreClock 在实际写代码中可以通过这个变量来获得当前所设置的系统时钟频率值
system_stm32f10x.h:
extern uint32_t SystemCoreClock;
system_stm32f10x.c:
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
#endif
#ifdef SYSCLK_FREQ_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_HSE;
#elif defined SYSCLK_FREQ_24MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz;
#elif defined SYSCLK_FREQ_36MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_36MHz;
#elif defined SYSCLK_FREQ_48MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz;
#elif defined SYSCLK_FREQ_56MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz;
#elif defined SYSCLK_FREQ_72MHz
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz;
#else /*!< HSI Selected as System Clock source */
uint32_t SystemCoreClock = HSI_VALUE;
#endif