新到一家公司后,有個項目要用到STM32F207Vx單片機,找到網上的例子照貓畫虎的寫了幾個例子,比如ADC,可是到了ADC多通道轉換的時候就有點傻眼了,這里面的時鍾跑的到底是多少M呢?單片機外掛的時鍾是25M,由於該單片機時鍾系統較為復雜,有內部高/低、外部高/低 、PLL鎖相環時鍾,又有AHB總線時鍾、APB1/2時鍾,而例子中很少講到系統時鍾的默認配置是怎么配置呢?那么就發點時間研究下這個單片機內部的復雜時鍾系統吧。
下圖是STM32F2系列的時鍾樹結構圖:
1、內部高速時鍾HSI、外部高速時鍾HSE和PLL時鍾PLLCLK時鍾都接到了SW開關處,通過SW選擇哪一路作為SYSCLK,SYSCLK經過AHB分頻器進行分頻得到HCLK,APB1和APB2是掛在總線AHB上的,通過APB1和APB2分頻得出fpclk1和fpclk2。
2、PLL輸入時鍾源主要是靠外部高速時鍾和內部高速時鍾作為時鍾源,通過PLLCFGR寄存器的bit22來選擇具體哪一路作為時鍾源。選擇好了時鍾源進入/M分頻器,也就是PLLM進行分頻,送入VCO,在通過xN,進行倍頻,也就是PLLN:(1)通過/P進行分頻(PLLP)得到PLLCLK;(2)通過/Q分頻(PLLQ),得到PLL48CK。
然后邊看代碼邊對照結構圖進行分析,看軟件如何給單片機配置系統時鍾的。
然后找到啟動代碼“startup_stmf32xx.s”,該代碼是用匯編寫的,可以看到,在調用main函數之前,是先調用了SystemInit函數的,該函數是在“system_stm32f2xx.c”中
; Reset handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT SystemInit IMPORT __main LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
代碼如下,變量直接賦個16進制的數,都不知道是啥意思,目的是干什么的,不知道,所以看下面代碼時最好對照STM32F2x用戶手冊。當然這個只是一個初始化,待會主要看SetSysClock();這個函數,在調用該函數之前,我們知道單片機是先啟用了內部高速時鍾等一些配置。
void SystemInit(void) { /* Reset the RCC clock configuration to the default reset state ------------*/ /* Set HSION bit */ RCC->CR |= (uint32_t)0x00000001; //RCC_CR復位值0x0000_xx83,內部高速時鍾使能,也就是說上電開始就使用內部高速時鍾,16MHZ /* Reset CFGR register */ RCC->CFGR = 0x00000000; //通過開關SW選擇內部高速時鍾作為系統時鍾16MHZ //AHB prescaler 不分頻 //APB Low speed prescaler (APB1) 不分頻,fplck1 = 16MHZ //APB high-speed prescaler (APB2)不分頻,fplck2 = 16MHZ //MCO1和MCO2時鍾輸出等配置可參考用戶手冊Page95 /* Reset HSEON, CSSON and PLLON bits */ RCC->CR &= (uint32_t)0xFEF6FFFF; /* Reset PLLCFGR register */ RCC->PLLCFGR = 0x24003010; //RCC_CFGR復位值是0x2400_3010 /* Reset HSEBYP bit */ RCC->CR &= (uint32_t)0xFFFBFFFF; //對bit18 HSEBYP 設置為0,外部高速時鍾被禁止 /* Disable all interrupts */ RCC->CIR = 0x00000000; //所有時鍾中斷都被禁止 #ifdef DATA_IN_ExtSRAM SystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM */ /* Configure the System clock source, PLL Multiplier and Divider factors, AHB/APBx prescalers and Flash settings ----------------------------------*/ SetSysClock(); /* Configure the Vector Table location add offset address ------------------*/ #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 }
在SystemInit(void)函數中在配置完一些參數后,還調用了SetSysClock()函數,該函數代碼如下
static void SetSysClock(void) { /******************************************************************************/ /* PLL (clocked by HSE) used as System clock source */ /******************************************************************************/ __IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* Enable HSE */ RCC->CR |= ((uint32_t)RCC_CR_HSEON); //外部高速時鍾使能,25MHZ /* Wait till HSE is ready and if Time out is reached exit */ //外部時鍾使能后,得需要一點時間到達各個端口 do { HSEStatus = RCC->CR & RCC_CR_HSERDY; //如果RCC_CR_HSERDY為0,說明外部時鍾還沒准備好,1說明外部時鍾已准備好 StartUpCounter++;//對讀的次數進行累加,當累加次數到達1280次時,就意味着啟動時間超時 } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET)//這里判斷上面的do while循環是因哪個條件結束的 { HSEStatus = (uint32_t)0x01; //說明時鍾已准備好了,才結束do whlie循環 } else { HSEStatus = (uint32_t)0x00; //說明是因為超時了而退出do while循環 } if (HSEStatus == (uint32_t)0x01) { /* HCLK = SYSCLK / 1*/ RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB不分頻,AHB出來后時鍾就是sysclk=120M /* PCLK2 = HCLK / 2*/ RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;//APB2 2分頻,fpclk2 = sysclk/2 = 60M /* PCLK1 = HCLK / 4*/ RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 4分頻,fplck1 = sysclk/4 = 30M /* Configure the main PLL */ //主要對PLL和PPI2S 進行配置 RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24); //配置完后RCC->PLLCFGR = 0x05403c19,然后對照寄存器RCC_PLLCFGR查看哪些位對應哪些功能 //通過bit5~bit0可計算出PLLM=25,那么input VCO = PLL input clock /PLLM = 25M/25 = 1M,對應時鍾結構圖中的/M //通過bit14~bit6可計算出倍頻因子PLLN = 240,那么VCO output clock = PLLN * input VCO = 240 * 1 = 240M,對應時鍾結構圖中的xN //bit17~bit16可計算出分頻因子PLLP = 2,那么PLLCLK = VCO output clock /PLLP = 240/2 = 120M //bit22是選擇給PLL輸入的時鍾源,輸入時鍾源有外部和內部高速時鍾,這里選擇的是外部高速時鍾即PLL input clock = HSE =25M //bit27~24可計算出分頻因子PLLQ = 5,那么PLL48CK = VCO output clock/PLLQ = 240/5 = 48M /* Enable the main PLL */ RCC->CR |= RCC_CR_PLLON; //使能PLL /* Wait till the main PLL is ready */ while((RCC->CR & RCC_CR_PLLRDY) == 0) { } //到這里RCC->CR已配置完,最終值0x03036783 //通過查看用戶手冊知道,內部高速時鍾、外部高速時鍾、PLL時鍾都已開啟 /* Configure Flash prefetch, Instruction cache, Data cache and wait state */ FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_3WS; /* Select the main PLL as system clock source */ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC->CFGR |= RCC_CFGR_SW_PLL; //到這里RCC->CFGR已配置完,最終值是0x0000_940A //通過查看用戶手冊,知道,PLL時鍾作為系統時鍾即120M //AHB不分頻,即HCLK = 120M //APB1 4分頻,即fpclk1 = 120/4=30M //APB2 2分頻,即fpclk2 = 120/2=60M /* Wait till the main PLL is used as system clock source */ while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL); { } } 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 */ } }
OK,分析完這段代碼后,調用系統固件函數后,現在知道了時鍾樹結構圖中右邊出來的時鍾是多少MHz了吧。
總結:
1、使用的是外部時鍾25MHZ,通過PLL進行分頻倍頻分頻得到PLLCLK 120M,PLLCLK作為系統時鍾SYSCLK。
2、APB1出來是30M,也就是FPCLK1。
3、APB2出來是60M,也就是FPCLK2。