zoukankan      html  css  js  c++  java
  • Cotex-M3内核STM32F10XX系列时钟及其配置方法

    一、背景
      最近做个项目,需要使用STM32,还是以前一样的观点,时钟就是MCU心脏,供血即时钟频率输出,想要弄明白一个MCU,时钟是一个非常好的切入点。言归正传,网上已经有太多大神详述过STM32的详细配置方法了,在此就简单介绍下STM32时钟系统,以及如何配置做个简单记录,方便以后的快速开发。

    二、正文
      废话不多说,上一张STM32F10xx的时钟树图:

      

      由图可知,STM32F10XX有两级时钟
      第一级时钟
        * 高速内部时钟(HSI)
        * 锁相环时钟(PLLCLK)
        * 高速外部时钟(HSE)
      第二级时钟
        * 低速内部时钟(LSI)
        * 低速外部时钟(LSE)

      又由图可知,
        * HSE由外部晶振从"OSC_OUT","OSC_IN"两脚输入提供。
        * LSE由外部晶振从"OSC32_OUT","OSC32_IN"两脚输入提供。
        * HSI由8MHZ高速内部RC震荡电路提供。
        * LSI由40kHZ低速内部RC震荡电路提供。

      STM32F10XX还可通过MCO脚向外提供时钟输出。时钟来源有PLLCLK/HSI/HSE/SYSCLK,由MCO选择器来选择。

      研究过时钟来源,再来研究时钟的去向,MCU自身要能正常运作,即需要一个时钟,这个时钟既是系统时钟(SYSCLK),而基本上所有外设的时钟均来自于这个系统时钟(SYSCLk)。然后由系统时钟对外提供各种外设时钟。详见图。  

      当然,也有例外,USB时钟必须为48MHZ,这里的USB时钟(USBCLK)由PLLCLK直接提供,RTC时钟 (RTCCLK)也不是来源于系统时钟(SYSCLK),详见图。  

      时钟结构大体也就如此,不再深究,网上有许多更加深入的讲解,接下来说说如何去配置。用代码来说明问题:

      先贴文件"system_stm32f10x.c",此文件即库文件。里面有一个很重要的函数"SystemInit()" 

      解析过STM32启动代码的朋友都应该知道,这个函数跑在进入main函数之前,里面做的事情即是配置系统时钟。代码如下:

    void SystemInit (void)
    {
      /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
      /* Set HSION bit */
      RCC->CR |= (uint32_t)0x00000001;
    
      /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
        #ifndef STM32F10X_CL
          RCC->CFGR &= (uint32_t)0xF8FF0000;
        #else
          RCC->CFGR &= (uint32_t)0xF0FF0000;
        #endif /* STM32F10X_CL */   
          
          /* Reset HSEON, CSSON and PLLON bits */
          RCC->CR &= (uint32_t)0xFEF6FFFF;
        
          /* Reset HSEBYP bit */
          RCC->CR &= (uint32_t)0xFFFBFFFF;
        
          /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
          RCC->CFGR &= (uint32_t)0xFF80FFFF;
        
        #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 
        
          /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
          /* Configure the Flash Latency cycles and enable prefetch buffer */
          SetSysClock();    //此函数在该函数末尾                                            
    
                                                
        
        #ifdef VECT_TAB_SRAM
          SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal 
    
    SRAM. */                   
        #else
          SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal 
    
    FLASH. */                     
        #endif                                                                              
    
                                      
    }
    
    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();    // 以设置成最大频率72MHZ为例
        #endif
         
         /* If none of the define above is enabled, the HSI is used as System clock
            source (default after reset) */ 
    }
            
    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;
        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)
      {
        /* Enable Prefetch Buffer */
        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
        /* 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 */ 
        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 */
        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 */
      }
    }    

      

      文件"system_stm32f10x.c"的属性默认被设置为只读文件,最好不要更改里面的东西,毕竟启动以及复位之后,最先运行的是这个文件内的函数。

      如果想要更改MCU时钟频率,建议自己写一个函数来进行更改,代码如下:

    /**
      * @brief  配置系统时钟(72MHz)
      * @param  None
      * @retval None
      */
    void RCC_Configuration(void)
    {
        ErrorStatus HSEStartUpStatus;
        
        RCC_DeInit();
        RCC_HSEConfig(RCC_HSE_ON);
        HSEStartUpStatus = RCC_WaitForHSEStartUp();
        if(HSEStartUpStatus == SUCCESS) {
            FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
            FLASH_SetLatency(FLASH_Latency_2);
            // HCLK(AHBCLK) = SYSCLK = 72MHZ
            RCC_HCLKConfig(RCC_SYSCLK_Div1);    
            // APB2CLK = HCLK = 72MHZ
            RCC_PCLK2Config(RCC_HCLK_Div1);        
            // APB1CLK = HCLK/4 = 18MHZ         
            RCC_PCLK1Config(RCC_HCLK_Div4);        
            // 选择PLLCLK为输入时钟,PLLMUL9倍频. 8MHZ*9 = 72MHZ
            RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); 
            RCC_PLLCmd(ENABLE);
            while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){
            ; }
    //PLL作为系统时钟的输入 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() != 0x08) {
            ; } } }

      至此,记录完毕。

    记录地点:深圳WZ

    记录时间:2016年8月16日

  • 相关阅读:
    Linux定时任务
    linux文件权限
    grep awk sed 三剑客
    用户管理
    find查找inode号删除文件
    find 查找文件或目录 及du命令
    11、注册新用户
    10、密码扩展,使用Flask-Login认证用户
    9、大型程序的结构
    8、目前flask程序结构
  • 原文地址:https://www.cnblogs.com/ChYQ/p/5776345.html
Copyright © 2011-2022 走看看