STM32从零开始(三) 点亮led灯并且配置时钟为72mhz

在这里插入图片描述**

这部分感觉有点乱可以先看后边的总结部分,代码的上边那里

**
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

————————————————————————————_________________------------------------------------------------------

总结一下。

  1. RCC_CR 地址:0x40021000 + 0 = 0x40021000 复位值:0x00000083
    16位HSEON置一,然后判断17位HSERDY,外部时钟就绪后进性下一步
  2. 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,不分频。
  3. RCC_CR
    24位PLLON置一,判断25位PLLRDY,为1说明就绪,继续下一步。
  4. 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 --);
}

这个是主函数,明显看到改变以后闪的慢了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章