再造STM32---第十三部分:RCC—使用 HSE/HSI 配置时钟

       配合《STM32F4xx 中文参考手册》 RCC 章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。
       RCC : reset clock control 复位和时钟控制器。本章我们主要讲解时钟部分,特别是要着重理解时钟树,理解了时钟树, F429 的一切时钟的来龙去脉都会了如指掌。


13.1 RCC 主要作用—时钟部分:

       设置系统时钟 SYSCLK、设置 AHB 分频因子(决定 HCLK 等于多少) 、 设置 APB2 分频因子(决定 PCLK2 等于多少)、设置 APB1 分频因子(决定 PCLK1 等于多少)、设置各个外设的分频因子;控制 AHB、 APB2 和 APB1 这三条总线时钟的开启、控制每个外设的时钟的开启。对于 SYSCLK、 HCLK、 PCLK2、 PCLK1 这四个时钟的配置一般是: HCLK =SYSCLK=PLLCLK = 180MPCLK1=HCLK/2 = 90MPCLK1=HCLK/4 = 45M。这个时钟配置也是库函数的标准配置,我们用的最多的就是这个。

13.2 RCC 框图剖析—时钟树:

       时钟树单纯讲理论的话会比较枯燥,如果选取一条主线,并辅以代码,先主后次讲解的话会很容易,而且记忆还更深刻。我们这里选取库函数时钟系统时钟函数:SetSysClock(); 以这个函数的编写流程来讲解时钟树,这个函数也是我们用库的时候默认的系统时钟设置函数。该函数的功能是利用 HSE 把时钟设置为: HCLK = SYSCLK=PLLCLK= 180M, PCLK1=HCLK/2 = 90M, PCLK1=HCLK/4 = 45M 下面我们就以这个代码的流程为主线, 来分析时钟树,对应的是图中的黄色部分,代码流程在时钟树中以数字的大小顺序标识。

                                  

13.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 必须使用 ,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。
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 的寄存器描述部分。

