时钟系统框图

在这里插入图片描述

观察上图时钟系统框图,可知道
  蓝色矩形表示时钟振荡源(5个):HSI RC、HSE Osc、PLL(锁相环、倍频器)、LSE Osc、LSI RC
    H:快速、L:低速、S:速度、I:内部、E:外部
  灰色梯形表示选择器:通过不同选择器的选择,SYSCLK系统时钟、RTCCLK实时时钟、IWDGCLK独立看门狗时钟、USBCLK USB时钟可有多种选择。
  黄色矩形css表示时钟监视系统:监控若时钟出错,则自动切换为HSI
  白色小矩形:OSC_OUT、OSC_IN表示外接时钟信号。MCO输出内部时钟的引脚PA8。
  绿色矩形表示分频器

总结:
STM32 有5个时钟源:HSI、HSE、LSI、LSE、PLL。
①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。
②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。WDG
④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。RTC
⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。 倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

系统时钟SYSCLK可来源于三个时钟源:
①、HSI振荡器时钟
②、HSE振荡器时钟
③、PLL时钟

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

任何一个外设在使用之前,必须首先使能其相应的时钟。

几个重要的时钟:
①、SYSCLK(系统时钟)
②、 AHB总线时钟
③、 APB1总线时钟(低速): 速度最高36MHz
④、 APB2总线时钟(高速): 速度最高72MHz
⑤、 PLL时钟

RCC相关配置寄存器
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相关文件和固件库源文件

头文件:stm32f10x_rcc.h、源文件:stm32f10x_rcc.c
在这里插入图片描述

systemInit()函数详细解读
打开项目中:system_stm32f10x.c下面的system_stm32f10x.h可找到systemInit()函数。也可直接从system_stm32f10x.c中找到systemInit()函数的相关定义。
再打开STM32参考手册6.3小结中,可知时钟控制寄存器(RCC_CR)的相关应用。在这里插入图片描述

在这里插入图片描述

根据参考手册中关于寄存器CR的介绍可知,CR是32位寄存器。做要打开振荡源HSI RC需要对CR寄存器中最后一位置1,即下面代码的操作:

  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

因为我们使用的大容量,末尾为HD,故下面这几段代码并不会执行:

#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */  

重置HSEON, CSSON and PLLON 三个位,根据下面代码把十六进制转换为二进制可知:Ob1111 1110 1111 0110 1111 1111 1111 1111 ,可知通过位与操作可将16位、19位、24位(HSEON, CSSON and PLLON ).即对应的三位重置为0。

  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

重置HSEBYP为0.(设置为0相当于关闭)

 /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF

寄存器CFGR中重置PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE为0.

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

根据主板的型号,执行对应位置的代码,我们用的是HD,故应该执行#else下的语句,将所有的中断都清理掉。

#ifdef STM32F10X_CL
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;

  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x00FF0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;      
#else
  /* Disable all interrupts and clear pending bits  */
  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 

接下来调用厦门这个函数,可以“Go To Definition of" SetSysClock();"”

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();

查看到以下语句:

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
 
 /* If none of the define above is enabled, the HSI is used as System clock
    source (default after reset) */ 
}

这边是根据宏定义是哪个,就执行里面的语句,可以接着从中随便“Go To Definition of" xxx;"”选择一个查看,可追溯到以下程序:

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
#define SYSCLK_FREQ_24MHz  24000000
#else
/* #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
#endif

这边根据需要,选择不同的频率,不用的频率要相应的注释掉。根据这边,我们知道这边选择的是:SYSCLK_FREQ_72MHz,所以就会执行的函数就是:SetSysClockTo72();即以下程序:

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
  /* Enable HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);  /Go To 后,可知是对第20位进行置1,即打开外部高速时钟/
 /等待对应的时钟源稳定,这边使用do while循环实现,判断对应位寄存器的数值是否为1/
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY; 	/判断RCC_CR_HSERDY(17位)的数值是否为1/
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)		/若这边就绪了则执行0x01赋值/
  {
    HSEStatus = (uint32_t)0x01;	
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

  if (HSEStatus == (uint32_t)0x01)
  {
    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;
    /关于flash的设置,要参考STM32参考手册下的STM32FLASHxxxxx里面的STM32F10xxx闪存编程参考手册/

    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    

 
    /* HCLK = SYSCLK *//根据判断这两个值是否相等来知道这边分频的系数(倍率),查看手册中关于寄存器CFGR/
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
      
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK /2*/					/这边是二分频/
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

#ifdef STM32F10X_CL
    /* 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_DIV5);
  
    /* Enable PLL2 */
    RCC->CR |= RCC_CR_PLL2ON;
    /* Wait till PLL2 is ready */
    while((RCC->CR & RCC_CR_PLL2RDY) == 0)
    {
    }
    
   
    /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ /系统时钟切换成PLL的来源/
    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    
    /*  PLL configuration: 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 */		/第24位,看手册/
        RCC->CR |= RCC_CR_PLLON;

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
    
    /* Select PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    

    /* Wait till PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
    {
    }
  }
  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 */
  }
}
#endif

为什么我们的main函数中并没有关于时钟相关的初始化,但是确能实现对时钟的初始化呢?选择工程中的CORE文件夹下的startup_stm32f10x_hd.s的文件,我们可以找到:

Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP

这个应该是汇编语句吧,看不懂(官方视频也说看不懂)。但是应该是实现的先执行SystemInit函数,然后在执行main函数,这就证明了为什么main函数里面不需要对时钟的初始化。