本章參考資料:《STM32F7xx參考手冊》RCC章節。
學習本章時,配合《STM32F7xx參考手冊》RCC章節一起閱讀,效果會更佳,特別是涉及到寄存器說明的部分。
RCC :reset clock control 復位和時鍾控制器。本章我們主要講解時鍾部分,特別是要着重理解時鍾樹,理解了時鍾樹,F767的一切時鍾的來龍去脈都會了如指掌。
15.1 RCC主要作用—時鍾部分
設置系統時鍾SYSCLK、設置AHB分頻因子(決定HCLK等於多少)、設置APB2分頻因子(決定PCLK2等於多少)、設置APB1分頻因子(決定PCLK1等於多少)、設置各個外設的分頻因子;控制AHB、APB2和APB1這三條總線時鍾的開啟、控制每個外設的時鍾的開啟。對於SYSCLK、HCLK、PCLK2、PCLK1這四個時鍾的配置一般是:HCLK = SYSCLK=PLLCLK = 216 MHz,PCLK1=HCLK/2 = 108 MHz,PCLK1=HCLK/4 = 54 MHz。這個時鍾配置也是庫函數的標准配置,我們用的最多的就是這個。
15.2 RCC框圖剖析—時鍾樹
時鍾樹單純講理論的話會比較枯燥,如果選取一條主線,並輔以代碼,先主后次講解的話會很容易,而且記憶還更深刻。這里以配置系統時鍾函數SystemClock_Config () 的編寫流程來講解時鍾樹。該函數的功能是利用外部晶振HSE=25 MHz把時鍾倍頻設置為:HCLK = SYSCLK=PLLCLK = 216 MHz,PCLK1=HCLK/2 =108 MHz,PCLK1=HCLK/4 = 54 MHz下面我們就以這個代碼的流程為主線,來分析時鍾樹,代碼流程在時鍾樹中以數字的大小順序標識。
圖 15-1 STM32F767時鍾樹1
圖 15-2 STM32F767時鍾樹2
15.2.1 系統時鍾
1. ①HSE高速外部時鍾信號
HSE是高速的外部時鍾信號,可以由有源晶振或者無源晶振提供,頻率從4-26 MHz不等。當使用有源晶振時,時鍾從OSC_IN引腳進入,OSC_OUT引腳懸空為高阻態,當選用無源晶振時,時鍾從OSC_IN進入,OSC_OUT輸出,並且要配諧振電容。HSE我們使用25 MHz的無源晶振。如果我們使用HSE或者HSE經過PLL倍頻之后的時鍾作為系統時鍾SYSCLK,當HSE故障時候,不僅HSE會被關閉,PLL也會被關閉,此時高速的內部時鍾時鍾信號HSI會作為備用的系統時鍾,直到HSE恢復正常,HSI=16 MHz。
2. ②鎖相環PLL
PLL的主要作用是對時鍾進行倍頻,然后把時鍾輸出到各個功能部件。PLL有三個,分別是主PLL、 PLLSAI、PLLI2S,他們均由HSE或者HSI提供時鍾輸入信號。
主PLL有兩路的時鍾輸出,第一個輸出時鍾PLLCLK用於系統時鍾,F767里面最高是216 MHz,第二個輸出用於USB OTG FS的時鍾(48 MHz)、RNG和SDMMC時鍾(<=48 MHz)。PLLI2S用於生成精確時鍾,給I2S、SAI、SPDIFRX提供時鍾。PLLSAI為SAI、LCD_TFT提供時鍾,同時也可以產生PLLSAI48CLK時鍾用於USB OTG FS的時鍾(48 MHz)、RNG和SDMMC時鍾。
HSE或者HSI經過PLL時鍾輸入分頻因子M(2~63)分頻后,成為VCO的時鍾輸入,VCO的時鍾必須在1~2 MHz之間,我們選擇HSE=25 MHz作為PLL的時鍾輸入,M設置為25,那么VCO輸入時鍾就等於1 MHz。
VCO輸入時鍾經過VCO倍頻因子N倍頻之后,成為VCO時鍾輸出,VCO時鍾必須在192~432 MHz之間。我們配置N為432,則VCO的輸出時鍾等於432 MHz。
VCO輸出時鍾之后有三個分頻因子:PLLCLK分頻因子p,USB OTG FS/RNG/SDMMC時鍾分頻因子Q,分頻因子R(F769才有,F767沒有)產生DSI時鍾驅動屏幕。p可以取值2、4、6、8,我們配置為2,則得到PLLCLK=216 MHz。Q可以取值4~15,但是USB OTG FS必須使用48M,Q=VCO輸出時鍾432/48=9。有關PLL的配置有一個專門的RCC PLL配置寄存器RCC_PLLCFGR,具體描述看手冊即可。
PLL的時鍾配置經過,稍微整理下可由如下公式表達:
VCOCLK_IN = PLLCLK_IN / M = HSE / 25 = 1 MHz
VCOCLK_OUT = VCOCLK_IN * N = 1M * 432 = 432 MHz
PLLCLK_OUT=VCOCLK_OUT/P=432/2=216 MHz
USBCLK = VCOCLK_OUT/Q=432/9=48。
3. ③系統時鍾SYSCLK
系統時鍾來源可以是:HSI、PLLCLK、HSE,具體的由時鍾配置寄存器RCC_CFGR的SW位配置。我們這里設置系統時鍾:SYSCLK = PLLCLK = 216 MHz。如果系統時鍾是由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=216 MHz。
5. ⑤APB1總線時鍾HCLK1
APB1總線時鍾PCLK1由HCLK經過低速APB預分頻器得到,分頻因子可以是: [1,2,4,8,16],具體由時鍾配置寄存器RCC_CFGR的PPRE1位設置。HCLK1屬於低速的總線時鍾,最高為54 MHz,片上低速的外設就掛載到這條總線上,比如LPTIM、USART2/3/4/5/7/8、SPI2/3,I2C1/2等。至於APB1總線上的外設的時鍾設置為多少,得等到我們使用該外設的時候才設置,我們這里只需粗線條的設置好APB1的時鍾即可。我們這里設置為4分頻,即PCLK1 = HCLK/4 = 54 MHz。
6. ⑥APB2總線時鍾HCLK2
APB2總線時鍾PCLK2由HCLK經過高速APB2預分頻器得到,分頻因子可以是: [1,2,4,8,16],具體由時鍾配置寄存器RCC_CFGR的PPRE2位設置。HCLK2屬於高速的總線時鍾,片上高速的外設就掛載到這條總線上,比如全部的GPIO、USART1/6、SPI1等。至於APB2總線上的外設的時鍾設置為多少,得等到我們使用該外設的時候才設置,我們這里只需粗線條的設置好APB2的時鍾即可。我們這里設置為2分頻,即PCLK2 = HCLK /2= 108 MHz。
7. 設置系統時鍾庫函數
上面的6個步驟對應的設置系統時鍾庫函數如下,為了方便閱讀,已經把跟767不相關的代碼刪掉,把英文注釋翻譯成了中文。該函數是直接填寫相應的結構體,最后調用HAL_RCC_OscConfig函數和HAL_RCC_ClockConfig函數就可以初始化時鍾,這里需要注意的是,由於在 PLL 使能后主 PLL 配置參數便不可更改,所以建議先對 PLL 進行配置,然后再使能(選擇 HSI 或 HSE 振盪器作為 PLL 時鍾源,並配置分頻系數 M、 N、 P 和 Q)。
代碼 10 設置系統時鍾庫函數
1 /** 2 * @brief System Clock 配置 3 * system Clock 配置如下 : 4 * System Clock source = PLL (HSE) 5 * SYSCLK(Hz) = 216000000 6 * HCLK(Hz) = 216000000 7 * AHB Prescaler = 1 8 * APB1 Prescaler = 4 9 * APB2 Prescaler = 2 10 * HSE Frequency(Hz) = 25000000 11 * PLL_M = 25 12 * PLL_N = 432 13 * PLL_P = 2 14 * PLL_Q = 9 15 * VDD(V) = 3.3 16 * Main regulator output voltage = Scale1 mode 17 * Flash Latency(WS) = 7 18 * @param 無 19 * @retval 無 20 */ 21 void SystemClock_Config(void) 22 { 23 RCC_ClkInitTypeDef RCC_ClkInitStruct; 24 RCC_OscInitTypeDef RCC_OscInitStruct; 25 HAL_StatusTypeDef ret = HAL_OK; 26 27 /* 28 使能HSE,配置HSE為PLL的時鍾源,配置PLL的各種分頻 29 因子M N P Q 30 * PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M 31 */ 32 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; 33 RCC_OscInitStruct.HSEState = RCC_HSE_ON; 34 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; 35 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; 36 RCC_OscInitStruct.PLL.PLLM = 25; 37 RCC_OscInitStruct.PLL.PLLN = 432; 38 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; 39 RCC_OscInitStruct.PLL.PLLQ = 9; 40 41 ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); 42 if (ret != HAL_OK) { 43 while (1) { 44 ; 45 } 46 } 47 48 /* 激活 OverDrive 模式以達到216M頻率 */ 49 ret = HAL_PWREx_EnableOverDrive(); 50 if (ret != HAL_OK) { 51 while (1) { 52 ; 53 } 54 } 55 56 /* 選擇PLLCLK作為SYSCLK,並配置 HCLK, PCLK1 and PCLK2 57 的時鍾分頻因子 58 * SYSCLK = PLLCLK = 216M 59 * HCLK = SYSCLK / 1 = 216M 60 * PCLK2 = SYSCLK / 2 = 108M 61 * PCLK1 = SYSCLK / 4 = 54M 62 */ 63 RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | 64 RCC_CLOCKTYPE_HCLK | 65 RCC_CLOCKTYPE_PCLK1 | 66 RCC_CLOCKTYPE_PCLK2); 67 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; 68 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; 69 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; 70 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; 71 72 ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7); 73 if (ret != HAL_OK) { 74 while (1) { 75 ; 76 } 77 } 78 }
15.2.2 其他時鍾
通過對系統時鍾設置的講解,整個時鍾樹我們已經把握的有六七成,剩下的時鍾部分我們講解幾個重要的。
1. RTC時鍾
圖 15-3 STM32F767 RTC和IWDG時鍾
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. 獨立看門狗時鍾
獨立看門狗時鍾由內部的低速時鍾LSI提供,大小為32KHZ。
3. I2S時鍾
I2S時鍾可由外部的時鍾引腳I2S_CKIN輸入,也可由專用的PLLI2SCLK提供,具體的由RCC 時鍾配置寄存器 (RCC_CFGR)的I2SSCR位配置。我們在使用I2S外設驅動W8978的時候,使用的時鍾是PLLI2SCLK,這樣就可以省掉一個有源晶振。
4. PHY以太網時鍾
F767要想實現以太網功能,除了有本身內置的MAC之外,還需要外接一個PHY芯片,常見的PHY芯片有DP83848和LAN8720,其中DP83848支持MII和RMII接口,LAN8720只支持RMII接口。秉火F767開發板用的是RMII接口,選擇的PHY芯片是LAN8720。使用RMII接口的好處是使用的IO減少了一半,速度還是跟MII接口一樣。當使用RMII接口時,PHY芯片只需輸出一路時鍾給MCU即可,如果是MII接口,PHY芯片則需要提供兩路時鍾給MCU。
5. USB PHY 時鍾
F767的USB沒有集成PHY,要想實現USB高速傳輸的話,必須外置USB PHY芯片,常用的芯片是USB3300。當外接USB PHY芯片時,PHY芯片需要給MCU提供一個時鍾。
外擴USB3300會占用非常多的IO,跟SDRAM和RGB888的IO會復用的很厲害,鑒於USB高速傳輸用的比較少,秉火767就沒有外擴這個芯片。
6. MCO時鍾輸出
圖 15-4 STM32F767 MCO時鍾
MCO是microcontroller clock output的縮寫,是微控制器時鍾輸出引腳,主要作用是可以對外提供時鍾,相當於一個有源晶振。F767中有兩個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 |
108MHz |
MCO2 |
PC9 |
HSE、PLLCLK、SYSCLK、PLLI2SCLK |
108MHz |
15.3 配置系統時鍾實驗
15.3.1 使用HSE
一般情況下,我們都是使用HSE,然后HSE經過PLL倍頻之后作為系統時鍾。F767系統時鍾最高為216 MHz,這個是官方推薦的最高的穩定時鍾。
如果我們使用庫函數編程,當程序來到main函數首先調用SystemClock_Config ()函數把系統時鍾初始化成216 MHz,SystemClock_Config ()在文件:main.c中定義。如果我們想把系統時鍾設置低一點或者超頻的話,可以修改底層的庫文件。
15.3.2 使用HSI
當HSE直接或者間接(HSE經過PLL倍頻)的作為系統時鍾的時候,如果HSE發生故障,不僅HSE會被關閉,連PLL也會被關閉,這個時候系統會自動切換HSI作為系統時鍾,此時SYSCLK=HSI=16M,如果沒有開啟CSS和CSS中斷的話,那么整個系統就只能在低速率運行,這是系統跟癱瘓沒什么兩樣。
如果開啟了CSS功能的話,那么可以當HSE故障時,在CSS中斷里面采取補救措施,使用HSI,重新設置系統頻率為216 MHz,讓系統恢復正常使用。但這只是權宜之計,並非萬全之策,最好的方法還是要采取相應的補救措施並報警,然后修復HSE。臨時使用HSI只是為了把損失降低到最小,畢竟HSI較於HSE精度還是要低點。
F103系列中,使用HSI最大只能把系統設置為64M,並不能跟使用HSE一樣把系統時鍾設置為72M,究其原因是HSI在進入PLL倍頻的時候必須2分頻,導致PLL倍頻因子調到最大也只能到64M,而HSE進入PLL倍頻的時候則不用2分頻。
在F767中,無論是使用HSI還是HSE都可以把系統時鍾設置為216 MHz,因為HSE或者HSI在進入PLL倍頻的時候都會被分頻為1M之后再倍頻。
還有一種情況是,有些用戶不想用HSE,想用HSI,但是又不知道怎么用HSI來設置系統時鍾,因為調用庫函數都是使用HSE,下面我們給出個使用HSI配置系統時鍾例子,起個拋磚引玉的作用。
15.3.3 硬件設計
1、RCC
2、LED一個
RCC是單片機內部資源,不需要外部電路。通過LED閃爍的頻率來直觀的判斷不同系統時鍾頻率對軟件延時的效果。
15.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配置系統時鍾
代碼 11 HSE作為系統時鍾來源
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 /* 16 * m: VCO輸入時鍾 分頻因子,取值2~63 17 * n: VCO輸出時鍾 倍頻因子,取值50~432 18 * p: PLLCLK時鍾分頻因子 ,取值2,4,6,8 19 * q: OTG FS,SDIO,RNG時鍾分頻因子,取值4~15 20 * 函數調用舉例,使用HSE設置時鍾 21 * SYSCLK=HCLK=216MHz,PCLK2=HCLK/2=108MHz,PCLK1=HCLK/4=54MHz 22 * HSE_SetSysClock(25, 432, 2, 9); 23 * 24 HSE作為時鍾來源,經過PLL倍頻作為系統時鍾,這是通常 25 的做法 26 */ 27 void HSE_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q) 28 { 29 RCC_ClkInitTypeDef RCC_ClkInitStruct; 30 RCC_OscInitTypeDef RCC_OscInitStruct; 31 HAL_StatusTypeDef ret = HAL_OK; 32 33 /* 34 使能HSE,配置HSE為PLL的時鍾源,配置PLL的各種分頻因 35 子M N P Q 36 * PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M 37 */ 38 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; 39 RCC_OscInitStruct.HSEState = RCC_HSE_ON; 40 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; 41 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; 42 RCC_OscInitStruct.PLL.PLLM = m; 43 RCC_OscInitStruct.PLL.PLLN = n; 44 RCC_OscInitStruct.PLL.PLLP = p; 45 RCC_OscInitStruct.PLL.PLLQ = q; 46 47 ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); 48 if (ret != HAL_OK) { 49 while (1) { 50 ; 51 } 52 } 53 54 /* 激活 OverDrive 模式以達到216M頻率 */ 55 ret = HAL_PWREx_EnableOverDrive(); 56 if (ret != HAL_OK) { 57 while (1) { 58 ; 59 } 60 } 61 62 /* 選擇PLLCLK作為SYSCLK,並配置 HCLK, PCLK1 and PCLK2 63 的時鍾分頻因子 64 * SYSCLK = PLLCLK = 216M 65 * HCLK = SYSCLK / 1 = 216M 66 * PCLK2 = SYSCLK / 2 = 108M 67 * PCLK1 = SYSCLK / 4 = 54M 68 */ 69 RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | 70 RCC_CLOCKTYPE_HCLK | 71 RCC_CLOCKTYPE_PCLK1 | 72 RCC_CLOCKTYPE_PCLK2); 73 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; 74 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; 75 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; 76 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; 77 78 ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7); 79 if (ret != HAL_OK) { 80 while (1) { 81 ; 82 } 83 } 84 }
這個函數采用庫函數編寫, 代碼理解參考注釋即可。函數有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, 400, 2, 7) 把系統時鍾設置為200 MHz。HSE_SetSysClock(25, 432, 2, 9)把系統時鍾設置為216M。
使用HSI配置系統時鍾
1 /* 2 * 使用HSI時,設置系統時鍾的步驟 3 * 1、開啟HSI ,並等待 HSI 穩定 4 * 2、設置 AHB、APB2、APB1的預分頻因子 5 * 3、設置PLL的時鍾來源 6 * 設置VCO輸入時鍾 分頻因子 m 7 * 設置VCO輸出時鍾 倍頻因子 n 8 * 設置SYSCLK時鍾分頻因子 p 9 * 設置OTG FS,SDIO,RNG時鍾分頻因子 q 10 * 4、開啟PLL,並等待PLL穩定 11 * 5、把PLLCK切換為系統時鍾SYSCLK 12 * 6、讀取時鍾切換狀態位,確保PLLCLK被選為系統時鍾 13 */ 14 15 /* 16 * m: VCO輸入時鍾 分頻因子,取值2~63 17 * n: VCO輸出時鍾 倍頻因子,取值50~432 18 * p: PLLCLK時鍾分頻因子 ,取值2,4,6,8 19 * q: OTG FS,SDIO,RNG時鍾分頻因子,取值4~15 20 * 函數調用舉例,使用HSI設置時鍾 21 * SYSCLK=HCLK=216MHz,PCLK2=HCLK/2=108M,PCLK1=HCLK/4=54MHz 22 * HSI_SetSysClock(16, 432, 2, 7); 23 24 */ 25 void HSI_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q) 26 { 27 RCC_ClkInitTypeDef RCC_ClkInitStruct; 28 RCC_OscInitTypeDef RCC_OscInitStruct; 29 HAL_StatusTypeDef ret = HAL_OK; 30 31 /* 32 使能HSE,配置HSE為PLL的時鍾源,配置PLL的各種分頻因 33 子M N P Q 34 * PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M 35 */ 36 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; 37 RCC_OscInitStruct.HSIState = RCC_HSI_ON; 38 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; 39 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; 40 RCC_OscInitStruct.PLL.PLLM = m; 41 RCC_OscInitStruct.PLL.PLLN = n; 42 RCC_OscInitStruct.PLL.PLLP = p; 43 RCC_OscInitStruct.PLL.PLLQ = q; 44 45 ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); 46 if (ret != HAL_OK) { 47 while (1) { 48 ; 49 } 50 } 51 52 /* 激活 OverDrive 模式以達到216M頻率 */ 53 ret = HAL_PWREx_EnableOverDrive(); 54 if (ret != HAL_OK) { 55 while (1) { 56 ; 57 } 58 } 59 60 /* 選擇PLLCLK作為SYSCLK,並配置 HCLK, PCLK1 and PCLK2 61 的時鍾分頻因子 62 * SYSCLK = PLLCLK = 216M 63 * HCLK = SYSCLK / 1 = 216M 64 * PCLK2 = SYSCLK / 2 = 108M 65 * PCLK1 = SYSCLK / 4 = 54M 66 */ 67 RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | 68 RCC_CLOCKTYPE_HCLK | 69 RCC_CLOCKTYPE_PCLK1 | 70 RCC_CLOCKTYPE_PCLK2); 71 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; 72 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; 73 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; 74 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; 75 76 ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7); 77 if (ret != HAL_OK) { 78 while (1) { 79 ; 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, 400, 2, 7) 把系統時鍾設置為200MHz,。HSI_SetSysClock(16, 432, 2, 9)把系統時鍾設置為216MHz。
軟件延時
1 void Delay(__IO uint32_t nCount) 2 { 3 for (; nCount != 0; nCount--); 4 }
軟件延時函數,使用不同的系統時鍾,延時時間不一樣,可以通過LED閃爍的頻率來判斷。
MCO輸出
在F767中,PA8/PC9可以復用為MCO1/2引腳,對外提供時鍾輸出,我們也可以用示波器監控該引腳的輸出來判斷我們的系統時鍾是否設置正確。HAL庫有現成的庫函數HAL_RCC_MCOConfig,配置MCO,只需確定輸出引腳,輸出時鍾源,以及分頻系數就可以輸出時鍾使用非常方便。
主函數
在主函數中,可以調用HSE_SetSysClock()或者HSI_SetSysClock()這兩個函數把系統時鍾設置成各種常用的時鍾,然后通過MCO引腳監控,或者通過LED閃爍的快慢體驗不同的系統時鍾對同一個軟件延時函數的影響。
1 int main(void) 2 { 3 4 // 5 程序來到main函數之前,啟動文件:statup_stm32f746xx. 6 s已經調用 7 // SystemInit()函數把系統時鍾初始化成16MHZ 8 // SystemInit()在system_stm32f7xx.c中定義 9 // 如果用戶想修改系統時鍾,可自行編寫程序修改 10 // 11 重新設置系統時鍾,這時候可以選擇使用HSE還是HSI 12 13 //系統時鍾設置為216M,最高是250M 14 HSE_SetSysClock(25, 432, 2, 9); 15 16 // 使用HSI,配置系統時鍾為216M 17 //HSI_SetSysClock(16, 50, 2, 9); 18 19 // LED 端口初始化 20 LED_GPIO_Config(); 21 22 // MCO1 輸出PLLCLK 23 HAL_RCC_MCOConfig(RCC_MCO1,RCC_MCO1SOURCE_PLLCLK, RCC_MCODIV_2); 24 25 // MCO2 輸出SYSCLK 26 HAL_RCC_MCOConfig(RCC_MCO2,RCC_MCO2SOURCE_SYSCLK, RCC_MCODIV_2); 27 28 while (1) { 29 LED1( ON ); // 亮 30 Delay(0x0FFFFF); 31 LED1( OFF ); // 滅 32 Delay(0x0FFFFF); 33 } 34 }
15.3.5 下載驗證
15.3.6 下載驗證
把編譯好的程序下載到開發板,可以看到設置不同的系統時鍾時,LED閃爍的快慢不一樣。更精確的數據我們可以用示波器監控MCO引腳看到。