STM32学习笔记——十一、RCC使用HSE/HSI配置时钟
1 RCC
RCC:reset clock control 复位和时钟控制器
- 本文了解时钟部分
- 着重理解时钟树
- 理解了时钟树,STM32的一切时钟的来龙去脉都会了如指掌
1.1 RCC主要作用——时钟部分
-
设置系统时钟SYSCLK
-
设置AHB分频因子(决定HCLK等于多少)
-
设置APB2分频因子(决定PCLK2等于多少)
-
设置APB1分频因子(决定PCLK1等于多少)
-
设置各个外设的分频因子
-
控制AHB、APB2、APB1三条总线时钟的开启
-
控制每个外设的时钟的开启
一般的时钟配置(库函数的标准配置):
- PCLK2 = HCLK = SYSCLK = PLLCLK = 72M
- PCLK1 = HCLK/2 = 36M
2 RCC框图剖析——时钟部分
时钟树:选取库函数时钟系统时钟函数:SetSysClockTo72(); 以这个函数的编写流程来讲解时钟树
- 该函数是用库的时候默认的系统时钟设置函数
- 功能:利用HSE把时钟设置为
- PCLK2 = HCLK = SYSCLK = 72M,
- PCLK1=HCLK/2 = 36M
- 以这个代码流程为主线,分析时钟树
- 对应的是图中黄色部分
- 代码流程在时钟树种以数字大小顺序标识
2.1 系统时钟
2.1.1 HSE 高速外部时钟信号
HSE:高速的外部时钟信号
- 由有源晶振或无源晶振提供, 频率从4~16MHZ不等
- 使用有源晶振:时钟从OSC_IN引脚进入,OSC_OUT引脚悬空
- 使用无源晶振:时钟从OSC_IN和OSC_OUT进入,并配谐振电容
最常用8M无源晶振
- 确定PLL时钟来源的时候,HSE可以不分频或者2分频
- 由时钟配置寄存器CFGR的位17:PLLXTPRE设置(设置为HSE不分频)
2.1.2 PLL时钟源
PLL时钟来源:一个来子HSE,一个来子HSI/2
- 具体用哪个来源,由时钟寄存器CFGR的16:PLLSRC设置
- HSI:内部高速的时钟信号
- 频率为8M
- 根据温度和环境的情况频率会有漂移
- 一般不作为PLL的时钟来源
选择HSE作为PLL时钟来源
2.1.3 PLL时钟PLLCLK
- 设置PLL的倍频因子,可以对PLL的时钟进行倍频
- 倍频因子可以是:[2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15, 16]
- 具体设置多少,由时钟配置寄存器CFGR的21-18:PLLMUL[3:0]设置
- 设置为9倍频
- 2.1.2种设置PLL的时钟来源位HSE = 8M,
- 经过PLL倍频之后PLL时钟:PLLCLK = 8M * 9 = 72M
- 72M是ST官方推荐的稳定运行时钟
- 超频:想超频的话,增大倍频因子即可,最高为128M
设置PLL时钟:PLLCLK = 8M * 9 = 72M
2.1.4 系统时钟 SYSCLK
系统时钟来源:可以是 HSI、PLLCLK、HSE
- 具体的时钟配置寄存器CFGR的位1-0:SW[1:0]设置
设置系统时钟:SYSCLK = PLLCLK = 72M
2.1.5 AHB 总线时钟HCLK
HCLK(APB总线时钟):系统时钟SYSCLK经过AHB预分频器分频后得到的时钟APB总线时钟
- 分频因子[1, 2, 4, 8, 16, 64, 128, 256, 512]
- 由时钟配置寄存器CFGR的位7-4:HPRE[3:0]设置
- 片上大部分外设的时钟都是经过HCLK分频得到
- AHB总线上的外设时钟设置
- 使用外设时才进行设置
- 当前仅需粗线条的设置好APB时钟
设置为1分频,HCLK = SYSCLK = 72M
2.1.6 APB1 总线时钟 PLCK1
APB1 总线时钟 PCLK1由HCLK经过低速APB预分频器得到
- 分频因子[1, 2, 4, 8, 16]
- 由时钟配置寄存器CFGR的位10-8:PRRE1[2:0]决定
- PCLK1属于低速的总线时钟
- 最高为32M
- 片上低速外设挂载在这条总线上 (USART2/3/4/5、SPI2/3、I2C1/2)
- 时钟设置需要用到该外设时,才进行设置(粗线条的设置APB1时钟即可)
设置为2分频, PCLK1 = HCLK/2 = 36M
2.1.7 APB2 总线时钟 PCLK2
APB2 总线时钟 PCLK2由HCLK经过告诉APB2经过预分频器得到
- 分频因子[1, 2, 4, 8, 16]
- 由时钟配置寄存器CFGR的位13-11:PPRE2[2:0]决定
- PCLK2属于高速的总线时钟
- 片上高速的外设挂载到这条总线上(GPIO、USART1、SPI1)
- 粗线条的设置好APB2即可
设置为1分频,PCLK2 = HCLK = 72M
2.1.8 设置系统时钟库函数
上述2.1.1~2.1.7种7个步骤对应的设置系统时钟库函数如下:
- 函数截取自固件库文件system_stm32f10x.c
- 方便阅读,删去了互联型相关的代码
- 总共七个步骤
- 直接操作寄存器
- 有关寄存器部分请参考数据手册的RCC 的寄存器描述部分
设置系统时钟库函数
1 static void SetSysClockTo72(void)
2 {
3 __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
5 // 使能HSE,并等待HSE 稳定
6 RCC->CR |= ((uint32_t)RCC_CR_HSEON);
7
8 // 等待HSE 启动稳定,并做超时处理
9 do {
10 HSEStatus = RCC->CR & RCC_CR_HSERDY;
11 StartUpCounter++;
12 } while ((HSEStatus == 0)
13 &&(StartUpCounter !=HSE_STARTUP_TIMEOUT));
14
15 if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
16 HSEStatus = (uint32_t)0x01;
17 } else {
18 HSEStatus = (uint32_t)0x00;
19 }
20 // HSE 启动成功,则继续往下处理
21 if (HSEStatus == (uint32_t)0x01) {
22
23 //-----------------------------------------------------------
24 // 使能FLASH 预存取缓冲区*/
25 FLASH->ACR |= FLASH_ACR_PRFTBE;
26
27 // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成2
28 // 设置成2 的时候,SYSCLK 低于48M 也可以工作,如果设置成0 或者1 的时候,
29 // 如果配置的SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
30 // 0:0 < SYSCLK <= 24M
31 // 1:24< SYSCLK <= 48M
32 // 2:48< SYSCLK <= 72M */
33 FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
34 FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
35 //------------------------------------------------------------
37 // 设置AHB、APB2、APB1 预分频因子
38 // HCLK = SYSCLK
39 RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
40 //PCLK2 = HCLK
41 RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
42 //PCLK1 = HCLK/2
43 RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
44
45 // 设置PLL 时钟来源,设置PLL 倍频因子,PLLCLK = HSE * 9 = 72 MHz
46 RCC->CFGR &= (uint32_t)((uint32_t)
47 ~(RCC_CFGR_PLLSRC
48 | RCC_CFGR_PLLXTPRE
49 | RCC_CFGR_PLLMULL));
50 RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE
51 | RCC_CFGR_PLLMULL9);
52
53 // 使能PLL
54 RCC->CR |= RCC_CR_PLLON;
55
56 // 等待PLL 稳定
57 while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
58 }
59
60 // 选择PLL 作为系统时钟来源
61 RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
62 RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
63
64 // 读取时钟切换状态位,确保PLLCLK 被选为系统时钟
65 while ((RCC->CFGR&(uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08){
66 }
67 } else {// 如果HSE 启动失败,用户可以在这里添加错误代码出来
68 }
69 }
2.2 其他时钟
2.2.1 USB时钟(A)
USB 时钟是由PLLCLK 经过USB 预分频器得到
- 分频因子:[1,1.5]
- 由时钟配置寄存器CFGR 的位22:USBPRE 配置
- USB 的时钟最高是48M
- 根据分频因子反推过来算,PLLCLK 只能是48M 或者是72M
一般我们设置PLLCLK=72M,USBCLK=48M
- USB 对时钟要求比较高,所以PLLCLK 只能是由HSE 倍频得到,不能使用HSI 倍频
2.2.2 Cortex 系统时钟(B)
Cortex 系统时钟由HCLK(AHB总线时钟) 8 分频得到
- 等于9M
- 驱动内核的系统定时器SysTick
- SysTick 一般用于操作系统的时钟节拍,也可以用做普通的定时
- SysTick 一般用于操作系统的时钟节拍,也可以用做普通的定时
2.2.3 ADC 时钟©
ADC 时钟由PCLK2 经过ADC 预分频器得到
- 分频因子可以是[2,4,6,8]
- 怎么没有1 分频。ADC 时钟最高只能是14M,如果采样周期设置成最短的1.5 个周期的话,ADC 的转换时间可以达到最短的1us。如果真要达到最短的转换时间1us 的话,那ADC 的时钟就得是14M,反推PCLK2 的时钟只能是:28M、56M、84M、112M,鉴于PCLK2 最高是72M,所以只能取28M 和56M。
- 由时钟配置寄存器CFGR 的位15-14:ADCPRE[1:0] 决定
2.2.4 RTC 时钟、独立看门狗时钟
RTC 时钟由HSE/128 分频得到
- 可由低速外部时钟信号LSE 提供,频率为32.768KHZ
- 可由低速内部时钟信号LSI 提供
- 选用哪个时钟,由备份域控制寄存器BDCR 的位9-8:RTCSEL[1:0] 配置
独立看门狗的时钟由LSI 提供,且只能是由LSI 提供
- LSI 是低速的内部时钟信号,频率为30~60KHZ 直接不等
- 一般取40KHZ。
2.2.5 MCO 时钟输出
MCO 是microcontroller clock output 的缩写,是微控制器时钟输出引脚
- 在STM32 F1系列中由PA8 复用所得
- 主要作用:可以对外提供时钟,相当于一个有源晶振。
- 除了对外提供时钟这个作用之外,还可以通过示波器监控MCO 引脚的时钟输出来验证我们的系统时钟配置是否正确。
- MCO的时钟来源可以是:PLLCLK/2、HSI、HSE、SYSCLK
- 具体选哪个,由时钟配置寄存器CFGR 的位26-24:MCO[2:0] 决定
2.3 配置系统时钟
2.3.1 使用HSE
- 一般情况下,我们都是使用HSE,HSE 经过PLL 倍频之后作为系统时钟
- 系统时钟就设置成:SYSCLK = 8M * 9 = 72M。
- HSE=8M
- PLL 的倍频因子为:9
- 使用HSE,系统时钟SYSCLK 最高是128M。
- 使用的库函数就是这么干的,当程序来到main 函数之前,启动文件:statup_stm32f10x_hd.s 已经调用SystemInit() 函数把系统时钟初始化成72MHZ
- SystemInit()在库文件:system_stm32f10x.c 中定义
- 想把系统时钟设置低一点或者超频的话,可以修改底层的库文件
- 为了维持库的完整性,我们可以根据时钟树的流程自行写一个
2.3.2 使用HSI
如果PLL 的时钟来源是HSE,那么当HSE 故障的时候,不仅HSE 不能使用,连PLL 也会被关闭,这个时候系统会自动切换HSI 作为系统时钟
- 此时SYSCLK=HSI=8M
- 如果没有开启CSS 和CSS 中断的话,那么整个系统就只能在低速率运行,这是系统跟瘫痪没什么两样
- 如果开启了CSS 功能的话,可以当HSE 故障时,在CSS 中断里面采取补救措施,使用HSI,并把系统时钟设置为更高的频率,最高是64M,64M 的频率足够一般的外设使用,如:ADC、SPI、I2C 等
- 又有一个问题,原来SYSCLK=72M,现在因为故障改成64M,那么那些外设的时钟肯定被改变了,那么外设工作就会被打乱
- 是不是在设置HSI 时钟的时候,也重新调整外设总线的分频因子,即AHB,APB2 和APB1 的分频因子,使外设的时钟达到跟HSE 没有故障之前一样
- 这也不是最保障的办法,不能一直使用HSI,当HSE 故障时还是要采取报警措施。
3 实验验证
3.1 使用HSE或HSI配置系统时钟
- RCC 是单片机内部资源,不需要外部电路。
- 通过LED 闪烁的频率来直观的判断不同系统时钟频率对软件延时的效果。
- 编写两个RCC 驱动文件,bsp_clkconfig.h 和bsp_clkconfig.c,用来存放RCC 系统时钟配置函数
- 编程要点:编程要点对应着时钟树图中的序号
- 1、开启HSE/HSI ,并等待HSE/HSI 稳定
- 2、设置AHB、APB2、APB1 的预分频因子
- 3、设置PLL 的时钟来源,和PLL 的倍频因子,设置各种频率主要就是在这里设置
- 4、开启PLL,并等待PLL 稳定
- 5、把PLLCK 切换为系统时钟SYSCLK
- 6、读取时钟切换状态位,确保PLLCLK 被选为系统时钟
3.1.1 使用HSE 配置系统时钟
- 采用库函数编写
- 函数调用举例:
- HSE_SetSysClock(RCC_PLLMul_9); 则设置系统时钟为:8MHZ * 9 = 72MHZ。
- HSE_SetSysClock(RCC_PLLMul_16); 则设置系统时钟为:8MHZ * 16 = 128MHZ 超频慎用。
- 函数有个形参pllmul,pllmul 用来设置PLL 的倍频因子
- 形参可以是:RCC_PLLMul_x , x:[2,3,⋯16]
- 宏来源于库函数的定义,宏展开是一些32 位的十六进制数
- 配置时钟配置寄存器CFGR 的位21-18PLLMUL[3:0],预先定义好倍频因子
1 void HSE_SetSysClock(uint32_t pllmul)
2 {
3 __IO uint32_t StartUpCounter = 0, HSEStartUpStatus = 0;
4
5 // 把RCC 外设初始化成复位状态,这句是必须的
6 RCC_DeInit();
7
8 //使能HSE,开启外部晶振,野火STM32F103 系列开发板用的是8M
9 RCC_HSEConfig(RCC_HSE_ON);
10
11 // 等待HSE 启动稳定
12 HSEStartUpStatus = RCC_WaitForHSEStartUp();
13
14 // 只有HSE 稳定之后则继续往下执行
15 if (HSEStartUpStatus == SUCCESS) {
16 //-----------------------------------------------------------------//
17
18 // 使能FLASH 预存取缓冲区
19 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
20
21 // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成2
22 // 设置成2 的时候,SYSCLK 低于48M 也可以工作,如果设置成0 或者1 的时候,
23 // 如果配置的SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
24 // 0:0 < SYSCLK <= 24M
25 // 1:24< SYSCLK <= 48M
26 // 2:48< SYSCLK <= 72M
27 FLASH_SetLatency(FLASH_Latency_2);
28 //-----------------------------------------------------------------//
29
30 // AHB 预分频因子设置为1 分频,HCLK = SYSCLK
31 RCC_HCLKConfig(RCC_SYSCLK_Div1);
32
33 // APB2 预分频因子设置为1 分频,PCLK2 = HCLK
34 RCC_PCLK2Config(RCC_HCLK_Div1);
35
36 // APB1 预分频因子设置为1 分频,PCLK1 = HCLK/2
37 RCC_PCLK1Config(RCC_HCLK_Div2);
38
39 //-----------------设置各种频率主要就是在这里设置-------------------//
40 // 设置PLL 时钟来源为HSE,设置PLL 倍频因子
41 // PLLCLK = 8MHz * pllmul
42 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
43 //-------------------------------------------------------------//
44
45 // 开启PLL
46 RCC_PLLCmd(ENABLE);
47
48 // 等待PLL 稳定
49 while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
50 }
51
52 // 当PLL 稳定之后,把PLL 时钟切换为系统时钟SYSCLK
53 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
54
55 // 读取时钟切换状态位,确保PLLCLK 被选为系统时钟
56 while (RCC_GetSYSCLKSource() != 0x08) {
57 }
58 } else {
59 // 如果HSE 开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
60 // 当HSE 开启失败或者故障的时候,单片机会自动把HSI 设置为系统时钟,
61 // HSI 是内部的高速时钟,8MHZ
62 while (1) {
63 }
64 }
65 }
3.1.2 使用HSI 配置系统时钟
- HSI 设置系统时钟函数跟HSE 设置系统时钟函数在原理是一样的
- 函数调用举例:
- HSI_SetSysClock(RCC_PLLMul_9); 则设置系统时钟为:4MHZ * 9 = 36MHZ。
- 区别的地方:
- HSI 必须2 分频之后才能作为PLL 的时钟来源
- 所以使用HSI 时,最大的系统时钟SYSCLK 只能是 HSI/2 * 16 = 4 * 16 = 64MHZ。
1 void HSI_SetSysClock(uint32_t pllmul)
2 {
3 __IO uint32_t HSIStartUpStatus = 0;
4
5 // 把RCC 外设初始化成复位状态,这句是必须的
6 RCC_DeInit();
7
8 //使能HSI
9 RCC_HSICmd(ENABLE);
10
11 // 等待HSI 就绪
12 HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
13
14 // 只有HSI 就绪之后则继续往下执行
15 if (HSIStartUpStatus == RCC_CR_HSIRDY) {
16 //-------------------------------------------------------------//
17
18 // 使能FLASH 预存取缓冲区
19 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
20
21 // SYSCLK 周期与闪存访问时间的比例设置,这里统一设置成2
22 // 设置成2 的时候,SYSCLK 低于48M 也可以工作,如果设置成0 或者1 的时候,
23 // 如果配置的SYSCLK 超出了范围的话,则会进入硬件错误,程序就死了
24 // 0:0 < SYSCLK <= 24M
25 // 1:24< SYSCLK <= 48M
26 // 2:48< SYSCLK <= 72M
27 FLASH_SetLatency(FLASH_Latency_2);
28 //------------------------------------------------------------//
29
30 // AHB 预分频因子设置为1 分频,HCLK = SYSCLK
31 RCC_HCLKConfig(RCC_SYSCLK_Div1);
32
33 // APB2 预分频因子设置为1 分频,PCLK2 = HCLK
34 RCC_PCLK2Config(RCC_HCLK_Div1);
35
36 // APB1 预分频因子设置为1 分频,PCLK1 = HCLK/2
37 RCC_PCLK1Config(RCC_HCLK_Div2);
38
39 //-----------设置各种频率主要就是在这里设置-------------------//
40 // 设置PLL 时钟来源为HSI,设置PLL 倍频因子
41 // PLLCLK = 4MHz * pllmul
42 RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);
43 //-- -----------------------------------------------------//
44
45 // 开启PLL
46 RCC_PLLCmd(ENABLE);
47
48 // 等待PLL 稳定
49 while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
50 }
51
52 // 当PLL 稳定之后,把PLL 时钟切换为系统时钟SYSCLK
53 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
54
55 // 读取时钟切换状态位,确保PLLCLK 被选为系统时钟
56 while (RCC_GetSYSCLKSource() != 0x08) {
57 }
58 } else {
59 // 如果HSI 开启失败,那么程序就会来到这里,用户可在这里添加出错的代码处理
60 // 当HSE 开启失败或者故障的时候,单片机会自动把HSI 设置为系统时钟,
61 // HSI 是内部的高速时钟,8MHZ
62 while (1) {
63 }
64 }
65 }
3.1.3 RCC 软件延时
软件延时函数,使用不同的系统时钟,延时时间不一样,可以通过LED 闪烁的频率来判断。
- nCount参数为————时延时为1s
- T=1/72M*(7210001000)=1s
1 void Delay(__IO uint32_t nCount)
2 {
3 for (; nCount != 0; nCount--);
4 }