/*
* 使用 HSE 时,设置系统时钟的步骤
* 1、开启 HSE ,并等待 HSE 稳定
* 2、设置 AHB、 APB2、 APB1 的预分频因子
* 3、设置 PLL 的时钟来源
* 设置 VCO 输入时钟 分频因子 m
* 设置 VCO 输出时钟 倍频因子 n
* 设置 PLLCLK 时钟分频因子 p
* 设置 OTG FS,SDIO,RNG 时钟分频因子 q
* 4、开启 PLL,并等待 PLL 稳定
* 5、把 PLLCK 切换为系统时钟 SYSCLK
* 6、读取时钟切换状态位,确保 PLLCLK 被选为系统时钟
*/
#define PLL_M 25
#define PLL_N 360
#define PLL_P 2
#define PLL_Q 7
/如果要超频的话,修改 PLL_N 这个宏即可,取值范围为: 192~432。
oid SetSysClock(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
// ①使能 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;
// ②设置 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;
// ③配置主 PLL 的时钟来源,设置 M,N,P,Q
// Configure the main 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-RIDE 模式,以能达到更改频率
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;
/*--------------------------------------------------
// ⑤选择主 PLLCLK 作为系统时钟源
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
// ⑥读取时钟切换状态位,确保 PLLCLK 选为系统时钟
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS )
!= RCC_CFGR_SWS_PLL);
{
}
} else {
// HSE 启动出错处理
}
}

13.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时钟来源最大输出速率
MCO1PA8HSI、 LSE、 HSE、 PLLCLK100M
MCO2PC9HSE、 PLLCLK、 SYSCLK、 PLLI2SCLK100M

13.3 配置系统时钟实验:

13.3.1 使用 HSE:

       一般情况下,我们都是使用 HSE,然后 HSE 经过 PLL 倍频之后作为系统时钟。 F429系统时钟最高为 180M,这个是官方推荐的最高的稳定时钟,如果你想铤而走险,也可以超频,超频最高能到 216M。
       如果我们使用库函数编程,当程序来到 main 函数之前,启动文件:startup_stm32f429_439xx.s 已经调用 SystemInit()函数把系统时钟初始化成 180MHZ,SystemInit()在库文件: system_stm32f4xx.c 中定义。如果我们想把系统时钟设置低一点或者超频的话,可以修改底层的库文件,但是为了维持库的完整性,我们可以根据时钟树的流程自行写一个。

13.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 配置系统时钟例子,起个抛砖引玉的作用。

13.3.3 硬件设计

1、 RCC
2、 LED 一个
       RCC 是单片机内部资源,不需要外部电路。通过 LED 闪烁的频率来直观的判断不同系统时钟频率对软件延时的效果。

13.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 作为系统时钟来源
 

/*
* m: VCO 输入时钟 分频因子,取值 2~63
* n: VCO 输出时钟 倍频因子,取值 192~432
* p: SYSCLK 时钟分频因子 ,取值 2, 4, 6, 8
* q: OTG FS,SDIO,RNG 时钟分频因子,取值 4~15
* 函数调用举例,使用 HSE 设置时钟
* SYSCLK=HCLK=180M,PCLK2=HCLK/2=90M,PCLK1=HCLK/4=45M
* HSE_SetSysClock(25, 360, 2, 7);
* HSE 作为时钟来源,经过 PLL 倍频作为系统时钟,这是通常的做法
* 系统时钟超频到 216M 爽一下
* HSE_SetSysClock(25, 432, 2, 9);
*/
void HSE_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)
{
__IO uint32_t HSEStartUpStatus = 0;
// 使能 HSE,开启外部晶振, 野火 F429 使用 HSE=25M
RCC_HSEConfig(RCC_HSE_ON);
// 等待 HSE 启动稳定
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if (HSEStartUpStatus == SUCCESS) {
// 调压器电压输出级别配置为 1,以便在器件为最大频率
// 工作时使性能和功耗实现平衡
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
// HCLK = SYSCLK / 1
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// PCLK2 = HCLK / 2
RCC_PCLK2Config(RCC_HCLK_Div2);
// PCLK1 = HCLK / 4
RCC_PCLK1Config(RCC_HCLK_Div4);
// 如果要超频就得在这里下手啦
// 设置 PLL 来源时钟,设置 VCO 分频因子 m,设置 VCO 倍频因子 n,
// 设置系统时钟分频因子 p,设置 OTG FS,SDIO,RNG 分频因子 q
RCC_PLLConfig(RCC_PLLSource_HSE, m, n, p, q);
// 使能 PLL
RCC_PLLCmd(ENABLE);
// 等待 PLL 稳定
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
}
/*-----------------------------------------------------*/
//开启 OVER-RIDE 模式,以能达到更高频率
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;
/*-----------------------------------------------------*/
// 当 PLL 稳定之后,把 PLL 时钟切换为系统时钟 SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 读取时钟切换状态位,确保 PLLCLK 被选为系统时钟
while (RCC_GetSYSCLKSource() != 0x08) {
}
} else {
// HSE 启动出错处理
while (1) {
}
}
}

这个函数采用库函数编写, 代码理解参考注释即可。函数有 4 个形参 m、 n、 p、 q,具体说明如下:

形参形参说明取值范围
mVCO 输入时钟 分频因子2~63
nVCO 输出时钟 倍频因子192~432
pPLLCLK 时钟分频因子2/4/6/8
qOTG 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 配置系统时钟

