1. 程式人生 > >STM32系統時鐘預設設定

STM32系統時鐘預設設定

“我們一直都說STM32有一個非常複雜的時鐘系統,然而在原子或者野火的例程中,只要涉及到時鐘,我們卻只能看到類似的庫函式呼叫,如RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);這個僅僅只是起到開啟掛載在APB2線上的USART1時鐘的作用罷了,APB2的時鐘頻率是多少我們並不知道”

我們先來了解一些stm32的時鐘結構。

這裡寫圖片描述

這個圖說明了STM32的時鐘走向,從圖的左邊開始,從時鐘源一步步分配到外設時鐘。
從時鐘頻率來說,又分為高速時鐘和低速時鐘,高速時鐘是提供給晶片主體的主時鐘,而低速時鐘只是提供給晶片中的RTC(實時時鐘)及獨立看門狗使用。

從晶片角度來說,時鐘源分為內部時鐘與外部時鐘源 ,內部時鐘是在晶片內部RC振盪器產生的,起振較快,所以時鐘在晶片剛上電的時候,預設使用內部高速時鐘。而外部時鐘訊號是由外部的晶振輸入的,在精度和穩定性上都有很大優勢,所以上電之後我們再通過軟體配置,轉而採用外部時鐘訊號。

所以,STM32有以下4個時鐘源:
高速外部時鐘(HSE):以外部晶振作時鐘源,晶振頻率可取範圍為4~16MHz,我們一般採用8MHz的晶振。
高速內部時鐘(HSI): 由內部RC振盪器產生,頻率為8MHz,但不穩定。
低速外部時鐘(LSE):以外部晶振作時鐘源,主要提供給實時時鐘模組,所以一般採用32.768KHz。
低速內部時鐘(LSI):由內部RC振盪器產生,也主要提供給實時時鐘模組,頻率大約為40KHz。

時鐘頻率當然是可以通過程式碼來配置的,然而main函式中我們卻沒看到配置時鐘的相關程式碼。這說明了我們一直用的是預設的設定,也就是說,在進入main函式之前時鐘頻率就已經被設定好了。這時候,大家應該都已經想到了,就是stm32的啟動檔案,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

從這裡我們可以看到,我們的程式在進入到main函式之前,先要執行systeminit,跳轉到這個函式的定義。裡面的程式碼是對暫存器直接進行操作了,我查找了使用者手冊,暫存器的相關配置說明寫在了註釋裡面(IDE裡面文字設定為ANSI格式了,直接複製過來會出現亂碼,大家將就下看看圖片吧)

這裡寫圖片描述

這裡涉及到了兩個暫存器RCC_CR,與RCC_CFGR,分別是時鐘控制暫存器與時鐘配置暫存器,它們的作用顧名思義,就是起到了控制和配置時鐘的作用。想具體瞭解暫存器每一位的功能,請查閱使用者手冊。
這段程式碼實現了時鐘的初始化,也就是所謂的預設設定了。
執行完後,我們繼續往下走

  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();

我們可以看到,這裡還有一個函式,看它的命名,顯然就是對時鐘進行設定的函式。
我們看一下它的實現過程。

這裡寫圖片描述

因為我們並沒有這些巨集定義,所以相當於執行了一個nop函式。顯然,在這個程式中,這個函式並沒有起到改變時鐘配置的作用,我們用的還是初始化的預設配置。
到這裡,關於stm32系統時鐘的預設設定就已經結束了。
對照著時鐘樹,我們結合程式碼來回顧一下。

首先從左端的OSC_OUT和OSC_IN開始,假設我們外接的是8MHz的晶振,兩個引腳分別接到外部晶振的兩端。

8MHz的時鐘遇到了第一個分頻器PLLXTPRE(HSE divider for PLL entry),在這個分頻器中,可以通過暫存器配置,選擇它的輸出。它的輸出時鐘可以是對輸入時鐘的二分頻或不分頻。本例子中,我們選擇不分頻,所以經過PLLXTPRE後,還是了8MHz的時鐘。

8MHz的時鐘遇到開關PLLSRC(PLL entry clock source),我們可以選擇其輸出,輸出為外部高速時鐘(HSE)或是內部高速時鐘(HSI)。這裡選擇輸出為HSI(HSE經二分頻後變成了4MHz的時鐘),接著遇到鎖相環PLL,具有倍頻作用,在這裡我們可以輸入倍頻因子PLLMUL(PLL multiplication factor),經過PLL的時鐘稱為PLLCLK。倍頻因子我們設定為2倍頻,也就是說,經過PLL之後,我們的時鐘從原來4MHz的 HSE變為8MHz的PLLCLK。

緊接著又遇到了一個開關SW,經過這個開關之後就是STM32的系統時鐘(SYSCLK)了。通過這個開關,可以切換SYSCLK的時鐘源,可以選擇為HSI、PLLCLK、HSE。我們選擇為HSI時鐘,所以SYSCLK就為8MHz了。

PLLCLK在輸入到SW前,還流向了USB預分頻器,這個分頻器輸出為USB外設的時鐘(USBCLK),在本例子中我們的USB預分頻係數為1.5,此時USBCLK為5.33MHz。

回到SYSCLK,SYSCLK經過AHB預分頻器,分頻後再輸入到其它外設。如輸出到稱為HCLK、FCLK的時鐘,還直接輸出到SDIO外設的SDIOCLK時鐘、儲存器控制器FSMC的FSMCCLK時鐘,和作為APB1、APB2的預分頻器的輸入端。本例子設定AHB預分頻器不分頻,即輸出的頻率為8MHz。

GPIO外設是掛載在APB2總線上的, APB2的時鐘是APB2預分頻器的輸出,而APB2預分頻器的時鐘來源是AHB預分頻器。因此,把APB2預分頻器設定為不分頻,那麼我們就可以得到GPIO外設的時鐘也等於HCLK,為8MHz了。

APB2的時鐘為8MHz,ADC預分頻係數為2,所以,ADCCLK為4MHz。

STM32的時鐘系統確實是很複雜,不僅有倍頻,分頻,還有一系列的外設時鐘開關。倍頻是考慮到了電磁相容性,如果外部直接提供一個72MHz的晶振,太高的震盪頻率會給電路板的製作帶來一定的難度。分頻則是因為STM32既有高速外設,也有低速外設,各外設的工作頻率不相同,需要分開來管理。最後,每個外設時鐘還有自己獨立的開關(在圖上可以看到,在外設時鐘之前需要經過一個與門,這就是它們的開關)在我們不使用該外設時,需要把時鐘關閉以減少STM32的功耗。

以上均為個人學習心得,如有謬誤之處,望指出,我會虛心接納。

                -----參考資料《STM32庫開發實戰指南》,STM32F10x使用者手冊