1. 程式人生 > 其它 >STM32系統時鐘配置筆記

STM32系統時鐘配置筆記

技術標籤:STM32stm32嵌入式微控制器

前言

由於個人部落格被攻擊,現逐漸將部落格內容搬運至CSDN,本文原寫於2020年4月。

時鐘筆記

本文為stm32時鐘和中斷筆記
時鐘樹
可以通過時鐘樹來大致瞭解配置時鐘的流程,可以更加清晰瞭解各個時鐘之間的關係
在這裡插入圖片描述

名詞解釋

HSE:HSE 高速外部時鐘訊號。HSE 是高速的外部時鐘訊號,可以由有源晶振或者無源晶振提供,頻率從 4-16MHZ不等。當使用有源晶振時,時鐘從 OSC_IN 引腳進入, OSC_OUT 引腳懸空。當選用無源晶振時,時鐘從 OSC_IN 和OSC_OUT 進入,並且要配諧振電容。HSE 最常使用的就是 8M 的無源晶振。當確定 PLL 時鐘來源的時候, HSE 可以不分頻或者 2 分頻,這個由時鐘配置暫存器 CFGR 的位 17:PLLXTPRE 設定。

PLL 時鐘源:PLL 時鐘來源可以有兩個,一個來自 HSE,另外一個是 HSI/2,具體用哪個由時鐘配置暫存器 CFGR 的位 16: PLLSRC 設定。 HSI 是內部高速的時鐘訊號,頻率為 8M,根據溫度和環境的情況頻率會有漂移,一般不作為 PLL 的時鐘來源。

PLL時鐘PLLCLK:通過設定 PLL 的倍頻因子,可以對 PLL 的時鐘來源進行倍頻,倍頻因子可以是:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],具體設定成多少, 由時鐘配置暫存器 CFGR 的位21-18: PLLMUL[3:0]設定。
(註釋:72M是st官方推薦穩定執行時鐘)

系統時鐘SYSCLK

:從時鐘樹上可以看出系統時鐘來源可以是: HSI、 PLLCLK、 HSE,具體的時鐘配置暫存器 CFGR 的位 1-0:SW[1:0]設定。

AHB 匯流排時鐘 HCLK:系統時鐘 SYSCLK 經過 AHB 預分頻器分頻之後得到時鐘叫 APB 匯流排時鐘,即 HCLK,分頻因子可以是:[1,2,4,8,16,64,128,256,512],具體的由時鐘配置暫存器 CFGR的位 7-4 : HPRE[3:0]設定。片上大部分外設的時鐘都是經過 HCLK 分頻得到,至於 AHB總線上的外設的時鐘設定為多少,得等到我們使用該外設的時候才設定。

APB2 匯流排時鐘 HCLK2:APB2 匯流排時鐘 PCLK2 由 HCLK 經過高速 APB2 預分頻器得到,分頻因子可以是:[1,2,4,8,16],具體由時鐘配置暫存器 CFGR 的位 13-11: PPRE2[2:0]決定。 HCLK2 屬於高速的匯流排時鐘,片上高速的外設就掛載到這條總線上,比如全部的 GPIO、 USART1、SPI1 等。

APB1 匯流排時鐘 HCLK1:APB1 匯流排時鐘 PCLK1 由 HCLK 經過低速 APB 預分頻器得到,分頻因子可以是:[1,2,4,8,16],具體的由時鐘配置暫存器 CFGR 的位 10-8: PRRE1[2:0]決定。HCLK1 屬於低速的匯流排時鐘,最高為36M,片上低速的外設就掛載到這條總線上,比如USART2/3/4/5、SPI2/3, I2C1/2 等。

其他時鐘先概不論述。

補充知識:

時鐘頻率來說,又分為高速時鐘和低速時鐘,高速時鐘是提供給晶片主體的主時鐘,而低速時鐘只是提供給晶片中的RTC(實時時鐘)及獨立看門狗使用。

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

stm32f103在剛開始都是很預設時鐘為72M,在啟動檔案中startup_stm32f10x_hd.s裡,規定了程式在進入到main函式之前,先要執行systeminit函式,該函式是通過操作時鐘控制暫存器與時鐘配置暫存器來實現初始化。詳情看下圖翻譯說明

systeminit函式


這裡只討論如圖路線(常用路線):

常用路線


配置HSE時鐘步驟

  1. 使用HSE時,設定系統時鐘的步驟:開啟HSE ,並等待 HSE 穩定
  2. 設定 AHB、APB2、APB1的預分頻因子
  3. 設定PLL的時鐘來源,和PLL的倍頻因子,設定各種頻率主要就是在這裡設定
  4. 開啟PLL,並等待PLL穩定
  5. 把PLLCK切換為系統時鐘SYSCLK
  6. 讀取時鐘切換狀態位,確保PLLCLK被選為系統時鐘

(註釋:對於 SYSCLK、 HCLK、 PCLK2、 PCLK1 這四個時鐘的配置一般是: PCLK2 =HCLK = SYSCLK=PLLCLK = 72M, PCLK1=HCLK/2 = 36M。 這個時鐘配置也是庫函式的標準配置。)

啟用使用HSE時鐘