/*
* m: VCO 输入时钟 分频因子,取值 2~63
* n: VCO 输出时钟 倍频因子,取值 192~432
* p: PLLCLK 时钟分频因子 ,取值 2, 4, 6, 8
* q: OTG FS,SDIO,RNG 时钟分频因子,取值 4~15
函数调用举例,使用 HSI 设置时钟
* SYSCLK=HCLK=180M,PCLK2=HCLK/2=90M,PCLK1=HCLK/4=45M
* HSI_SetSysClock(16, 360, 2, 7);
* HSE 作为时钟来源,经过 PLL 倍频作为系统时钟,这是通常的做法
* 系统时钟超频到 216M 爽一下
* HSI_SetSysClock(16, 432, 2, 9);
*/
void HSI_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q)
{
__IO uint32_t HSIStartUpStatus = 0;
// 把 RCC 外设初始化成复位状态
RCC_DeInit();
//使能 HSI, HSI=16M
RCC_HSICmd(ENABLE);
// 等待 HSI 就绪
HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
// 只有 HSI 就绪之后则继续往下执行
if (HSIStartUpStatus == RCC_CR_HSIRDY) {
// 调压器电压输出级别配置为 1,以便在器件为最大频率
// 工作时使性能和功耗实现平衡
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
// HCLK = SYSCLK / 1
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// PCLK2 = HCLK / 2
RCC_PCLK2Config(RCC_HCLK_Div2);
// PCLK1 = HCLK / 4
RCC_PCLK1Config(RCC_HCLK_Div4);
// 如果要超频就得在这里下手啦
// 设置 PLL 来源时钟,设置 VCO 分频因子 m,设置 VCO 倍频因子 n,
// 设置系统时钟分频因子 p,设置 OTG FS,SDIO,RNG 分频因子 q
RCC_PLLConfig(RCC_PLLSource_HSI, m, n, p, q);
// 使能 PLL
RCC_PLLCmd(ENABLE);
// 等待 PLL 稳定
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
}
/*-----------------------------------------------------*/
//开启 OVER-RIDE 模式,以能达到更高频率
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;
/*-----------------------------------------------------*/
// 当 PLL 稳定之后,把 PLL 时钟切换为系统时钟 SYSCLK
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 读取时钟切换状态位,确保 PLLCLK 被选为系统时钟
while (RCC_GetSYSCLKSource() != 0x08) {
}
} else {
// HSI 启动出错处理
while (1) {
}
}
}

这个函数采用库函数编写, 代码理解参考注释即可。函数有 4 个形参 m、 n、 p、 q,具体说明如下:

形参形参说明取值范围
mVCO 输入时钟 分频因子2~63
nVCO 输出时钟 倍频因子192~432
pPLLCLK 时钟分频因子2/4/6/8
qOTG 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,这个是超频,要慎用。
软件延时

void Delay(__IO uint32_t nCount)
{
for (; nCount != 0; nCount--);
}

        软件延时函数,使用不同的系统时钟,延时时间不一样,可以通过 LED 闪烁的频率来判断。
MCO 输出
        在 F429 中, PA8/PC9 可以复用为 MCO1/2 引脚,对外提供时钟输出,我们也可以用示波器监控该引脚的输出来判断我们的系统时钟是否设置正确。
代码 15 MCO GPIO 初始化
 

// 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);
}

      野火 F429 中 PA8 并没有引出,只引出了 PC9,如果要用示波器监控 MCO,只能用PC9。
代码 16 MCO 输出时钟选择

// MCO1 输出 PLLCLK
RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_1);
// MCO1 输出 SYSCLK
RCC_MCO2Config(RCC_MCO2Source_SYSCLK, RCC_MCO1Div_1);

       我们初始化 MCO 引脚之后,可以直接调用库函数 RCC_MCOxConfig()来选择 MCO 时钟来源,同时还可以分频,这两个参数的取值参考库函数说明即可。
主函数
       在主函数中,可以调用 HSE_SetSysClock()或者 HSI_SetSysClock()这两个函数把系统时钟设置成各种常用的时钟,然后通过 MCO 引脚监控,或者通过 LED 闪烁的快慢体验不同的系统时钟对同一个软件延时函数的影响。

