STM32F4 相對於 STM32F1 來說,時鍾部分復雜了很多, STM32F4 的時鍾配置,我們提供兩個函數: Sys_Clock_Set 和 Stm32_Clock_Init。其中 Sys_Clock_Set 是核心的系統時鍾配置函
數,由 Stm32_Clock_Init 調用,實現對系統時鍾的配置。外部程序,一般調用 Stm32_Clock_Init函數來配置時鍾。 sys文件夾中
在 STM32F4 中,有 5 個最重要的時鍾源,為 HSI、 HSE、 LSI、 LSE、 PLL。 其中 PLL 實際是分為兩個時鍾源,分別為主 PLL 和專用 PLL。 從時鍾頻率來分可以分為高速時鍾源和低速
時鍾源,在這 5 個中 HSI, HSE 以及 PLL 是高速時鍾, LSI 和 LSE 是低速時鍾。從來源可分為外部時鍾源和內部時鍾源,外部時鍾源就是從外部通過接晶振的方式獲取時鍾源,其中 HSE 和
LSE 是外部時鍾源,其他的是內部時鍾源。
①、 LSI 是低速內部時鍾, RC 振盪器,頻率為 32kHz 左右。 供獨立看門狗和自動喚醒單元使用。
②、 LSE 是低速外部時鍾,接頻率為 32.768kHz 的石英晶體。 這個主要是 RTC 的時鍾源。
③、 HSE 是高速外部時鍾,可接石英/陶瓷諧振器,或者接外部時鍾源,頻率范圍為 4MHz~26MHz。
我們的開發板接的是 8M 的晶振。 HSE 也可以直接做為系統時鍾或者 PLL 輸入。
④、 HSI 是高速內部時鍾, RC 振盪器, 頻率為 16MHz。 可以直接作為系統時鍾或者用作 PLL
輸入。
⑤、 PLL 為鎖相環倍頻輸出。 STM32F4 有兩個 PLL:
1) 主 PLL(PLL)由 HSE 或者 HSI 提供時鍾信號,並具有兩個不同的輸出時鍾。
第一個輸出 PLLP 用於生成高速的系統時鍾(最高 168MHz)
第二個輸出 PLLQ 用於生成 USB OTG FS 的時鍾(48MHz),隨機數發生器的時鍾和 SDIO
時鍾。
2) 專用 PLL(PLLI2S)用於生成精確時鍾,從而在 I2S 接口實現高品質音頻性能。
STM32F4 的時鍾樹圖(非常重要)
上圖從左往右看,就是整個 STM32F4 的時鍾走向。這里,我們挑選出 9 個重要的地方進行介紹(圖 5.2.2.1 中標出的①~⑨)。
① 這是進入 PLL 之前的一個時鍾分頻系數(M),取值范圍是: 2~63,一般取 8。注意,這個分頻系數,對主 PLL 和 PLLI2S 都有效。
② 這是 STM32F4 的主 PLL,該部分控制 STM32F4 的主頻率(PLLCLK)和 USB/SDIO/隨機數發生器等外設的頻率(PLL48CK)。其中, N 是主 PLL vco 的倍頻系數,其取值
范圍是: 64~432; P 是系統時鍾的主 PLL 分頻系數,其取值范圍是: 2、 4、 6 和 8(僅 限這四個值); Q 是 USB/SDIO/隨機數產生器等的主 PLL 分頻系數,其取值范圍是:
2~15; R 沒用到。
③ 這是 STM32F4 I2S 部分的 PLL,該部分主要用於設置 STM32F4 I2S 內部輸入時鍾頻率。其中, N 是用於 PLLI2S vco 的倍頻系數,其取值范圍是: 192~432; R 是 I2S 時鍾的分
頻系數,其取值范圍是: 2~7; P 和 Q 沒用到。
④ 這是 PLL 之后的系統主時鍾(PLLCLK), STM32F4 的主頻最高是 168Mhz,所以我們一般設置 PLLCLK 為 168Mhz(M=8,N=336,P=2),通過 SW 選擇 SYSCLK=PLLCLK
即可得到 168Mhz 的系統運行頻率。
⑤ 這是 PLL 之后的 USB/SDIO/隨機數發生器時鍾頻率,由於 USB 必須是 48Mhz 才可以正常工作,所以這個頻率一般設置為 48Mhz(M=8,N=336,Q=7)。
⑥ 是 I2S 的時鍾,通過 I2SSRC 選擇內部 PLLI2SCLK 還是外部 I2SCKIN 作為時鍾。 探索者 STM32F4 開發板使用的是內部 PLLI2SCLK。
⑦ 這是 Cortex 系統定時器,也就是 SYSTICK 的時鍾。上圖清楚的表明 SYSTICK 的來源是 AHB 分頻后再 8 分頻(這個 8 分頻是可以設置的,即 8 分頻,或者不分頻,一般使
用 8 分頻), 我們一般設置 AHB 不分頻,則 SYSTICK 的頻率為: 168M/8=21Mhz。前面介紹的延時函數,就是基於 SYSTICK 來實現的。
⑧ 這里是 STM32F4 很多外設的時鍾來源,即兩個總線橋: APB1 和 APB2,其中 APB1是低速總線(最高 42Mhz), APB2 是高速總線(最高 84Mhz)。另外定時器部分,如
果所在總線(APB1/APB2)的分頻系數為 1,那么就不倍頻,如果不為 1(比如 2/4/8/16),那么就會 2 倍頻(Fabpx*2)后,作為定時器時鍾輸入。
⑨ 這是 STM32F4 內部以太網 MAC 時鍾的來源。對於 MII 接口來說,必須向外部 PHY芯片提供 25Mhz 的時鍾,這個時鍾,可以由 PHY 芯片外接晶振,或者使用 STM32F4
的 MCO 輸出來提供。然后, PHY 芯片再給 STM32F4 提供 ETH_MII_TX_CLK 和ETH_MII_RX_CLK 時鍾。對於 RMII 接口來說,外部必須提供 50Mhz 的時鍾驅動 PHY
和 STM32F4 的 ETH_RMII_REF_CLK,這個 50Mhz 時鍾可以來自 PHY、有源晶振或者 STM32F4 的 MCO。 我們的開發板使用的是 RMII 接口,使用 PHY 芯片提供 50Mhz
時鍾驅動 STM32F4 的 ETH_RMII_REF_CLK。
關於時鍾的詳細介紹,在《STM32F4xx 中文參考手冊》第 6.2 節(106~113 頁)有詳細介紹。有不明白的地方,可以對照手冊仔細研究。 最后,提醒下大家, STM32F4 默認的情況下
(比如串口 IAP 時或未初始化時鍾時),使用的是內部 16M 的 HSI 作為時鍾的,所以不需要外部晶振也可以下載和運行代碼的。
從上圖可以看出 STM32F4 的時鍾設計的比較復雜,各個時鍾基本都是可控的,任何外設都有對應的時鍾控制開關,這樣的設計,對降低功耗是非常有用的,不用的外設不開啟時鍾,
就可以大大降低其功耗。
下面開始 Sys_Clock_Set 函數的介紹, 該函數用於配置 STM32F4 的時鍾,包括系統主時鍾、USB/SDIO/隨機數發生器時鍾、 APB1 和 APB2 時鍾等。 該函數代碼如下:
//時鍾設置函數 //Fvco=Fs*(plln/pllm); //Fsys=Fvco/pllp=Fs*(plln/(pllm*pllp)); //Fusb=Fvco/pllq=Fs*(plln/(pllm*pllq)); //Fvco:VCO 頻率 //Fsys:系統時鍾頻率 //Fusb:USB,SDIO,RNG 等的時鍾頻率 //Fs:PLL 輸入時鍾頻率,可以是 HSI,HSE 等. //plln:主 PLL 倍頻系數(PLL 倍頻),取值范圍:64~432. //pllm:主 PLL 和音頻 PLL 分頻系數(PLL 之前的分頻),取值范圍:2~63. //pllp:系統時鍾的主 PLL 分頻系數(PLL 之后的分頻),取值范圍:2,4,6,8.(僅限這 4 個值!) //pllq:USB/SDIO/隨機數產生器等的主 PLL 分頻系數(PLL 之后的分頻),取值范圍:2~15. //外部晶振為 8M 的時候,推薦值:plln=336,pllm=8,pllp=2,pllq=7. //得到:Fvco=8*(336/8)=336Mhz // Fsys=336/2=168Mhz // Fusb=336/7=48Mhz //返回值:0,成功;1,失敗。 u8 Sys_Clock_Set(u32 plln,u32 pllm,u32 pllp,u32 pllq) { u16 retry=0; u8 status=0; RCC->CR|=1<<16; //HSE 開啟 while(((RCC->CR&(1<<17))==0)&&(retry<0X1FFF))retry++;//等待 HSE RDY if(retry==0X1FFF)status=1; //HSE 無法就緒 else { RCC->APB1ENR|=1<<28; //電源接口時鍾使能 PWR->CR|=3<<14; //高性能模式,時鍾可到 168Mhz RCC->CFGR|=(0<<4)|(5<<10)|(4<<13);//HCLK 不分頻;APB1 4 分頻;APB2 2 分頻. RCC->CR&=~(1<<24); //關閉主 PLL RCC->PLLCFGR=pllm|(plln<<6)|(((pllp>>1)-1)<<16)|(pllq<<24)|(1<<22); //配置主 PLL,PLL 時鍾源來自 HSE RCC->CR|=1<<24; //打開主 PLL while((RCC->CR&(1<<25))==0);//等待 PLL 准備好 FLASH->ACR|=1<<8; //指令預取使能. FLASH->ACR|=1<<9; //指令 cache 使能. FLASH->ACR|=1<<10; //數據 cache 使能. FLASH->ACR|=5<<0; //5 個 CPU 等待周期. RCC->CFGR&=~(3<<0); //清零 RCC->CFGR|=2<<0; //選擇主 PLL 作為系統時鍾 while((RCC->CFGR&(3<<2))!=(2<<2));//等待主 PLL 作為系統時鍾成功. } return status; }
在 Sys_Clock_Set 函數中,我們設置了 APB1 為 4 分頻, APB2 為 2 分頻, HCLK 不分頻,同時選擇 PLLCLK 作為系統時鍾。該函數有 4 個參數,具體意義和計算方法,見函數前面的說
明。 一般我們推薦設置為: Sys_Clock_Set(336,8,2,7),即可設置 STM32F4 運行在 168Mhz 的頻率下, APB1 為 42Mhz, APB2 為 84Mhz, USB/SDIO/隨機數發生器時鍾為 48Mhz。
以上代碼中, RCC 和 FLASH 都是 MDK 定義的一個結構體,包含 RCC/FLASH 相關的寄存器組。其寄存器名與《STM32F4xx 中文參考手冊》 里面定義的寄存器名字是一摸一樣的,所
以在你不明白某個寄存器干什么用的時候,可以到《STM32F4xx 中文參考手冊》里面查找一下,你就可以迅速查到這個寄存器的作用以及每個位所代表的意思。 特別注意,由於 FLASH 速度
遠遠跟不上 CPU 的運行頻率,所以這里我們設置了 FLASH 的等待周期為 5, 很明顯, FLASH會大大拖慢程序的運行,不過 STM32F4 有自適實時存儲器加速器(ART),通過這個加速器,可
以讓 STM32F4 獲得相當於 0 FLASH 等待周期的運行效果。關於 STM32F4 的 FLASH 以及 ART等的介紹,請大家參考《STM32F4xx 中文參考手冊》第 3.3 節(59 頁開始)。
接下來,我們再看下 Stm32_Clock_Init 函數,該函數代碼如下:
//系統時鍾初始化函數 //plln:主 PLL 倍頻系數(PLL 倍頻),取值范圍:64~432. //pllm:主 PLL 和音頻 PLL 分頻系數(PLL 之前的分頻),取值范圍:2~63. //pllp:系統時鍾的主 PLL 分頻系數(PLL 之后的分頻),取值范圍:2,4,6,8.(僅限這 4 個值!) //pllq:USB/SDIO/隨機數產生器等的主 PLL 分頻系數(PLL 之后的分頻),取值范圍:2~15. void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq) { RCC->CR|=0x00000001; //設置 HISON,開啟內部高速 RC 振盪 RCC->CFGR=0x00000000; //CFGR 清零 RCC->CR&=0xFEF6FFFF; //HSEON,CSSON,PLLON 清零 RCC->PLLCFGR=0x24003010; //PLLCFGR 恢復復位值 RCC->CR&=~(1<<18); //HSEBYP 清零,外部晶振不旁路 RCC->CIR=0x00000000; //禁止 RCC 時鍾中斷 Sys_Clock_Set(plln,pllm,pllp,pllq);//設置時鍾 //配置向量表 #ifdef VECT_TAB_RAM MY_NVIC_SetVectorTable(1<<29,0x0); #else MY_NVIC_SetVectorTable(0,0x0); #endif }
該函數主要進行了時鍾配置前的一些設置工作,然后通過調用 Sys_Clock_Set 函數,實現對 STM32F4 的時鍾配置。最后,根據代碼運行的位置( FLASH or SRAM), 調用函數
MY_NVIC_SetVectorTable 進行中斷向量表偏移設置。MY_NVIC_SetVectorTable 函數的代碼如下:
//設置向量表偏移地址 //NVIC_VectTab:基址 //Offset:偏移量 void MY_NVIC_SetVectorTable(u32 NVIC_VectTab,u32 Offset) { SCB->VTOR=NVIC_VectTab|(Offset&(u32)0xFFFFFE00); //設置 NVIC 的向量表偏移寄存器,VTOR 低 9 位保留,即[8:0]保留。 }
該函數是用來配置中斷向量表基址和偏移量,決定是在那個區域。當在 RAM 中調試代碼 的時候,需要把中斷向量表放到 RAM 里面, 這就需要通過這個函數來配置。關於向量表的詳
細介紹請參考《Cortex M3 與 M4 權威指南》第 4.5.3 節(117 頁)或者《Cortex M3 權威指南》第七章,第 113 頁的向量表一章。 關於 SCB->VTOR 寄存器,請參考《STM32F3 與 F4 系列 Cortex
M4 內核編程手冊》第 4.4.4 節(212 頁),有詳細描述。