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五分钟入门系列蜂鸣器实验(库函数+寄存器)