int main(void)
{
// 程序来到 main 函数之前,启动文件: statup_stm32f10x_hd.s 已经调用
// SystemInit()函数把系统时钟初始化成 72MHZ
// SystemInit()在 system_stm32f10x.c 中定义
// 如果用户想修改系统时钟,可自行编写程序修改
// 重新设置系统时钟,这时候可以选择使用 HSE 还是 HSI
// 使用 HSE,配置系统时钟为 180M
HSE_SetSysClock(25, 360, 2, 7);
//系统时钟超频到 216M 爽一下,最高是 216M,别往死里整
//HSE_SetSysClock(25, 432, 2, 9);
// 使用 HSI,配置系统时钟为 180M
//HSI_SetSysClock(16, 360, 2, 7);
// LED 端口初始化
LED_GPIO_Config();
// MCO GPIO 初始化
MCO1_GPIO_Config();
MCO2_GPIO_Config();
// MCO1 输出 PLLCLK
RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_1);
// MCO2 输出 SYSCLK
RCC_MCO2Config(RCC_MCO2Source_SYSCLK, RCC_MCO1Div_1);
while (1) {
LED1( ON ); // 亮
Delay(0x0FFFFF);
LED1( OFF ); // 灭
Delay(0x0FFFFF);
}
}

13.3.5 下载验证

       把编译好的程序下载到开发板,可以看到设置不同的系统时钟时, LED 闪烁的快慢不一样。更精确的数据我们可以用示波器监控 MCO 引脚看到。

13.4 总结:

1、时钟树:

2、HSE时钟:

HSE: High Speed External Clock signal, 即高速的外部时钟。
来源:有源晶振(1-50M)价格贵一般不采用、无源晶振( 4-26M)
控制: RCC_CR 时钟控制寄存器的位16: HSEON控制
3、HSI时钟:
HSI: Low Speed Internal Clock signal, 即高速的内部时钟。
来源:芯片内部,大小为16M,当HSE故障时,系统时钟会自动切换到HSI, 直到HSE启动成功。
控制: RCC_CR 时钟控制寄存器的位0: HSION控制

4、锁相环时钟PLL:
锁相环时钟: PLLCLK
来源: HSI、 HSE。由PLLSRC位配置。
HSE或者HSI先经过一个分频因子M进行分频,然后再经过一个倍频因子N,然后再经过一个分频因子P,最后成为锁相环时钟
PLLCLK = (HSE/M) * N / P = 25/25 * 360 / 2 = 180M
控制: RCC_PLLCFGR :RCC PLL 配置寄存器
PLL48CK: USB_FS、 RANG、 SDIO提供时钟

5、系统时钟:

缩写: SYSCLK,最高为180M, F407最高为168M。
来源: HSI、 HSE, PLLCLK。
控制: RCC_CFGR 时钟配置寄存器的SW位
6、HCLK时钟;

HCLK: AHB高速总线时钟, 最高为180M。为AHB总线的外设提供时钟、为Cortex系统定时器提供时钟( SysTick)、为内核提供时钟( FCLK)。
AHB为advanced high-performance bus。
来源:系统时钟分频得到。
控制: RCC_CFGR 时钟配置寄存器的HPRE位
7、PCLK1时钟:

PCLK1: APB1低速总线时钟,最高为45M。 为APB1总线的外设提供时钟。 2倍频之后则为APB1总线的定时器提供时钟,最大为90M。
来源: HCLK分频得到。
控制: RCC_CFGR 时钟配置寄存器的PPRE1位
8、PCLK2时钟:
PCLK2: APB2高速总线时钟,最高为90M。为APB2总线的外设提供时钟。 2倍频之后则为APB2总线的定时器提供时钟,最大为180M。
来源: HCLK分频得到。
控制: RCC_CFGR 时钟配置寄存器的PPRE2位

9、RTC时钟
RTC:为芯片内部的RTC提供时钟。
来源: HSE_RTC( HSE分频得到)、 LSE(外部32.768KHZ的晶体提供)、 LSI( 32KHZ)。
控制: RCC备份域控制寄存器RCC_BDCR: RTCSEL位控制

