深度剖析STM32时钟系统

电子说

1.2w人已加入

描述

 下图是STM32的时钟树。从树上我们可以看到,STM32的时钟有两个来源——内部时钟和外部时钟。按时钟频率来分,又分为高速时钟和低速时钟。所以STM32的时钟有四个来源——高速外部时钟信号(HSE)、低速外部时钟信号(LSE)、高速内部时钟信号(HSI)和低速内部时钟信号(LSI),图中分别用蓝色的①~④标注。
STM32

①HSE高速外部时钟:由外部4~16MHz的晶体或有源晶振提供,通常采用8MHz,ST三合一板上的也是8MHz。

②LSI低速外部时钟:外部晶体提供,主要是给实时时钟(RTC),一般为32.768kHz。

③HSI高速内部时钟:由内部RC振荡器产生的8MHz时钟,但不够稳定。

④LSI低速内部时钟:内部RC振荡器产生的供给RTC的时钟,频率在30kHz~60kHz之间,通常约40kHz。

时钟在STM32内部最终是供给四大块,图中用红色椭圆圈出——USB的48MHz时钟、系统时钟SYSCLK、实时时钟模块RTC、独立看门狗的时钟IWDGCLK。其中最主要的,也是最大头是系统时钟SYSCLK,它可以是内部或外部高速时钟直接接过来,也可以内、外部高速时钟是PLL倍频后提供的,系统时钟再分别供给Cortex内核、SDIO、AHB总线、DMA、APB1、APB2等。

我们通常是采用外部8MHz高速时钟(HSE),所以着重说HSE。我们以前面的GPIO上的时钟为例,由ST的Datasheet可知,GPIO是在APB2高速外设总线上的,图中绿色的线就是时钟的流程,我们一步步地来看。

8MHz外部晶体(或晶振)输入后,先经过一个开关PLLXTPRE(HSE divider for PLL entry),此开关决定对HSE进行2分频再输入到PLL或直接到PLL。我们选择不分频。

这样时钟又到了第二个开关PLLSRC(PLL entry clock source),此开关决定PLL的时钟来源,是内部高速时钟二分频的时钟还是PLLXTPRE的输出。我们选择后者,这时的时钟在进入PLL前还是8MHz,因为在PLLXTPRE我们没有分频。

到了PLL倍频器,由PLLMUL决定倍频系统数,可以选择2~16倍频输出,但记住,PLL输出频率最高72MHz,所以我们选择9倍频,这样PLL输出就是最高72MHz的PLLCLK时钟了。这时的PLLCLK为USB提供时钟。

开关SW来决定SYSCLK的时钟来源,前面已经提到,这里我们由PLLCLK做为SYSCLK的来源,这样系统时钟SYSCLK就是72MHz了。

在供给外设前,先经过AHB预分频,我们选择不分频;在供给GPIO前,还要再经过APB2预分频,因为APB2为高速外设,所以我们选择不分频,这样GPIO的时钟就是72MHz了。注意,低速外设APB1最高频率为36MHz,所以在使用APB1的外设时,要注意设置好分频系统。还要注意,要使用外设,先要对外设时钟进行使能,见图中黄色云形框。这是因为STM32采用了低功耗的设计,对不使用的外设,其时钟不使能,以达到降低功耗的效果。

时钟的设置在程序中是怎么来实现的呢?这里我们以前面GPIO的程序来一步步分析。当然,前面的程序是基于ST库的,其实也就是分析ST的官方库了。

我们看到main()函数中的第一行代码是调用了一个函数:

SystemInit();

这个函数是在system_stm32f10x.c中:

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

}

我们可以看到,程序前面一系统Reset或Disable,最后调用了(红色标记出来)

SetSysClock();

SetSysClock()函数也位于system_stm32f10x.c源文件中:

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) */

}

因为我们使用的是72MHz时钟,那肯定我们定义了宏SYSCLK_FREQ_72MHz,所以才调用的函数SetSysClockTo72()。我们继续跟踪,会发现在源文件开头,我们的确定义了:

#define SYSCLK_FREQ_72MHz  72000000

在SetSysClockTo72()函数中详细地对72MHz进行设置,这里就不一步步分析。

前面已经提到,在使用外设时,要使能相应的外设时钟,例如,在使用GPIOB进行流水灯实验时,程序中调用了下面的库函数,对外设时钟进行设置:

RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);

另外,STM32还可以把时钟输出,如图左下角的咖啡色方框里面,可以由MCO决定,PLL时钟二分频或HIS或HSE或系统时钟作为主时钟输出。


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分