zoukankan      html  css  js  c++  java
  • STM32F4 时钟体系

    时钟体系

      给处理器提供一个时钟信号,能使处理器内部组件同步工作 ,并且和外部设备通信时也能达到同步。根据功耗需求,可以选择三种不同的时钟源:
       HSI振荡器时钟:这个为处理器内部时钟,能进行简单控制的洗衣机,热水器,电饭锅一般会使用这个时钟,精度低;
       HSE振荡器时钟:这是高速外部时钟,我使用的开发板的高速外部时钟为8MHz,标准库函数中的时钟初始化的默认高速外部时钟为25MHz,需要更改为8Mhz;
       PLL时钟: HSE时钟或HSI时钟经过PLL后的输出时钟,系统运行一般都使用这个时钟;
      开发板还有两个低速时钟分别是:
       32 kHz低速内部RC(LSI RC),用于驱动独立看门狗。如果看门狗和系统时钟一个时钟的话,要是系统出现问题,系统的时钟信号不工作了,看门狗也会卡死无法正常工作,所以就需要单独给看门狗一个时钟源,定时检查处理器是否正常工作;
       32.768kHz低速外部晶振(LSE晶振),此时钟是用于系统断电后,用于RTC实时时钟的一些操作。

    PLL基本组成


      锁相环路是一种反馈控制电路,简称锁相环(PLL,Phase-Locked Loop),锁相环通常由鉴相器(PD,Phase Detector)、环路滤波器(LF,Loop Filter)和压控振荡器(VCO,Voltage Controlled Oscillator)三部分组成。锁相环的特点是:通过外部输入的参考信号和控制环路的压控振荡器的输入信号的相位差,来得到它们的电压差,以这个电压作为误差电压来控制振荡器的频率,使压控振荡器频率达到与输入参考信号频率按一定比例输出的频率,因锁相环可以实现输出信号频率对输入信号频率的自动跟踪,所以锁相环通常用于闭环跟踪电路。锁相环在工作过程中,当输出信号的频率成比例地反映输入信号的频率时,输出电压与输入电压保持固定的相位差值,这样输出电压与输入电压的相位就被锁住了。

    stm32f4 PLL参数配置


      根据参考芯片手册107页中的时钟树,大概可以知道需要PLL参数配置需要先选择HSI时钟源或HSE时钟源,根据上图的标注可以得到如下的公式:

    一共有三个倍/分频因子PLLM、PLLN、PLLP,需要注意不同的芯片,倍频公式是不一样的,需要查询芯片手册。

    #if !defined  (HSE_VALUE)
      #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
    #endif /* HSE_VALUE */
    

    修改stm32f4xx.h,127行将外部振荡器频率修改为所使用的外部振荡器频率,注意需要将该头文件的只读权限修改为可读可写。

    /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
    #define PLL_M      8     //为方便计算当HSE为8MHz时便修改为8,为25时便修改为25
    /* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
    #define PLL_Q      7
     
    #if defined (STM32F40_41xxx)
    #define PLL_N      336
    /* SYSCLK = PLL_VCO / PLL_P */
    #define PLL_P      2
    #endif /* STM32F40_41xxx */
    

      在system_stm32f4xx.c文件中可以修改这三个因子的值,以此来到达自己需要的频率。上面代码是将PLL频率修改为了168MHz,每个因子的值都有一定的取值范围,具体需要查阅芯片手册的PLL寄存器说明;

    stm32f4 时钟配置


      从时钟树中可以看出,系统时钟源的选择有三个,在特殊的应用场景,为了达到最高的能效比,没有必要使用到PLL,可将HSE、HSI作为系统时钟源。例如,在智能手表,智能手机锁屏的情况下,如果使用PLL配置过后输出的频率会造成过多的功耗,降低自身的续航能力。因此,PLL在锁屏下的应用场景并不合适,在保证功能实现的前提下,尽可能降低功耗,可以切换频率更低的时钟源提供给系统时钟。
      从时钟树中可以看出,时钟源选择完成之后还需要配置AHB,APBx,以此来得到时钟FCLK(CPU核供给时钟),HCLK(高速总线时钟),PCLKx(外设总线时钟),查看源码的汇编启动文件可以发现,汇编代码设置好了栈、堆、中断向量表后便跳转到了系统初始化C语言函数SystemInit(),在这个函数中可以发现其又调用了系统时钟设置函数SetSysClock(),可以发现对于stm32f4的时钟设置有如下代码:

    SetSysClock()
    {
    ......
          /* Enable HSE */
          RCC->CR |= ((uint32_t)RCC_CR_HSEON);
          ......
          /* HCLK = SYSCLK / 1*/
          RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
          ......
          /* PCLK2 = HCLK / 2*/
          RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
        
          /* PCLK1 = HCLK / 4*/
          RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
          .......
          RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                       (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
    .......
    }
    

      从上面代码可以看出系统默认使用外部振荡器HSE,当HSE响应成功后便可以配置HCLK时钟,还可以看出stm32f4有两条外设总线PCLK1,PCLK2,根据时钟树可上面的代码便可以得出SYSCLK=FCLK=HCLK=168MHz,PCLK1=84MHz,PCLK1=42MHz,注意HCLK时钟是给很多个AHB使用的。
      外设的时钟在没有使用的时候一般都是关闭的,因为可以节约功耗,当需要使用的时候就需要将其时钟打开才能进行初始化配置,在文件stm32f4xx_rcc.c中的最后三分之一中根据函数的注释可以知道AHB1,AHB2,AHB3,APB1,APB2分别连接了哪些外设,如下AHB1所示:

      可以看出GPIO端口全部都属于AHB1总线,如需要使用端口A时,需要先打开端口A的时钟进行才能进行初始化,如下所示

          RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    

    更换时钟源的代码

          //选择PLL作为系统时钟源
          RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
          RCC->CFGR |= RCC_CFGR_SW_PLL;
    
          //选择HSI作为系统时钟源
          RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
          RCC->CFGR |= RCC_CFGR_SW_HSI;
    
          //选择HSE作为系统时钟源
          RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
          RCC->CFGR |= RCC_CFGR_SW_HSE;
    

    总结

      1.时间配置是很重要的,很多外设都会涉及到时间的计算,如果一开始配置的时间和我们预期的不符合,那将发生很严重的错误;
      2.时钟树需要理解看明白,因为其中涉及了全部外设的时钟来源,和时钟配置方法;
      3.使用一个外设时不要忘记,需要打开它的时钟。

    相关下载

    链接:https://pan.baidu.com/s/10gTR5-5cFMnb9XZZmtxjsQ 
    提取码:nfsj 
    复制这段内容后打开百度网盘手机App,操作更方便哦
    
  • 相关阅读:
    ASP.NET中无刷新分页
    Http之基础
    ASP.NET中刷新分页
    SQL之Case when 语句
    在线HTML编辑器
    Highcharts 统计图
    Leetcode练习(Python):数组类:第162题:峰值元素是指其值大于左右相邻值的元素。 给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。 数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。 你可以假设 nums[-1] = nums[n] = -∞。
    Leetcode练习(Python):数组类:第154题:假设按照升序排序的数组在预先未知的某个点上进行了旋转。 ( 例如,数组&#160;[0,1,2,4,5,6,7] 可能变为&#160;[4,5,6,7,0,1,2]&#160;)。 请找出其中最小的元素。 注意数组中可能存在重复的元素。
    Leetcode练习(Python):数组类:第153题:假设按照升序排序的数组在预先未知的某个点上进行了旋转。 ( 例如,数组&#160;[0,1,2,4,5,6,7] 可能变为&#160;[4,5,6,7,0,1,2]&#160;)。 请找出其中最小的元素。 你可以假设数组中不存在重复元素。
    Leetcode练习(Python):数组类:第152题:给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字)。
  • 原文地址:https://www.cnblogs.com/ding-ding-light/p/14405696.html
Copyright © 2011-2022 走看看