【reset clock control 復位和時鍾控制器】
時鍾信號對於處理器非常重要,比如我們熟悉的 CPU 就是由時鍾信號驅動的,而主頻就是內核的的時鍾信號頻率。Cortex-M3 有着復雜的時鍾樹架構,而且我們需要在初始化階段配置好時鍾參數。
本文將會先介紹時鍾相關的概念,然后介紹使用庫函數便捷設置時鍾總線的方法,在文章最后再深入學習庫函數背后等效的時鍾寄存器原理。
時鍾源
STM32F103
中有 4 種可選時鍾源:
高速外部時鍾
(HSE): 以外部晶振作時鍾源,晶振頻率可取范圍為 4~16 MHz,常用 8MHz 晶振,開發板上的 8MHz 時鍾就是指的這個。高速內部時鍾
(HSI): 由內部 RC 振盪器產生,頻率為8MHz, 無須外接晶振,但精確性比外部時鍾差。低速外部時鍾
(LSE): 以外部晶振作時鍾源,可以提供時鍾信號給實時時鍾模塊,一般采用 32.768KHz 晶振,較為少用。低速內部時鍾
(LSI): 由內部 RC 振盪器產生,也主要提供信號給實時時鍾模塊,頻率在 30-60KHz 間浮動,較為少用。
單片機啟動時默認使用高速內部時鍾 (HSI),啟動之后可以通過 RCC
時鍾控制寄存器器改用其他時鍾源。
系統時鍾 (SYSCLK)
系統時鍾 SYSCLK 最大頻率為 72MHz,它是供 STM32 中絕大部分部件工作的時鍾源。系統時鍾可由 PLL(鎖相環)、HSI 或者 HSE 提供輸入,並且通過 AHB(高速總線) 分頻器分頻后輸出送給各模塊使用。
鎖相環 (PLL)
如果打算使單片機運行在最高頻率 (72 MHz),我們還需要倍頻高速時鍾源的時鍾信號。鎖相環能夠將輸出頻率鎖定在輸入頻率的正整數倍。STM32F103
的鎖相環提供了 2 到 16 倍的倍頻系數。假設我們使用高速內部時鍾源 (8 MHz) ,想要使 STM32F103
達到最高主頻 (72 MHz),那么我們就要要啟用鎖相環,設置為 9 倍倍頻,並將 SYSCLK 時鍾源設置為 PLL。
時鍾總線
STM32F103
中有 4 條時鍾總線:
- AHB 高速總線,時鍾為 HCLK,最大頻率為 72 MHz,時鍾信號提供給存儲器,DMA 及 Cortex 內核,是內核運行的時鍾,也就是主頻,它的大小與 STM32 運算速度,數據存取速度密切相關。
- APB1 低速外設總線,時鍾為 PCLK1,最大頻率為 36 MHz,提供給掛載在APB1總線上的外設, 如
USART2
, - APB2 高速外設總線,時鍾為 PCLK2,最大頻率為 72 MHz,提供給掛載在APB2總線上的外設,如
GPIO
,USART1
。 - FCLK 自由運行時鍾,獨立於內核運行,一般設置為與 HCLK 同頻率,常用於采樣中斷和調試模塊供時。
使用庫函數設置 復位時鍾控制器 (RCC)
復位時鍾控制器是 STM32F103
提供的一組寄存器,負責控制和設置上面提到的時鍾源,鎖相環倍頻,各總線分頻系數以及開關總線上的各類外設。
stm32f1xx_hal
提供了簡潔的接口幫我們設置 RCC 寄存器,足夠滿足日常工作需要。工程上也推薦使用這種方式進行時鍾樹初始化。
下面例子中單片機使用外部 8MHz 晶振,並將所有總線設置為最高頻率:
#![no_main] #![no_std] extern crate cortex_m; extern crate cortex_m_rt as rt; extern crate panic_halt; extern crate stm32f103xx_hal as hal; use hal::prelude::*; use hal::stm32f103xx; use rt::entry; #[entry] fn main() -> ! { let dp = stm32f103xx::Peripherals::take().unwrap(); let mut flash = dp.FLASH.constrain(); let mut rcc = dp.RCC.constrain(); let clocks = rcc .cfgr .use_hse(8.mhz()) // 高速外部時鍾源 .sysclk(72.mhz()) // 系統時鍾 .hclk(72.mhz()) // AHB 高速總線 .pclk1(36.mhz()) // APB1 低速外設總線 .pclk2(72.mhz()) // APB2 高速外設總線 .freeze(&mut flash.acr); // 應用時鍾配置 loop {} }
深入了解 RCC 寄存器
RCC 寄存器的設置比其他一般外設寄存器要更為復雜,因此一般不會手動設置。但是我們可以通過學習它來掌握的庫函數背后的原理。
RCC 時鍾設置一般分為以下幾個步驟:
- 啟用外部晶振 (可選)
- 等待外部晶振穩定
- 設置各總線分頻系數
- 設置 FLASH 等待系數與預讀取
- 啟動鎖相環
- 等待鎖相環鎖定
- 將 SYSCLK 切換到鎖相環信號輸入
PS: 這里提到的 FLASH 等待系數和預讀取都與 Cortex-M 架構設計有關。Cortex-M 核心采用了三級管道技術,簡單來說就是當一個指令正在處理時,下一個指令已經被解碼的同時第三個指令已經被預讀取進緩存區了,這鍾處理方式能夠大幅提高處理器的運行效率。另外,FLASH 由於原理限制,很難達到與核心相同的高頻率,因此在核心讀取指令的速度超過 FLASH 發送指令速度的時候,核心需要暫時停下來等待。根據手冊,STM32F103
的 FLASH 等待系數應根據核心頻率 (HCLK) 設置:
HCLK | FLASH WAIT STATE
-------------------------------
0 - 24 MHz | 0 wait state
24 - 48 MHz | 1 wait state
48 - 72 MHz | 2 wait state
分頻
事實上,AHB,APB1,APB2 等各總線的頻率都嚴格成正整數倍關系,因為它們的時鍾信號並不是單獨產生的,而是根據特定結構分頻出來的。分頻就是按照預分頻系數降低輸入頻率,預分頻系數都為正整數。STM32F103 中的時鍾樹簡化之后是這樣的:
Example
我們這里用寄存器代替庫函數初始化時鍾樹,代碼與上節的庫函數完全等效。下面用到的寄存器有 RCC_CR
,RCC_CFGR
,FLASH_ACR
,由於篇幅較長,手冊截圖將放在后面以供參考。
use stm32f103xx; pub fn rcc_clock_init(rcc: &mut stm32f103xx::RCC, flash: &mut stm32f103xx::FLASH) { // 啟動外部高速時鍾 (HSE) rcc.cr.write(|w| w.hseon().enabled()); // 等待外部高速時鍾穩定 while !rcc.cr.read().hserdy().is_ready() {} // 設置分頻 AHB(HCLK) = SYSCLK, APB1(PCLK1) = SYSCLK / 2, APB2(PCK2) = SYSCLK rcc.cfgr .write(|w| w.hpre().no_div().ppre1().div2().ppre2().no_div()); // 設置鎖相環為 9 x HSE rcc.cfgr .write(|w| w.pllsrc().external().pllxtpre().no_div().pllmul().mul9()); // 設置 flash : two wait states, 啟用預讀取 flash.acr.write(|w| w.latency().two().prftbe().enabled()); // 啟動鎖相環 rcc.cr.write(|w| w.pllon().enabled()); // 等待鎖相環鎖定 while !rcc.cr.read().pllrdy().is_locked() {} // 使用鎖相環輸出作為 SYSCLK rcc.cfgr.write(|w| w.sw().pll()); // 等待 SYSCLK 切換為鎖相環 while !rcc.cfgr.read().sws().is_pll()