STM32F429–RCC時鐘樹簡介
- RCC全稱:reset clock config ,復位和時鐘控制,在中文參考手冊的第六章
- 系統時鐘: SYSCLK,首選PPL爲系統時鐘,可達180MHZ
- 時鐘樹 :單片機所有的時鐘
學前概念須知:
STM32F429有5條總線 越高時鐘越快由低到高分別是 :
APB1 -->APB2–>AHB1–>AHB2–>AHB3
-
HSE: 高速的外部時鐘,板子採用無源晶振,設置爲25M,精度較高,一般配置爲這個。
-
HSI: 高速的內部時鐘,爲16M,當HSE故障時,自動切換到HSI,直到HSE啓動.
-
PLLCLK:鎖相環時鐘 HSE->PLL->倍頻到180M M/N/P
系統時鐘:來源 HSI HSE PLLCLK 控制RCC_CFGR時鐘配置寄存器的SW位 配置爲10PLLCLK -
HCLK:【 AHB高速總線時鐘】,最高180M,爲AHB總線外設提供 [important]
-
AHB: advanced high-performance bus 【高性能總線】
-
FCLK:free clock 內核CPU自由時鐘
-
RTC: real time clock,實時時鐘,爲芯片內部的RTC提供時鐘,具體看功能框圖
下面都是由低到高速,對應5條總線的時鐘 ,後面是分頻因子
- PCLK1: 爲APB1總線提供45M時鐘,APB1總線定時器2倍頻後90M時鐘 RCC_CFGR寄存器配置,PPRE1位
- PCLK2: 爲APB2總線提供90M時鐘,APB2總線定時器2倍頻後180M時鐘 RCC_CFGR寄存器配置,PPRE2位
- PPRE2: APB高速預分頻器
- PPRE1: APB地速預分頻器
- APB1一般設置爲45M , APB2一般設置爲 90M
STM32F429時鐘樹功能框圖
說明:
1–6部分爲主系統時鐘,A~F爲外設和其他時鐘
圖中最高爲168MHZ有誤,應改爲180MHZ
系統時鐘配置流程:
static void SetSysClock(void)
選擇HSE打開,默認25M -->
進入鎖相環/M=25 ,最終爲1M–>
設置倍頻因子xN = 360M,VCO默認1 最終爲360M–>
/ P設置爲2 最終360/2=180M–>
選擇PLLCLK作爲系統時鐘,不選HSI/HSE–>
設置1倍分頻,得HCLK 360M–>
設置倍頻因子爲8,360/8=45M,最後得到APB1 45MHZ
設置倍頻因子爲8,360/4=90M,最後得到APB2 90MHZ
其他外設的時鐘需要用到的時候再進行配置,這裏不作說明。
總結:
先配置好HSI/HSEPLL振盪器作爲PLL時鐘源,並配置分頻係數M/N/P/Q
A,B,C,D,F時鐘在需要的時候才需要配置。
可通過MCO時鐘輸出來檢測自己配置的PLLCLK在示波器顯示,來判斷是否成功配置爲180MHZ。
程序部分
1-使能HSE,並等待HSE穩定
2-配置 AHB APB2 APB1 總線的預分頻因子
3-配置 PLL的各種分頻因子,並使能PLL
4-選擇系統時鐘來源
想要配置自己想要的時鐘,可以直接修改提供的SetSysClock函數,不過爲了保證庫函數的完整性,我們可以通過固件庫來自己實現一個,一般不選用HSI,精確度沒有HSE的高。下面程序採用條件編譯的方式,#if 0#else #endif 部分是從系統自帶的時鐘配置函數裁剪下來的部分。
bsp_rccclkconfig.c
#include "bsp_rccclkconfig.h"
#if 0
static void SetSysClock(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/*-------------------------1-使能HSE,並等待HSE穩定----------------------------------------*/
/* 使能 HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待HSE啓動完成,如果超時則跳出 */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
/* HSE啓動成功 */
if (HSEStatus == (uint32_t)0x01)
{
/*----------------------------------------------------------------------------------------*/
/* 選擇電壓調節器輸出爲模式1 */
/* 使能電源接口時鐘 */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
/*----------------------------2-配置 AHB APB2 APB1 總線的預分頻因子-----------------------*/
/* HCLK = SYSCLK / 1*/
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK / 2*/
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
/* PCLK1 = HCLK / 4*/
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
/*----------------------------3-配置 PLL的各種分頻因子,並使能PLL-----------------------*/
/* 配置 主 PLL */
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
/* 使能 PLL */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL啓動穩定 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/*----------------------------------------------------------------------------------------*/
/* 打開OVER-Drive模式,爲的是達到更高的頻率 */
PWR->CR |= PWR_CR_ODEN;
while((PWR->CSR & PWR_CSR_ODRDY) == 0)
{
}
PWR->CR |= PWR_CR_ODSWEN;
while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
{
}
/* 配置 Flash 預取指, 指令緩存, 數據緩存 和等待週期 */
FLASH->ACR = FLASH_ACR_PRFTEN |
FLASH_ACR_ICEN |
FLASH_ACR_DCEN |
FLASH_ACR_LATENCY_5WS;
/*----------------------------4-選擇系統時鐘來源-----------------------------------------*/
/* 選擇主鎖相環時鐘作爲系統時鐘 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
/* 等待PLLCLH切換稱系統時鐘 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
{
}
}
else
{ /* HSE啓動失敗後,用戶糾錯的代碼 */
}
}
#endif
取值範圍:
// m : 2~63
// n :192~432
// p :2、4、6、8
// q :2~15
// SYSCLK = (HSE/m) * n /p = 25/25 * 432 / 2 = 216M
// HSE_SetSysClk(25,432,2,7)
void HSE_SetSysClk(uint32_t m,uint32_t n,uint32_t p,uint32_t q)
{
__IO uint32_t HSEStartUpStatus = 0;
/* 使能HSE 並等待HSE穩定*/
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if( HSEStartUpStatus == SUCCESS )
{
/* 選擇電壓調節器輸出爲模式1 */
/* 使能電源接口時鐘 */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div2);
RCC_PCLK1Config(RCC_HCLK_Div4);
RCC_PLLConfig(RCC_PLLSource_HSE, m, n, p, q);
RCC_PLLCmd(ENABLE);
while ( ( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) ) == RESET )
{
}
/* 打開OVER-Drive模式,爲的是達到更高的頻率 */
PWR->CR |= PWR_CR_ODEN;
while((PWR->CSR & PWR_CSR_ODRDY) == 0)
{
}
PWR->CR |= PWR_CR_ODSWEN;
while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
{
}
/* 配置 Flash 預取指, 指令緩存, 數據緩存 和等待週期 */
FLASH->ACR = FLASH_ACR_PRFTEN |
FLASH_ACR_ICEN |
FLASH_ACR_DCEN |
FLASH_ACR_LATENCY_5WS;
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while ( ( RCC_GetSYSCLKSource() ) != 0X08 )
{
}
}
else{ /* HSE啓動識別,用戶在這裏添加糾錯代碼 */ }
}
/*
*以下部分是配置MCO引腳輸出的配置函數
*/
// MCO1 PA8 GPIO 初始化
void MCO1_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// MCO1 GPIO 配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
// MCO2 PC9 GPIO 初始化
void MCO2_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
// MCO2 GPIO 配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
bsp_rccclkconfig.h
#ifndef __BSP_RCCCLKCONFIG_H
#define __BSP_RCCCLKCONFIG_H
#include "stm32f4xx_rcc.h"
void HSE_SetSysClk(uint32_t m,uint32_t n,uint32_t p,uint32_t q);
#endif /*__BSP_RCCCLKCONFIG_H*/
main.c
#include "stm32f4xx.h"
#include "bsp_rccclkconfig.h"
int main(void)
{
// 程序來到main函數之前,啓動文件已經調用SystemInit()函數把系統時鐘初始化成180MHZ
// SystemInit()在system_stm32f4xx.c中定義,如果想修改系統時鐘,可自行編寫程序修改
// 重新設置系統時鐘,這時候可以選擇使用HSE還是HSI
/* 把系統時鐘初始化爲216M */
HSE_SetSysClk(25,432,2,9);
// MCO1 輸出PLLCLK
RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_1);
while (1);
}