【STM32】时钟设置函数(寄存器版)

一、STM32时钟设置函数移植

1.时钟模块回顾

一个疑问

前面代码并没有设置时钟为什么可以直接使用。

2.时钟树

3.时钟树分析

1.内部晶振(HSI)

内部晶振不稳定,当我们上电后,会自动产生振动,自动产生时钟,但是晶振不稳定。

不经过PPLMUL,默认使用8MHZ。所以如果我们想要72MHZ,则需要使用外部晶振

2.外部晶振(HSE)

当接上外部晶振,当接通电源之后,不用软件操作,会自动产生振动。可以进行分频等操作。

从外部接上外部晶振的时候,我们需要等待一段时间,让其稳定后,才开始工作。(所以要进行判断)

3.PLLMUL

当上电后,经过他时,要等待一段时间,让其稳定后,才可以开始工作。(所以我们有一个寄存器专门用来判断其是否准备好开始工作,当我们去读取到其准备好了才可以进行下一步)

二、代码移植

cpp 复制代码
#ifndef __CLOCK_H__
#define __CLOCK_H__

#include "gpio.h"


// 寄存器宏定义
// RCC寄存器基地址为0x40021000
#define RCC_BASE	0x40021000			// RCC部分寄存器的基地址
#define RCC_CR		(RCC_BASE + 0x00)	// RCC_CR的地址
#define RCC_CFGR	(RCC_BASE + 0x04)

#define FLASH_ACR	0x40022000

// 用C语言来访问寄存器的宏定义
#define rRCC_CR		(*((volatile unsigned int *)RCC_CR))
#define rRCC_CFGR	(*((volatile unsigned int *)RCC_CFGR))
#define rFLASH_ACR	(*((volatile unsigned int *)FLASH_ACR))



// 函数作用:时钟源切换到HSE并且使能PLL,将主频设置为72MHz
void Set_SysClockTo72M(void);



#endif

1.复位RCC_CR寄存器

#define rRCC_APB2ENR (*((unsigned int *)RCC_APB2ENR))

RCC->CR就相当于rRCC_APB2ENR

cpp 复制代码
	//复位RCC_CR寄存器
	rRCC_CR=0x00000083;

2.开启外部时钟(就是开启外部晶振)

&:将某一些位置0

|:将某一些位置1

cpp 复制代码
	//开启外部时钟(外部晶振)
	//第一步:先置0【将bit16清零】
	rRCC_CR &= ~(1<<16);//关闭HSEON
	
	//第二步:在置1
	rRCC_CR |= (1<<16);//打开HSEON,让HSE开始工作

3.检测外部时钟开启是否成功(HSEREDY)

do while十分适合检测是否超时!!!!!!!

cpp 复制代码
	do{
		//检测HSEREAY(bit17)是否为1,1表示准备好
		Rcc_CR_HSE_Ready=rRCC_CR&(1<<17);//取出bit17
		faultTime++;
	}while((faultTime<0x0fffffff) && (Rcc_CR_HSE_Ready==0))
	//跳出do-while 1)要么超时2)要么准好了

4.当准备好进入下一步

5.Flash的设置

cpp 复制代码
		rFLASH_ACR |= 0x10;
		rFLASH_ACR &= (~0x03);
		rFLASH_ACR |= (0x02);

6.对其进行预分频

cpp 复制代码
		//HPRE【AHB】:对应bit4-bit7:不分频(000)
		//PPRE1【APB1】:对应bit8-bit10:进行二分频(100)
		//PPRE2【APB2】:对应bit11-bit13:不分频(000)
		//AHB和APB2未分频,APB1被2分频
		//所以最终:AHB和APB2都是72MHZ,APB1是36MHZ
		//第一步:先置0
		rRCC_CFGR=(~((0x0f<<4) | (0x07<<8) | (0x07<<11)));
		//等价于:rRCC_CFGR=(~(0x3ff<<4));
		//第二步:置1
		rRCC_CFGR=(((0x0<<4) | (0x04<<8) | (0x0<<11)));

