STM32F103五分钟入门系列SystemInit()函数SetSysClock()函数
Posted 自信且爱笑‘
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103五分钟入门系列SystemInit()函数SetSysClock()函数相关的知识,希望对你有一定的参考价值。
学习板:STM32F103ZET6
强推系列:
STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结
STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置
STM32F103五分钟入门系列(三)GPIO的常用库函数使用方法总结+一个网络上的误区
参考:
STM32F103五分钟入门系列(六)时钟框图+相关寄存器总结+系统时钟来源代码(寄存器)
SystemInit()函数、SystemInit()函数
前言
上一篇博客总结了时钟框图、时钟相关寄存器。在博客的最后又总结了如何在main.c中重新设置系统时钟并通过MCO(PA8复用引脚)输出系统时钟,再通过示波器察看系统时钟的波形。上一篇博客也总结到,在main.c中重新设置系统时钟时,如果设置不成功,就依旧采用默认的系统时钟;当然重新设置系统时钟时,AHB、APB1、APB2等分频系数、flash等不需要再重新设置,因为程序编译时已经在底层的system.stm32f10x.c中设置了这些,如果外设没有特殊要求,在main.c中重新设置时钟时这些就不用再设置了。
本博客对system.stm32f10x.c中时钟相关的函数进行总结,通过本篇博客应该就可以清楚知道默认的系统时钟是怎么来的?如何在底层中修改系统时钟?如果外部晶振接的不是8MHZ,那么此时默认的系统时钟是多少、怎么计算?
一、SystemInit()
打开system_stm32f10x.c文件,找到SystemInit()函数
现一句句分析:
1、打开HSI时钟
RCC->CR |= (uint32_t)0x00000001 //打开HSI时钟
HSI在这里打开后就不会再关闭了,所以也不用等待它使能就绪。HSI打开的目的很明确,就是在HSE、PLL作为系统时钟源发生故障时,通过CSS的中断来自动将HSI切换为系统时钟源,所以HSI一直开启。
2、关闭MCO、ADCPRE、PPRE2、PPRE1、HPRE、SWS、SW
RCC->CFGR &= (uint32_t)0xF0FF0000; //关闭MCO、ADCPRE、PPRE2、PPRE1、HPRE、SWS、SW
还是觉得这个底层有问题,位27应该要置0(我的板子是HD,不是CL),但是根据寄存器,位27是保留位,应该与1进行与运算,所以这个地方有问题,但不影响使用。
这行代码设置的东西:
即:关闭MCO时钟输出、AHB不分频、APB1不分频、APB2不分频、ADC 2分频、SHI作为系统时钟
3、使能PPL和CSS、关闭HSE
RCC->CR &= (uint32_t)0xFEF6FFFF; //使能PPL和CSS、关闭HSE
4、HSEBYP清零
RCC->CR &= (uint32_t)0xFFFBFFFF;
位18置零。表示HSE没有被旁路
5、PLLSRC、PLLXTPRE、PLLMUL、USBPRE
RCC->CFGR &= (uint32_t)0xFF80FFFF;
位16~22清零,表示:USB预分配系数1.5、PLLMUL 2倍频输出、SHE不分频做PLL的时钟来源、PLL输入时钟源为HSI/2。如下图紫色部分所示。
6、CSSC、PLLRDYC
RCC->CIR = 0x009F0000;
此代码清除CSS时钟的系统中断、清除PLL就绪中断
7、进入SysClock();
接下来就进入SysClock()函数了。
8、总结(重要)
从之前的七小节得到以下结论:
现在的状态:
1、HSI:打开,并作为系统时钟
2、HSE:关闭
3、MCO:关闭
4、PLL:打开
5、CSS:打开
6、CSS系统中断标志位:清除
7、PLL就绪中断:清除
8、HSE旁路:未旁路
9、USB预分频:1.5,USB采用SYSCLK/1.5时钟
10、AHB预分频:1,AHB输出后为系统时钟的大小
11、APB1预分频:1,到APB1外设的时钟PCLK1=SYSCLK
12、APB2预分频:1,到APB2外设的时钟PCLK2=SYSCLK
13、ADC预分频:2,到ADC的时钟ADCCCLK=SYSCLK/2
14、PLLMUL选择器输出倍频:2
15、SHE可直接提供PLL时钟来源
16、HSI/2可直接提供PLL时钟来源
在时钟框图中标注(绿色)
注意以下几点:
1、APB1低速外设总线的时钟最大为36MHZ,所以这个地方之后可能需要更改倍频系数
2、ADC的最大时钟为14MHZ,所以这个地方之后可能需要更改倍频系数
二、SetSysClock()
底层更改系统时钟的方法
SystemInit()函数执行到最后就进入SetSysClock()函数了
点击进入SetSysClock()
可以看到定义不同的标识符,就执行对应的函数,从名称上也能看出是在配置哪个频率的系统函数
打开定义,注意此时只有72MHZ的函数和标识被定义,其他的都被注释了,所以只能用72MHZ相关的标识和函数打开定义(如果打不开,就一个个的试吧)
所以也看出来了,要想在底层中修改系统时钟,就在这里修改,如更改为36MHZ,就将SYSCLK_FREQ_36MHz解除注释,将SYSCLK_FREQ_72MHz再注释掉:
现分别看看这几个时钟配置函数
(一)SetSysClockTo72()函数
1、使能HSE时钟
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
即:
RCC->CR |= 0x00010000;
该行代码是使能HSE时钟
2、等待HSE时钟就绪
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
分析一下这几行代码
即
HSEStatus = RCC->CR & 0x00020000;
当HSE就绪后,位17变为1,此时 HSEStatus!=0
StartUpCounter != HSE_STARTUP_TIMEOUT是保证在一定时间内HSE就绪,不能无期限的等待它就绪,当就绪后,while后面的判断条件就不成立了,跳出循环。
当然保证HSE不会出错的情况下,这几行代码还可以改为:
while((RCC->CR & 0x00020000)==0)
{
}
3、flash设置
上图中框中代码是判断HSE是否就绪,如果就绪就执行接下来的代码。如果没有就绪,也不会重新执行HSE使能了,说明HSE出现了问题,系统时钟配置也就无法成功。
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
flash相关文件:stm32f10xxx闪存编程参考手册.pdf
这三行代码的定义的标识不好理解,直接换成十六进制数:
/* Enable Prefetch Buffer */
FLASH->ACR |= 0x10;
/* Flash 2 wait state */
FLASH->ACR &= (uint32_t)((uint32_t)~)0x03);
FLASH->ACR |= (uint32_t)0x02;
注意:stm32中二进制数是低位对齐,高位补零的,所以可以用一个8位二进制数对32位寄存器运算,只需前面补零就行。
第一行代码:
该行代码使能预取缓冲区
第二行代码:
该行代码是为了将位1:0置0,当然位31:8这些保留位也变为了0。
第三行代码:
该行的目的是把位1置1
此时位2:0位010,意思是设置设置SYSCLK周期与闪存访问时间的比例为2(目前是把系统时钟设置为72MHZ)
这里还有有点问题的,在第二行代码中,没有把位2置0,(底层有好几处不影响使用、但逻辑错误的地方)所以可以改为:
FLASH->ACR &= (uint32_t)((uint32_t)~)0x07);//第二行修改
所以flash设置为:每两个时钟周期,访问一次闪存。
4、设置分频系数(APB1)
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
前两行的标识都为0,,或运算后CFGR寄存器的状态不变,就不用看了。直接第三行:
或运算后CFGR寄存器的位10变为1,即PPRE1为100
即设置APB1的分频系数为/2。
此时时钟框图状态:(绿色标注为SystemInit()后的状态,蓝色标注为新加状态)
5、设置倍频系数(PLLMUL)
/* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
不好理解,把定义的标识重新用16进制数表示:
RCC->CFGR &= ~(0x00010000 | 0x00020000 |0x003C0000);
RCC->CFGR |= 0x00010000| 0x001C0000;
还是不好理解,再简化,将或运算和取反运算求出来:
RCC->CFGR &=0xffc0ffff;
RCC->CFGR |=0x001d0000;
第一行
目的是将位21:16置0。
第二行
目的是将位20、19、18、16置1
经过这俩行代码,位21:18=0111,位16=1
即这俩行代码设置PLL时钟源为HSE提供、PLLMUL为9倍频。
时钟框图中标注:(绿色为SystemInit()函数设置后状态,蓝色为本函数新设置的状态)
6、使能PLL时钟
RCC->CR |= RCC_CR_PLLON;
将标识写为16进制数:
RCC->CR |= 0x01000000;
所以该行代码使能PLL时钟
接下来的代码等待PLL时钟就绪,之前已经总结过了,不在赘述。
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
7、将PLL设置为系统时钟
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
将标识改为16进制数:
RCC->CFGR &= (uint32_t)((uint32_t)~(0x00000003));
RCC->CFGR |= (uint32_t)0x00000002;
即:
RCC->CFGR &= 0xfffffffc;
RCC->CFGR |= 0x00000002;
第一行
将CFGR寄存器的位1:0设置为00
第二行
将CFGR寄存器的位1:0设置为10
这俩行代码将PLL设置为系统时钟。
之后就是等待PLL时钟就绪:
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
8、总结(重要)
目前的时钟状态:
72MHZ系统时钟来源:HSE(8M)经过9倍频得到PLL时钟,PLL时钟做系统时钟输入
如果外接的晶振为24MHZ,则只需将PLLMUL的倍频系数9改为3即可。还有一些晶振比如15M,不管如何设置分频、倍频都无法达到72MHZ,此时系统时钟只能选用<72MHZ的一个值。
(二)SetSysClockTo56()函数
代码与SetSysClockTo72()函数基本一样,唯一不同的是设置PLLMUL的倍频系数。
1、设置倍频系数(PLLMUL)
/* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL7);
将标识写成16进制的形式:
/* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(0x00010000 | 0x00020000 | 0x003C0000));
RCC->CFGR |= (uint32_t)(0x00010000 | 0x00140000);
还是不好理解,将或运算和取反运算算出来:
/* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */
RCC->CFGR &= 0xffc0ffff;
RCC->CFGR |= 0x00150000;
第一行:
将CFGR寄存器的位21:16置0
第二行:
将CFGR寄存器的位18、为16置1.
经过这俩行代码后,位21:18=0101,位16=1,位17=0
所以这俩行代码将HSE直接设置为PLL时钟源的输入,PLLMUL倍频系数为7,此时PLL时钟为7*8MHZ=56MHZ
之后就是使能PLL时钟、等待PLL时钟就绪、将PLL设置为系统时钟、等待系统时钟就绪,这些代码与SetSysClockTo72()函数的代码一样。
2、总结(重要)
SetSysClockTo56()函数的设置与SetSysClockTo72()函数基本相同,唯一不同的地方是PLLMUL倍频系数的设置。
56MHZ时钟来源为:HSE(8MHZ)经过7倍频(PLLMUL)生成PLL时钟,PLL做为系统时钟输出。
时钟框图:(绿色为SystemInit()函数设置后的结果,蓝色为本函数设置的结果)
(三)SetSysClockTo48()函数
代码与SetSysClockTo72()函数基本一样,唯一不同的是设置PLLMUL的倍频系数。
1、设置倍频系数(PLLMUL)
/* PLL configuration: PLLCLK = HSE * 6 = 48 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL6);
将标识改为16进制数:
/* PLL configuration: PLLCLK = PREDIV1 * 6 = 48 MHz */
RCC->CFGR &= (uint32_t)~(0x00010000| 0x00020000 | 0x003C0000);
RCC->CFGR |= (uint32_t)()0x00010000 | 0x00100000);
将或运算和取反运算算出来:
/* PLL configuration: PLLCLK = PREDIV1 * 6 = 48 MHz */
RCC->CFGR &= 0xffc0ffff;
RCC->CFGR |= 0x00110000;
第一行:
将CFGR寄存器位21:16置为0
第二行:
将位20和位16置1
此时位21:18=0100,位17=0,位16=1。
所以这俩行代码将HSE直接设置为PLL时钟源的输入,PLLMUL倍频系数为6,此时PLL时钟为6*8MHZ=48MHZ
之后就是使能PLL时钟、等待PLL时钟就绪、将PLL设置为系统时钟、等待系统时钟就绪,这些代码与SetSysClockTo72()函数的代码一样。
2、总结(重要)
SetSysClockTo48()函数的设置与SetSysClockTo72()函数基本相同,唯一不同的地方是PLLMUL倍频系数的设置。
48MHZ时钟来源为:HSE(8MHZ)经过6倍频(PLLMUL)生成PLL时钟,PLL做为系统时钟输出。
时钟框图:(绿色为SystemInit()函数设置后的结果,蓝色为本函数设置的结果)
(四)SetSysClockTo36()函数
代码与SetSysClockTo72()函数基本一样,不同的是设置PLLMUL的倍频系数、PLLXTPRE选择器设置。
1、设置倍频系数(PLLMUL)、设置选择器(PLLXTPRE)
/* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL9);
将标识改为16进制数:
/* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(0x00010000 | 0x00020000 | 0x003C0000));
RCC->CFGR |= (uint32_t)(0x00010000 |0x00020000| 0x001C0000);
将或运算和取反运算算出来:
/* PLL configuration: PLLCLK = (HSE / 2) * 9 = 36 MHz */
RCC->CFGR &= 0xffc0ffff;
RCC->CFGR |= 0x001f0000;
第一行:
将CFGR寄存器的位21:16置0
第二行:
将位20:16置1
执行这两行代码后,位21:18=0111;位17=1;位16=1;
所以是将HSE经过2分频后再9倍频作为PLL时钟来源,然后PLL时钟做系统时钟输入。此时PLL时钟为:9×(8MHZ/2)=36MHZ
之后就是使能PLL时钟、等待PLL时钟就绪、将PLL设置为系统时钟、等待系统时钟就绪,这些代码与SetSysClockTo72()函数的代码一样。
2、总结(重要)
SetSysClockTo36()函数的设置与SetSysClockTo72()函数基本相同,唯一不同的地方是PLLMUL倍频系数的设置。
36MHZ时钟来源为:HSE(8MHZ)2分频后再经过9倍频作为PLL时钟来源,然后PLL时钟作为系统时钟来源。
时钟框图:(绿色为SystemInit()函数设置后的结果,蓝色为本函数设置的结果)
(五)SetSysClockTo24()函数
代码与SetSysClockTo72()函数基本一样,不同的是设置PLLMUL的倍频系数、PLLXTPRE选择器设置。
1、设置倍频系数(PLLMUL)
/* PLL configuration: = (HSE / 2) * 6 = 24 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL6);
将标识
以上是关于STM32F103五分钟入门系列SystemInit()函数SetSysClock()函数的主要内容,如果未能解决你的问题,请参考以下文章
STM32F103五分钟入门系列(十五)输入捕获(精雕细琢-.-)