stm32简明教程系列(一)——时钟源概述

首先给出时钟树,我们这篇以时钟树为大纲:

这里我们只讲解最重要也是最常见的8MHz高速外部时钟源(HSE)。如下:

首先,从8Mhz经过OSC_OUT和OSC_IN两个口构成的时钟电路进入。这里有四个出路,我们只关注与系统时钟相关的。

(1)一个是直接进入SW。如下:

(2)经过CSS后进入SW。

(3)进入PLLXTPRE再进入后进入锁相环源(PLLSRC),再进入锁相环(PLLMUL),经过倍频后(*2,*3,*4。。。。通常我们是18倍频到72MHZ),得到我们常用的系统时钟频率72MHZ。这条时钟源可以直接通过usb 预分频后作为usb时钟源来使用。72M直接到达AHB预分频器,使用这条时钟源的外设有DMA,CPU,内存。然后到达分管其他外设的APB1和APB2。其中APB1有TIM2,3,4,APB2有ADC,TIM1。这就是stm32时钟源最重要的部分。stm32上电复位默认是HSI内部时钟源,所以我们需要在程序里面对PLLSRC设置,选择外部时钟源

如下图:

(4)同上种,只不过这里是先二分频后再进入PLLXRPRE。

这里有一点要注意:

低速OSC32_INM,OSC32_OUT时钟源必须接,因为32.768KHz的时钟是直接连接到RTC模块的,没有该时钟源,RTC模块将不能用!!

另外,

具体的时钟挂在如下图:

这里我们捎带说一句,一般的系统板子通常采用以下方式来连接晶振:

以下是我们分析一下system_stm32f10x.c库代码:

/*!< Uncomment the line corresponding to the desired System clock (SYSCLK)
   frequency (after reset the HSI is used as SYSCLK source)
   
   IMPORTANT NOTE:
   ============== 
   1. After each device reset the HSI is used as System clock source.

   2. Please make sure that the selected System clock doesn't exceed your device's
      maximum frequency.
      
   3. If none of the define below is enabled, the HSI is used as System clock
    source.

   4. The System clock configuration functions provided within this file assume that:
        - For Low, Medium and High density devices an external 8MHz crystal is
          used to drive the System clock.
        - For Connectivity line devices an external 25MHz crystal is used to drive
          the System clock.
     If you are using different crystal you have to adapt those functions accordingly.
    */

我们首先看到四个注意:

1.在每次复位后,系统时钟源会默认使用HSI。这个印证了我们上面的要点。

2.确保系统时钟不超过最大的频率。

3.如果以下定义都未使能,那么系统启用HSI时钟。

4.对于低密度,中密度和高密度器件,8MHz外部晶体是用于驱动系统时钟;对于互联型设备,用25MhZ外部晶振来做系统时钟。

接下来,是:

/* #define SYSCLK_FREQ_HSE    HSE_Value */
/* #define SYSCLK_FREQ_24MHz  24000000 */
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000

/*!< Uncomment the following line if you need to use external SRAM mounted
     on STM3210E-EVAL board (STM32 High density devices) as data memory  */ 
#ifdef STM32F10X_HD
/* #define DATA_IN_ExtSRAM */
#endif /* STM32F10X_HD */

这里我们启用定义72mhz的系统时钟评率,而屏蔽了其他频率。这意味我们开启72mhz的频率。

接下来是时钟定义:

#ifdef SYSCLK_FREQ_HSE
  const uint32_t SystemFrequency         = SYSCLK_FREQ_HSE;        /*!< System Clock Frequency (Core Clock) */
  const uint32_t SystemFrequency_SysClk  = SYSCLK_FREQ_HSE;        /*!< System clock                        */
  const uint32_t SystemFrequency_AHBClk  = SYSCLK_FREQ_HSE;        /*!< AHB System bus speed                */
  const uint32_t SystemFrequency_APB1Clk = SYSCLK_FREQ_HSE;        /*!< APB Peripheral bus 1 (low)  speed   */
  const uint32_t SystemFrequency_APB2Clk = SYSCLK_FREQ_HSE;        /*!< APB Peripheral bus 2 (high) speed   */

~~~~~~~~~~~~~~~~~~~~
#elif defined SYSCLK_FREQ_72MHz
  const uint32_t SystemFrequency         = SYSCLK_FREQ_72MHz;      /*!< System Clock Frequency (Core Clock) */
  const uint32_t SystemFrequency_SysClk  = SYSCLK_FREQ_72MHz;      /*!< System clock                        */
  const uint32_t SystemFrequency_AHBClk  = SYSCLK_FREQ_72MHz;      /*!< AHB System bus speed                */
  const uint32_t SystemFrequency_APB1Clk = (SYSCLK_FREQ_72MHz/2);  /*!< APB Peripheral bus 1 (low)  speed   */
  const uint32_t SystemFrequency_APB2Clk = SYSCLK_FREQ_72MHz;      /*!< APB Peripheral bus 2 (high) speed   */