7.设置SHE为输入时钟,同时HSE不分频

cpp 复制代码
		//设置为输入时钟:bit16
		//设置为不分频:bit17
		//第一步:先置0
		rRCC_CFGR &=(~((1<<16) | (1<<17)));
		//第二步:置1
		rRCC_CFGR |= ((1<<18) | (0<<17));

8.设置PLL倍频系数

因为我们在开发板上接上的外部晶振就是8MHZ,如果我们想要在内部使用72MHZ,则需要在内部进行分频率(9倍)

cpp 复制代码
		//9分频:0111:0x07
		
		rRCC_CFGR &=(~(0x0f<<18));//清零bit18-bit21
		rRCC_CFGR |= (0x07<<18);//设置为9倍频

9.打开使能

cpp 复制代码
		//七、打开PLL开关
		rRCC_CR |= (1<<24);

10.等待开启PLL开启成功

cpp 复制代码
		//八、等待开启PLL开启成功
		do{
			
			Rcc_CR_PLL_Ready=rRcc_CR & (1<<25);//检测第25位是否为1
			faultTime++;
		}while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready==0))

11.将PLL作为SYSCLK的时钟来源

cpp 复制代码
			//到这里说明PLL已经稳定,可以用了,下面可以切换成外部时钟了
			rRCC_CFGR &=(~(0x03)<<0);
			rRCC_CFGR |=(0x10<<0);

12. 判断切换成PLL是否成功

cpp 复制代码
		do{
			RCC_CF_SWS_PLL=rRCC_CFGR & (0x03<<2);//读出bit2-bit3
			faultTime++;
			//0x02<<2:表示此时转换成PLL
		}while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready!=(0x02<<2)))

13.此时PLL转换成功

14.完整代码

cpp 复制代码
#include "clock.h"

