STM32完全学习——系统时钟设置

一、时钟框图的解读

首先我们知道STM32在上电初始化之后使用的是内部的HSI未经过分频直接通过SW供给给系统时钟,由于内部HSI存在较大的误差,因此我们在系统完成上电初始化,之后需要将STM32的时钟切换到外部HSE作为系统时钟,那么我们需要完成下面一些设置完成切换。首先我们需要打开外部HSE的震荡电路然后,等待外部震荡电路就绪后,在切换PLLTPRE为HSE的1分频,在设置PLLSRC为PLLXTPRE过来的时钟。然后设置PLLMUL倍频系数,然后打开PLL开关等待PLL稳定。然后设置APB1总线为2分频,最后切换系统时钟为PLL输出的时钟。

二、相关寄存器

三、编程实现

cpp 复制代码
#ifndef __SYSTEM_H__
#define __SYSTEM_H__

#define uint unsigned int
	
typedef struct rRCC
{
	uint CR;
	uint CFGR;
	uint CIR;
	uint APB2RSTR;
	uint APB1RSTR;
	uint AHBENR;
	uint APB2ENR;
	uint APB1ENR;
	uint BDCR;
	uint CSR;
	uint AHBRSTR;
	uint CFGR2;	
}RCC_TypeDef;

static RCC_TypeDef *RCC = (RCC_TypeDef *)0x40021000;

void set_system_clock(void);

#endif
cpp 复制代码
#include "system.h"

uint HSERDY_FLAG = 0;     //HSE就绪标志
uint PLLRDY_FLAG = 0;     //PLL就绪标志
uint SWRDY_FLAG  = 0;     //时钟切换标志
uint false_time = 0;      //超时时间标志

void set_system_clock(void)
{
	rFLASH_ACR = 0x00000032; //这个用来设置系统时钟于Flash访问时间的比率 根据系统时钟的不同设置的值不同
	RCC->CR &= (~(0x01 << 16));      //将HSEON这位清零
	RCC->CR |= (0x01 << 16);         //开启外部HSE振荡器电路
	
	
	do                               //判断外部HSE电路是否就绪
	{
		HSERDY_FLAG = ((RCC->CR) & (0x01 << 17));
		false_time++;
	}while((false_time < 0x00ffffff) && (HSERDY_FLAG == 0));
	false_time = 0;
	if (HSERDY_FLAG != 0)            //如果外部振荡器电路就绪才会执行
	{ 
		RCC->CFGR &= (~((0x01 << 17) | (0x01 << 16) | (0x0f << 18)));  //设置PLL倍频系数为9倍频
		RCC->CFGR |= ((0x01 << 16) | (0x0 << 17) | (0x07 << 18));  //设置PLL时钟来源为PREDIV1 并且设置PLLXTPRE不对输入的时钟分频
		RCC->CR &= (~(0x01 << 24));       //使能PLL时钟
		RCC->CR |= (0x01 << 24);
		do 
		{
			PLLRDY_FLAG = ((RCC->CR) & (0x01 << 25));
			false_time++;
		}while((false_time < 0x00ffffff) && (PLLRDY_FLAG == 0));  //等待PLL时钟就绪
		false_time = 0;
		if (((RCC->CR) & (0x01 << 25)) != 0)
		{	
			RCC->CFGR &= (~((0x01 << 22) | (0x07 << 8)));  
			RCC->CFGR |= ((0x0 << 22) | (0x04 << 8));     //全速USB OTG配置为PLL输出2分频 APB1总线2分频 PLL输出作为系统时钟
			RCC->CFGR &= ~(0x03 << 0); 
			RCC->CFGR |= (0x02);    //切换系统时钟为PLL输出时钟
			do
			{
				SWRDY_FLAG = ((RCC->CFGR) & (0x03 << 2));
		        false_time++;	
			}while((false_time < 0x00ffffff) && ((SWRDY_FLAG) == 0));
			if (((RCC->CFGR) & (0x03 << 2)) != 0)  //判断时钟是否切换成功
			{
				
			}
			else
			{
				while(1);
			}
		}
	}
	else
	{
		while(1);
	}
}

上面的代码一个是.h文件另一个是.c文件,但是当我们运行上面的代码时我发现了一个问题,当我们将时钟切换到PLL输出作为系统时钟时,系统就不运行了。就死机了。这是为什么呢,通过翻看数据手册发现,系统时钟和内部flah有一些关系,我们来看flash的寄存器就可以发现。

