热度 5
2013-4-7 17:33
4214 次阅读|
2 个评论
上图是 STM32 的时钟树。从树上我们可以看到, STM32 的时钟有两个来源——内部时钟和外部时钟。按时钟频率来分,又分为高速时钟和低速时钟。所以 STM32 的时钟有四个来源——高速外部时钟信号( HSE )、低速外部时钟信号( LSE )、高速内部时钟信号( HSI )和低速内部时钟信号( LSI ),图中分别用蓝色的 ①~④ 标注。 ①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 或系统时钟作为主时钟输出。