void Set_SysClockTo72M(void){
	
	//检测外部晶振是否准备好
	unsigned int Rcc_CR_HSE_Ready=0;
	//等待开启PLL开启成功
	unsigned int Rcc_CR_PLL_Ready=0;
	//判断切换成PLL是否成功
	unsigned int RCC_CF_SWS_PLL=0;
	unsigned int faultTime=0;//判断等待是否超时
	
	
	//一、复位RCC_CR寄存器
	rRCC_CR = 0x00000083;
	
	//二、开启外部时钟(外部晶振)
	//第一步:先置0【将bit16清零】
	rRCC_CR &= ~(1<<16);//关闭HSEON
	
	//第二步:在置1
	rRCC_CR |= (1<<16);//打开HSEON,让HSE开始工作
	
	//三、检测外部时钟开启是否成功
	do{
		//检测HSEREAY(bit17)是否为1,1表示准备好
		Rcc_CR_HSE_Ready=rRCC_CR&(1<<17);//取出bit17
		faultTime++;
	}while((faultTime<0x0fffffff) && (Rcc_CR_HSE_Ready==0));
	//跳出do-while 1)要么超时2)要么准好了
	
	
	//判断是超时还是准备好
	//注意点:不能直接使用"Rcc_CR_HSE_Ready"因为rRCC_CR是需要读一次寄存器
	//但是读出的结果可能还未改变,所以一定不能直接使用
	if((rRCC_CR&(1<<17))!=0)//rRCC_CR&(1<<17)==1
	{//这里HSE就ready,下面再去配置PLL并且等待他ready
		
		//四、对其进行预分频
		//HPRE【AHB】:对应bit4-bit7:不分频(000)
		//PPRE1【APB1】:对应bit8-bit10:进行二分频(100)
		//PPRE2【APB2】:对应bit11-bit13:不分频(000)
		//AHB和APB2未分频,APB1被2分频
		//所以最终:AHB和APB2都是72MHZ,APB1是36MHZ
		//第一步:先置0
		rRCC_CFGR=(~((0x0f<<4) | (0x07<<8) | (0x07<<11)));
		//等价于:rRCC_CFGR=(~(0x3ff<<4));
		//第二步:置1
		rRCC_CFGR=(((0x0<<4) | (0x04<<8) | (0x0<<11)));
		
		
		//五、设置SHE为输入时钟,同时HSE不分频
		//选择HSE作为PLL输入并且HSE不分频
		//设置为输入时钟:bit16
		//设置为不分频:bit17
		//第一步:先置0
		rRCC_CFGR &=(~((1<<16) | (1<<17)));
		//第二步:置1,bit16
		rRCC_CFGR |= ((1<<18) | (0<<17));
		
		
		//六、设置PLL倍频系数
		//9分频:0111:0x07
		
		rRCC_CFGR &=(~(0x0f<<18));//清零bit18-bit21
		rRCC_CFGR |= (0x07<<18);//设置为9倍频
		
		//七、打开PLL开关
		rRCC_CR |= (1<<24);
		
		
		//八、等待开启PLL开启成功
		do{
			
			Rcc_CR_PLL_Ready=rRCC_CR & (1<<25);//检测第25位是否为1
			faultTime++;
		}while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready==0));
		
		if((rRCC_CR & (1<<25)) == (1<<25)){
					//到这里说明PLL已经稳定,可以用了,下面可以切换成外部时钟了
					
					//九、切换成PLL
					rRCC_CFGR &=(~(0x03)<<0);
					rRCC_CFGR |=(0x10<<0);
					
				//十、判断切换成PLL是否成功
				do{
					RCC_CF_SWS_PLL=rRCC_CFGR & (0x03<<2);//读出bit2-bit3
					faultTime++;
					//0x02<<2:表示此时转换成PLL
				}while((faultTime<0x0fffffff) && (Rcc_CR_PLL_Ready!=(0x02<<2)));
					
				//十一、此时PLL转换成功
				if((rRCC_CFGR & (0x03<<2))==(0x02<<2)){
					
					//到这里我们的时钟整个就设置好了,可以结束了
				}else{
					//到这里说明PLL输出作为PLL失败
					while(1);
				}
		}
		else{
			//到这里说明PLL启动时出错了,PLL不能稳定工作
			while(1);
		}
	}else{//超时,或者未准备好,此时HSE不可以使用
		while(1);
	}
	
}

三、问题解决

1.我们想要让led快速闪3下,然后换成72MHZ的频率接着闪

cpp 复制代码
void delay(){
	unsigned int i=0,j=0;
	for(i=0;i<1000;i++){
		for(j=0;j<2000;j++){
		}
	}
}

void led_init(){
	rRCC_APB2ENR = 0x00000008;

	rGPIOB_CRH = 0x33333333;
	rGPIOB_ODR = 0x0000ff00;//全灭
	
}
void led_flash(void){
	unsigned int i=0;
	for(i=0;i<3;i++){
		rGPIOB_ODR = 0x00000000;//全亮
		delay();
		rGPIOB_ODR = 0x0000ff00;//全灭
		delay();
	} 
}
void main(void){
	led_init();
	led_flash();
	Set_SysClockTo72M();
	led_flash();
}

但是实际上并无法实现,只能在闪烁完3次后就熄灭。

2.问题解决

led初始化时,默认是全亮的

1.degger方法

把点亮led灯的函数加到clock中去,看看代码运行到哪里不会亮

2.判断超时变量的初始化

因为我们多次使用到超时变量,则每一个进入do-while循环之前要重新置0

3.出错点

相关推荐
yutian060642 分钟前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
析木不会编程4 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
枯无穷肉7 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名6778 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普8 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
嵌入式大圣8 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
云山工作室9 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费9 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
qq_3975623110 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
liyinuo201710 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范