我们来看bit0-2发现,当复位时默认是000,因此系统时钟必须在0到24MHz之间,这时候我们使用的是内部的HSI时钟,时钟只有8MHz,因此这个时候系统是可以正常运行的,但是当我们将时钟切换到外部HSE时,由于我们做了一系列的倍频,因此这个时候系统时钟到了72MHz,因此我们呢这里需要将这个寄存器的值也设置为010,系统就可以正常运行了。

cpp 复制代码
#define FLASH_ACR  0x40022000
#define rFLASH_ACR *((uint *)FLASH_ACR)

void set_system_clock(void)
{
	rFLASH_ACR = 0x00000032;     //这个用来设置系统时钟于Flash访问时间的比率 根据系统时钟的不同设置的值不同
	
	RCC->CR &= (~(0x01 << 16));      //将HSEON这位清零
	RCC->CR |= (0x01 << 16);         //开启外部HSE振荡器电路
	
	
	do                               //判断外部HSE电路是否就绪
	{
		HSERDY_FLAG = ((RCC->CR) & (0x01 << 17));
		false_time++;
	}while((false_time < 0x00ffffff) && (HSERDY_FLAG == 0));
	false_time = 0;
	if (HSERDY_FLAG != 0)            //如果外部振荡器电路就绪才会执行
	{ 
		RCC->CFGR &= (~((0x01 << 17) | (0x01 << 16) | (0x0f << 18)));  //设置PLL倍频系数为9倍频
		RCC->CFGR |= ((0x01 << 16) | (0x0 << 17) | (0x07 << 18));  //设置PLL时钟来源为PREDIV1 并且设置PLLXTPRE不对输入的时钟分频
		RCC->CR &= (~(0x01 << 24));       //使能PLL时钟
		RCC->CR |= (0x01 << 24);
		do 
		{
			PLLRDY_FLAG = ((RCC->CR) & (0x01 << 25));
			false_time++;
		}while((false_time < 0x00ffffff) && (PLLRDY_FLAG == 0));  //等待PLL时钟就绪
		false_time = 0;
		if (((RCC->CR) & (0x01 << 25)) != 0)
		{	
			RCC->CFGR &= (~((0x01 << 22) | (0x07 << 8)));  
			RCC->CFGR |= ((0x0 << 22) | (0x04 << 8));     //全速USB OTG配置为PLL输出2分频 APB1总线2分频 PLL输出作为系统时钟
			RCC->CFGR &= ~(0x03 << 0);  
			RCC->CFGR |= (0x02); 
			do
			{
				SWRDY_FLAG = ((RCC->CFGR) & (0x03 << 2));
		    false_time++;	
				
			}while((false_time < 0x00ffffff) && ((SWRDY_FLAG) == 0));
			
			if (((RCC->CFGR) & (0x03 << 2)) != 0)
			{
				
			}
			else
			{
				while(1);
			}
		}
	}
	else
	{
		while(1);
	}
}
相关推荐
柒月玖.4 小时前
基于AT89C52单片机的温度检测报警设计
单片机·嵌入式硬件
m0_555762904 小时前
GPIO配置详细解析
单片机·嵌入式硬件
盈创力和20074 小时前
构建未来工业感知层:以太网多参量传感器的融合架构与技术实践
嵌入式硬件·架构·以太网温湿度传感器·多参数传感器
黑科技Python4 小时前
生活中的“小智慧”——认识算法
学习·算法·生活
Yupureki4 小时前
从零开始的C++学习生活 16:C++11新特性全解析
c语言·数据结构·c++·学习·visual studio
青云交4 小时前
Java 大视界 -- Java 大数据在智能教育学习社区互动模式创新与用户活跃度提升中的应用(426)
java·大数据·学习·flink 实时计算·智能教育社区·互动模式创新·用户活跃度
兆龙电子单片机设计4 小时前
【STM32项目开源】STM32单片机多功能饮水机系统
stm32·单片机·物联网·开源·毕业设计
ShiMetaPi5 小时前
基于M4-R1开发板的OpenHarmony开发实战丨创建第一个应用工程
嵌入式硬件·开放原子·鸿蒙系统·openharmony·开源鸿蒙·北向开发
m0_555762906 小时前
STM32 的“内存划分”
stm32·单片机·嵌入式硬件
武清伯MVP6 小时前
阮一峰《TypeScript 教程》学习笔记——类型映射
笔记·学习·typescript