STM32F103五分钟入门系列时钟框图+相关寄存器总结+系统时钟来源代码(寄存器)

Posted 自信且爱笑‘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103五分钟入门系列时钟框图+相关寄存器总结+系统时钟来源代码(寄存器)相关的知识,希望对你有一定的参考价值。

学习板:STM32F103ZET6

强推系列:
STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结

STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置

STM32F103五分钟入门系列(三)GPIO的常用库函数使用方法总结+一个网络上的误区

前言

之前的几篇博客将STM32的GPIO相关库函数和寄存器总结了一遍,并且通过跑马灯实验、蜂鸣器实验、按键实验对GPIO进行了实战训练。从本博开始,在接下来几篇博客中,总结时钟、中断、串口通信。相信总结完这些后,别人的其它实验源代码都应该可以看懂、修改,甚至自己也可以编写其它实验的代码了。

本博总结STM32五大时钟源,以及由这五大时钟源怎么生成各类实验所需时钟。我用的开发板外部高速时钟输入是外接了8MHZ的晶振,外部低速时钟是外接了32.769KHZ晶振。其实在许多工厂项目中,用到的STM32都不是用开发板来实现各类功能的,因为开发板带有各类多余的外设,对于批量生产很不友好。所以许多项目都是用AD等软件自画PCB去加工,绘制PCB时,只需在STM32芯片外围添加项目所需要的外设即可。(扯远了)在自制PCB时,选用晶振有的使用25MHZ晶振、有的使用12MHZ晶振、8MHZ…等等,此时就有一个问题,怎么才能把这些晶振的频率搞到我们外设所需的频率上来,相信本博可以给出一个很好的回答。

本博同时会对与时钟相关的寄存器做初步总结,旨在为下一博客中各类库函数、系统时钟函数的总结做铺垫。

一、时钟源

(一)时钟类型

STM32的五个时钟源 HSI、HSE、LSI、LSE、PLL

①HSI( High Speed Internal)内部高速时钟
该时钟有内部的振荡电路生成的,频率为8MHZ,不过如果学过模电或者高频的话,应该知道,RC振荡回路生成的振荡信号的频率不稳定,所以该“8MHZ”是约等于。正因为它的不准确,所以许多时候都采用较为准确的外部晶振。

②HSE(High Speed External)外部高速时钟
该时钟由外部晶振提供,较为准确,因为在程序里可以分频、倍频,所以外界晶振频率没有严格要求,本博使用的板子外接8MHZ晶振。

③LSI(Low Speed Internal)内部低速时钟
与HSI一样,该时钟也是由内部RC振荡回路生成的,约等于40KHZ

④LSE(Low Speed External)外部低速时钟
与HSE一样,也是外接晶振,不过这个晶振频率一般很低,本博板子外接32.769KHZ

⑤PLL(Phase locked loop)锁相环
该时钟来源:HSI、HSE、HSI的二分频之后的N倍频(PLL最大为72MHZ,所以这个N不能太大),三种来源。

