1. 程式人生 > >關於嵌入式學習隨筆->4《F7系統時鐘》

關於嵌入式學習隨筆->4《F7系統時鐘》

proc 很慢 控制 隨筆 reserve add 定時器 pread none

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 */
4 __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;
結構體: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系統時鐘》