独立看门狗时钟: IWDGCLK,由LSI提供

10、其他时钟:

I2S时钟:由外部的引脚I2S_CKIN或者PLLI2SCLK提供。以太网PHY时钟: 429没有集成PHY,只能外接PHY芯片,比如LAN8720,那PHY时钟就由外部的PHY芯片提供,大小为50M。
USB PHY时钟: 429的USB没有集成PHY, 要想实现USB高速传输,只能外接PHY芯片,比如USB33000。那USB PHY时钟就由外部的PHY芯片提供。

11、MCO时钟输出:
MCO:把控制器的时钟通过外部的引脚输出,可以外外部的设备提供时钟。 MCO1为PA8, MCO2为PC9。
控制: RCC_CFGR 时钟配置寄存器的MCOX的PREx位

12、相关程序:

/**
  * @brief  Configures the System clock source, PLL Multiplier and Divider factors, 
  *         AHB/APBx prescalers and Flash settings
  * @Note   This function should be called only once the RCC clock configuration  
  *         is reset to the default reset state (done in SystemInit() function).   
  * @param  None
  * @retval None
  */

/*
* 使用 HSE 时,设置系统时钟的步骤
* 1、开启 HSE ,并等待 HSE 稳定
* 2、设置 AHB、 APB2、 APB1 的预分频因子
* 3、设置 PLL 的时钟来源
*    设置 VCO 输入时钟 分频因子 m
*    设置 VCO 输出时钟 倍频因子 n
*    设置 PLLCLK 时钟分频因子 p
*    设置 OTG FS,SDIO,RNG 时钟分频因子 q
* 4、开启 PLL,并等待 PLL 稳定
* 5、把 PLLCK 切换为系统时钟 SYSCLK
* 6、读取时钟切换状态位,确保 PLLCLK 被选为系统时钟
*/
static void SetSysClock(void)
{
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F446xx)
/******************************************************************************/
/*   PLL (clocked by HSE) used as System clock source锁相环作为系统时钟的源   */
/******************************************************************************/
/*-------------------------1-使能HSE,并等待HSE稳定----------------------------------------*/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* Enable HSE 使能HSE*/
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  /* 等待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;
  }
		 
  if (HSEStatus == (uint32_t)0x01)    //HSE启动成功
  {
/*----------------------------------------------------------------------------------------*/
    /* Select regulator voltage output Scale 1 mode */
		/* 选择电压调节器输出为模式1 */
		/* 使能电源接口时钟 */
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;
/*----------------------------2-配置 AHB APB2 APB1 总线的预分频因子-----------------------*/
    /* HCLK = SYSCLK / 1*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;

#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F446xx)     
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
#endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx || STM32F446xx */

#if defined(STM32F401xx)
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
#endif /* STM32F401xx */

#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx)
/*----------------------------3-配置 PLL的各种分频因子,并使能PLL-----------------------*/
    /* Configure the main PLL */
		/* 配置 主 锁相环 PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
#endif /* STM32F40_41xxx || STM32F427_437x || STM32F429_439xx || STM32F401xx */

#if defined(STM32F446xx)
    /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24) | (PLL_R << 28);
#endif /* STM32F446xx */    
    
    /* Enable the main PLL */
		/* 使能 PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till the main PLL is ready */
		/* 等待PLL启动稳定 */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }
   
#if defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F446xx) 
    /* Enable the Over-drive to extend the clock frequency to 180 Mhz */
/*----------------------------------------------------------------------------------------*/
    /* 打开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)
    {
    }      
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    /* 配置 Flash 预取指, 指令缓存, 数据缓存 和等待周期 */   
		FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F427_437x || STM32F429_439xx || STM32F446xx */

#if defined(STM32F40_41xxx)     
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;
#endif /* STM32F40_41xxx  */

