stm32 时钟配置——外部时钟倍频、内部时钟倍频 【worldsing笔记】

时间:2023-12-22 22:59:20

stm32可选的时钟源

在STM32中,可以用内部时钟,也可以用外部时钟,在要求进度高的应用场合最好用外部晶体震荡器,内部时钟存在一定的精度误差。

准确的来说有4个时钟源可以选分别是HSI、LSI、HSE、LSE(即内部高速,内部低速,外部高速,外部低速),高速时钟主要用于系统内核和总线上的外设时钟。低速时钟主要用于独立看门狗IWDG、实时时钟RTC。

①、HSI是高速内部时钟,RC振荡器,频率为8MHz,上电后默认的系统时时钟 SYSCLK = 8MHz,Flash编程时钟。

①、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。

③、LSI是低速内部时钟,RC振荡器,频率为40kHz,可用于独立看门狗IWDG、实时时钟RTC。

④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。

系统时钟SYSCLK输入

这里重点在高速时钟,因为大部分外设时钟都是用的高速时钟。系统内核和外设时钟的时钟只有一个源,那就是SYSCLK, 即常说的系统时钟, 他是有一个选择器SW来做选择的,有3种选择HSI、HSE和PLL, 上电后默认选择内部HSI, HSI虽然存在精度误差,但是能保证不会挂。这点比AVR单片机做的好一些,AVR单片机时钟一旦配置成外部的,如果外部时钟正常那时无法下载程序的。PLL为锁相环倍频输出,PLL时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,其输出频率最大不得超过72MHz, 也有网有超频工作的,但不提倡。

也就是说系统时钟SYSCLK最终有以下几种选择:

①、SYSCLK = HSI

①、SYSCLK = HSE

③、SYSCLK = PLL

stm32 时钟配置——外部时钟倍频、内部时钟倍频 【worldsing笔记】

关于PLL锁相环倍频(输入和输出):

将输入时钟乘以一个系数后输出时钟,可以百度PLL原理。

PLL的输入3种选择:

①、PLLi =  HSI /2

①、PLLi =  HSE /2

③、PLLi =  HSE

PLL的输出有15种选择: PLLout = PLLi Xn (n = 2…16)

stm32 时钟配置——外部时钟倍频、内部时钟倍频 【worldsing笔记】

关于USB时钟

STM32中有一个全速功能的USB模块,其串行接口引擎需要一个频率为48MHz的时钟源。该时钟源只能从PLL输出端获取(唯一的),,可以选择为1.5分频或者1分频,也就是,当需要使用USB模块时,PLL必须使能,并且时钟频率配置为48MHz或72MHz。

stm32 时钟配置——外部时钟倍频、内部时钟倍频 【worldsing笔记】

关于把时钟信号输出到外部

另外,STM32还可以选择一个时钟信号输出到MCO脚(PA8)上,可以选择为PLL输出的2分频、HSI、HSE、或者系统时钟。

stm32 时钟配置——外部时钟倍频、内部时钟倍频 【worldsing笔记】

关于系统时钟SYSCLK分配

系统时钟SYSCLK,它是供STM32中绝大部分部件工作的时钟源。系统时钟可选择为PLL输出、HSI(8MHz)或者HSE(外部晶振)。系统时钟最大频率为72MHz,供给I2S音频总线和AHB总线时钟。

stm32 时钟配置——外部时钟倍频、内部时钟倍频 【worldsing笔记】

它通过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),另一路送给定时器(Timer)2、3、4倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器2、3、4使用。

⑤、APB2外设:送给APB2分频器。APB2分频器可选择1、2、4、8、16分频,其输出一路供APB2外设使用(PCLK2,最大频率72MHz),另一路送给定时器(Timer)1倍频器使用。该倍频器可选择1或者2倍频,时钟输出供定时器1使用。另外,APB2分频器还有一路输出供ADC分频器使用,分频后送给ADC模块使用。ADC分频器可选择为2、4、6、8分频。

在以上的时钟输出中,有很多是带使能控制的,例如AHB总线时钟、内核时钟、各种APB1外设、APB2外设等等。当需要使用某模块时,记得一定要先使能对应的时钟。

需要注意的是定时器的倍频器,当APB的分频为1时,它的倍频值为1,否则它的倍频值就为2。

连接在APB1(低速外设)上的设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、Timer2、Timer3、Timer4。注意USB模块虽然需要一个单独的48MHz时钟信号,但它应该不是供USB模块工作的时钟,而只是提供给串行接口引擎(SIE)使用的时钟。USB模块工作的时钟应该是由APB1提供的。

连接在APB2(高速外设)上的设备有:UART1、SPI1、Timer1、ADC1、ADC2、所有普通IO口(PA~PE)、第二功能IO口。

对于单片机系统来说,CPU和总线以及外设的时钟设置是非常重要的,因为没有时钟就没有时序。

由于时钟是一个由内而外的东西,具体设置要从寄存器开始。
RCC 寄存器结构,RCC_TypeDeff,在文件“stm32f10x.h”中定义如下: (v3.4库)

1059行->1081行。

  1. typedef struct
  2. {
  3. __IO uint32_t CR;
  4. __IO uint32_t CFGR;
  5. __IO uint32_t CIR;
  6. __IO uint32_t APB2RSTR;
  7. __IO uint32_t APB1RSTR;
  8. __IO uint32_t AHBENR;
  9. __IO uint32_t APB2ENR;
  10. __IO uint32_t APB1ENR;
  11. __IO uint32_t BDCR;
  12. __IO uint32_t CSR;
  13. #ifdef STM32F10X_CL
  14. __IO uint32_t AHBRSTR;
  15. __IO uint32_t CFGR2;
  16. #endif /* STM32F10X_CL */
  17. #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)
  18. uint32_t RESERVED0;
  19. __IO uint32_t CFGR2;
  20. #endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */
  21. } RCC_TypeDef;