(二)时钟框图(极其重要

打开STM32开发指南库函数,或者STM32中文参考手册,在时钟章节找到时钟框图:
在这里插入图片描述

图中红色部分已经标出五大时钟源,之前总结过,HSI和LSI是由内部RC振荡电路生成,HSE和LSE由外接晶振提供,现以图中的系统时钟STSCLK为参考点,将上图分为左右两边。

可以看出系统时钟SYSCLK的来源:

来源一:8MHZ的高速内部时钟HSI直接提供,此时系统时钟为8MHZ
在这里插入图片描述

来源二:8MHZ的8MHZ的高速内部时钟HSI二分频后变为4MHZ,经过倍频器后提供,根据倍频系数N的不同,系统时钟为:4×2=8MHZ(N=2)、4×3=12MHZ(N=3)、…4×16=64MHZ(N=16),所以当HSI提供系统时钟时,系统时钟:4、8、12、16、20、24、28、32、36、40、44、48、52、56、60、64,最大系统时钟为64MHZ(根据CFGR寄存器的位21:18,这里的倍频系数只有4、5、6、7、8、9、6.5)
在这里插入图片描述

来源三:由外部高速时钟HSE经过倍频后得到,需要注意的是系统时钟最高为72MHZ,如我使用开发板外接8MHZ的晶振,则它提供系统时钟时,将它倍频2、3、…9倍可得到不同的系统时钟,但是倍频系数不能为9~16,因为此时系统时钟超过了72MHZ。
在这里插入图片描述

来源四:由外部高速时钟HSE直接提供
在这里插入图片描述

来源五:由外部高速时钟HSE二分频后再倍频提供,如8MHZ的外部晶振,二分频再倍频,系统时钟4×2=8、…4×16=64,,此时系统时钟最大为64MHZ(根据CFGR寄存器的位21:18,这里的倍频系数只有4、5、6、7、8、9、6.5,所以最大时钟频率应该是4×9=36MHZ)

在这里插入图片描述

STM32F1在写代码时默认系统时钟是72MHZ,是“来源三”的情况,下一博客会根据代码来总结

然后再看一下HSI、LSI和LES的用途:
在这里插入图片描述

可以看到HSE除了可以提供精确时钟给SYSCLK,还可以通过128分频当做RTC(Real Time Clock)实时时钟。

LSE外部低速时钟除了可以做RTC外还可以独立看门狗的时钟

LSI内部低速时钟做独立看门狗时钟

独立看门狗实验之后会总结,当程序正确执行时,会不停的给看门狗的计数器复位,使得看门狗不会发生中断。如果程序未能正确执行,看门狗的计数器未能及时复位,从而发生中断,在中断服务函数里执行整个程序的复位操作,使程序重新开始执行。独立看门狗可以保证程序跑飞后及时重新执行程序,类似于开发板的RESET按键。

MCO为时钟输出,可以连接其他设备提供时钟信号,可输出PLLCLK、HSI、HSE、SYSCLK时钟,输出哪种信号,在程序中都可以设置。(这个之后再总结,其实整个框图中的“小梯形”都是“选择”的意思,既然是选择,那么在程序中都可以通过编程来选择哪种输出)
MCO时钟输出为PA8引脚,有的板子会将PA8引脚专门引出到板子边缘插针,作为时钟输出引脚。注意的是此时PA8引脚是复用引脚!

在这里插入图片描述在这里插入图片描述

再看一下右半边:
在这里插入图片描述

从上到下:

(1)USBCLK
无论是异步还是同步串行传输,收发双方都需要时钟的,USB传输也不例外。图中USB的时钟最高为48MHZ,而且来源为PLLCLK锁相环输出。
所以它的时钟来源可以是:
①HSI 2分频后再倍频。此时倍频系数最大为12,得到的时钟频率不能大于48MHZ
②HSE经过PLLMUL倍频器后输出。
③HSE经过2分频后再通过PLLMUL倍频输出。

(2)I2S3CLK、I2S2
直接使用系统时钟

(3)剩下的一些常用外设的时钟来源:SYSCLK时钟经过AHB预分频器来实现。具体到达外设的时钟频率是多少,应该去察看外设的参考手册,然后再通过设置AHB、APB1、APB2、ADC等预分频器使时钟输出达到对应频率。

二、时钟的相关寄存器

关于时钟控制寄存器的声明在stm32f10x.h头文件中:
在这里插入图片描述

我们只总结一下CR、CFGR、AHBENR、APB1ENR、APB2ENR这五个寄存器,其它的寄存器基本不会用到,所以就不总结了。当然触类旁通,学会了这五种寄存器用法,剩下的时钟寄存器也就会用了。

(一)时钟控制寄存器(RCC_CR)

在这里插入图片描述

现从低位到高位分析该寄存器:

第0位、第1位

在这里插入图片描述第0位、第1位控制着内部高速时钟HSI,第0位可以软件置1,此时打开内部高速时钟HSI。第0位置0的时候就关闭了HSI。第1位为HSI时钟就绪位,如果第0位置1打开HSI,再经过6个HSI时钟周期后,第1位会硬件置1,表示HSI时钟已经准备就绪。

需要注意的是CR寄存器是对HSI、HSE、PLL使能,而这几种时钟源都可以作为系统时钟SYSCLK的来源。如HSI现在为系统时钟来源,则不能对它置0来关闭它,同HSE、PLL。至于用哪个时钟源作为系统时钟来源,在CFGR寄存器中会总结。

代码:

RCC->CR|=0x00000001;

CR寄存器定义时是用的RCC_TypeDef,但是给CR寄存器赋值时用到是RCC->…因为RCC_TypeDef是一个结构体指针,它的地址是程序编译时分配的地址,并不是物理层的地址,单片机要识别CR的值,需要的是物理层地址。所以不能RCC_TypeDef->…
现对RCC溯源:
在这里插入图片描述
在这里插入图片描述
继续溯源:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
到这里应该明白了RCC是定义的寄存器的物理层的地址,所以写程序的时候应该RCC->…而且这几类的时钟寄存器都是用的RCC->…的形式。

第2位
没有实际意义,保持0就好

第7~ 3位
在这里插入图片描述

这个一般也用不到,不过可以用来调整HSI的输出频率,毕竟HSI输出的频率不稳定,不是一个准确值,如果用到HSI,可以通过这5位来微调输出频率。当然如果有些项目需要8MHZ的脉冲,可以用开发板的MCO(CFGR寄存器设置)设置HSI时钟输出,用于其他开发板、接收机等的信号输入,用这5位来微调输出脉冲。

第15~ 8位
无用
在这里插入图片描述

第16、17位
在这里插入图片描述

与HSI类似

第18位
在这里插入图片描述

好好总结一下这个地方

首先要明白什么是旁路,旁路的意思是HSE的晶振还在工作,但是不给单片机提供时钟输入了。看一下原理图
在这里插入图片描述

当HSE被选为系统时钟后,8MHZ的晶振就当做了时钟输入,但是如果不想需要8MHZ晶振了,或者晶振因为故障不准确了,需要使用另一个时钟信号,可以将另一个时钟信号通入OSC_IN,然后将CR寄存器的第18位软件置1就可以使用外来的时钟了,此时原来的外部晶振被旁路了(但是它还在振荡)。

第19位

在这里插入图片描述

CSS为时钟安全系统,用于监视HSE。当HSE做系统时钟输入,如果HSE发生故障,会导致HSE关闭;或者HSE通过PLL当做系统时钟输入,如果HSE发生故障,会导致PLL和HSE都关闭。此时CSS会产生中断,允许软件完成自救。
所以说白了CSS是监视外部高速时钟的东西。置1后,如果HSE被用作系统时钟,CSS开始发挥作用。

第20~23位
保持0

第24、25位

在这里插入图片描述

与HSI等类似,不在赘述。

总结一下:
1、CR寄存器的用法:

RCC->CR|=0x...
RCC->CR&=0x...

2、CR寄存器主要是对HSI、HSE、CSS、PLL的使能和设置。

(二)时钟配置寄存器(RCC_CFGR)

在这里插入图片描述

前面的CR寄存器是用来开启哪个时钟的,本节的CFGR寄存器是来设置的。设置的内容如图中“√”所示。主要是分频系数、倍频系数、选择器(图中梯形)的设置。现从低位到高位分析一下:

在这里插入图片描述

第0、1位
在这里插入图片描述该位是用来设置系统时钟来源的,之前也总结过,系统时钟的来源:
①HSI直接做系统时钟
②HSI、HSE当做PPL时钟输入,PLL提供系统时钟
③HSE直接当做系数时钟

注意的是当单片机上电后寄存器清零,所以默认状态下位1:0是00,为HSI作为系统时钟。
设置程序与之前的CR寄存器一致:

RCC->CFGR&=0x1111111c;//第0、1位清零
RCC->CFGR|=0x0000000d;//选择HSE作为系统时钟
RCC->CFGR|=0x0000000e//选择PLL输出作为系统时钟

需要注意的是如果采用PLL作为系统时钟,需要配置选择器和分频系数,这些也在本寄存器中设置

第2、3位
在这里插入图片描述

这两位是来显示系统时钟状态的。比如程序中可以这样得到状态:

		if((RCC->CFGR&0x0000000c)==0)//HSI
		{
		
		}
		if((RCC->CFGR&0x0000000c)==4)//HSE
		{
		
		}
		if((RCC->CFGR&0x0000000c)==8)//PLL
		{
		
		}

或者直接:

a=RCC->CFGR&0x0000000c;

再用串口调试输出来。还可以用j-link仿真,在a前后设置断点,观察前后置的变化。

第7:4位
在这里插入图片描述

AHB分频系数设置
在这里插入图片描述

第10:8位

在这里插入图片描述

APB1分频系数设置

在这里插入图片描述
第13:11位
在这里插入图片描述

APB2分频系数设置

在这里插入图片描述

第15:14位

在这里插入图片描述

ADC分频系数设置

在这里插入图片描述

第16位
在这里插入图片描述

设置到PLL的“选择器”

在这里插入图片描述

其中图中橙色标准为该位所说的PREDIV1,而PREDIV1又有两个来源:
①HSE直接提供
②HSE经过2分频提供
所以之后第17位来选择哪种来源。

第17位

在这里插入图片描述

用来设置PREDIV1的来源的。

在这里插入图片描述

这里的设置会与RCC_CFGR2寄存器产生联系,但是CFGR2寄存器一般用不到,所以一般情况下CFGR2寄存器的[3:1]都为0,所以只需要在CFGR寄存器的17位设置PREDIV1的来源即可(即设置选择器PLLXTPRE)

第21:18位

在这里插入图片描述

这里需要注意寄存器的倍频系数与图中的倍频系数不一致,所以编程的时候需要以寄存器的标注为准。

第22位

在这里插入图片描述

USB的分频系数设置

在这里插入图片描述

这里注意的是最高输出频率为48MHZ,如系统时钟为72MHZ时,必须采用1.5分频,此时USB输出为48MHZ。系统时钟小于72MHZ时,两个分频系数都可用,根据实际情况选用即可。

第22位

在这里插入图片描述

该位设置MCO输出时钟,MCO在PA8引脚,使用时需要使能复用时钟和GPIOA的时钟,然后选择系统时钟以及设置选择器和分频、倍频系数。

如利用PA8引脚输出系统时钟:

	RCC->APB2ENR|=1<<0;//使能AFIO时钟
	RCC->APB2ENR|=1<<2;//先使能外设PORTA时钟
	GPIOA->CRH&=0XFFFFFFF0; 
	GPIOA->CRH|=0X0000000B;//PA8复用输出
	RCC->CFGR&=0xf0ffffff;
	RCC->CFGR|=4<<24;

(三)外设时钟使能寄存器(RCC_AHBENR)

在这里插入图片描述

该寄存器是对AHB总线上的外设配置,只需了解有哪些外设就好,用到时再察看中文参考手册即可。

(四)APB2 外设时钟使能寄存器(RCC_APB2ENR)

在这里插入图片描述

APB2上搭载的是高速外设,UART1、SPI1、Timer1、ADC1、ADC2、所有普通 IO 口(PA~PE)、第二功能 IO 口。

(五)APB1 外设时钟使能寄存器(RCC_APB1ENR)

在这里插入图片描述

APB1 上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3 、UART34、UART35、SPI1、SPI2、WWDG、Timer2~7

(三)、(四)、(五)的寄存器配置方法一样,只需了解哪些外设在哪个寄存器上即可,以后使用时察看中文参考手册即可。

三、系统时钟更改(附代码+测试)

1、默认条件下的系统时钟

利用PA8引脚输出默认状态下的系统时钟
(1)使能复用时钟和GPIOA时钟

在这里插入图片描述

	RCC->APB2ENR|=1<<0;//使能AFIO时钟
	RCC->APB2ENR|=1<<2;//使能外设GPIOA时钟

(2)设置PA8为复用推挽输出,最大速度为50MHZ

在这里插入图片描述

	GPIOA->CRH&=0XFFFFFFF0; 
	GPIOA->CRH|=0X0000000B;//PA8复用输出

(3)输出系统时钟

在这里插入图片描述

	RCC->CFGR&=0xf0ffffff;
	RCC->CFGR|=4<<24;   //输出系统时钟

完整代码:

#include "stm32f10x.h"
 int main(void)
 {	
	 
	RCC->APB2ENR|=1<<0;//使能AFIO时钟
	RCC->APB2ENR|=1<<2;//使能外设GPIOA时钟
	GPIOA->CRH&=0XFFFFFFF0; 
	GPIOA->CRH|=0X0000000B;//PA8复用输出
	 
	RCC->CFGR&=0xf0ffffff;
	RCC->CFGR|=4<<24;   //输出系统时钟
	 
	while(1)
	{	
	
	}
 }

示波器效果:

在这里插入图片描述

2、内部高速时钟做系统时钟(HSI——>SYSCLK)

(1)打开HSI时钟
在这里插入图片描述

	 RCC->CR|=1;//打开HSI

(2)将HSI设置为系统时钟
在这里插入图片描述在这里插入图片描述

	RCC->CFGR&=0xfffffffc;//将HSI设置为系统时钟

完整代码:

#include "stm32f10x.h"
 int main(void)
 {	
	 
	RCC->APB2ENR|=1<<0;//使能AFIO时钟
	RCC->APB2ENR|=1<<2;//使能外设GPIOA时钟
	GPIOA->CRH&=0XFFFFFFF0; 
	GPIOA->CRH|=0X0000000B;//PA8复用输出
	 
	 RCC->CR|=1;//打开HSI
	 RCC->CFGR&=0xfffffffc;//将HSI设置为系统时钟

	RCC->CFGR&=0xf0ffffff;
	RCC->CFGR|=4<<24;   //输出系统时钟
	 
	while(1)
	{	
	
	}
 }

示波器效果:

在这里插入图片描述

3、外部高速时钟HSE做系统时钟(HSE——>SYSCLK)

(1)打卡HSE时钟

在这里插入图片描述

RCC->CR|=0x00010000;//打开HSE

(2)将HSE设置为系统时钟

在这里插入图片描述在这里插入图片描述

	 RCC->CFGR&=0xfffffffc;
	 RCC->CFGR|=0x00000001;//将HSE设置为系统时钟

完整代码:

#include "stm32f10x.h"
 int main(void)
 {	
	 
	RCC->APB2ENR|=1<<0;//使能AFIO时钟
	RCC->APB2ENR|=1<<2;//使能外设GPIOA时钟
	GPIOA->CRH&=0XFFFFFFF0; 
	GPIOA->CRH|=0X0000000B;//PA8复用输出
	 
	 RCC->CR|=0x00010000;//打开HSE
	 RCC->CFGR&=0xfffffffc;
	 RCC->CFGR|=0x00000001;//将HSE设置为系统时钟

	RCC->CFGR&=0xf0ffffff;
	RCC->CFGR|=4<<24;   //输出系统时钟
	 
	while(1)
	{	
	
	}
 }

示波器效果:

在这里插入图片描述

4、HSE经过4倍频后做系统时钟(HSE——>PLL——>SYSCLK)

(1)使能HSE

在这里插入图片描述

RCC->CR|=1<<16;

(2)设置PLLXTPRE选择器
在这里插入图片描述

RCC->CFGR&=0<<17;

(3)设置PLLSRC选择器
在这里插入图片描述

RCC->CFGR|=1<<16;

(4)设置PLLMUL倍频系数
在这里插入图片描述
在这里插入图片描述

RCC->CFGR&=0xffc3ffff;
RCC->CFGR|=0x00080000;

(5)使能PLL时钟
在这里插入图片描述

RCC->CR|=1<<24;

(6)设置PLL为系统时钟输入
在这里插入图片描述

RCC->CFGR&=0xfffffffc;
RCC->CFGR|=2;

完整代码:

#include "stm32f10x.h"
 int main(void)
 {	
	 
	RCC->APB2ENR|=1<<0;//使能AFIO时钟
	RCC->APB2ENR|=1<<2;//使能外设GPIOA时钟
	GPIOA->CRH&=0XFFFFFFF0; 
	GPIOA->CRH|=0X0000000B;//PA8复用输出

	 
	RCC->CFGR&= (uint32_t)0xF0FF0000; 
	RCC->CR&= 0xFEF6FFFF;  //关闭PLL、CSS、HSE
	RCC->CR&= (uint32_t)0xFFFBFFFF;  //位18 HSEBYP置0,表示未旁路
	RCC->CFGR &= 0xFF80FFFF;//关闭USBPRE预分频、PLLMUL倍频系数、HSE分频器作为PLL输入、PLL作为时钟源都关闭
	RCC->CIR = 0x009F0000;//关闭中断标志位(这个可以不用清零)
	
	 //前面清零是必要的,否则会失败
	 
	RCC->CR|=1<<16;//使能HSE
	while((RCC->CR&0x00020000)==0)  //等待HSE时钟就绪
	 {
	                  
	 }
	FLASH->ACR |= FLASH_ACR_PRFTBE;
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    
	 //flash设置也非必要,运行底层时已经设置过了
	 
	RCC->CFGR&=0xfffdffff;//设置PLLXTPRE选择器
	RCC->CFGR|=1<<16;//设置PLLSRC选择器
	 
	RCC->CFGR&=0xffc3ffff;
	RCC->CFGR|=0x00080000;//设置PLLMUL倍频系数4
	RCC->CFGR&=0xffffff0f;//设置AHB分频系数为1  目的是对AHB总线设置、核心存储器、DMA
	 //AHB设置非必要,比较最大是72MHZ,所以运行底层时,已经设置过了,保持不分频就行
	RCC->CFGR&=0xfffff8ff;
	RCC->CFGR|=1<<10;    //设置APB1为2分频(所有设置完成后系统时钟为32MHZ,所以也可以不设置,因为每超过36MHZ)
	 //AHB1非必要,运行底层时设置过了,

以上是关于STM32F103五分钟入门系列时钟框图+相关寄存器总结+系统时钟来源代码(寄存器)的主要内容,如果未能解决你的问题,请参考以下文章

STM32F103五分钟入门系列(十六)输入捕获(精雕细琢-.-)

STM32F103五分钟入门系列按键实验(库函数+寄存器)

STM32F103五分钟入门系列蜂鸣器实验(库函数+寄存器)

STM32F103(十九)ADC相关的几个实验—内部温度传感器内部参照电压光敏传感器

STM32F103(二十)DAC(贼详细)

STM32F103五分钟入门系列(十四)窗口看门狗WWDG