#if defined(STM32F401xx)
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;
#endif /* STM32F401xx */
		
/*----------------------------4-选择系统时钟来源-----------------------------------------*/
    /* Select the main PLL as system clock source */
		/* 选择主锁相环时钟作为系统时钟 */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* Wait till the main PLL is used as system clock source */
		/* 等待PLLCLH切换称系统时钟 */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
    {
    }
  }
  else
  { 
    /* HSE启动失败后,用户纠错的代码 */		
		/* If HSE fails to start-up, the application will have wrong clock
         configuration. User can add here some code to deal with this error */
  }
#elif defined(STM32F411xE)
#if defined(USE_HSE_BYPASS) 
/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* Enable HSE and HSE BYPASS */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON | RCC_CR_HSEBYP);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  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;
  }

  if (HSEStatus == (uint32_t)0x01)
  {
    /* Select regulator voltage output Scale 1 mode */
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;

    /* HCLK = SYSCLK / 1*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;

    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;

    /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
    
    /* Enable the main PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till the main PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }

    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;

    /* Select the main PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* Wait till the main PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
    {
    }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock
         configuration. User can add here some code to deal with this error */
  }
#else /* HSI will be used as PLL clock source */
  /* Select regulator voltage output Scale 1 mode */
  RCC->APB1ENR |= RCC_APB1ENR_PWREN;
  PWR->CR |= PWR_CR_VOS;
  
  /* HCLK = SYSCLK / 1*/
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
  
  /* PCLK2 = HCLK / 2*/
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;
  
  /* PCLK1 = HCLK / 4*/
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;
  
  /* Configure the main PLL */
  RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) | (PLL_Q << 24); 
  
  /* Enable the main PLL */
  RCC->CR |= RCC_CR_PLLON;
  
  /* Wait till the main PLL is ready */
  while((RCC->CR & RCC_CR_PLLRDY) == 0)
  {
  }
  
  /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
  FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_2WS;
  
  /* Select the main PLL as system clock source */
  RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  RCC->CFGR |= RCC_CFGR_SW_PLL;
  
  /* Wait till the main PLL is used as system clock source */
  while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
  {
  }
#endif /* USE_HSE_BYPASS */  
#endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx */  
}
///
//超频部分代码/
// 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;
	// 把RCC外设初始化成复位状态
  RCC_DeInit(); 
/*-------------------------1-使能HSE,并等待HSE稳定----------------------------------------*/	
	/* 使能HSE 并等待HSE稳定*/
	RCC_HSEConfig(RCC_HSE_ON);
  /*判断HSE是否启动成功*/	
	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	
	if( HSEStartUpStatus == SUCCESS )
  {
/*----------------------------------------------------------------------------------------*/		
			/* 选择电压调节器输出为模式1 */
			/* 使能电源接口时钟 */
			RCC->APB1ENR |= RCC_APB1ENR_PWREN;
			PWR->CR |= PWR_CR_VOS;
/*----------------------------2-配置 AHB APB2 APB1 总线的预分频因子-----------------------*/
			RCC_HCLKConfig(RCC_SYSCLK_Div1);  /* HCLK = SYSCLK / 1*/
			RCC_PCLK2Config(RCC_HCLK_Div2);   /* PCLK2 = HCLK / 2*/
			RCC_PCLK1Config(RCC_HCLK_Div4);   /* PCLK1 = HCLK / 4*/
/*----------------------------3-配置 PLL的各种分频因子,并使能PLL-----------------------*/
			RCC_PLLConfig(RCC_PLLSource_HSE, m, n, p, q);  /* 配置 PLL的各种分频因子 */

			RCC_PLLCmd(ENABLE);    /*使能PLL*/
			while ( ( RCC_GetFlagStatus(RCC_FLAG_PLLRDY) ) == RESET )  /* 等待PLL启动稳定 */
			{
			}
/*----------------------------------------------------------------------------------------*/
			/* 打开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_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);   /* 选择主锁相环时钟作为系统时钟 */
			while ( ( RCC_GetSYSCLKSource() ) != 0X08 )  /* 等待PLLCLH切换称系统时钟 */
			{
			}	  
  }
  else{ /* HSE启动识别,用户在这里添加纠错代码 */ }
}

