zoukankan      html  css  js  c++  java
  • STM32 时钟树配置快速入门


    layout: post
    tags: [STM32]
    comments: true

    为什么要了解时钟树?

    最近项目开发的时候,外部时钟源是16MHz,结果配置错了系统时钟,STM32F103的系统时钟频率最高为72MHz,错误地配置到了144MHz,但是AHB总线时钟又正确地配置到72MHz,最终导致了以下几种情况:

    • 程序会意外跑飞,然后进入Hard fault,甚至不知道发生了什么,就出现这样的错误;
    • 数据意外错误,int32负数乘法的时候,第30位数据异常;
      以上的问题,较难排查,而且给人一种芯片不稳定的错觉,对的,没错,这就是超频的代价,高速行驶的汽车,更容易翻车,所以,对于芯片的时钟配置有一个全面的了解,有着弥足轻重的作用。

    树的根

    在这里插入图片描述
    先看一下这张从网上扒来的图,说到树,不由自主想到了二叉树,设备树,森林里的树,而时钟树,也不例外,和这些树一样,都有根,有叶子,做一下简单的类比;
    是树汲取营养的地方,是树赖以生存的一个部位;
    而时钟树的根就是时钟输入源,用于产生系统的时钟节拍,即系统时钟

    系统时钟源

    • 高速内部时钟 HSI8MHz;
    • 高速外部时钟 HSE4MHz25MHz;
    • 锁相环时钟 PLL;
      常用的配置方案例如:选用外部时钟HSE,频率为8MHz,经过锁相环时钟,PLL进行9倍频,则系统时钟频率:

    SysClock = 8MHz*9 = 72Mhz

    或者使用外部时钟HSE,频率为16MHz,则经过锁相环时钟,PLL2分频,然后9倍频,则系统时钟频率:

    SysClock = 16MHz/2*9 = 72Mhz

    可见,PLL时钟源的使用很灵活,可以灵活运用;

    标准库的时钟配置

    STM32标准库的启动文件中可以发现,在main()运行前,已经运行了SystemInit()函数,代码如下,IDE是MDK,代码如下,具体可以参考

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

    标准库是默认按照外部高速时钟频率为8MHz进行配置的,SystemInit()函数原型位于system_stm32f10x.c,具体的调用关系如下,具体源码可以参考标准库;

        SystemInit()
            -->SetSysClock()
                -->SetSysClockTo72()
    

    在函数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) */ 
    }
    

    函数SetSysClockTo72默认定义系统时钟为72MHz

    #define SYSCLK_FREQ_72MHz   72000000
    

    初始化后系统的状态:

    时钟 频率
    SYSCLK 72MHz
    AHB 72MHz
    PCLK1 36MHz
    PCLK2 72MHz
    PLL 72MHz

    外部时钟源16M

    SetSysClockTo72

    如果外部时钟源的频率是16M,需要进行哪些修改?
    根据源码可以知,最终在SetSysClock中进行修改即可,因为要配置到72M的系统时钟频率,则直接进入函数SetSysClockTo72()进行修改;
    SetSysClockTo72源码如下;或者跳过源码直接看patch文件;

    /**
      * @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;
        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 */
      }
    }
    #endif
    
    

    patch

    需要修改两个地方:

    • stm32f10x.hHSE_VALUE的值;
    • system_stm32f10x.cSetSysClockTo72(void)函数的PLL配置;
      patch如下所示;
    diff --git a/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h b/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h
    index 8bf7624..e0ad316 100644
    --- a/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h
    +++ b/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/stm32f10x.h
    @@ -116,7 +116,9 @@
      #ifdef STM32F10X_CL
       #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
      #else
    -  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
    +//  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
    
    diff --git a/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c b/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
    index 71efc85..4ff040a 100644
    --- a/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
    +++ b/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c
    @@ -1053,7 +1053,7 @@ static void SetSysClockTo72(void)
         /*  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);
    +    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLXTPRE_HSE_Div2);
     #endif /* STM32F10X_CL */
    
         /* Enable PLL */
    
    

    这样就完成了基本配置的修改。

    其他细节

    • RTC的时钟来源:LSE, LSI,LSE的128分频
    • 独立看门狗IWDGCLK的时钟来源:LSI
    • APB1总线的时钟,最大到36M
    • APB2总线的时钟,最大到72M
    • APB,APB1,APB2为外设提供时钟。
  • 相关阅读:
    BZOJ 2876 骑行川藏
    BZOJ 2875 随机数生成器
    DT_修改注册项
    ip001
    ip
    阿里大鱼 阿里云api
    JS_全
    destoon框架二次开发【整理】
    destoon_笔记
    栏目class导航
  • 原文地址:https://www.cnblogs.com/unclemac/p/12783372.html
Copyright © 2011-2022 走看看