2021/10/13 智慧傢俱 嵌入式實訓 第三天 時鐘 (3)
時鐘系統框圖
觀察上圖時鐘系統框圖,可知道:
藍色矩形:表示時鐘振盪源(5個):HSI RC、HSE Osc、PLL(鎖相環、倍頻器)、LSE Osc、LSI RC
H:快速、L:低速、S:速度、I:內部、E:外部
灰色梯形:表示選擇器:通過不同選擇器的選擇,SYSCLK系統時鐘、RTCCLK實時時鐘、IWDGCLK獨立看門狗時鐘、USBCLK USB時鐘可有多種選擇。
黃色矩形css:表示時鐘監視系統:監控若時鐘出錯,則自動切換為HSI
白色小矩形:OSC_OUT、OSC_IN表示外接時鐘訊號。MCO輸出內部時鐘的引腳PA8。
綠色矩形:表示分頻器
總結:
作用是降低功耗,降低cpu佔用率
STM32 有5個時鐘源:HSI、HSE、LSI、LSE、PLL。
①、HSI是高速內部時鐘,RC振盪器,頻率為8MHz,精度不高。
②、HSE是高速外部時鐘,可接石英/陶瓷諧振器,或者接外部時鐘源,頻率範圍為4MHz~16MHz。
③、LSI是低速內部時鐘,RC振盪器,頻率為40kHz,提供低功耗時鐘。WDG
④、LSE是低速外部時鐘,接頻率為32.768kHz的石英晶體。RTC
⑤、PLL為鎖相環倍頻輸出,其時鐘輸入源可選擇為HSI/2、HSE或者HSE/2。 倍頻可選擇為2~16倍,但是其輸出頻率最大不得超過72MHz。
系統時鐘SYSCLK可來源於三個時鐘源:
①、HSI振盪器時鐘
②、HSE振盪器時鐘
③、PLL時鐘
STM32可以選擇一個時鐘訊號輸出到MCO腳(PA8)上,可以選擇為PLL 輸出的2分頻、HSI、HSE、或者系統時鐘。
任何一個外設在使用之前,必須首先使能其相應的時鐘。
幾個重要的時鐘:
①、SYSCLK(系統時鐘)
②、 AHB匯流排時鐘
③、 APB1匯流排時鐘(低速): 速度最高36MHz
④、 APB2匯流排時鐘(高速): 速度最高72MHz
⑤、 PLL時鐘
RCC相關配置暫存器
stm32f10x.h中可找到以下結構體。
typedef struct { __IO uint32_t CR; //HSI,HSE,CSS,PLL等的使能和就緒標誌位 __IO uint32_t CFGR; //PLL等的時鐘源選擇,分頻係數設定 __IO uint32_t CIR; // 清除/使能 時鐘就緒中斷 __IO uint32_t APB2RSTR; //APB2線上外設復位暫存器 __IO uint32_t APB1RSTR; //APB1線上外設復位暫存器 __IO uint32_t AHBENR; //DMA,SDIO等時鐘使能(外設) __IO uint32_t APB2ENR; //APB2線上外設時鐘使能(外設) __IO uint32_t APB1ENR; //APB1線上外設時鐘使能(外設) __IO uint32_t BDCR; //備份域控制暫存器 __IO uint32_t CSR; //控制狀態暫存器 } RCC_TypeDef;
RCC相關檔案和韌體庫原始檔
標頭檔案:stm32f10x_rcc.h、原始檔:stm32f10x_rcc.c
systemInit()函式詳細解讀
開啟專案中:system_stm32f10x.c下面的system_stm32f10x.h可找到systemInit()函式。
也可直接從system_stm32f10x.c中找到systemInit()函式的相關定義。
再開啟STM32參考手冊6.3小結中,可知時鐘控制暫存器(RCC_CR)的相關應用。
根據參考手冊中關於暫存器CR的介紹可知,CR是32位暫存器。做要開啟振盪源HSI RC需要對CR暫存器中最後一位置1,即下面程式碼的操作:
/* Set HSION bit */ RCC->CR |= (uint32_t)0x00000001;
相當於位置0置1 開啟
因為我們使用的大容量,末尾為HD,故下面這幾段程式碼並不會執行:
#ifndef STM32F10X_CL RCC->CFGR &= (uint32_t)0xF8FF0000; #else RCC->CFGR &= (uint32_t)0xF0FF0000; #endif /* STM32F10X_CL */
重置HSEON, CSSON and PLLON 三個位,根據下面程式碼把十六進位制轉換為二進位制可知:Ob1111 1110 1111 0110 1111 1111 1111 1111 ,可知通過位與操作可將16位、19位、24位(HSEON, CSSON and PLLON ).即對應的三位重置為0。
/* Reset HSEON, CSSON and PLLON bits */ RCC->CR &= (uint32_t)0xFEF6FFFF;
重置HSEBYP為0.(設定為0相當於關閉)
/* Reset HSEBYP bit */ RCC->CR &= (uint32_t)0xFFFBFFFF
暫存器CFGR中重置PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE為0.
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */ RCC->CFGR &= (uint32_t)0xFF80FFFF;
根據主機板的型號,執行對應位置的程式碼,我們用的是HD,故應該執行#else下的語句,將所有的中斷都清理掉。
#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
接下來呼叫這個函式,可以“Go To Definition of" SetSysClock();"”
/* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */ /* Configure the Flash Latency cycles and enable prefetch buffer */ SetSysClock();
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) */ }
這邊是根據巨集定義是哪個,就執行裡面的語句,可以接著從中隨便“Go To Definition of" xxx;"”選擇一個檢視,可追溯到以下程式:
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL) /* #define SYSCLK_FREQ_HSE HSE_VALUE */ #define SYSCLK_FREQ_24MHz 24000000 #else /* #define SYSCLK_FREQ_HSE HSE_VALUE */ /* #define SYSCLK_FREQ_24MHz 24000000 */ /* #define SYSCLK_FREQ_36MHz 36000000 */ /* #define SYSCLK_FREQ_48MHz 48000000 */ /* #define SYSCLK_FREQ_56MHz 56000000 */ #define SYSCLK_FREQ_72MHz 72000000 #endif
這邊根據需要,選擇不同的頻率,不用的頻率要相應的註釋掉。根據這邊,我們知道這邊選擇的是:SYSCLK_FREQ_72MHz
,所以就會執行的函式就是: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); /Go To 後,可知是對第20位進行置1,即開啟外部高速時鐘/ /等待對應的時鐘源穩定,這邊使用do while迴圈實現,判斷對應位暫存器的數值是否為1/ /* Wait till HSE is ready and if Time out is reached exit */ do { HSEStatus = RCC->CR & RCC_CR_HSERDY; /判斷RCC_CR_HSERDY(17位)的數值是否為1/ StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET) /若這邊就緒了則執行0x01賦值/ { HSEStatus = (uint32_t)0x01; } else { HSEStatus = (uint32_t)0x00; } if (HSEStatus == (uint32_t)0x01) { /* Enable Prefetch Buffer */ FLASH->ACR |= FLASH_ACR_PRFTBE; /關於flash的設定,要參考STM32參考手冊下的STM32FLASHxxxxx裡面的STM32F10xxx快閃記憶體程式設計參考手冊/ /* Flash 2 wait state */ FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY); FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; //2個等待狀態/ /* HCLK = SYSCLK *//根據判斷這兩個值是否相等來知道這邊分頻的係數(倍率),檢視手冊中關於暫存器CFGR/ 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 */ /系統時鐘切換成PLL的來源/ 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 */ /第24位,看手冊/ 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 */ } } #endif
為什麼我們的main函式中並沒有關於時鐘相關的初始化,但是確能實現對時鐘的初始化呢?選擇工程中的CORE資料夾下的startup_stm32f10x_hd.s的檔案,我們可以找到:
Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
這個應該是彙編語句吧,看不懂(官方視訊也說看不懂)。但是應該是實現的先執行SystemInit函式,然後在執行main函式,這就證明了為什麼main函式裡面不需要對時鐘的初始化。