關於嵌入式學習隨筆->4《F7系統時鐘》
1、STM32有5個時鐘源:HSI、HSE、LSI、LSE、PLL。
--》HSI是高速內部時鐘,RC振蕩器,頻率為16MHz,精度不高。可以直接作為系統時鐘或者用作PLL時鐘輸入。
--》HSE是告訴外部時鐘,可接石英/陶瓷諧振器,或者接外部時鐘源,頻率範圍為4MHz~26MHz。
--》LSI是低速內部時鐘,RC振蕩器,頻率為32KHz,提供低功耗時鐘。LSI主要可以作為IWDG獨立看門狗時鐘,LPTimer低功耗定時器時鐘以及RTC時鐘。
--》LSE是低速外部時鐘,接頻率為32.768KHz的石英晶體。為RTC提供精準的時鐘頻率。
--》PLL為鎖相環倍頻輸出。
STM32F767內部時鐘最快可達216MHz,這就是用的PLL倍頻輸出。其實尤其時鐘電路可以知道,系統時鐘由HSI、HSE、PLL提供,如果HSE出現故障,那麽就會由HSI提供系統時鐘源,雖然很慢,但是仍然可以是系統正常運行。下邊是STM32F767的時鐘樹,很用以理解。
主PLL時鐘計算:PLCK = HSE * N / ( M * P ),這個公式可以由電路圖導出。(一般用的是外部告訴時鐘,xN倍頻一般為50~432,R分頻系數一般為2~7,Q分頻系數一般為2~15)
2、任何一個外設在使用前必須首先要使能相應的時鐘
其實任何一個電路的邏輯控制器都是通過一個寄存器來控制的。RCC控制時鐘的相關寄存器定義在RCC_TypeDef中。
1 typedef struct 2 { 3 __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */結構體:RCC_TypeDef4 __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register, Address offset: 0x04 */ 5 __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ 6 __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */7 __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ 8 __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ 9 __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ 10 uint32_t RESERVED0; /*!< Reserved, 0x1C */ 11 __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ 12 __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ 13 uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ 14 __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ 15 __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ 16 __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ 17 uint32_t RESERVED2; /*!< Reserved, 0x3C */ 18 __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ 19 __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ 20 uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ 21 __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */ 22 __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */ 23 __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */ 24 uint32_t RESERVED4; /*!< Reserved, 0x5C */ 25 __IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */ 26 __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */ 27 uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */ 28 __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */ 29 __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ 30 uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */ 31 __IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */ 32 __IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */ 33 __IO uint32_t PLLSAICFGR; /*!< RCC PLLSAI configuration register, Address offset: 0x88 */ 34 __IO uint32_t DCKCFGR1; /*!< RCC Dedicated Clocks configuration register1, Address offset: 0x8C */ 35 __IO uint32_t DCKCFGR2; /*!< RCC Dedicated Clocks configuration register 2, Address offset: 0x90 */ 36 37 } RCC_TypeDef;
3、時鐘系統初始化(程序角度HAL庫)
在STM32的啟動文件中都會由如下代碼段(我們稱之為引導代碼):
1 Reset_Handler PROC 2 EXPORT Reset_Handler [WEAK] 3 IMPORT SystemInit 4 IMPORT __main 5 6 LDR R0, =SystemInit 7 BLX R0 8 LDR R0, =__main 9 BX R0 10 ENDP;
很容易看出,在執行__main函數之前需要先執行SystemInit函數進行系統初始化,而不是我們自認為的會直接到
main函數開始執行(註意__main函數與main函數有區別,詳細區別參見之後的STM32之啟動文件講解,暫時未出),總之我們這裏只要知道在main函數執行之前系統要先執行SystemInit函數,其定義為:
1 void SystemInit(void) 2 { 3 /* FPU settings ------------------------------------------------------------*/ 4 #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) 5 SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ 6 #endif 7 /* Reset the RCC clock configuration to the default reset state ------------*/ 8 /* Set HSION bit */ 9 RCC->CR |= (uint32_t)0x00000001; 10 11 /* Reset CFGR register */ 12 RCC->CFGR = 0x00000000; 13 14 /* Reset HSEON, CSSON and PLLON bits */ 15 RCC->CR &= (uint32_t)0xFEF6FFFF; 16 17 /* Reset PLLCFGR register */ 18 RCC->PLLCFGR = 0x24003010; 19 20 /* Reset HSEBYP bit */ 21 RCC->CR &= (uint32_t)0xFFFBFFFF; 22 23 /* Disable all interrupts */ 24 RCC->CIR = 0x00000000; 25 26 /* Configure the Vector Table location add offset address ------------------*/ 27 #ifdef VECT_TAB_SRAM 28 SCB->VTOR = RAMDTCM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ 29 #else 30 SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */ 31 #endif 32 }
SystemInit函數主要有三個功能:
--》首先是對FPU的設置,關於浮點運算的設置。
--》其次就是對一些寄存器的默認配置,也就是將其設置為初始值。
其中 RCC->CR |= (uint32_t)0x00000001; /* Set HSION bit */ 目的就是設置系統默認時鐘為高速內部時鐘(HSI),因為在剛開始時系統不確定你是否使用了外部時鐘,在這裏會進行初試默認值的設置,包括其他寄存器,在這裏都有設置開始時的默認值。
--》最後是對中斷像量表的地址的設置,包括他的基地址和偏移地址。
HAL庫中沒有對於系統時鐘的設置,只是有個初始值,而在標準庫(M3,M4所使用的)中則實在系統初始化函數中對系統時鐘進行設置的,不需要用戶單獨去寫時鐘配置的函數。
HAL庫中系統時鐘配置的一般步驟(stm32_clock_init函數):
1—>使能PWR時鐘,調用:
1 /* Enable Power Clock*/ 2 __HAL_RCC_PWR_CLK_ENABLE();
2—>設置調壓器輸出電壓級別,調用:
1 /* Set Range */ 2 __HAL_PWR_VOLTAGESCALING_CONFIG(VoltageScaling);
3—>選擇是否開啟Over-Driver功能,調用:
1 HAL_PWREx_EnableOverDrive(); //開啟Over-Driver功能
4—>配置時鐘源相關參數,調用:
1 HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化
5—>配置系統時鐘源以及AHB、APB1和APB2的分頻系數,調用函數:
1 HAL_RCC_ClockConfig();
最主要的兩個配置函數:
1 HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct);//時鐘源配置函數 2 HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency);//時鐘配置函數
時鐘源配置函數入口參數時鐘源結構體定義:
1 typedef struct 2 { 3 uint32_t OscillatorType; /*!< The oscillators to be configured. 4 This parameter can be a value of @ref RCC_Oscillator_Type */ 5 6 uint32_t HSEState; /*!< The new state of the HSE. 7 This parameter can be a value of @ref RCC_HSE_Config */ 8 9 uint32_t LSEState; /*!< The new state of the LSE. 10 This parameter can be a value of @ref RCC_LSE_Config */ 11 12 uint32_t HSIState; /*!< The new state of the HSI. 13 This parameter can be a value of @ref RCC_HSI_Config */ 14 15 uint32_t HSICalibrationValue; /*!< The HSI calibration trimming value (default is RCC_HSICALIBRATION_DEFAULT). 16 This parameter must be a number between Min_Data = 0x00 and Max_Data = 0x1F */ 17 18 uint32_t LSIState; /*!< The new state of the LSI. 19 This parameter can be a value of @ref RCC_LSI_Config */ 20 21 RCC_PLLInitTypeDef PLL; /*!< PLL structure parameters */ 22 23 }RCC_OscInitTypeDef;
剛好有5個時鐘源:HSI、HSE、LSI、LSE、PLL,其中在時鐘源配置函數中要先對時鐘源類型,在進行相應的時鐘源配置,而不同的時鐘原配置所需要的操作是不同的,因此這裏就要先用時鐘源類型來進行判斷。要設置主PLL參數就又需要一個結構體來實現(RCC_PLLInitTypeDef ):
1 typedef struct 2 { 3 uint32_t PLLState; /*!< The new state of the PLL. 4 This parameter can be a value of @ref RCC_PLL_Config */ 5 6 uint32_t PLLSource; /*!< RCC_PLLSource: PLL entry clock source. 7 This parameter must be a value of @ref RCC_PLL_Clock_Source */ 8 9 uint32_t PLLM; /*!< PLLM: Division factor for PLL VCO input clock. 10 This parameter must be a number between Min_Data = 2 and Max_Data = 63 */ 11 12 uint32_t PLLN; /*!< PLLN: Multiplication factor for PLL VCO output clock. 13 This parameter must be a number between Min_Data = 50 and Max_Data = 432 */ 14 15 uint32_t PLLP; /*!< PLLP: Division factor for main system clock (SYSCLK). 16 This parameter must be a value of @ref RCC_PLLP_Clock_Divider */ 17 18 uint32_t PLLQ; /*!< PLLQ: Division factor for OTG FS, SDMMC and RNG clocks. 19 This parameter must be a number between Min_Data = 2 and Max_Data = 15 */ 20 #if defined (STM32F765xx) || defined (STM32F767xx) || defined (STM32F769xx) || defined (STM32F777xx) || defined (STM32F779xx) 21 uint32_t PLLR; /*!< PLLR: Division factor for DSI clock. 22 This parameter must be a number between Min_Data = 2 and Max_Data = 7 */ 23 #endif /* STM32F767xx || STM32F769xx || STM32F777xx || STM32F779xx */ 24 25 }RCC_PLLInitTypeDef;
這些程序代碼結合最上邊的時鐘樹就可以很容易的理解了,其中PLLM、PLLN、PLLP、PLLQ、PLLR與時鐘樹中主PLL中參數M、N、P、Q、R一一對應。
以下代碼為時鐘源配置相關代碼(HAL_RCC_OscConfig函數配置方法):
1 RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //時鐘源為HSE 2 RCC_OscInitStructure.HSEState=RCC_HSE_ON; //打開HSE 3 RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON; //打開PLL 4 RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE; //PLL時鐘源選擇HSE 5 RCC_OscInitStructure.PLL.PLLM=pllm; //主PLL和音頻PLL分頻系數(PLL之前的分頻) 6 RCC_OscInitStructure.PLL.PLLN=plln; //主PLL倍頻系數(PLL倍頻) 7 RCC_OscInitStructure.PLL.PLLP=pllp; //系統時鐘的主PLL分頻系數(PLL之後的分頻) 8 RCC_OscInitStructure.PLL.PLLQ=pllq; //USB/SDIO/隨機數產生器等的主PLL分頻系數(PLL之後的分頻) 9 ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化
時鐘配置函數相關代碼(HAL_RCC_ClockConfig函數配置方法):
1 //選中PLL作為系統時鐘源並且配置HCLK,PCLK1和PCLK2 2 RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2); 3 RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;//設置系統時鐘時鐘源為PLL 4 RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;//AHB分頻系數為1 5 RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV4;//APB1分頻系數為4 6 RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV2;//APB2分頻系數為2 7 ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_7);//同時設置FLASH延時周期為7WS,也就是8個CPU周期。
這樣就將系統時鐘配置好了,註意與時鐘樹相結合,更容易理解。
而要想達到最大時鐘頻率216MHz那麽就需要我們開啟Over-Driver的功能,還要設置調壓器的級別為1。這樣還沒有設置完全,還有一個等待周期的設置,一般電壓範圍確定後等待周期也就固定了,這個參數的配置其實就是時鐘入口參數的第二個值FLatency。
關於嵌入式學習隨筆->4《F7系統時鐘》