众所周知,时钟是MCU能正常运行的基本条件,就好比心跳或脉搏,为所有的工作单元提供时间 基数。时钟控制单元提供了一系列频率的时钟功能,包括多个内部RC振荡器时钟(IRC)、一个外部 高速晶体振荡器时钟(HXTAL)、一个外部低速晶体振荡器时钟(LXTAL)、一个或多个锁相环(PLL) 一个HXTAL时钟和LXTAL时钟监视器、时钟预分频器、时钟多路复用器和时钟门控电路等。 本章,我们将通过一个“输出HXTAL时钟信号” 的实验来熟悉RCU的工作流程。
1.1RCU 配置
GD32系列MCU在启动后首先会执行Reset Handler,紧接着就会执行SystemInit()函数,而时钟的初始化,就是在这个函数中进行,其主要的功能是配置系统时钟CK_SYS(即主频),AHB、APB1以及APB2时钟。SystemInit()函数由GD32官方库提供,不同系列的MCU有一些差别,但实现方式基本相同:首先将RCU关于CK_SYS,AHB、APB1以及APB2时钟配置的一些寄存器恢复到默认值,然后再执行system_clock_config()函数,用于具体的时钟配置。
实际上用户可以不用过于关心上述的实现方式,因为GD32库已经为您提供了多种时钟源及时钟选择,您只需按照以下步骤即可将时钟设置为您期望的值(以GD32F30x为例,其他系列类似):
(1) 在system_gd32f30x.c中,用户可通过选择宏来进行预设的时钟配置,如下图代码清单时钟配置选择宏定义,选择了HXTAL作为PLL时钟源,且配置CK_SYS为120MHz。
/* system frequency define */ #define __IRC8M (IRC8M_VALUE) /* internal 8 MHz RC oscillator frequency */ #define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */ #define __SYS_OSC_CLK (__IRC8M) /* main oscillator frequency */ /* select a system clock by uncommenting the following line */ /* use IRC8M */ //#define __SYSTEM_CLOCK_IRC8M (uint32_t)(__IRC8M) //#define __SYSTEM_CLOCK_48M_PLL_IRC8M (uint32_t)(48000000) //#define __SYSTEM_CLOCK_72M_PLL_IRC8M (uint32_t)(72000000) //#define __SYSTEM_CLOCK_108M_PLL_IRC8M (uint32_t)(108000000) //#define __SYSTEM_CLOCK_120M_PLL_IRC8M (uint32_t)(120000000) /* use HXTAL(XD series CK_HXTAL = 8M, CL series CK_HXTAL = 25M) */ //#define __SYSTEM_CLOCK_HXTAL (uint32_t)(__HXTAL) //#define __SYSTEM_CLOCK_48M_PLL_HXTAL (uint32_t)(48000000) //#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000) //#define __SYSTEM_CLOCK_108M_PLL_HXTAL (uint32_t)(108000000) #define __SYSTEM_CLOCK_120M_PLL_HXTAL (uint32_t)(120000000)
但这种情况下您使用的外部晶振需要是默认值,此值由HXTAL_VALUE定义,如为8000000,那么您应该选择8MHz的外部晶振。
当然,您可以使用其他规格的外部晶振,这种情况下就需要去修改RCU配置函数里面的一些参数,主要是分频和倍频系数,以达到期望的配置,具体如何修改,可以结合GD32的User manual中定义的RCU寄存器来对配置函数进行分析。
(2) 设置HXTAL_VALUE的值。
此数值和RCU的初始化其实并没有太大关系,但如果您使用的外部晶振不是默认值,那么除了按照步骤(1)修改配置参数外,您还必须将此HXTAL_VALUE的值修改为实际的外部晶振频率,这是因为在一些通信外设配置时,库函数会调用HXTAL_VALUE值来设置波特率,如此值设置错误,会导致通信异常。
1.2.非默认外部晶振配置时钟实例
GD32各系列固件库都已提供配置系统时钟的函数。需要注意的是,在使用外部晶振时,固件库中HXTAL_VALUE值规定了 外部晶振的默认值,以 GD32F30x系列为例,如下图代码清单HXTAL_VALUE选择宏定义所示,当芯片为非互联型(GD32F303)时,默认使用的外部晶振频率为8MHz,当芯片为互联型(GD32F305/307)时,默认使用的外部晶振频率为25MHz。
#ifdef GD32F30X_CL #define HXTAL_VALUE ((uint32_t)25000000) #else #define HXTAL_VALUE ((uint32_t)8000000)
那么,当我们使用非默认值的外部晶振时,该如何修改时钟配置函数呢?以GD32F303为例,首先我们先看下GD32F303的时钟树,如图所示。

预分频器可以配置AHB、APB2和APB1域的时钟频率。 AHB、APB2、APB1域的最高时钟频率分别为120MHz、120MHz、60MHz。RCU通过AHB时钟(HCLK)8分频后作为Cortex系统定时器(SysTick)的外部时钟。通过对SysTick控制和状态寄存器的设置,可选择上述时钟或AHB(HCLK)时钟作为SysTick时钟。
ADC时钟由APB2时钟经2、4、6、8、12、16分频或由AHB时钟经5、6、10、20分频获得,它们是通过设置RCU_CFG0和RCU_CFG1寄存器的ADCPSC位来选择。
SDIO, EXMC的时钟由CK_AHB提供。
TIMER时钟由CK_APB1和CK_APB2时钟分频获得,如果APBx(x=0,1)的分频系数不为1,则TIMER时钟为CK_APBx(x=0,1)的两倍。
USBD的时钟由CK48M时钟提供。通过配置 RCU_ADDCTL寄存器的CK48MSEL及PLL48MSEL位可以选择CK_PLL时钟或IRC48M时钟做为CK48M的时钟源。
CTC时钟由IRC48M时钟提供,通过CTC单元,可以实现IRC48M时钟精度的自动调整。
I2S的时钟由CK_SYS提供。
通过配置RCU_BDCTL寄存器的RTCSRC位, RTC时钟可以选择由LXTAL时钟、IRC40K时钟或HXTAL时钟的128分频提供。RTC时钟选择HXTAL时钟的128分频做为时钟源后,当1.2V内核电压域掉电时,时钟将停止。 RTC时钟选择IRC40K时钟做为时钟源后,当VDD掉电时,时钟将停止。
RTC时钟选择LXTAL时钟做为时钟源后,当VDD和VBAT都掉电时,时钟将停止。
当FWDGT启动时, FWDGT时钟被强制选择由IRC40K时钟做为时钟源。
现在,我们结合图GD32F303系统时钟树对时钟树进行分析:
(1) 标注A为CK_SYS,即系统主时钟,它一条线连接至CK_I2S,给I2S外设提供时钟,另一条线经过AHB分频器,输出到CK_AHB,即标注B。
(2) CK_AHB为AHB总线时钟,AHB总线时钟或直连,或经过APB1/APB2分频,给标注C位置的外设提供时钟。
(3) 那么,CK_SYS从何而来呢,我们看标注A的左边,CK_SYS通过SCS位域选择CK_IRC8M、CK_PLL、CK_HXTAL作为时钟来源,其中CK_IRC8M来源于标注D,即IRC8M(MCU内部8M RC时钟);CK_HXTAL来源于标注F,即HXTAL(外部时钟);CK_PLL的来源较复杂,我们单独拿出来说。
(4) CK_PLL来源于锁相环倍频器输出,倍频系数通过PLLMF位域选择,而PLLMF来源于两个地方,一个为 IRC8M 的 2 分 频 , 另 外 一 个 为 预 分 频 器 PREDV0 , 而 PREDV0 来 源 于 标 注 E, 即CK_IRC48M(内部48M RC时钟)和标注F,即HXTAL(外部高速时钟)。
(5) 通过以上分析可以得出结论,CK_PLL的时钟源为D:IRC8M、E:IRC48M、F:HXTAL,用户通过相关寄存器设置选择时钟线。
(6) 和前面分析相同,RTC的时钟来自于F:HXTAL的128分频、G:LXTAL(外部32.768K低速时钟)、F:IRC40K(内部40K RC时钟);FWDGT的时钟来源于F:IRC40K。
(7) 标注I位置为时钟输出线,它的作用是将MCU内部的一些时钟信号线输出到特定IO口上(大部分系列MCU的PA8口都可被设置为时钟输出口0,有些系列MCU含有两组输出IO,具体IO配置请参考各系列MCU Datasheet)用来给其他器件提供基准时钟。由图中可看出通过设置位域CK_OUT0,输出的时钟包括CK_PLL、CK_IRC8M、CK_HXTAL、CK_PLL的2分频。
结合以上分析,我们来看下GD32F30x固件库时钟配置函数(因篇幅有限,只贴出各分频和倍频配置部分),还是以GD32F303芯片为例,如下图代码清单时钟配置部分代码所示:
/* select HXTAL/2 as clock source */ RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); /* CK_PLL = (CK_HXTAL/2) * 30 = 120 MHz */ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); RCU_CFG0 |= RCU_PLL_MUL30;
可以看出,8MHz的HXTAL经过预分频器PREDV0分频成4MHz,再通过锁相环PLL倍频30倍到了120MHz。
那么,当您选择其他规格的外部晶振,比如12MHz,则可以先通过预分频器PREDV0分频成6MHz,再通过锁相环PLL倍频20倍即可,如代码清单 0-4. 使用12MHz外部晶振配置120M系统时钟所示。
/* select HXTAL/2 as clock source */ RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0); RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0); /* CK_PLL = (CK_HXTAL/2) * 20 = 120 MHz */ RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5); RCU_CFG0 |= RCU_PLL_MUL20;
当然,在修改完配置函数后,别忘了将HXTAL_VALUE值改为12000000。
需要注意的是,在进行时钟配置时,要严格按照Datasheet中规定的时钟范围进行配置,如GD32F303的 HXTAL的选 择范 围是4~32MHz, PLL的输 入范 围是 1~25MHz,输出范围是16~120Mhz,所以当使用32MHz的外部晶振时,不进行预分频,而直接倍频是不被允许的。
1.3.硬件连接说明
本章通过“输出HXTAL时钟信号”实验来熟悉RCU的工作流程。
通过前面内容讲解可知,本章实验为“输出HXTAL时钟信号”,即通过PA8口将HXTAL输出,我们使用示波器,将探头连接到PA8口,从示波器上读取PA8口波形即可。
1.4.软件配置说明
本小节讲解RCU_Example例程中RCU的配置说明,主要包括外设时钟配置、GPIO引脚配置、主函数介绍以及运行结果。
软件设计的流程如下:
(1)使能GPIOA时钟
(2)初始化PA8,将此端口设置为备用功能模式(AFIO)
(3)通过调用库函数选择HXTAL作为PA8时钟信号源
外设时钟配置
void rcu_config(void) { /* enable the GPIOA clock */ rcu_periph_clock_enable(RCU_GPIOA); }
GPIO 引脚配置
代码清单 0-6. RCU 例程引脚配置
void gpio_config(void) { /* configure PA8 port */ #if defined GD32F10X_HD || GD32F30X_HD || GD32F20X_CL || GD32E10X gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8); #elif GD32F1X0 || GD32F4XX || GD32F3X0 || GD32E23X gpio_mode_set(GPIOA,GPIO_MODE_AF,GPIO_PUPD_NONE,GPIO_PIN_8); gpio_af_set(GPIOA,GPIO_AF_0,GPIO_PIN_8); #endif }
GPIO的配置说明,请参考GPIO章节。
主函数说明
代码清单 0-7 . RCU 例程主函数
int main(void) { rcu_config(); gpio_config(); #if defined GD32F10X_HD || GD32F30X_HD || GD32E10X rcu_ckout0_config(RCU_CKOUT0SRC_HXTAL); #elif defined GD32F20X_CL || GD32F4XX rcu_ckout0_config(RCU_CKOUT0SRC_HXTAL,RCU_CKOUT0_DIV1); #elif GD32F1X0 || GD32F3X0 || GD32E23X rcu_ckout_config(RCU_CKOUTSRC_HXTAL,RCU_CKOUT_DIV1); #endif while(1){ } }
如代码清单RCU例程主函数,该主函数主要分成四部分,RCU时钟配置、GPIO配置、RCU输出相关库函数调用和while(1)主循环,其中RCU输出相关库函数请读者结合各系列MCU Datasheet、User Manual进行RCU例程的分析。
注意:因为是输出HXTAL,所以必须要使能HXTAL,否则PA8将无波形输出。一个简单的办法是将HXTAL作为CK_SYS时钟源,请参考本章第一节内容。
1.5.运行结果
如图所示 RCU 例程运行结果为 RCU 例程运行结果,可看出,PA8 口正确输出了 HXTAL 波形。

-
单片机
+关注
关注
6050文章
44707浏览量
641320 -
mcu
+关注
关注
146文章
17486浏览量
354652 -
嵌入式
+关注
关注
5106文章
19294浏览量
310228 -
时钟树
+关注
关注
0文章
55浏览量
10862 -
rcu
+关注
关注
0文章
21浏览量
5500
发布评论请先 登录
相关推荐
GD32 MCU 入门教程】GD32 MCU 常见外设介绍(12)FMC 模块介绍

《GD32 MCU原理及固件库开发指南》 + 初读感悟
《GD32 MCU原理及固件库开发指南》+读后感
兆易创新GD32 MCU选型手册,适用于GD32全系列MCU
【GD32 MCU 入门教程】一、GD32 MCU 开发环境搭建(1)使用Keil开发GD32

【GD32 MCU 入门教程】一、GD32 MCU 开发环境搭建(2)使用 IAR 开发 GD32

【GD32 MCU 入门教程】一、GD32 MCU 开发环境搭建(3)使用 Embedded Builder 开发 GD32

【GD32 MCU 入门教程】二、GD32 MCU 烧录说明(1)ISP 烧录

【GD32 MCU 入门教程】GD32 MCU 常见外设介绍(14)RTC 模块介绍

【GD32 MCU入门教程】GD32 MCU GPIO 结构与使用注意事项

评论