第15章 RCC—使用HSE/HSI配置時鍾
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載:www.firebbs.cn
野火視頻教程優酷觀看網址:http://i.youku.com/firege
本章參考資料:《STM32F4xx中文參考手冊》RCC章節。
學習本章時,配合《STM32F4xx中文參考手冊》RCC章節一起閱讀,效果會更佳,特別是涉及到寄存器說明的部分。
RCC :reset clock control 復位和時鍾控制器。本章我們主要講解時鍾部分,特別是要着重理解時鍾樹,理解了時鍾樹,F429的一切時鍾的來龍去脈都會了如指掌。
1.1 RCC主要作用—時鍾部分
設置系統時鍾SYSCLK、設置AHB分頻因子(決定HCLK等於多少)、設置APB2分頻因子(決定PCLK2等於多少)、設置APB1分頻因子(決定PCLK1等於多少)、設置各個外設的分頻因子;控制AHB、APB2和APB1這三條總線時鍾的開啟、控制每個外設的時鍾的開啟。對於SYSCLK、HCLK、PCLK2、PCLK1這四個時鍾的配置一般是:HCLK = SYSCLK=PLLCLK = 180M,PCLK2=HCLK/2 = 90M,PCLK1=HCLK/4 = 45M。這個時鍾配置也是庫函數的標准配置,我們用的最多的就是這個。
1.2 RCC框圖剖析—時鍾樹
時鍾樹單純講理論的話會比較枯燥,如果選取一條主線,並輔以代碼,先主后次講解的話會很容易,而且記憶還更深刻。我們這里選取庫函數時鍾系統時鍾函數:SetSysClock(); 以這個函數的編寫流程來講解時鍾樹,這個函數也是我們用庫的時候默認的系統時鍾設置函數。該函數的功能是利用HSE把時鍾設置為:HCLK = SYSCLK=PLLCLK = 180M,PCLK1=HCLK/2 = 90M,PCLK1=HCLK/4 = 45M下面我們就以這個代碼的流程為主線,來分析時鍾樹,對應的是圖中的黃色部分,代碼流程在時鍾樹中以數字的大小順序標識。
圖 151 STM32F429時鍾樹
1.2.1 系統時鍾
1. ①HSE高速外部時鍾信號
HSE是高速的外部時鍾信號,可以由有源晶振或者無源晶振提供,頻率從4-26MHZ不等。當使用有源晶振時,時鍾從OSC_IN引腳進入,OSC_OUT引腳懸空,當選用無源晶振時,時鍾從OSC_IN和OSC_OUT進入,並且要配諧振電容。HSE我們使用25M的無源晶振。如果我們使用HSE或者HSE經過PLL倍頻之后的時鍾作為系統時鍾SYSCLK,當HSE故障時候,不僅HSE會被關閉,PLL也會被關閉,此時高速的內部時鍾時鍾信號HSI會作為備用的系統時鍾,直到HSE恢復正常,HSI=16M。
2. ②鎖相環PLL
PLL的主要作用是對時鍾進行倍頻,然后把時鍾輸出到各個功能部件。PLL有兩個,一個是主PLL,另外一個是專用的PLLI2S,他們均由HSE或者HSI提供時鍾輸入信號。
主PLL有兩路的時鍾輸出,第一個輸出時鍾PLLCLK用於系統時鍾,F429里面最高是180M,第二個輸出用於USB OTG FS的時鍾(48M)、RNG和SDIO時鍾(<=48M)。專用的PLLI2S用於生成精確時鍾,給I2S提供時鍾。
HSE或者HSI經過PLL時鍾輸入分頻因子M(2~63)分頻后,成為VCO的時鍾輸入,VCO的時鍾必須在1~2M之間,我們選擇HSE=25M作為PLL的時鍾輸入,M設置為25,那么VCO輸入時鍾就等於1M。
VCO輸入時鍾經過VCO倍頻因子N倍頻之后,成為VCO時鍾輸出,VCO時鍾必須在192~432M之間。我們配置N為360,則VCO的輸出時鍾等於360M。如果要把系統時鍾超頻,就得在VCO倍頻系數N這里做手腳。PLLCLK_OUTMAX = VCOCLK_OUTMAX/P_MIN = 432/2=216M,即F429最高可超頻到216M。
VCO輸出時鍾之后有三個分頻因子:PLLCLK分頻因子p,USB OTG FS/RNG/SDIO時鍾分頻因子Q,分頻因子R(F446才有,F429沒有)。p可以取值2、4、6、8,我們配置為2,則得到PLLCLK=180M。Q可以取值4~15,但是USB OTG FS必須使用48M,Q=VCO輸出時鍾360/48=7.5,出現了小數這明顯是錯誤,權衡之策是是重新配置VCO的倍頻因子N=336,VCOCLK=1M*336=336M,PLLCLK=VCOCLK/2=168M,USBCLK=336/7=48M,細心的讀者應該發現了,在使用USB的時候,PLLCLK被降低到了168M,不能使用180M,這實乃ST的一個奇葩設計。有關PLL的配置有一個專門的RCC PLL配置寄存器RCC_PLLCFGR,具體描述看手冊即可。
PLL的時鍾配置經過,稍微整理下可由如下公式表達:
VCOCLK_IN = PLLCLK_IN / M = HSE / 25 = 1M
VCOCLK_OUT = VCOCLK_IN * N = 1M * 360 = 360M
PLLCLK_OUT=VCOCLK_OUT/P=360/2=180M
USBCLK = VCOCLK_OUT/Q=360/7=51.7。暫時這樣配置,到真正使用USB的時候會重新配置。
3. ③系統時鍾SYSCLK
系統時鍾來源可以是:HSI、PLLCLK、HSE,具體的由時鍾配置寄存器RCC_CFGR的SW位配置。我們這里設置系統時鍾:SYSCLK = PLLCLK = 180M。如果系統時鍾是由HSE經過PLL倍頻之后的PLLCLK得到,當HSE出現故障的時候,系統時鍾會切換為HSI=16M,直到HSE恢復正常為止。
4. ④AHB總線時鍾HCLK
系統時鍾SYSCLK經過AHB預分頻器分頻之后得到時鍾叫APB總線時鍾,即HCLK,分頻因子可以是:[1,2,4,8,16,64,128,256,512],具體的由時鍾配置寄存器RCC_CFGR的HPRE位設置。片上大部分外設的時鍾都是經過HCLK分頻得到,至於AHB總線上的外設的時鍾設置為多少,得等到我們使用該外設的時候才設置,我們這里只需粗線條的設置好APB的時鍾即可。我們這里設置為1分頻,即HCLK=SYSCLK=180M。功能框圖中的最高168M指的是F407,F429最高應該是180M,是官方中文翻譯文檔的一個疏忽。
5. ⑤APB2總線時鍾HCLK2
APB2總線時鍾PCLK2由HCLK經過高速APB2預分頻器得到,分頻因子可以是:[1,2,4,8,16],具體由時鍾配置寄存器RCC_CFGR的PPRE2位設置。HCLK2屬於高速的總線時鍾,片上高速的外設就掛載到這條總線上,比如全部的GPIO、USART1、SPI1等。至於APB2總線上的外設的時鍾設置為多少,得等到我們使用該外設的時候才設置,我們這里只需粗線條的設置好APB2的時鍾即可。我們這里設置為2分頻,即PCLK2 = HCLK /2= 90M。
6. ⑥APB1總線時鍾HCLK1
APB1總線時鍾PCLK1由HCLK經過低速APB預分頻器得到,分頻因子可以是:[1,2,4,8,16],具體由時鍾配置寄存器RCC_CFGR的PPRE1位設置。
HCLK1屬於低速的總線時鍾,最高為45M,片上低速的外設就掛載到這條總線上,比如USART2/3/4/5、SPI2/3,I2C1/2等。至於APB1總線上的外設的時鍾設置為多少,得等到我們使用該外設的時候才設置,我們這里只需粗線條的設置好APB1的時鍾即可。我們這里設置為4分頻,即PCLK1 = HCLK/4 = 45M。
7. 設置系統時鍾庫函數
上面的6個步驟對應的設置系統時鍾庫函數如下,為了方便閱讀,已經把跟429不相關的代碼刪掉,把英文注釋翻譯成了中文,並把代碼標上了序號,總共6個步驟。該函數是直接操作寄存器的,有關寄存器部分請參考數據手冊的RCC的寄存器描述部分。
代碼 13 設置系統時鍾庫函數
1 /*
2 * 使用HSE時,設置系統時鍾的步驟
3 * 1、開啟HSE ,並等待 HSE 穩定
4 * 2、設置 AHB、APB2、APB1的預分頻因子
5 * 3、設置PLL的時鍾來源
6 * 設置VCO輸入時鍾分頻因子 m
7 * 設置VCO輸出時鍾倍頻因子 n
8 * 設置PLLCLK時鍾分頻因子 p
9 * 設置OTG FS,SDIO,RNG時鍾分頻因子 q
10 * 4、開啟PLL,並等待PLL穩定
11 * 5、把PLLCK切換為系統時鍾SYSCLK
12 * 6、讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾
13 */
14
15 #define PLL_M 25
16 #define PLL_N 360
17 #define PLL_P 2
18 #define PLL_Q 7
如果要超頻的話,修改PLL_N這個宏即可,取值范圍為:192~432。
1 void SetSysClock(void)
2 {
3
4 __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
5
6 // ①使能HSE
7 RCC->CR |= ((uint32_t)RCC_CR_HSEON);
8
9 // 等待HSE啟動穩定
10 do {
11 HSEStatus = RCC->CR & RCC_CR_HSERDY;
12 StartUpCounter++;
13 } while ((HSEStatus==0)&&(StartUpCounter
14 !=HSE_STARTUP_TIMEOUT));
15
16 if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
17 HSEStatus = (uint32_t)0x01;
18 } else {
19 HSEStatus = (uint32_t)0x00;
20 }
21
22 // HSE 啟動成功
23 if (HSEStatus == (uint32_t)0x01) {
24 // 調壓器電壓輸出級別配置為1,以便在器件為最大頻率
25 // 工作時使性能和功耗實現平衡
26 RCC->APB1ENR |= RCC_APB1ENR_PWREN;
27 PWR->CR |= PWR_CR_VOS;
28
29 // ②設置AHB/APB2/APB1的分頻因子
30 // HCLK = SYSCLK / 1
31 RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
32 // PCLK2 = HCLK / 2
33 RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
34 // PCLK1 = HCLK / 4
35 RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
36
37 // ③配置主PLL的時鍾來源,設置M,N,P,Q
38 // Configure the main PLL
39 RCC->PLLCFGR = PLL_M|(PLL_N<<6)|
40 (((PLL_P >> 1) -1) << 16) |
41 (RCC_PLLCFGR_PLLSRC_HSE) |
42 (PLL_Q << 24);
43
44 // ④使能主PLL
45 RCC->CR |= RCC_CR_PLLON;
46
47 // 等待PLL穩定
48 while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
49 }
50 /*----------------------------------------------------*/
51 // 開啟 OVER-RIDE模式,以能達到更改頻率
52 PWR->CR |= PWR_CR_ODEN;
53 while ((PWR->CSR & PWR_CSR_ODRDY) == 0) {
54 }
55 PWR->CR |= PWR_CR_ODSWEN;
56 while ((PWR->CSR & PWR_CSR_ODSWRDY) == 0) {
57 }
58 // 配置FLASH預取指,指令緩存,數據緩存和等待狀態
59 FLASH->ACR = FLASH_ACR_PRFTEN
60 |FLASH_ACR_ICEN
61 |FLASH_ACR_DCEN
62 |FLASH_ACR_LATENCY_5WS;
63 /*---------------------------------------------------*/
64
65 // ⑤選擇主PLLCLK作為系統時鍾源
66 RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
67 RCC->CFGR |= RCC_CFGR_SW_PLL;
68
69 // ⑥讀取時鍾切換狀態位,確保PLLCLK選為系統時鍾
70 while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS )
71 != RCC_CFGR_SWS_PLL);
72 {
73 }
74 } else {
75 // HSE 啟動出錯處理
76 }
77 }
1.2.2 其他時鍾
通過對系統時鍾設置的講解,整個時鍾樹我們已經把握的有六七成,剩下的時鍾部分我們講解幾個重要的。
1. A、RTC時鍾
RTCCLK 時鍾源可以是 HSE 1 MHz( HSE 由一個可編程的預分頻器分頻)、 LSE 或者 LSI時鍾。選擇方式是編程 RCC 備份域控制寄存器 (RCC_BDCR) 中的 RTCSEL[1:0] 位和 RCC時鍾配置寄存器 (RCC_CFGR) 中的 RTCPRE[4:0] 位。所做的選擇只能通過復位備份域的方式修改。我們通常的做法是由LSE給RTC提供時鍾,大小為32.768KHZ。LSE由外接的晶體諧振器產生,所配的諧振電容精度要求高,不然很容易不起震。
2. B、獨立看門狗時鍾
獨立看門狗時鍾由內部的低速時鍾LSI提供,大小為32KHZ。
3. C、I2S時鍾
I2S時鍾可由外部的時鍾引腳I2S_CKIN輸入,也可由專用的PLLI2SCLK提供,具體的由RCC 時鍾配置寄存器 (RCC_CFGR)的I2SSCR位配置。我們在使用I2S外設驅動W8978的時候,使用的時鍾是PLLI2SCLK,這樣就可以省掉一個有源晶振。
4. D、PHY以太網時鍾
F429要想實現以太網功能,除了有本身內置的MAC之外,還需要外接一個PHY芯片,常見的PHY芯片有DP83848和LAN8720,其中DP83848支持MII和RMII接口,LAN8720只支持RMII接口。秉火F429開發板用的是RMII接口,選擇的PHY芯片是LAB8720。使用RMII接口的好處是使用的IO減少了一半,速度還是跟MII接口一樣。當使用RMII接口時,PHY芯片只需輸出一路時鍾給MCU即可,如果是MII接口,PHY芯片則需要提供兩路時鍾給MCU。
5. E、USB PHY 時鍾
F429的USB沒有集成PHY,要想實現USB高速傳輸的話,必須外置USB PHY芯片,常用的芯片是USB3300。當外接USB PHY芯片時,PHY芯片需要給MCU提供一個時鍾。
外擴USB3300會占用非常多的IO,跟SDRAM和RGB888的IO會復用的很厲害,鑒於USB高速傳輸用的比較少,秉火429就沒有外擴這個芯片。
6. F、MCO時鍾輸出
MCO是microcontroller clock output的縮寫,是微控制器時鍾輸出引腳,主要作用是可以對外提供時鍾,相當於一個有源晶振。F429中有兩個MCO,由PA8/PC9復用所得。MCO1所需的時鍾源通過 RCC 時鍾配置寄存器 (RCC_CFGR) 中的 MCO1PRE[2:0] 和 MCO1[1:0]位選擇。MCO2所需的時鍾源通過 RCC 時鍾配置寄存器 (RCC_CFGR) 中的 MCO2PRE[2:0] 和 MCO2位選擇。有關MCO的IO、時鍾選擇和輸出速率的具體信息如下表所示:
時鍾輸出 |
IO |
時鍾來源 |
最大輸出速率 |
MCO1 |
PA8 |
HSI、LSE、HSE、PLLCLK |
100M |
MCO2 |
PC9 |
HSE、PLLCLK、SYSCLK、PLLI2SCLK |
100M |
1.3 配置系統時鍾實驗
1.3.1 使用HSE
一般情況下,我們都是使用HSE,然后HSE經過PLL倍頻之后作為系統時鍾。F429系統時鍾最高為180M,這個是官方推薦的最高的穩定時鍾,如果你想鋌而走險,也可以超頻,超頻最高能到216M。
如果我們使用庫函數編程,當程序來到main函數之前,啟動文件:startup_stm32f429_439xx.s已經調用SystemInit()函數把系統時鍾初始化成180MHZ,SystemInit()在庫文件:system_stm32f4xx.c中定義。如果我們想把系統時鍾設置低一點或者超頻的話,可以修改底層的庫文件,但是為了維持庫的完整性,我們可以根據時鍾樹的流程自行寫一個。
1.3.2 使用HSI
當HSE直接或者間接(HSE經過PLL倍頻)的作為系統時鍾的時候,如果HSE發生故障,不僅HSE會被關閉,連PLL也會被關閉,這個時候系統會自動切換HSI作為系統時鍾,此時SYSCLK=HSI=16M,如果沒有開啟CSS和CSS中斷的話,那么整個系統就只能在低速率運行,這是系統跟癱瘓沒什么兩樣。
如果開啟了CSS功能的話,那么可以當HSE故障時,在CSS中斷里面采取補救措施,使用HSI,重新設置系統頻率為180M,讓系統恢復正常使用。但這只是權宜之計,並非萬全之策,最好的方法還是要采取相應的補救措施並報警,然后修復HSE。臨時使用HSI只是為了把損失降低到最小,畢竟HSI較於HSE精度還是要低點。
F103系列中,使用HSI最大只能把系統設置為64M,並不能跟使用HSE一樣把系統時鍾設置為72M,究其原因是HSI在進入PLL倍頻的時候必須2分頻,導致PLL倍頻因子調到最大也只能到64M,而HSE進入PLL倍頻的時候則不用2分頻。
在F429中,無論是使用HSI還是HSE都可以把系統時鍾設置為180M,因為HSE或者HSI在進入PLL倍頻的時候都會被分頻為1M之后再倍頻。
還有一種情況是,有些用戶不想用HSE,想用HSI,但是又不知道怎么用HSI來設置系統時鍾,因為調用庫函數都是使用HSE,下面我們給出個使用HSI配置系統時鍾例子,起個拋磚引玉的作用。
1.3.3 硬件設計
1、RCC
2、LED一個
RCC是單片機內部資源,不需要外部電路。通過LED閃爍的頻率來直觀的判斷不同系統時鍾頻率對軟件延時的效果。
1.3.4 軟件設計
我們編寫兩個RCC驅動文件,bsp_clkconfig.h和bsp_clkconfig.c,用來存放RCC系統時鍾配置函數。
1. 編程要點
1、開啟HSE/HSI ,並等待 HSE/HSI 穩定
2、設置 AHB、APB2、APB1的預分頻因子
3、設置PLL的時鍾來源,設置VCO輸入時鍾 分頻因子PLL_M,設置VCO輸出時鍾
倍頻因子PLL_N,設置PLLCLK時鍾分頻因子PLL_P,設置OTG FS,SDIO,RNG
時鍾分頻因子 PLL_Q
4、開啟PLL,並等待PLL穩定
5、把PLLCK切換為系統時鍾SYSCLK
6、讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾
2. 代碼分析
這里只講解核心的部分代碼,有些變量的設置,頭文件的包含等並沒有涉及到,完整的代碼請參考本章配套的工程。
使用HSE配置系統時鍾
代碼 14 HSE作為系統時鍾來源
1 /*
2 * m: VCO輸入時鍾分頻因子,取值2~63
3 * n: VCO輸出時鍾倍頻因子,取值192~432
4 * p: SYSCLK時鍾分頻因子,取值2,4,6,8
5 * q: OTG FS,SDIO,RNG時鍾分頻因子,取值4~15
6 * 函數調用舉例,使用HSE設置時鍾
7 * SYSCLK=HCLK=180M,PCLK2=HCLK/2=90M,PCLK1=HCLK/4=45M
8 * HSE_SetSysClock(25, 360, 2, 7);
9 * HSE作為時鍾來源,經過PLL倍頻作為系統時鍾,這是通常的做法
10
11 * 系統時鍾超頻到216M爽一下
12 * HSE_SetSysClock(25, 432, 2, 9);
13 */
14 void HSE_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)
15 {
16 __IO uint32_t HSEStartUpStatus = 0;
17
18 // 使能HSE,開啟外部晶振,秉火F429使用 HSE=25M
19 RCC_HSEConfig(RCC_HSE_ON);
20
21 // 等待HSE啟動穩定
22 HSEStartUpStatus = RCC_WaitForHSEStartUp();
23
24 if (HSEStartUpStatus == SUCCESS) {
25 // 調壓器電壓輸出級別配置為1,以便在器件為最大頻率
26 // 工作時使性能和功耗實現平衡
27 RCC->APB1ENR |= RCC_APB1ENR_PWREN;
28 PWR->CR |= PWR_CR_VOS;
29
30 // HCLK = SYSCLK / 1
31 RCC_HCLKConfig(RCC_SYSCLK_Div1);
32
33 // PCLK2 = HCLK / 2
34 RCC_PCLK2Config(RCC_HCLK_Div2);
35
36 // PCLK1 = HCLK / 4
37 RCC_PCLK1Config(RCC_HCLK_Div4);
38
39 // 如果要超頻就得在這里下手啦
40 // 設置PLL來源時鍾,設置VCO分頻因子m,設置VCO倍頻因子n,
41 // 設置系統時鍾分頻因子p,設置OTG FS,SDIO,RNG分頻因子q
42 RCC_PLLConfig(RCC_PLLSource_HSE, m, n, p, q);
43
44 // 使能PLL
45 RCC_PLLCmd(ENABLE);
46
47 // 等待 PLL穩定
48 while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
49 }
50
51 /*-----------------------------------------------------*/
52 //開啟 OVER-RIDE模式,以能達到更高頻率
53 PWR->CR |= PWR_CR_ODEN;
54 while ((PWR->CSR & PWR_CSR_ODRDY) == 0) {
55 }
56 PWR->CR |= PWR_CR_ODSWEN;
57 while ((PWR->CSR & PWR_CSR_ODSWRDY) == 0) {
58 }
59 // 配置FLASH預取指,指令緩存,數據緩存和等待狀態
60 FLASH->ACR = FLASH_ACR_PRFTEN
61 | FLASH_ACR_ICEN
62 | FLASH_ACR_DCEN
63 | FLASH_ACR_LATENCY_5WS;
64 /*-----------------------------------------------------*/
65
66 // 當PLL穩定之后,把PLL時鍾切換為系統時鍾SYSCLK
67 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
68
69 // 讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾
70 while (RCC_GetSYSCLKSource() != 0x08) {
71 }
72 } else {
73 // HSE啟動出錯處理
74
75 while (1) {
76 }
77 }
78 }
這個函數采用庫函數編寫, 代碼理解參考注釋即可。函數有4個形參m、n、p、q,具體說明如下:
形參 |
形參說明 |
取值范圍 |
m |
VCO輸入時鍾 分頻因子 |
2~63 |
n |
VCO輸出時鍾 倍頻因子 |
192~432 |
p |
PLLCLK時鍾分頻因子 |
2/4/6/8 |
q |
OTG FS,SDIO,RNG時鍾分頻因子 |
4~15 |
HSE我們使用25M,參數m我們一般也設置為25,所以我們需要修改系統時鍾的時候只需要修改參數n和p即可,SYSCLK=PLLCLK=HSE/m*n/p。
函數調用舉例:HSE_SetSysClock(25, 360, 2, 7) 把系統時鍾設置為180M,這個跟庫里面的系統時鍾配置是一樣的。HSE_SetSysClock(25, 432, 2, 9)把系統時鍾設置為216M,這個是超頻,要慎用。
使用HSI配置系統時鍾
1 /*
2 * m: VCO輸入時鍾分頻因子,取值2~63
3 * n: VCO輸出時鍾倍頻因子,取值192~432
4 * p: PLLCLK時鍾分頻因子,取值2,4,6,8
5 * q: OTG FS,SDIO,RNG時鍾分頻因子,取值4~15
6 * 函數調用舉例,使用HSI設置時鍾
7 * SYSCLK=HCLK=180M,PCLK2=HCLK/2=90M,PCLK1=HCLK/4=45M
8 * HSI_SetSysClock(16, 360, 2, 7);
9 * HSE作為時鍾來源,經過PLL倍頻作為系統時鍾,這是通常的做法
10
11 * 系統時鍾超頻到216M爽一下
12 * HSI_SetSysClock(16, 432, 2, 9);
13 */
14
15 void HSI_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)
16 {
17 __IO uint32_t HSIStartUpStatus = 0;
18
19 // 把RCC外設初始化成復位狀態
20 RCC_DeInit();
21
22 //使能HSI, HSI=16M
23 RCC_HSICmd(ENABLE);
24
25 // 等待 HSI 就緒
26 HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
27
28 // 只有 HSI就緒之后則繼續往下執行
29 if (HSIStartUpStatus == RCC_CR_HSIRDY) {
30 // 調壓器電壓輸出級別配置為1,以便在器件為最大頻率
31 // 工作時使性能和功耗實現平衡
32 RCC->APB1ENR |= RCC_APB1ENR_PWREN;
33 PWR->CR |= PWR_CR_VOS;
34
35 // HCLK = SYSCLK / 1
36 RCC_HCLKConfig(RCC_SYSCLK_Div1);
37
38 // PCLK2 = HCLK / 2
39 RCC_PCLK2Config(RCC_HCLK_Div2);
40
41 // PCLK1 = HCLK / 4
42 RCC_PCLK1Config(RCC_HCLK_Div4);
43
44 // 如果要超頻就得在這里下手啦
45 // 設置PLL來源時鍾,設置VCO分頻因子m,設置VCO倍頻因子n,
46 // 設置系統時鍾分頻因子p,設置OTG FS,SDIO,RNG分頻因子q
47 RCC_PLLConfig(RCC_PLLSource_HSI, m, n, p, q);
48
49 // 使能PLL
50 RCC_PLLCmd(ENABLE);
51
52 // 等待 PLL穩定
53 while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
54 }
55
56 /*-----------------------------------------------------*/
57 //開啟 OVER-RIDE模式,以能達到更高頻率
58 PWR->CR |= PWR_CR_ODEN;
59 while ((PWR->CSR & PWR_CSR_ODRDY) == 0) {
60 }
61 PWR->CR |= PWR_CR_ODSWEN;
62 while ((PWR->CSR & PWR_CSR_ODSWRDY) == 0) {
63 }
64 // 配置FLASH預取指,指令緩存,數據緩存和等待狀態
65 FLASH->ACR = FLASH_ACR_PRFTEN
66 | FLASH_ACR_ICEN
67 |FLASH_ACR_DCEN
68 |FLASH_ACR_LATENCY_5WS;
69 /*-----------------------------------------------------*/
70
71 // 當PLL穩定之后,把PLL時鍾切換為系統時鍾SYSCLK
72 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
73
74 // 讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾
75 while (RCC_GetSYSCLKSource() != 0x08) {
76 }
77 } else {
78 // HSI啟動出錯處理
79 while (1) {
80 }
81 }
82 }
這個函數采用庫函數編寫, 代碼理解參考注釋即可。函數有4個形參m、n、p、q,具體說明如下:
形參 |
形參說明 |
取值范圍 |
m |
VCO輸入時鍾分頻因子 |
2~63 |
n |
VCO輸出時鍾倍頻因子 |
192~432 |
p |
PLLCLK時鍾分頻因子 |
2/4/6/8 |
q |
OTG FS,SDIO,RNG時鍾分頻因子 |
4~15 |
HSI為16M,參數m我們一般也設置為16,所以我們需要修改系統時鍾的時候只需要修改參數n和p即可,SYSCLK=PLLCLK=HSI/m*n/p。
函數調用舉例:HSI_SetSysClock(16, 360, 2, 7) 把系統時鍾設置為180M,這個跟庫里面的系統時鍾配置是一樣的。HSI_SetSysClock(16, 432, 2, 9)把系統時鍾設置為216M,這個是超頻,要慎用。
軟件延時
1 void Delay(__IO uint32_t nCount)
2 {
3 for (; nCount != 0; nCount--);
4 }
軟件延時函數,使用不同的系統時鍾,延時時間不一樣,可以通過LED閃爍的頻率來判斷。
MCO輸出
在F429中,PA8/PC9可以復用為MCO1/2引腳,對外提供時鍾輸出,我們也可以用示波器監控該引腳的輸出來判斷我們的系統時鍾是否設置正確。
代碼 15 MCO GPIO初始化
1 // MCO1 PA8 GPIO 初始化
2 void MCO1_GPIO_Config(void)
3 {
4 GPIO_InitTypeDef GPIO_InitStructure;
5 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
6
7 // MCO1 GPIO 配置
8 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
11 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
12 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
13 GPIO_Init(GPIOA, &GPIO_InitStructure);
14 }
15
16 // MCO2 PC9 GPIO 初始化
17 void MCO2_GPIO_Config(void)
18 {
19 GPIO_InitTypeDef GPIO_InitStructure;
20 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
21
22 // MCO2 GPIO 配置
23 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
24 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
25 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
26 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
27 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
28 GPIO_Init(GPIOC, &GPIO_InitStructure);
29 }
秉火F429中PA8並沒有引出,只引出了PC9,如果要用示波器監控MCO,只能用 PC9。
代碼 16 MCO輸出時鍾選擇
1 // MCO1 輸出PLLCLK
2 RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_1);
3
4 // MCO1 輸出SYSCLK
5 RCC_MCO2Config(RCC_MCO2Source_SYSCLK, RCC_MCO1Div_1);
我們初始化MCO引腳之后,可以直接調用庫函數RCC_MCOxConfig()來選擇MCO時鍾來源,同時還可以分頻,這兩個參數的取值參考庫函數說明即可。
主函數
在主函數中,可以調用HSE_SetSysClock()或者HSI_SetSysClock()這兩個函數把系統時鍾設置成各種常用的時鍾,然后通過MCO引腳監控,或者通過LED閃爍的快慢體驗不同的系統時鍾對同一個軟件延時函數的影響。
1 int main(void)
2 {
3 // 程序來到main函數之前,啟動文件:statup_stm32f10x_hd.s已經調用
4 // SystemInit()函數把系統時鍾初始化成72MHZ
5 // SystemInit()在system_stm32f10x.c中定義
6 // 如果用戶想修改系統時鍾,可自行編寫程序修改
7 // 重新設置系統時鍾,這時候可以選擇使用HSE還是HSI
8
9 // 使用HSE,配置系統時鍾為180M
10 HSE_SetSysClock(25, 360, 2, 7);
11
12 //系統時鍾超頻到216M爽一下,最高是216M,別往死里整
13 //HSE_SetSysClock(25, 432, 2, 9);
14
15 // 使用HSI,配置系統時鍾為180M
16 //HSI_SetSysClock(16, 360, 2, 7);
17
18 // LED 端口初始化
19 LED_GPIO_Config();
20
21 // MCO GPIO 初始化
22 MCO1_GPIO_Config();
23 MCO2_GPIO_Config();
24
25 // MCO1 輸出PLLCLK
26 RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_1);
27
28 // MCO2 輸出SYSCLK
29 RCC_MCO2Config(RCC_MCO2Source_SYSCLK, RCC_MCO1Div_1);
30
31 while (1) {
32 LED1( ON ); // 亮
33 Delay(0x0FFFFF);
34 LED1( OFF ); // 滅
35 Delay(0x0FFFFF);
36 }
37 }
1.3.5 下載驗證
把編譯好的程序下載到開發板,可以看到設置不同的系統時鍾時,LED閃爍的快慢不一樣。更精確的數據我們可以用示波器監控MCO引腳看到。
1.4 每課一問
1、簡述系統時鍾的配置過程
2、F429系統時鍾最 高穩定運行是多少M