STM32完全学习——系统时钟设置
一、时钟框图的解读
首先我们知道STM32在上电初始化之后使用的是内部的HSI未经过分频直接通过SW供给给系统时钟,由于内部HSI存在较大的误差,因此我们在系统完成上电初始化,之后需要将STM32的时钟切换到外部HSE作为系统时钟,那么我们需要完成下面一些设置完成切换。首先我们需要打开外部HSE的震荡电路然后,等待外部震荡电路就绪后,在切换PLLTPRE为HSE的1分频,在设置PLLSRC为PLLXTPRE过来的时钟。然后设置PLLMUL倍频系数,然后打开PLL开关等待PLL稳定。然后设置APB1总线为2分频,最后切换系统时钟为PLL输出的时钟。
二、相关寄存器
三、编程实现
#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
#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,系统就可以正常运行了。
#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);
}
}