STM32学习笔记——十一、RCC使用HSE/HSI配置时钟

1 RCC

RCCreset 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 一般用于操作系统的时钟节拍,也可以用做普通的定时
      在这里插入图片描述

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配置系统时钟

  1. RCC 是单片机内部资源,不需要外部电路。
  2. 通过LED 闪烁的频率来直观的判断不同系统时钟频率对软件延时的效果。
  3. 编写两个RCC 驱动文件,bsp_clkconfig.h 和bsp_clkconfig.c,用来存放RCC 系统时钟配置函数
  4. 编程要点:编程要点对应着时钟树图中的序号
    • 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 }
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值