IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32-时钟系统 -> 正文阅读

[嵌入式]STM32-时钟系统

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 ?__mainIMPORT ?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的。该如何设置?或者想倍频到更高的主频该怎么修改?

属性原来现在
外部晶振8M12M
倍频96
主频72M72M

????????想从原来的8M修改到现在的12M,但是主频仍为72M,该如何修改:

????????(1)【设置外部晶振】打开stm32f10x.h,找到下图部分:

?????????修改之前如上图一样,HSE_VALUE为外部晶振,如果你用的是f103的芯片,则修改下边这个为12M;

?????????(2)【设置主频】打开system_stm32f10x.c,找到下图部分:

?????????也可以根据自己的需要修改为56M、48M等;

?????????(3)【设置倍频】打开system_stm32f10x.c,找到自己的想要的主频函数:

123

?????????再找到这里(默认的情况下):

?????????①修改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.

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-22 14:50:57  更:2021-09-22 14:51:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:26:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码