1? 时钟的定义
????????时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。时钟系统就是CPU的脉搏,决定cpu速率,像人的心跳一样 只有有了心跳,人才能做其他的事情,而单片机有了时钟,才能够运行执行指令,才能够做其他的处理 (点灯,串口,ADC),时钟的重要性不言而喻。??????
????????为什么 STM32 要有多个时钟源呢???
????????STM32本身十分复杂,外设非常多 ?但我们实际使用的时候只会用到有限的几个外设,使用任何外设都需要时钟才能启动,但并不是所有的外设都需要系统时钟那么高的频率,为了兼容不同速度的设备,有些高速,有些低速,如果都用高速时钟,势必造成浪费 ? 并且,同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题。所以便有了STM32的时钟系统和时钟树。
????????总结: ? ? ? ? 1)STM32时钟系统主要的目的就是给相对独立的外设模块提供时钟,也是为了降低整个芯片的耗能。 ? ? ? ? 2)系统时钟,是处理器运行时间基准(每一条机器指令一个时钟周期) ? ? ? ? 3)时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。 ? ? ? ? 4)一个单片机内提供多个不同的系统时钟,可以适应更多的应用场合。 ????????5)不同的功能模块会有不同的时钟上限,因此提供不同的时钟,也能在一个单片机内放置更多的功能模块。对不同模块的时钟增加开启和关闭功能,可以降低单片机的功耗。 ????????6)STM32为了低功耗,他将所有的外设时钟都设置为disable(不使能),用到什么外设,只要打开对应外设的时钟就可以, 其他的没用到的可以还是disable(不使能),这样耗能就会减少。 ?这就是为什么不管你配置什么功能都需要先打开对应的时钟的原因
2? STM32F1时钟系统框图
?????????乍一看很吓人,但其实很好理解,我们看系统时钟SYSCLK 的左边,系统时钟有很多种选择,而左边的部分就是设置系统时钟使用那个时钟源,系统时钟SYSCLK 的右边,则是系统时钟通过AHB预分频器,给相对应的外设设置相对应的时钟频率。 ????????从左到右可以简单理解为:各个时钟源--->系统时钟来源的设置--->各个外设时钟的设置。
2.1??时钟系统各个时钟源(左边的部分)
????????STM32 有4个独立时钟源:HSI、HSE、LSI、LSE。 ????????????????①? HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。 ????????????????②? HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。 ????????????????③? LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。 ? ????????????????④? LSE是低速外部时钟,接频率为32.768kHz的石英晶体。 ????????其中LSI是作为IWDGCLK(独立看门狗)时钟源和RTC时钟源而独立使用? ????????而HSI高速内部时钟、HSE高速外部时钟、PLL锁相环时钟这三个时钟经过分频或者倍频可作为系统时钟来使用 ????????PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。 ?通过倍频之后作为系统时钟的时钟源
????????举个例子:Keil编写程序是默认的时钟为72Mhz,其实是这么来的:外部晶振(HSE)提供的8MHz(与电路板上的晶振的相关)通过PLLXTPRE分频器后,进入PLLSRC选择开关,进而通过PLLMUL锁相环进行倍频(x9)后,为系统提供72MHz的系统时钟(SYSCLK)。之后是AHB预分频器对时钟信号进行分频,然后为低速外设提供时钟。 或者内部RC振荡器(HSI) 为8MHz/2为4MHz 进入PLLSRC选择开关,通过PLLMUL锁相环进行倍频(x18)后为72MHz。
2.2? 系统时钟SYSCLK
????????系统时钟SYSCLK可来源于三个时钟源: ????????????????①HSI振荡器时钟 ????????????????②HSE振荡器时钟 ????????????????③PLL时钟 ????????系统时钟SYSCLK最大为72Mhz
2.3??USB时钟
????????STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取(唯一的),,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。
2.4??把时钟信号输出到外部
????????STM32可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。可以把时钟信号输出供外部使用
2.5? 系统时钟通过AHB分频器给外设提供时钟(右边的部分)??重点
????????从左到右可以简单理解为:系统时钟--->? ?AHB分频器--->? ?各个外设分频倍频器---> ?外设时钟的设置 ????????右边部分为:系统时钟SYSCLK通过AHB分频器分频后送给各模块使用,AHB分频器可选择1、2、4、8、16、64、128、256、512分频。其中AHB分频器输出的时钟送给5大模块使用:? ????????①内核总线:送给AHB总线、内核、内存和DMA使用的HCLK时钟。? ????????②Tick定时器:通过8分频后送给Cortex的系统定时器时钟。? ????????③I2S总线:直接送给Cortex的空闲运行时钟FCLK。? ????????④APB1外设:送给APB1分频器。APB1分频器可选择1、2、4、8、16分频,其输出一路供APB1外设使用(PCLK1,最大频率36MHz),另一路送给通用定时器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2-7使用。? ????????⑤APB2外设:送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给高级定时器。该倍频器可选择1或者2倍频,时钟输出供定时器1和定时器8使用。? ????????另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。? ????????需要注意的是,如果 APB 预分频器分频系数是 1,则定时器时钟频率 (TIMxCLK) 为 PCLKx。否则,定 ? ? ?时器时钟频率将为 APB 域的频率的两倍:TIMxCLK = 2xPCLKx。?
2.6? APB1和APB2的对应外设
????????F1系列
????????APB1上面连接的是低速外设,包括电源接口、备份接口、CAN、USB、I2C1、I2C2、USART2、USART3、UART4、UART5、SPI2、SP3等; ????????APB2上面连接的是高速外设,包括UART1、SPI1、Timer1、ADC1、ADC2、ADC3、所有的普通I/O口(PA-PE)、第二功能I/O(AFIO)口等。
????????F4系列
????????这个和F1系列类似,我们就举几个特殊的 ????????APB2总线:高级定时器timer1, timer8以及通用定时器timer9, timer10, timer11 ? UTART1,USART ????????APB1总线:通用定时器timer2~timer5,通用定时器timer12~timer14以及基本定时器timer6,timer7 ?UTART2~UTART5 ? ? ? ? F4系列的系统时钟频率最高能到168M ? ? ? ? 具体可以在stm32f10x_rcc.h和stm32f40x_rcc.h中查看,或者通过 STM32参考手册搜索“系统架构”或者“系统结构” ?查看外设挂在哪个时钟下
2.7??时钟监视系统(CSS)
????????STM32还提供了一个时钟监视系统(CSS),用于监视高速外部时钟(HSE)的工作状态。倘若HSE失效,会自动切换(高速内部时钟)HSI作为系统时钟的输入,保证系统的正常运行。
3? STM32F1RCC相关寄存器:
????????RCC 寄存器结构,RCC_TypeDef,在文件“stm32f10x.h”中定义如下:
typedef struct
{
__IO uint32_t CR; //HSI,HSE,CSS,PLL等的使能和就绪标志位
__IO uint32_t CFGR; //PLL等的时钟源选择,分频系数设定
__IO uint32_t CIR; //清除/使能 时钟就绪中断
__IO uint32_t APB2RSTR; //APB2线上外设复位寄存器
__IO uint32_t APB1RSTR; //APB1线上外设复位寄存器
__IO uint32_t AHBENR; //DMA,SDIO等时钟使能
__IO uint32_t APB2ENR; //APB2线上外设时钟使能
__IO uint32_t APB1ENR; //APB1线上外设时钟使能
__IO uint32_t BDCR; //备份域控制寄存器
__IO uint32_t CSR; //控制状态寄存器
} RCC_TypeDef;
????????可以对上上面的时钟框图和RCC寄存器来学习,对STM32的时钟系统有个大概的了解? ?其实也就是我们上面介绍的流程,理解了自然也就能写出来
3.1??RCC初始化
? ? ? ? 我们使用HSE时钟,正常使用的时候也都是使用外部时钟,程序设置时钟参数流程:
????????????????1、将RCC寄存器重新设置为默认值???RCC_DeInit; ????????????????2、打开外部高速时钟晶振HSE? ? ? ? ? RCC_HSEConfig(RCC_HSE_ON); ????????????????3、等待外部高速时钟晶振工作? ? ? ? ? HSEStartUpStatus = RCC_WaitForHSEStartUp(); ????????????????4、设置AHB时钟? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?RCC_HCLKConfig; ????????????????5、设置高速AHB时钟? ? ? ? ? ? ? ? ? ? ? ? RCC_PCLK2Config; ????????????????6、设置低速速AHB时钟? ? ? ? ? ? ? ? ? ? RCC_PCLK1Config; ????????????????7、设置PLL? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?RCC_PLLConfig; ????????????????8、打开PLL? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? RCC_PLLCmd(ENABLE); ????????????????9、等待PLL工作? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) ????????????????10、设置系统时钟? ? ? ? ? ? ? ? ? ? ? ? ? ? ?RCC_SYSCLKConfig; ????????????????11、判断是否PLL是系统时钟? ? ? ? ?? ?while(RCC_GetSYSCLKSource() != 0x08) ????????????????12、打开要使用的外设时钟? ? ? ? ? ? ? RCC_APB2PeriphClockCmd()/RCC_APB1PeriphClockCmd()
3.2? 代码实现
????????对RCC的配置函数(使用外部8MHz晶振) :系统时钟72MHz,APH 72MHz,APB2 72MHz,APB1 32MHz,USB 48MHz TIMCLK=72M ? ? ? ? 下面是STM32软件固件库的程序中对RCC的配置函数(使用外部8MHz晶振)
void RCC_Configuration(void)
{
//----------使用外部RC晶振-----------
RCC_DeInit(); //初始化为缺省值
RCC_HSEConfig(RCC_HSE_ON); //使能外部的高速时钟
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); //等待外部高速时钟使能就绪
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch Buffer
FLASH_SetLatency(FLASH_Latency_2); //Flash 2 wait state
RCC_HCLKConfig(RCC_SYSCLK_Div1); //HCLK = SYSCLK
RCC_PCLK2Config(RCC_HCLK_Div1); //PCLK2 = HCLK
RCC_PCLK1Config(RCC_HCLK_Div2); //PCLK1 = HCLK/2
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9); //PLLCLK = 8MHZ * 9 =72MHZ
RCC_PLLCmd(ENABLE); //Enable PLLCLK
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLLCLK is ready
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Select PLL as system clock
while(RCC_GetSYSCLKSource()!=0x08); //Wait till PLL is used as system clock source
//---------打开相应外设时钟--------------------
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能APB2外设的GPIOA的时钟
}
????????也就是我们时钟树框图从左到右的配置
4? STM32F1系统时钟详解&&移植
4.1??对系统时钟的设置的相关函数
????????高人指点,我看了一下启动文件startup_stm32f10x_hd.s,其中有一段汇编:
// Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
????????其中,IMPORT ?__main和IMPORT ?SystemInit语句:IMPORT 声明了需要引用C语言中的main函数、systeminit函数; ? ? ? ? ? ? ? ? LDR R0, = systeminint语句:把systeminit 函数地址放到r0 寄存器; ? ? ? ? ? ? ? ? BLX ? R0语句:跳到到R0 寄存器中的地址执行; ????????所以,在跳到main函数执行前,已经在SystemInit 函数中把系统时钟给设置好了;
4.2??启动外部晶振的操作
????????我们在回答这个问题前,先到system_stm32f10x.h文件下的SystemInit()函数里面做更深入的了解:
/**
* @摘要 设置微控制器系统,初始化嵌入式Flash接口,PLL和更新SystemCoreClock变量。
* @注释 此功能只能在重置后使用。
* @参数 无
* @返回值 无
*/
void SystemInit (void)
{
/* 将RCC时钟配置重置为默认的重置状态(用于调试) */
/* 设置HSION位 */
RCC->CR |= (uint32_t)0x00000001;
/* 复位SW、HPRE、PPRE1、PPRE2、ADCPRE、MCO位 */
#ifndef STM32F10X_CL
RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */
/* 重置HSEON, CSSON和PLLON位 */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* 重置HSEBYP位 */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* 复位PLLSRC, PLLXTPRE, PLLMUL和USBPRE/OTGFSPRE位 */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
#ifdef STM32F10X_CL
/* 复位PLL2ON和PLL3ON位 */
RCC->CR &= (uint32_t)0xEBFFFFFF;
/* 禁用所有中断并清除挂起位 */
RCC->CIR = 0x00FF0000;
/* 复位CFGR2寄存器 */
RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* 禁用所有中断并清除挂起位 */
RCC->CIR = 0x009F0000;
/* 复位CFGR2寄存器 */
RCC->CFGR2 = 0x00000000;
#else
/* 禁用所有中断并清除挂起位 */
RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */
#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* 配置the System clock frequency, HCLK, PCLK2和PCLK1预分频器 */
/* 配置Flash Latency周期并开启预取缓冲区*/
SetSysClock();
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* 在内部SRAM中的向量表重定位。 */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* 在内部FLASH中的矢量表重定位。 */
#endif
}
????????我们进入system_stm32f10x.h文件下的SetSysClock()函数,看其中作了哪些操作:
/**
* @摘要 配置“System clock frequency”、“HCLK”、“PCLK2”和“PCLK1”预分频器。
* @参数 无
* @返回值 无
*/
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
/* 如果上面的定义都没有启用,则使用HSI作为系统时钟源(重置后默认) */
}
????????我项目中用的是此款芯片STM32f103ZET6,内部晶振是72MHz, 所以应进入执行system_stm32f10x.h文件下SetSysClockTo72() 函数:
#elif defined SYSCLK_FREQ_72MHz
/**
* @摘要 设置“系统时钟频率”为72MHz,并配置“HCLK”、“PCLK2” 和PCLK1预分频器。
* @注释 此功能只能在重置后使用。
* @参数 无
* @返回值 无
*/
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2和PCLK1配置---------------------------*/
/* 使能HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待HSE准备就绪,如果达到“Time out”,则退出 */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* 使预取缓冲器 */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* Flash 2等待状态 */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
#ifdef STM32F10X_CL
/* 配置锁相环 ------------------------------------------------------*/
/* PLL2配置: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
/* PREDIV1配置: PREDIV1CLK = PLL2 / 5 = 8 MHz */
RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
/* 使能PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* 等待PLL2准备好 */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
}
/* 锁相环配置: PLLCLK = PREDIV1 * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
#else
/* 锁相环配置: PLLCLK = HSE * 9 = 72 MHz */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
RCC_CFGR_PLLMULL));
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL */
/* 使能PLL */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL准备好 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* 选择“PLL”作为系统时钟源 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* 等待,直到使用锁相环作为系统时钟源 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /* 如果HSE启动失败,应用程序时钟错误配置。用户可以在这里添加一些代码来处理这个错误 */
}
}
#endif
????????解析:void SetSysClockTo72()函数中主要做了下面几件事:(不同颜色功能分别对应上面对应颜色代码部分) ????????????????1.启动外部晶振作为系统时钟源; ????????????????2.等待外部晶振起振; ????????????????3.(起振后)对系统时钟源进行分频:(默认外部是HSE--8MHZ晶振) ?PLL configuration: PLLCLK = HSE * 9 = 72 MHz ????????????????4. 配置HCLK、PCLK1、PCLK2 与系统时钟源的关系;
4.3??串口时钟源跟系统时钟的关系
????????回答这个问题前,我们先来看STM32f103ZET6芯片手册数据图:
?????????从图里面我们可看出串口1的时钟源是PCLK1;上面4.2小结的代码中我们看到PCLK1是时钟源72MHz的1分频,即还是72MHz。
4.4????STM32宏定义和更换外部晶振以及代码的修改
????????STM32宏定义可以在KEIL下可以在项目的选项C/C++/PREPROMCESSOR symbols的Define栏里定义,比如STM32F10X_CL ????????也可以在STM32F10X.H里用宏定义:
#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL)
/* #define STM32F10X_LD */ /*!< STM32F10X_LD: STM32 Low density devices */
/* #define STM32F10X_LD_VL */ /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */
/* #define STM32F10X_MD */ /*!< STM32F10X_MD: STM32 Medium density devices */
/* #define STM32F10X_MD_VL */ /*!< STM32F10X_MD_VL: STM32 Medium density Value Line devices */
/* #define STM32F10X_HD */ /*!< STM32F10X_HD: STM32 High density devices */
/* #define STM32F10X_HD_VL */ /*!< STM32F10X_HD_VL: STM32 High density value line devices */
/* #define STM32F10X_XL */ /*!< STM32F10X_XL: STM32 XL-density devices */
/* #define STM32F10X_CL */ /*!< STM32F10X_CL: STM32 Connectivity line devices */
#endif
????????如果外部晶振更换,例如STM32用的是8M晶振。比如你想更换到为外部晶振为12M,但是主频仍想用72M的。该如何设置?或者想倍频到更高的主频该怎么修改?
属性 | 原来 | 现在 |
---|
外部晶振 | 8M | 12M | 倍频 | 9 | 6 | 主频 | 72M | 72M |
????????想从原来的8M修改到现在的12M,但是主频仍为72M,该如何修改:
????????(1)【设置外部晶振】打开stm32f10x.h,找到下图部分:
?????????修改之前如上图一样,HSE_VALUE为外部晶振,如果你用的是f103的芯片,则修改下边这个为12M;
?????????(2)【设置主频】打开system_stm32f10x.c,找到下图部分:
?????????也可以根据自己的需要修改为56M、48M等;
?????????(3)【设置倍频】打开system_stm32f10x.c,找到自己的想要的主频函数:
?????????再找到这里(默认的情况下):
?????????①修改F103(外部晶振12M倍频到72M):
?????????②修改F105、F107(外部晶振12M倍频到72M):
?????????还有一个是F103倍频到128M的教程。单片机超频太多不稳定,建议先更换更快的晶振,在进行倍频到128M。
5? STM32F429时钟概述
????????时钟系统是 CPU 的脉搏,就像人的心跳一样。所以时钟系统的重要性就不言而喻了。 STM32有多个时钟来源的选择,采用一个系统时钟不是很简单吗?为什么 STM32 要有多个时钟源呢? 因为首先 STM32 本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及 RTC 只需要几十 k 的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。
???????下面是stm32f4的时钟系统图:
????????STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。 ????????????????①HSI是高速内部时钟,RC振荡器,频率为16MHz,精度不高。可以直接作为系统时钟或者用作PLL时钟输入。 ? ????????????????②HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~26MHz。(此处以25M为例) ????????????????③LSI是低速内部时钟,RC振荡器,频率为32kHz,提供低功耗时钟。主要供独立看门狗和自动唤醒单元使用。 ? ????????????????④LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC ? ????????????????⑤PLL为锁相环倍频输出。
?????????PLL 为锁相环倍频输出。 STM32F4 有三个 PLL: ????????1)主 PLL(PLL)由 HSE 或者 HSI 提供时钟信号,并具有两个不同的输出时钟。第一个输出 PLLP 用于生成高速的系统时钟(最高 180MHz)第二个输出 PLLQ 为 48M 时钟, 用于 USB OTG FS 时钟,随机数发生器的时钟和 SDIO时钟。
????????2)第一个专用 PLL(PLLI2S)用于生成精确时钟, 在 I2S 和 SAI1 上实现高品质音频性能。 其中, N 是用于 PLLI2S vco 的倍频系数,其取值范围是: 192~432; R 是 I2S 时钟的分频系数,其取值范围是: 2~7; Q 是 SAI 时钟分频系数,其取值范围是: 2~15; P 没用到。
????????3)第二个专用 PLL(PLLSAI)同样用于生成精确时钟,用于 SAI1 输入时钟,同时还为 LCD_TFT接口提供精确时钟。 其中, N 是用于 PLLSAI vco 的倍频系数,其取值范围是: 192~432;Q 是 SAI 时钟分频系数,其取值范围是: 2~15; R 是 LTDC 时钟的分频系数,其取值范围是: 2~7; P 没用到。
????????例子:主 PLL 时钟第一个高速时钟输出 PLLP 的计算方法:配置180MHz为例:
????????分析:主 PLL 时钟的时钟源要先经过一个分频系数为 M 的分频器,然后经过倍频系数为 N 的倍频器出来之后还需要经过一个分频系数为 P(第一个输出 PLLP)或者 Q(第二个输出 PLLQ)的分频器分频之后,最后才生成最终的主 PLL 时钟。例如我们的外部晶振选择 25MHz。同时我们设置相应的分频器 M=25,倍频器倍频系数 N=360,分频器分频系数 P=2,那么主 PLL 生成的第一个输出高速时钟 PLLP 为:PLL=25MHz * N/ (M*P)=25MHz* 360 /(25*2) = 180MHz
????????配置过程如下图所示:?
6? STM32F429系统时钟的初始化寄存器源码分析
?????????在系统进入主函数之前,首先会执行SystemInit这个函数对系统进行初始化
?????????看一看这个程序的内容,源代码如下:
void SystemInit(void)
{
/* FPU 设置------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* 复位 RCC 时钟配置为默认配置-----------*/
RCC->CR |= (uint32_t)0x00000001;//打开 HSION 位
RCC->CFGR = 0x00000000;//复位 CFGR 寄存器
RCC->CR &= (uint32_t)0xFEF6FFFF;//复位 HSEON, CSSON and PLLON 位
RCC->PLLCFGR = 0x24003010; //复位寄存器 PLLCFGR
RCC->CR &= (uint32_t)0xFFFBFFFF;//复位 HSEBYP 位
RCC->CIR = 0x00000000;//关闭所有中断
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/* 配置中断向量表地址=基地址+偏移地址 ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET;
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
#endif
}
????????可以看出这段代码的作用是: ????????????????1)FPU 设置 ????????????????2)复位 RCC 时钟配置为默认复位值(默认开始了 HIS) ????????????????3)外部存储器配置 ????????????????4)中断向量表地址配置 ????????做了这些工作,但是在F4的HAL库汇总SystemInit函数,并没有设置系统的主频和外设时钟的频率,所以所以需要自己去写这个函数。
????????首先先分析一下使用寄存器版本来写的SystemInit函数吧:
//系统时钟初始化函数
//plln:主PLL倍频系数(PLL倍频),取值范围:64~432.
//pllm:主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
//pllp:系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
RCC->CR|=0x00000001; //设置HISON,开启内部高速RC振荡
RCC->CFGR=0x00000000; //CFGR清零
RCC->CR&=0xFEF6FFFF; //HSEON,CSSON,PLLON清零
RCC->PLLCFGR=0x24003010; //PLLCFGR恢复复位值
RCC->CR&=~(1<<18); //HSEBYP清零,外部晶振不旁路
RCC->CIR=0x00000000; //禁止RCC时钟中断
Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟
//配置向量表
#ifdef VECT_TAB_RAM
MY_NVIC_SetVectorTable(1<<29,0x0);
#else
MY_NVIC_SetVectorTable(0,0x0);
#endif
}
????????这个函数需要的参数是4个,分别与系统原理图对应的是,如下图所示:
????????接下来,查看时钟控制CR寄存器的描述:
????????1)开启HISON,开启内部高速RC震荡
????????2)时钟配置寄存器CFRG清零
????????3)配置时钟控制CR寄存器,?使位HSEON,CSSON,PLLON清零(第16、19、24位)
????????4)RCC PLL 配置寄存器 (RCC_PLLCFGR),恢复默认值:RCC->PLLCFGR=0x24003010;?? ?//PLLCFGR恢复复位值
????????5)设置CR寄存器,外部晶振不旁路
????????6)RCC 时钟中断寄存器 (RCC_CIR),RCC->CR=0X00000000;? //禁止RCC时钟中断
????????7)设置时钟,使用函数Sys_Clock_Set(plln,pllm,pllp,pllq);//设置时钟,函数原型如下:
u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq)
{
u16 retry=0;
u8 status=0;
RCC->CR|=1<<16; //HSE 开启
while(((RCC->CR&(1<<17))==0)&&(retry<0X1FFF))retry++;//等待HSE RDY
if(retry==0X1FFF)status=1; //HSE无法就绪
else
{
RCC->APB1ENR|=1<<28; //电源接口时钟使能
PWR->CR|=3<<14; //高性能模式,时钟可到180Mhz
RCC->CFGR|=(0<<4)|(5<<10)|(4<<13);//HCLK 不分频;APB1 4分频;APB2 2分频.
RCC->CR&=~(1<<24); //关闭主PLL
RCC->PLLCFGR=pllm|(plln<<6)|(((pllp>>1)-1)<<16)|(pllq<<24)|(1<<22);//配置主PLL,PLL时钟源来自HSE
RCC->CR|=1<<24; //打开主PLL
while((RCC->CR&(1<<25))==0);//等待PLL准备好
FLASH->ACR|=1<<8; //指令预取使能.
FLASH->ACR|=1<<9; //指令cache使能.
FLASH->ACR|=1<<10; //数据cache使能.
FLASH->ACR|=5<<0; //5个CPU等待周期.
RCC->CFGR&=~(3<<0); //清零
RCC->CFGR|=2<<0; //选择主PLL作为系统时钟
while((RCC->CFGR&(3<<2))!=(2<<2));//等待主PLL作为系统时钟成功.
}
return status;
}
????????8)RCC->CR|=1<<16;?? ??? ?//设置CR寄存器的第16位为1,HSE 开启
????????9)等待HSE时钟就绪,判断CR寄存器的第17位是否为1,返回1准备就绪,如果超时,返回标志位status=1.
????????10)如果准备就绪,使能电源接口时钟,RCC->APB1ENR|=1<<28;?? ?//设置APB1ENR寄存器28位为1电源接口时钟使能
????????11)开始电源的高性能模式,PWR->CR|=3<<14; ?? ??? ?//高性能模式,时钟可到180Mhz
????????12)配置RCC的CFGR寄存器,//HCLK 不分频;APB1 4分频;APB2 2分频
????????13)RCC->CR&=~(1<<24);?? ?//关闭主PLL,配置主PLL的一些参数,配置完再重新打开
????????14)//配置主PLL,PLL时钟源来自HSE,RCC->PLLCFGR=pllm|(plln<<6)|(((pllp>>1)-1)<<16)|(pllq<<24)|(1<<22);
????????????????pllm,设置PLLM
?????????????????(plln<<6),设置PLLN
?????????????????(((pllp>>1)-1)<<16),设置系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
????????????????为何是:(((pllp>>1)-1)<<16)呢?如下解释,当我们取值pllp为2时,可以得到(((pllp>>1)-1)<<16)=0 ????????????????当pllp取值为4时,(((pllp>>1)-1)<<16)=1,同理,pllp取值为6时,(((pllp>>1)-1)<<16)=3.其实就是在调用函数的时候方便设置。可以看都这个寄存器的位,当第17:16为0时,就是这只pllp为2分频。
?????????????????(pllq<<24),pllq:USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
?????????????????(1<<22),选择主PLL的时钟源是HSE外部震荡时钟
?????????所以第14步骤,完成的工作就是如下如所示的设置
????????15)设置完主PLL之后,重新打开主PLL:??RCC->CR|=1<<24;?? ??? ??? ?//打开主PLL
????????16)每次打开PLL之前,都要等待就绪:while((RCC->CR&(1<<25))==0);//等待PLL准备好
????????17)设置FLASH寄存器参数
FLASH->ACR|=1<<8; //指令预取使能.
FLASH->ACR|=1<<9; //指令cache使能.
FLASH->ACR|=1<<10; //数据cache使能.
FLASH->ACR|=5<<0; //5个CPU等待周期.
????????18)设置CFRG(RCC时钟配置寄存器)选择PLL作为系统时钟,RCC->CFGR&=~(3<<0);?? ???//清零?RCC->CFGR|=2<<0;? ? ?
????????19)等待主PLL设置为系统主时钟成功:while((RCC->CFGR&(3<<2))!=(2<<2)); ????????????????语句含义:当CFGR的3:2位不等于01时就说明主PLL还未就绪,就绪等待。???????
????????20)最后一步,配置向量表。
????????????????通过这样的设置系统时钟的整体配置就OK了。 ????????????????如果调用函数:u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq),产生180Mhz的主频 ????????????????设置参数:外部晶振为25M的时候:plln=360,pllm=25,pllp=2,pllq=8.
|