STM32F4步步为营二:系统时钟

作者: ddllxxrr
上传时间为: 2014-07-18 09:48 PM
2014-07-18
阅读:

STM32F429 Discovery开发板,集成了ST_LINK2和2.4寸的TFT LCD彩屏,可以为学习开发带来不少的方便。此外,还有64Mbits SDRAM,ST MEMS,LED,按键及USB OTG micro-B接口。本文将介绍STM32F4的系统时钟编程。

系统时钟,单片机的时钟好比人们的心脏的脉波,没有脉了人就完了。同样,单片机没有系统时钟了基本上就是废片一块。

STM32F429的时钟必须搞明白才能编程为我所用。

以下是系统时鈡的框图:

      

看上去很复杂。现在一点一点的剖释。SYSCLK有三种不同的时钟可以驱动

  • HIS内部高速时钟
  • HSE外部高速时钟
  • PLL

还有两个辅助的时钟源,分别是

  • LSI内部RC低速时钟32KHz主要驱动内部看门狗和RTC电路
  • LSE外部RC低速时钟32.768主要用于驱动RTC时钟

大部分外设都在SYSCLK下工作。除了以下一些时钟:

  • • USB OTG FS 时钟 (48 MHz)、random analog generator (RNG)时钟(<= 48 MHz)和SDIO时钟 (<= 48 MHz)来自PLL的输出PLL48CLK
  • I2S clock,可以从PLL(PLLI2S)或者一个映射到I2S_CKIN管脚的外部时钟得到。
  • SAI1 clock,可以从PLL (PLLSAI or PLLI2S)或从外部时钟映射管脚I2S_CKIN得到。PLLSAI 能被用作SAI1设备的时钟,在这种情况下PLLI2S可以被用作另外的音频(49.152 MHz or11.2896 MHz),并且两个频率同时发生。
  • LTDC clock,LTDC时钟从PLL (PLLSAI)产生。
  • The USB OTG HS (60 MHz) 时钟是由外部PHY提供的。
  • The Ethernet MAC clocks (TX, RX and RMII) 时钟是由外部PHY提供的。当Ethernet被用的时候AHB时钟必须达到25MHZ

要想平时顺利操作时钟,就必须了解几个寄存器:

其中一个是RCC PLL配置寄存器RCC_PLLCFGR(RCC PLL configuration register),其控制着频率的生成。

  • • f(VCO clock) = f(PLL clock input) × (PLLN / PLLM)
  • • f(PLL general clock output) = f(VCO clock) / PLLP
  • • f(USB OTG FS, SDIO, RNG clock output) = f(VCO clock) / PLLQ

大家看到上边的公式,也许会发愁,这是怎么回事,其实只要细细研究下就会非常明白:

RCC_PLLCFGR寄存器如下:

  • Bits(27:24): PLLQUSB OTG FS、SDIO 和隨机数字发生器的分频值,在PLL无效时才能改写。USB OTG FS需要48 MHz 时钟才能正确工作. SDIO和隨机数字发生器需要比48MHz小或者等于48MHz时才能工作。Bits(27:24)值为2到15。
  • Bit 22:PLLSRC,主PLL(PLL) 和音频PLL (PLLI2S) 的时钟源。当PLL和PLLI2S没使能的情况下可改写,0为内部高速时钟被选择;1为外部晶振时钟被选择。
  • Bits(17:16):PLLP,主PLL (PLL) 分频为系统时钟的分频值。当PLL没有使能时改写。注意该值不应超过180MHz,PLL 输出时钟 = VCO 频率 / PLLP (PLLP = 2, 4, 6, or 8)。
  • Bits(14:6):PLLN,主PLL (PLL)乘积因子,VCO的乘积因子,可以软件清除和设置。这个只有两种选项192MHzt和432MHz,输入频率最小为1MHzVCO output frequency = VCO input frequency × PLLN with 192 MHz或432MHz。
  • Bits(5:0):PLLM,主 PLL (PLL) 和音频PLL (PLLI2S) 输入时钟的分频因子,这个是保证在输入VCO的频率在1到2MHz之间,它的值为2到63。VCO input frequency = PLL input clock frequency / PLLM with (2-63)

另一个寄存器是:RCC时钟控制 寄存器RCC_CR(RCC clock control register )

  • Bit29:PLLSAIRDY,PLLSAI 时钟锁定标志
  • Bit28:PLLISAION,PLLSAI有效
  • Bit27:PLLI2SRDY,PLLI2S准备好标志
  • Bit26:PLLI2SON,PLLI2S 有效
  • Bit25:PLLRDY,主PLL (PLL) clock 准备好标志
  • Bit24:PLLON,主PLL (PLL)使能

还有一个寄存器是:RCC时钟配置寄存器RCC_CFGR(RCC clock configuration register)

里边有APB1,APB2,AHB 各个总线的分频因子。

由此,我们不难读懂如下函数:

static void SetSysClock(void)
    {
    /******************************************************************************
    * PLL (clocked by HSE) used as System clock source *
    ******************************************************************************/
    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
    #ifdef PLL_SOURCE_HSI //如果时钟源定义为内部低速时钟
           RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |(RCC_PLLCFGR_PLLSRC_HSI) | (PLL_Q << 24);
    #else
           RCC->CR |= ((uint32_t)RCC_CR_HSEON); //定义时钟源为内部高速时钟
    #ifdef PLL_SOURCE_HSE_BYPASS //如果定义外部时钟为 BYPASS模式
           RCC->CR |= ((uint32_t)RCC_CR_HSEBYP);
    #endif
         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)
      {
        //如果外部时钟稳定就开始配置主时钟
        RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                      (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
      }
        else
      {
        //否则就表示外部时钟出了问题,用户可以自己加些代码处理这个问题
      }
    #endif
        //设置系统频率为180MHZ
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
        PWR->CR |= PWR_CR_VOS;
    //设置HCLK频率
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
        //设置PCLK2 = HCLK / 2
        RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
         //设置PCLK1 = HCLK / 4
        RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
        //使能PLL
        RCC->CR |= RCC_CR_PLLON;
       //等待PLL准备好
        while((RCC->CR & RCC_CR_PLLRDY) == 0)
        {
        }
        //设置扩展时钟为180MHZ
        PWR->CR |= PWR_CR_ODEN;
        while((PWR->CSR & PWR_CSR_ODRDY) == 0)
        {
        }
        PWR->CR |= PWR_CR_ODSWEN;
        while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
        {
        }
         //设置FLASH,预取指令缓冲和数据缓冲和等待状态
         FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
        //选取主PLL时钟为系统时钟源
         RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= RCC_CFGR_SW_PLL;
    //等待时钟稳定
        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
        {
        }
    }
	
全部评论 ()
条评论
写评论

创建讨论帖子

登录 后参与评论
系统提示