简介:
最近在学习时钟树时,顺便看了一下启动文件,启动文件涉及到汇编和很多底层堆栈,暂时不做过多的记录,主要是记录RCC时钟树,是怎么配置PLL、System、AHB、APB2、APB1总线和外设时钟的。
我们的目标是:了解stm32f103外接8M晶振时,系统上电后调用SystemInitSet将PLLCLK、SYSCLK、AHBCLK、APBCLK2的时钟初始化为72MHZ,APBCLK1的时钟初始化为SYSCLK的一半即36MHZ的这个过程。
这个工作在SystemInitSet->SysClock()->SetSysClockTo72()中已经完成,我们在此处只是学习他到底是如何初始化的。即main函数执行之前,系统一上电之后就会先执行SystemInitSet完成总线时钟初始化,然后再调用main函数,后续我们在main函数中只需要开启外设的时钟即可。下面先讲述启动文件,再讲SystemInit函数。
一、stm32f10x启动文件startup_stm32f10x_hd.s
1.在startup_stm32f10x_hd.s文件的114行有如下代码
单片机上电复位之后,会先执行此处Reset_Hander的汇编代码,然后调用SystemInit函数初始RCC时钟,然后调用__main函数,最后调用我们的main()函数。SystemInit函数里面就执行了初始化RCC的命令。
SystemInit函数在system_stm32f10x.c中实现,SystemInit调用了SetSysClock()调用了SetSysClockTo72()。SetSysClockTo72()函数将SystemCLK初始化到72M。
二、stm32f10x的RCC时钟树
要弄懂RCC是怎么初始化总线和外设时钟的,需要查看《stm32f10x参考手册》里面RCC章节的时钟树和RCC寄存器章节的控制寄存器和配置寄存器(CR和CFG)。
1.时钟树
重点关注图中的①②③④⑤⑥⑦部分,看图从左往右看,从①依次看到⑦。stm32一共四个时钟源,外部高速时钟、外部低速时钟、内部高速时钟、内部低速时钟。但是我们常用外部高速时钟HSE,即①号处外接两个电容+8M晶振。
野火的stm32f103指南者开发板也是外接8M晶振,然后调用SystemInit函数初始化RCC的,图中PLLXTPRE、PLLSRC、PLLMUL、SW、AHB、APB1、APB2均是RCC的配置寄存器里面某个位的名称,具体作用需要看《参考手册》,下面我从手册中截图如下。下面的配置是按照我们的目标来配置的。
即PLLCLK=SysCLK=AHBCLK = APBCLK2 = 72MHZ,APBCLK1 = SysCLK/2 = 36MHZ。
2.参考手册中,对RCC控制寄存器定义如下。
对应的各位的解释如下(此处截图野火提供的中文版参考手册,上图寄存器定义是截的英文手册的图)
①PLLXTPRE:在①处可以看到PLLXTPRE可以将HSE二分频或者不分,此处我们的HSE是8M,我们选择不分频。故我们需要将PLLXTPRE置0.
②PLLSRC:在②处,我们选择HSE还是HSI作为PLL的输入时钟,此处我们选择HSE,故我们需要将PLLSRC置为1。
③PLLMUL:③处是将HSE频率放大多少倍,我们选择放大9倍,则PLLCLK等于8*9=72MHZ。我们需要将PLLMUL位置为0111.
④SW:④处sw的可以选择HSI、HSE、PLLCLK作为SYSCLK的时钟源,我们选择常规PLLCLK,故我们要将SW为置为10.另外SWS是我们置了SW位之后,用来读取的,看SYSCLK时钟源选择是否成功的标志位,是只读的。
⑤AHB预分频器:可以对SYSCLK进行分频,但是往往我们不分频。即把HPRE置为0000.故SYSCLK = PLLCLK = 72M。
⑥APB1预分频器:由于APB1是低速总线,我们常常将它设置为SYSCLK的一半,即AP1CLK = 72/2=36MHZ.我们将PPRE1置为100.
⑦APB2预分频器:由于APB2是低速总线,我们常常将它设置为SYSCLK一样,即APCLK2 = SYSCLK = 72MHZ.我们将PPRE2置为000.
3.systemInit->SetSysClock->SetSysClockTo72源码实现如下:
//1.上电后调用SystemInit 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 } //2.SystemInit调用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) */ } //3.SetSysClock()调用SetSysClockTo72() 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/2 */ 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 */ } }
三、总结
1.原来没有看懂RCC时钟树的组织图,感觉里面名字好多,没耐心看
2.其实归根结底还是去配置寄存器,但是参考手册看着太抽象,所以刚开始最好是跟着教程,然后根据教程自己学着看参考手册,最后就是参考源码的实现。
3.然后自己参照着源码写一遍,或者以HSI作为时钟源,自己来配置SYSCLK。