#else /*!< HSI Selected as System Clock source */
  const uint32_t SystemFrequency         = HSI_Value;              /*!< System Clock Frequency (Core Clock) */
  const uint32_t SystemFrequency_SysClk  = HSI_Value;              /*!< System clock                        */
  const uint32_t SystemFrequency_AHBClk  = HSI_Value;              /*!< AHB System bus speed                */
  const uint32_t SystemFrequency_APB1Clk = HSI_Value;              /*!< APB Peripheral bus 1 (low)  speed   */
  const uint32_t SystemFrequency_APB2Clk = HSI_Value;              /*!< APB Peripheral bus 2 (high) speed   */
#endif

这里我们可以看到第一段是我们自定义系统时钟频率,如果我们之前:

#define SystemFrequency=12345678

那么,时钟频率就是12345678HZ。这里我们在上面定义了系统频率为72Mhz,则sysclk,AHBCLK,APB2为72MHZ。APB1为36Mhz。

如果没有定义时钟频率,那么系统频率都为内部时钟频率8mHZ。

再往下看:

static void SetSysClock(void);

#ifdef SYSCLK_FREQ_HSE
  static void SetSysClockToHSE(void);

~~~~~~~~~~~~~~~
#elif defined SYSCLK_FREQ_72MHz
  static void SetSysClockTo72(void);
#endif

这里提前申明时钟定义函数。

接下来,就进入了核心的寄存器配置部分了。

我们先看一下datasheet。这里RCC有以下几个寄存器:

1.时钟控制寄存器(RCC_CR)

2.时钟配置寄存器(RCC_CFGR)

3.时钟中断寄存器 (RCC_CIR)

4.控制/状态寄存器 (RCC_CSR)

5.AHB,APB1,APB2相关的一些寄存器。

第一个函数是首先时钟初始化函数,将所有的相关寄存器都复位。

void SystemInit (void)
{
  /* 复位时钟状态 */
  /* 打开HSI内部高速时钟,保证有时钟可用 */
  RCC->CR |= (uint32_t)0x00000001;

  /*复位 SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO 位 ,全都置为0*/
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
//1111 1000 1111 1111 0000 0000 0000 0000
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */   
  
  /* 复位 HSEON, CSSON and PLLON */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* 复位 HSEBYP  */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE  */
  RCC->CFGR &= (uint32_t)0xFF80FFFF;

#ifndef STM32F10X_CL
  /* 如果没有定义该单片机是互联设备,失能所有的中断位和等待位  */
  RCC->CIR = 0x009F0000;
#else
  /* 复位 PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;

  /* 关闭所有的中断和等待位  */
  RCC->CIR = 0x00FF0000;

  /* 复位 CFGR2 寄存器 */
  RCC->CFGR2 = 0x00000000;
#endif /* STM32F10X_CL */
    
  /* 配置系统时钟, 设置HCLK, PCLK2 and PCLK1 预分频器 */

  SetSysClock();

}

这里有以下要注意:

1.库文件中出现了一个RCC->CFGR2寄存器,但这个寄存器在stm32103系列当中是没有的,在107系列里面有

2.我们可以看到由于cortex—m3是32位的,其寄存器设置也是32位。

3.我们这里经常能看#ifndef STM32F10X_CL这就话,这个是用来区分103和107(Connectivity line devices )的一句话。这里我们      用的是103设备,自然不用定义STM32F10X_CL。然后,以下的程序便都会执行了。

接下来,是一个外部内存设置函数,这里我们不管。

然后,是一个将系统系统设置到外部高速时钟源,同时进行时钟寄存器的正式配置;

这里的代码,我们只粘贴72Mhz相关的代码,其他类似:

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* 配置SYSCLK, HCLK, PCLK2 and PCLK1  ---------------------------*/    
  /* 使能外部时钟 HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /*等待起振,失败则退出 */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSEStartUp_TimeOut));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  
 //如果HSE启动成功
  if (HSEStatus == (uint32_t)0x01)
  {
    /* 使能 预取址 缓存 */
    FLASH->ACR |= FLASH_ACR_PRFTBE;

    /* Flash 2 wait state */
    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
    /* 这里如果用的是107等互联设备的话,我们需要如下配置
      Configure PLLs ------------------------------------------------------*/
    /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
    /* PREDIV1 configuration: 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_DIV3);
  
    /* 使能 PLL2 */
    RCC->CR |= RCC_CR_PLL2ON;
    /*等待起振 */
    while((RCC->CR & RCC_CR_PLL2RDY) == 0)
    {
    }
    
   
    /* 配置PLL: 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    
    /*  这里才是103 72mhz时钟频率的时钟配置  PLL  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 */

    /* Enable PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till PLL is ready */
    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)
    {
    }
  }
 //HSE启动失败
  else
  { /* If HSE fails to start-up, the application will have wrong clock 
         configuration. User can add here some code to deal with this error */    

    /* Go to infinite loop */
    while (1)
    {
    }
  }
}

这里关于时钟配置一切就都讲完了。

猜你喜欢

转载自blog.csdn.net/quinn1994/article/details/82227828