///
//超频部分代码/
// m : 2~63
// n :192~432
// p :2、4、6、8
// q :2~15
// SYSCLK = (HSI/m) * n /p = 16/16 * 432 / 2 = 216M 
// HSI_SetSysClk(16,432,2,7)
///
void HSI_SetSysClk(uint32_t m,uint32_t n,uint32_t p,uint32_t q)
{
	__IO uint32_t HSIStartUpStatus = 0;
/*-------------------------1-使能HSI 并等待HSI稳定----------------------------------------*/		
		// 把RCC外设初始化成复位状态
  RCC_DeInit();  
	
	/* 使能HSI 并等待HSI稳定*/
	RCC_HSICmd(ENABLE);	
	HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;  /*判断HSI是否启动成功*/	
	
	if( HSIStartUpStatus == RCC_CR_HSIRDY )
  {
/*----------------------------------------------------------------------------------------*/		
			/* 选择电压调节器输出为模式1 */
			/* 使能电源接口时钟 */
			RCC->APB1ENR |= RCC_APB1ENR_PWREN;
			PWR->CR |= PWR_CR_VOS;
/*----------------------------2-配置 AHB APB2 APB1 总线的预分频因子-----------------------*/
			RCC_HCLKConfig(RCC_SYSCLK_Div1);  /* HCLK = SYSCLK / 1*/
			RCC_PCLK2Config(RCC_HCLK_Div2);   /* PCLK2 = HCLK / 2*/
			RCC_PCLK1Config(RCC_HCLK_Div4);   /* PCLK1 = HCLK / 4*/
/*----------------------------3-配置 PLL的各种分频因子,并使能PLL-----------------------*/
			RCC_PLLConfig(RCC_PLLSource_HSI, 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;

/*----------------------------4-选择系统时钟来源-----------------------------------------*/
			RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
			while ( ( RCC_GetSYSCLKSource() ) != 0X08 )
			{
			}	  
  }
  else{ /* HSI启动识别,用户在这里添加纠错代码 */ }
}

// 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);
}
/**
  ******************************************************************************
  * @file    main.c
  * @author  Sumjess
  * @version V1.0
  * @date    2019-05-xx
  * @brief   MDK5.27
  ******************************************************************************
  * @attention
  *
  * 实验平台   :STM32 F429 
  * CSDN Blog  :https://blog.csdn.net/qq_38351824
  * 微信公众号 :Tech云
  *
  ******************************************************************************
  */
  
#include "stm32f4xx.h"
#include "bsp_led.h"
#include "bsp_key.h" 

void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
	for(; nCount != 0; nCount--);
}
int main(void)
{
    // 程序来到main函数之前,启动文件:statup_stm32f10x_hd.s已经调用
	// SystemInit()函数把系统时钟初始化成180MHZ
	// SystemInit()在system_stm32f10x.c中定义
	// 如果用户想修改系统时钟,可自行编写程序修改	
	// 重新设置系统时钟,这时候可以选择使用HSE还是HSI
	
	/* 把系统时钟初始化为216M */
	//HSE_SetSysClk(25,432,2,9);      直接调用使用即可
	/* 把系统时钟初始化为216M */
	HSI_SetSysClk(16,432,2,7);      直接调用使用即可
	LED_GPIO_Config();
	MCO1_GPIO_Config();
	// MCO1 输出PLLCLK	
  RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_1);
	
	while (1)
	{
		LED1( ON );			 // 亮 
		Delay(0xFFFFF);
		LED1( OFF );		  // 灭
	  Delay(0xFFFFF);
	}
}


 

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值