void HSE_SetSysClock(uint32_t pllmul)
{
__IO uint32_t  HSEStartUpStatus = 0;

// 把RCC外設初始化成復位狀態,這句是必須的,該命令在韌體庫rcc.c檔案中。
RCC_DeInit();

//使能HSE,開啟外部晶振,該開發板用的是8M,命令在韌體庫rcc.c/277行。
RCC_HSEConfig(RCC_HSE_ON);

// 等待 HSE 啟動穩定
HSEStartUpStatus = RCC_WaitForHSEStartUp();//rcc.c/304行,成功後RCC_WaitForHSEStartUp()會傳入引數SUCCESS。

// 只有 HSE 穩定之後則繼續往下執行
if (HSEStartUpStatus == SUCCESS)
{
//----------------------------------------------------------------------//
// 使能FLASH 預存取緩衝區
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//

// AHB預分頻因子設定為1分頻,HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1); //rcc.c /605

// APB2預分頻因子設定為1分頻,PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);

// APB1預分頻因子設定為1分頻,PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);

//-----------------設定各種頻率主要就是在這裡設定-------------------//
// 設定PLL時鐘來源為HSE,設定PLL倍頻因子
// PLLCLK = 8MHz * pllmul
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
//------------------------------------------------------------------//

// 開啟PLL
RCC_PLLCmd(ENABLE);

// 等待 PLL穩定
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}

// 當PLL穩定之後,把PLL時鐘切換為系統時鐘SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

// 讀取時鐘切換狀態位,確保PLLCLK被選為系統時鐘
while (RCC_GetSYSCLKSource() != 0x08)
{
}
}
else
{ // 如果HSE開啟失敗,那麼程式就會來到這裡,使用者可在這裡添加出錯的程式碼處理
// 當HSE開啟失敗或者故障的時候,微控制器會自動把HSI設定為系統時鐘,
// HSI是內部的高速時鐘,8MHZ
while (1)
{
}
}
}

問題

  1. 為什麼使能FLASH 預存取緩衝區?

使能FLASH指令緩衝器,用於當CPU主頻提升後快取多條指令已避免CPU等待FLASH傳資料導致的時間浪費

“預取緩衝區(2個64位):在每一次復位以後被自動開啟,由於每個緩衝區的大小(64位)與快閃記憶體的頻寬相同,因此只通過需一次讀快閃記憶體的操作即可更新整個緩衝區的內容。由於預取緩衝區的存在,CPU可以工作在更高的主頻。CPU每次取指最多為32位的字,取一條指令時,下一條指令已經在緩衝區中等待。”具體暫存器配置參見:PM0075:STM32F10xxx Flash memory microcontrollers(Flash程式設計手冊)

  1. 形參 pllmul?

pllmul 用來設定 PLL 的倍頻因子,在呼叫的時候形參可以是: RCC_PLLMul_x , x:[2,3,…16],這些巨集來源於庫函式的定義,巨集展開是一些 32 位的十六進位制數,具體功能是配置了時鐘配置暫存器 CFGR 的位 21-18PLLMUL[3:0],預先定義好倍頻因子,方便呼叫。

配置HSI時鐘步驟

  1. 使用HSI時,設定系統時鐘的步驟:開啟HSI ,並等待 HSI 穩定
  2. 設定 AHB、APB2、APB1的預分頻因子
  3. 設定PLL的時鐘來源,和PLL的倍頻因子,設定各種頻率主要就是在這裡設定
  4. 開啟PLL,並等待PLL穩定
  5. 把PLLCK切換為系統時鐘SYSCLK
  6. 讀取時鐘切換狀態位,確保PLLCLK被選為系統時鐘

(註釋:HSI作為時鐘來源,經過PLL倍頻作為系統時鐘,這是在HSE故障的時候才使用的方法,HSI會因為溫度等原因會有漂移,不穩定,一般不會用HSI作為時鐘來源,除非是迫不得已的情況。)


"程式碼"





void HSI_SetSysClock(uint32_t pllmul)
{
__IO uint32_t HSIStartUpStatus = 0;

// 把RCC外設初始化成復位狀態,這句是必須的
RCC_DeInit();

//使能HSI
RCC_HSICmd(ENABLE);

// 等待 HSI 就緒
HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;

// 只有 HSI就緒之後則繼續往下執行
if (HSIStartUpStatus == RCC_CR_HSIRDY)
{
//----------------------------------------------------------------------//
// 使能FLASH 預存取緩衝區
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

// SYSCLK週期與快閃記憶體訪問時間的比例設定,這裡統一設定成2
// 設定成2的時候,SYSCLK低於48M也可以工作,如果設定成0或者1的時候,
// 如果配置的SYSCLK超出了範圍的話,則會進入硬體錯誤,程式就死了
// 0:0 < SYSCLK <= 24M
// 1:24< SYSCLK <= 48M
// 2:48< SYSCLK <= 72M
FLASH_SetLatency(FLASH_Latency_2);
//----------------------------------------------------------------------//

// AHB預分頻因子設定為1分頻,HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);

// APB2預分頻因子設定為1分頻,PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);

// APB1預分頻因子設定為1分頻,PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);

//-----------------設定各種頻率主要就是在這裡設定-------------------//
// 設定PLL時鐘來源為HSE,設定PLL倍頻因子
// PLLCLK = 4MHz * pllmul
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);
//------------------------------------------------------------------//

// 開啟PLL
RCC_PLLCmd(ENABLE);

// 等待 PLL穩定
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}

// 當PLL穩定之後,把PLL時鐘切換為系統時鐘SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

// 讀取時鐘切換狀態位,確保PLLCLK被選為系統時鐘
while (RCC_GetSYSCLKSource() != 0x08)
{
}
}
else
{ // 如果HSI開啟失敗,那麼程式就會來到這裡,使用者可在這裡添加出錯的程式碼處理
// 當HSE開啟失敗或者故障的時候,微控制器會自動把HSI設定為系統時鐘,
// HSI是內部的高速時鐘,8MHZ
while (1)
{
}
}
}

擴:如何具體知道時鐘頻率?

在 STM32F103 系列中, PA8 可以複用為 MCO 引腳,對外提供時鐘輸出,我們也可以用示波器監控該引腳的輸出來判斷我們的系統時鐘是否設定正確。