天天看点

STM32F10X 时钟相关代码及分析

今天学习STM32F107VC时钟相关寄存器以及控制,配合源代码,数据手册,写了点学习笔记供以后参考。

很重要的示意图:

STM32F10X 时钟相关代码及分析
STM32F10X 时钟相关代码及分析

代码已加注释,如下:

//引自: system_stm32f10x.c 文件
void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  //先将HSION 内部高速时钟使能,HSIRDY指示内部8MHz RC振荡器是否就绪
  RCC->CR |= (uint32_t)0x00000001; 

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL //如果不是互联网型
  //RCC->CFGR &= 0xF8FF0000  意思是清除低16位(SW,HPRE,PPRE1,PPRE2,ADCPRE),第24~26位(MC0)
  RCC->CFGR &= (uint32_t)0xF8FF0000; 
#else 
  //STM32F107VC 是互联网型芯片,只是将MC0的位数多加了一位,由之前的第24~26位变成第24~27位。MC0[2:0] -> MC0[3:0]
  //所以这里清除第24~27位和低16位
  RCC->CFGR &= (uint32_t)0xF0FF0000; 
#endif /* STM32F10X_CL */   
  
  //这里值得注意,按最初的想法是这里可以合并起来做清除,而不用RCC->CR &= XXXX 然后又 RCC->CR &= XXXX,之所以这样做的原因
  //是有些位必须在关闭关闭时钟时才能写入。如:PLLXTPRE PLLMUL PLLSRC USBPRE PLLON等
  /* Reset HSEON, CSSON and PLLON bits */
  //必须关闭HSEON后才能写HSEBYP位,所以这里将Reset HSEBYP bit单独拿出来放在下面。
  RCC->CR &= (uint32_t)0xFEF6FFFF; 

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

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  //清除第16~22位 注意这几个寄存器位很关键,必须将对应的时钟关闭后才能配置
  //PLLSRC 选择时钟源作为PLL的时钟,PLLXTPRE 当且仅当PLLSRC选择HSE作为时钟时有效,PLLMUL 为倍频系数位,控制在2~16倍,但PLL的输出频率最大不能超过72M
  //USBPRE 预分频后频率为48MHz或24MHz

  RCC->CFGR &= (uint32_t)0xFF80FFFF; 

#ifndef STM32F10X_CL
  /* Disable all interrupts and clear pending bits  */
  //关闭所有时钟中断,并清除所有中断标志位 (CSSC PLL HSER HIS LSE LSI)
  RCC->CIR = 0x009F0000; 
#else //F107VC指向下面的代码
  /* Reset PLL2ON and PLL3ON bits */
  //其实就是关闭PLL2和PLL3
  RCC->CR &= (uint32_t)0xEBFFFFFF; 

  /* Disable all interrupts and clear pending bits  */
  //将CSSC PLL3 PLL2 HSE HIS LSE LSI的中断标志位都清除,这里和C51有点不同,中断标志位寄存器分离了,
  //如: HSI_RDYF  HSIRDYC,前者是只读,由硬件置位;后者只写,软件置位HSIRDYC来控制清除HSI_RDYF位。
  RCC->CIR = 0x00FF0000; 

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000; //清空PREDIV PLL2MUL PLL3MUL等
#endif /* STM32F10X_CL */
    
  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();

}

//这个函数其实就是根据宏的定义调用对应的时钟设置函数,代码里面定义了SYSCLK_FREQ_72MHz,所以默认是调用SetSysClockTo72()
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) */ 
}