一般板子上只有8Mhz的晶振,而增强型最高工作频率为72Mhz,显然需要用PLL倍频9倍,这些设置都需要在初始化阶段完成。

使用外部高速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()

为了方便说明,借用一下例程的RCC设置函数,并用中文注释的形式加以说明:

    static void RCC_Config(void) {
    /* 这里是重置了RCC的设置,类似寄存器复位 */
    RCC_DeInit(); /* 使能外部高速晶振 */
    RCC_HSEConfig(RCC_HSE_ON); /* 等待高速晶振稳定 */
    HSEStartUpStatus = RCC_WaitForHSEStartUp(); if (HSEStartUpStatus == SUCCESS)
    {
    /* 使能flash预读取缓冲区 */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); /* 令Flash处于等待状态,2是针对高频时钟的,这两句跟RCC没直接关系,可以暂且略过 */
    FLASH_SetLatency(FLASH_Latency_2); /* HCLK = SYSCLK 设置高速总线时钟=系统时钟*/
    RCC_HCLKConfig(RCC_SYSCLK_Div1); /* PCLK2 = HCLK 设置低速总线2时钟=高速总线时钟*/
    RCC_PCLK2Config(RCC_HCLK_Div1); /* PCLK1 = HCLK/2 设置低速总线1的时钟=高速时钟的二分频*/
    RCC_PCLK1Config(RCC_HCLK_Div2); /* ADCCLK = PCLK2/6 设置ADC外设时钟=低速总线2时钟的六分频*/
    RCC_ADCCLKConfig(RCC_PCLK2_Div6); /* Set PLL clock output to 72MHz using HSE (8MHz) as entry clock */
    //这句很关键
    /* 利用锁相环讲外部8Mhz晶振9倍频到72Mhz */
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); /* Enable PLL 使能锁相环*/
    RCC_PLLCmd(ENABLE); /* Wait till PLL is ready 等待锁相环输出稳定*/
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) ; /* Select PLL as system clock source 将锁相环输出设置为系统时钟 */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* Wait till PLL is used as system clock source 等待校验成功*/
    while (RCC_GetSYSCLKSource() != 0x08);
    } /* Enable FSMC, GPIOD, GPIOE, GPIOF, GPIOG and AFIO clocks */
    //使能外围接口总线时钟,注意各外设的隶属情况,不同芯片的分配不同,到时候查手册就可以
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE |
    RCC_APB2Periph_GPIOF | RCC_APB2Periph_GPIOG |
    RCC_APB2Periph_AFIO, ENABLE);
    }

使用内部高速HSI时钟,程序设置时钟参数流程:
     1、将RCC寄存器重新设置为默认值      RCC_DeInit;
     2、设置AHB时钟                           RCC_HCLKConfig;
     3、设置高速AHB时钟                      RCC_PCLK2Config;
     4、设置低速速AHB时钟                   RCC_PCLK1Config;
    5、设置PLL选择时钟源和倍频数         RCC_PLLConfig(RCC_PLLSource_HSI_Div2, 倍频数);

6、打开PLL                                 RCC_PLLCmd(ENABLE);
     7、等待PLL工作                            while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
     8、设置系统时钟                           RCC_SYSCLKConfig;
     9、判断是否PLL是系统时钟               while(RCC_GetSYSCLKSource() != 0x08)
   10、打开要使用的外设时钟                 RCC_APB2PeriphClockCmd()/RCC_APB1PeriphClockCmd()

void RCC_Configuration(void){

    /*将外设RCC寄存器重设为缺省值*/
RCC_DeInit(); /*设置AHB时钟(HCLK)*/
RCC_HCLKConfig(RCC_SYSCLK_Div1); //RCC_SYSCLK_Div1——AHB时钟= 系统时钟 /* 设置高速AHB时钟(PCLK2)*/
RCC_PCLK2Config(RCC_HCLK_Div1); //RCC_HCLK_Div1——APB2时钟= HCLK /*设置低速AHB时钟(PCLK1)*/
RCC_PCLK1Config(RCC_HCLK_Div2); //RCC_HCLK_Div2——APB1时钟= HCLK / 2 /*设置FLASH存储器延时时钟周期数*/
FLASH_SetLatency(FLASH_Latency_2); //FLASH_Latency_2 2延时周期 /*选择FLASH预取指缓存的模式*/
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); // 预取指缓存使能 /*设置PLL时钟源及倍频系数*/
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_12); /*使能PLL */
RCC_PLLCmd(ENABLE); /*检查指定的RCC标志位(PLL准备好标志)设置与否*/
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) ; /*设置系统时钟(SYSCLK)*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* PLL返回用作系统时钟的时钟源*/
while(RCC_GetSYSCLKSource() != 0x08); //0x08:PLL作为系统时钟
}

PLL的设定需要在使能之前,一旦PLL使能后参数不可更改。

在STM32中,连接在APB1(低速外设)上的设备有:电源接口、备份接口、CAN、USB、I2C1、I2C2、UART2、UART3、SPI2、窗口看门狗、Timer2、Timer3、Timer4 。

连接在APB2(高速外设)上的设备有:GPIO_A-E、USART1、ADC1、ADC2、ADC3、TIM1、TIM8、SPI1、ALL。

程序举例:

APB1(低速外设)

RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE);

APB2(高速外设)

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA  , ENABLE);

STM32和其它单片机相比时钟更为复杂,就是为了灵活配置,降低功耗,不用的外设可以关闭时钟,看似复杂但是,打开时钟树一看,就是几个根的选择,和几个枝叶的选择。