**
这部分感觉有点乱可以先看后边的总结部分,代码的上边那里
**
stm32的时钟就是这个图。时钟她是一级一级整上去的。通过pll锁相环,把外部晶振的频率给他顶上去。再需要多少就分频多少,得到需要的频率
我们要通过外部晶振HSE来得到apb2处的点亮gpiob处的流水灯。需要的是72mhz的频率
所以看图
是这么个路线
但是这个是要配置的。所以
这个是内部时钟,上电以后内部时钟先运行,让cpu能够切换到外部时钟那里去。
所以我们看图,
先是这个,决定输入的时钟是否二分频。
然后是src,选择内部还是外部时钟源
mul来倍频
下一步这里,sw选择用哪个做系统时钟源
下一步ahb分频。
下一步apb2分频,也就是pclk2
这样就可以得到想要的时钟频率
然后我们来查手册
第一个PLLXTPRE,
这个寄存器
第十七位,就是我们的第一道门槛,因为复位值是0x00000000
所以这个不用管,自动不分频。
然后PLLSRC
用来选择哪个时钟源来pll倍频
我们选hse,置一。
然后还是这个寄存器,PLLMUL,第18-21位。我们的晶振是8mhz,所以要9倍频,就是0111。
然后sw,0-1位,我们选10,倍频过后的时钟给他。
往下走,是ahb预分频器,我们不让他分频,0000
继续往下,apb2分频器,不分频,000
输出就是72mhz
这里注意了,pll还有个专门配置的寄存器,就是RCC_CR
偏移量0,复位值0x00000083
24位置一,然后等PLL准备好后,25位由硬件置一,代表可用了。
16位,我们要启用外部时钟源,得置一。
准备好后,这个17位就会被硬件置一,代表准备完毕
0位是1,因为一开始cpu肯定要有一个时钟来提供时钟源,就是这个高速内部时钟源。他先运行起来,然后才能切换到外部
1位也是1,能用肯定是就绪的。下一个是内部高速时钟调整,这个是人家系统默认的。10000
所以0x83是这么来的。1000 0011
————————————————————————————_________________------------------------------------------------------
总结一下。
- RCC_CR 地址:0x40021000 + 0 = 0x40021000 复位值:0x00000083
16位HSEON置一,然后判断17位HSERDY,外部时钟就绪后进性下一步 - RCC_CFGR 地址:0x40021000 + 0x04 = 0x40021004 复位值:0x00000000
就是我们上边说的哪个顺序,16位PLLSRC置1,17位PLLXTPRE置0 ,然后18-21位的PLLMUL 置0111,4-7位AHB预分频置0000不分频,8-10位APB1预分频置100,2分频(因为ahb是72mhz,apb1最高可以36mhz,所以必须分频)。11-13位APB2预分频置000,不分频。 - RCC_CR
24位PLLON置一,判断25位PLLRDY,为1说明就绪,继续下一步。 - RCC_CFGR
0-1位SW置10,PLL输出作为系统时钟,然后判断2-3位,是否为10(PLL输出作为系统时钟)
配置完毕
思路很简单,先配置外部时钟稳定,然后设置里边的时钟路线,配置完毕就启动PLL开始倍频。倍频完毕后把倍频出来的结果送给系统。切换完成
所以是三层的嵌套。第一层判断HSE是否准备完毕,第二层判断PLL是否倍频完毕,第三层判断系统时钟是否已经切换为PLL的输出
下面是代码部分
设置时钟函数。我都给他分频了贼低。
#include "setClock.h"
void SetClock72MHZ(void )
{
uint GPIO_LED_HSERDY = 0;
uint GPIO_LED_PLLRDY = 0;
uint GPIO_LED_SWS = 0;
uint WAIT_TIME = 0;
rRCC_CR = 0x00000083;
rRCC_CR &= ~(1 << 16);
rRCC_CR |= (1 << 16);
do
{
GPIO_LED_HSERDY = rRCC_CR & (1 << 17);
WAIT_TIME ++;
}
while((WAIT_TIME < 0x0fffffff) && (GPIO_LED_HSERDY == 0));//confirm it's 1 or no respond
if((rRCC_CR & (1 << 17)) != 0) //confirm it's 1 again
{
//rFLASH_ACR |= 0x10;
//rFLASH_ACR &= (~0x03);
//rFLASH_ACR |= (0x02);
//now, HSE is ok, you can set references
rRCC_CFGR &= ~((0x0f << 4) | (0x07 << 8) | (0x07 << 11));
rRCC_CFGR |= ((0x09 << 4) | (0x04 << 8) | (0x07 << 11));
rRCC_CFGR &= ~((1 << 16) | (1 << 17));
rRCC_CFGR |= ((1 << 16) | (0 << 17));
rRCC_CFGR &= ~(0x0f << 18);
rRCC_CFGR |= (0x07 << 18);
rRCC_CR &= ~(1 << 24);
rRCC_CR |= (1 << 24);
WAIT_TIME = 0;
do
{
//ledFlash();
delay(50);
GPIO_LED_PLLRDY = rRCC_CR & (1 << 25);
WAIT_TIME ++;
}
while(GPIO_LED_PLLRDY == 0);
if((rRCC_CR & (1 << 25)) != 0)
{
//PLL is ready
rRCC_CFGR &= ~(0x03 << 0);
rRCC_CFGR |= (0x02 << 0);
WAIT_TIME = 0;
ledFlash();
do
{
GPIO_LED_SWS = rRCC_CR & (0x03 << 2);
WAIT_TIME ++;
}
while((WAIT_TIME < 0x0fffffff) && (GPIO_LED_SWS != (0x02 << 2)));
if((rRCC_CR & (0x03 << 2)) == (0x02 << 2))
{
//all ready
}
else
{
while(1); //pll set is wrong
}
}
else
{
while (1);// PLL is wrong
}
}
else
{
while (1);//HSE is wrong
}
}
咱们是用位操作干的,不是用库函数。用位操作就很容易理解这玩意的原理。
为什么要先与取反呢
是因为先要给他清零,再给值
//2020.5.5, the last day of haoliday. The first day for study every day...
#include "GPIO_LED72MHZ.h"
#include "setClock.h"
void main(void)
{
ledInit();
ledFlash();
ledInit();
SetClock72MHZ();
ledFlash();
while(1);
//return 0;
}
void ledInit(void)
{
rRCC_APB2ENR = 0x00000008;
rGPIOB_CRH = 0x33333333;
rGPIOB_ODR = 0x0000ff00;
}
void ledFlash(void)
{
unsigned char i,j;
for(i = 0; i < 4; i ++)
{
rGPIOB_ODR = 0x0000ff00;
for(j = 0; j < 100; j++)
delay(50000);
rGPIOB_ODR = 0x0000f000;
for(j = 0; j < 100; j++)
delay(50000);
}
}
void delay(unsigned int i)
{
while(i --);
}
这个是主函数,明显看到改变以后闪的慢了