本章參考資料:《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 }