//肯定是执行这个宏里面的代码
#elif defined SYSCLK_FREQ_72MHz
/**
  * @brief  Sets System clock frequency to 72MHz and configure HCLK, PCLK2 
  *          and PCLK1 prescalers. 
  * @note   This function should be used only after reset.
  * @param  None
  * @retval None
  */
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); //开启外部高速振荡器
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY; //等待HSE进入稳定状态,就绪后会将HSERDY置1
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSEStartUp_TimeOut)); //HSEStartUp_TimeOut 0x0500

  //执行到这里有可能是超时,也有可能是HSE稳定了
  if ((RCC->CR & RCC_CR_HSERDY) != RESET) 
  {
    HSEStatus = (uint32_t)0x01; //这里表明却是稳定了
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

  //注意只有在系统时钟(SYSCLK)小于24MHz并且没有打开AHB的预分频器(即HCLK必须等于
  //SYSHCLK)时,才能执行预取缓冲器的打开和关闭操作,所以一般是在初始化时进行操作。
  if (HSEStatus == (uint32_t)0x01) //HSE 外部高速时钟晶振已经开启并且稳定
  {
    /* Enable Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE; //开启 Flash 预取缓冲区

	//2等待周期,当 48MHz < SYSCLK ≤ 72MHz
    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    

	//AHB桥的时钟等于SYSCLK 72MHz
	//设置AHB预分频,CFGR[7:4] -> HPRE[3:0] |= 0x00000000,SYSCLK不分频
    /* HCLK = SYSCLK */
	RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; 
      
	//APB2桥的时钟等于AHB时钟 72MHz
	//设置PPRE2预分频,CFGR[13:11] -> PPRE2[2:0] |= 0x00000000,HCLK不分频
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; 
    
	//APB1桥的时钟等于AHB时钟的一半 36MHz,低速APB桥
	//设置PPRE1预分频,CFGR[10:8] -> PPRE1[2:0] |= 0x00000400,使用HCLK二分频
    /* PCLK1 = HCLK/2 */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; 

#ifdef STM32F10X_CL //STM32F107X 
    /* 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);
							  
	//从数据手册的时钟树图上可以看出,PLL2唯一的时钟源就是PREDIV2预分频后的HSE,这里典型值取5分频,PREDIV2 = 25 / 5 = 5MHz
	//PLL2通过PLL2MUL倍频寄存器提高工作频率,典型值为8倍频,PLL2CLK = 40 MHz。
	//PLL的时钟源可以由PREDIV1和HSI二分频提供,这里选用PREDIV1。PREDIV1自身的时钟源又可以由HSE直接提供和PLL2提供,这里选用PLL2提供。
	
	//设置PREDIV2、PREDIV1、PREDIV1SRC预分频和PLL2MUL倍频寄存器值
	//HSE频率为25MHz,PREDIV2 5分频后传给PLL2的频率为5MHz,然后PLL2MUL 选择为8倍频,PLL2的频率为5 × 8 = 40MHz
	//使用PLL2为PREDIV1的时钟源,并且PREDIV1 5分频,PREDIV1的频率为8MHz
    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; //开启PLL2时钟
    /* Wait till PLL2 is ready */
    while((RCC->CR & RCC_CR_PLL2RDY) == 0) //等待稳定
    {
    }
    
    /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ 
    RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
	//RCC_CFGR_PLLXTPRE_PREDIV1 0x00000000 不对输入时钟进行分频
	//选择PREDIV1为PLL的时钟源,并且设置PLL倍频为9倍频,所以频率为 72MHz
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | 
                            RCC_CFGR_PLLMULL9); 
#else    
    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
	//先清除PLLMUL 倍频系数位,PLLSRC 输入时钟源位,PLLXTPRE HSE分频位
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL));
										
	//使用HSE为PLL的输入时钟源,并且不对HSE分频,另外PLLMUL倍频系数选择9倍频,外部高速时钟被限定为8MHz频率									
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9); 
	//
#endif /* STM32F10X_CL */

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

    /* Wait till PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0) //等待PLL就绪
    {
    }
    
    /* Select PLL as system clock source */
	//将PLL配置为系统时钟SYSCLK频率
    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 */
	//检查SWS位是不是是10,如果是就表明是PLL作为系统时钟
    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 */    

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

总的来说,控制STM32F107 互联网系列的时钟可以分为下面几步:

第一步、 开启外部高速HSE时钟,需要其等待其稳定 (因为后面PLL2要使用其作为时钟源)

第二步、开启Flash闪存预取缓冲区 (这个东西还没有很了解)

第三步、设置AHB、APB1、APB2的预分频寄存器和PLL相关寄存器 (主要是选定PLL2 、PLL时钟源)

第四步、开启PLL2和PLL并选用PLL作为系统时钟源

继续阅读