历史上的今天
返回首页

历史上的今天

今天是:2024年09月28日(星期六)

2019年09月28日 | 再造STM32---第十三部分:RCC—使用 HSE/HSI 配置时钟

2019-09-28 来源:eefocus

       配合《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 = 180M,PCLK1=HCLK/2 = 90M, PCLK1=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、时钟选择和输出速率的具体信息如下表所示:

image.png?imageView2/2/w/550

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

推荐阅读

史海拾趣

问答坊 | AI 解惑

学习DSP,准备了这些够了么?

计划用2407开发一个电力录波装置,首先想买些东西熟悉一下,学习学习。 准备购买闻亭的产品: 仿真器 SEED-XDSusb 1800元 开发板 SEED-DEC2407 2980元 usb转串口线一根 数百元 仿真开发软件 CCS3.3卖得太贵了,不知道有没有下 ...…

查看全部问答>

我电脑的串口很神经.

来自EEWORLD合作群:arm linux fpga 嵌入0(49900581) 群主:wangkj 用着用着 就只能收, 不能发. 重启一下电脑就好了. 各位大神 , 可能是哪方面的原因?…

查看全部问答>

转帖,免费的开发板及资料

第二次免费开发板赠送,40mb 开发资料包:实例,原理图,keil 正式版,免费下载 第二次免费开发板赠送,40mb 开发资料包:实例,原理图,keil 正式版,免费下载 下载50个单片机程序实例和开发板原理图,学林电子免费开发板暑期助学活动报名!暑假在家 ...…

查看全部问答>

怎样得到本机IP?

我用的C#。使用API得到也可以。…

查看全部问答>

Evc4 下对话框中使用垂直滚动条的问题?

我在wince下做了个对话框程序 ,使用evc4,增加了垂直滚动条,并增加了OnVScroll,OnSize 事件, 结果滑动滚动条,没有任何效果,在vc++6.0 用同样的方法,就没有问题; 下面是代码,高手看看有什么问题! 代码如下: 先在OnInitDialog 中 ...…

查看全部问答>

我的串口读不到数据.net程序

public void Open()                         {                                 //Init_Com ...…

查看全部问答>

STM32FLASH读保护测试,片子挂了,高手帮忙请进!

你必须写一个程序并使用JTAG在RAM中运行,这个程序的目的是清除Flash的读写保护,这样Flash的内容会自动被擦除,然后Flash就可以重新使用了。下次做这样的操作时,请预置一段清除Flash的读写保护的程序,这段程序能够通过某种机制启动,这样就 ...…

查看全部问答>

求助下,关于是否烧了G2

问个问题啊,我的G2553单片机突然出现了P1口任何操作不管用,但P2口依旧正常。我烧任何程序只要用到P1口,那用到P1口的那部分就不管用,但是P2口一点问题都没。。。求解啊 ,这是不是烧了1组GPIO啊~…

查看全部问答>

赠送一枚TI eStore 8折优惠码

可以购买: MSP430 Capacitive Touch Bundle,MSP430 Capacitive Touch BoosterPack,MSP430 USB Debugging Interface,MSP430 USB Stick Development Tool , MSP-EXP430FR5739 Experimenter Board ,MSP430F5438 Experimenter Board 有效期至 ...…

查看全部问答>

EDA实验与实践 任意整数分频模块

//*******************************************************// //                  任意整数分频模块                 & ...…

查看全部问答>