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函數里面不需要對時鍾的初始化。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM