GD32F20x系列的时钟配置
前言
第一次使用兆易芯片写完验证程序调试时发现时钟不对,摸索了一下才搞清楚,分享出来供大家参考。调试所用到的芯片是GD32F205RCT6,系统时钟源选择外部高速时钟hxtal,时钟源配置为120MHz。
一、GD32F20x的时钟树
该芯片有四个时钟源,我选择了HXTAL(外部高速时钟),硬件方面采用了一个8MHz的无源晶振。下图为官方文档中的时钟树:
按照官方demo中的时钟配置配置代码,在时钟树中表现出来就是图中橙色线的走向,也就是时钟源选择了HXTAL,PLL选择了PPL1(HXTAL产生了PLL1)作为输入时钟源。官方demo的时钟配置(以120MHz为例)代码如下:
static void system_clock_120m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable HXTAL */
RCU_CTL |= RCU_CTL_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do{
timeout++;
stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);
}while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)) //超时结束了循环,停止运行
{
while(1){
}
}
/* HXTAL is stable */
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1; //AHB不分频
/* APB2 = AHB/1 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1; //APB2不分频
/* APB1 = AHB/2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2; //APB1两分频
/* CK_PLL = (CK_PREDIV0) * 10 = 120 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLSEL );
RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL10);
/* CK_PREDIV0 = (CK_HXTAL) / 5 * 12 /5 = 12 MHz */
RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL12 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV5);
/* enable PLL1 */
RCU_CTL |= RCU_CTL_PLL1EN;
/* wait till PLL1 is ready */
while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){
}
/* enable PLL */
RCU_CTL |= RCU_CTL_PLLEN;
/* wait until PLL is stable */
while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
}
/* select PLL as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL;
/* wait until PLL is selected as system clock */
while(0U == (RCU_CFG0 & RCU_SCSS_PLL)){
}
}
二、时钟配置
1.设定外部时钟为系统时钟主振荡频率
打开“system_gdf20x.c”文件,找到下边的代码,将宏定义“#define __SYS_OSC_CLK (__IRC8M)”修改为“#define __SYS_OSC_CLK (__HXTAL)”,修改后的代码如下:
/* 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 (__HXTAL) /* main oscillator frequency */
2.设置外部晶振频率
上段代码的第二句中,在“(HXTAL_VALUE)”上右键跳转到其定义(位于gd32f20x.h),将HXTAL_VALUE的值改为你所使用的外部晶振频率,此处使用8MHz晶振,修改后的代码如下:
#define HXTAL_VALUE ((uint32_t)8000000) /*!< value of the external oscillator in Hz */
3.选择一个系统时钟
打开“system_gdf20x.c”文件,找到下边的代码,选择哪一个时钟就将哪一段代码取消注释,此处选择外部高速时钟120MHz,代码如下:
* 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 */
//#define __SYSTEM_CLOCK_HXTAL (uint32_t)(__HXTAL)
//#define __SYSTEM_CLOCK_24M_PLL_HXTAL (uint32_t)(24000000)
//#define __SYSTEM_CLOCK_36M_PLL_HXTAL (uint32_t)(36000000)
//#define __SYSTEM_CLOCK_48M_PLL_HXTAL (uint32_t)(48000000)
//#define __SYSTEM_CLOCK_56M_PLL_HXTAL (uint32_t)(56000000)
//#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000)
//#define __SYSTEM_CLOCK_96M_PLL_HXTAL (uint32_t)(96000000)
//#define __SYSTEM_CLOCK_108M_PLL_HXTAL (uint32_t)(108000000)
#define __SYSTEM_CLOCK_120M_PLL_HXTAL (uint32_t)(120000000)
4.修改时钟配置
打开“system_gdf20x.c”文件,找到下边的代码,源代码是基于25MHz的外部晶振进行配置的,此处我们把它改成8MHz晶振,配置时钟的核心就是配置RCU_CFG0和RCU_CFG1这两个寄存器的值。修改后的代码如下:
/* CK_PLL = (CK_PREDIV0) * 10 = 120 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLSEL );
RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL10);
/* CK_PREDIV0 = (CK_HXTAL) / 2 * 9 /3 = 12 MHz */
RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL9 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV3);
代码说明:
按照计算公式,首先要计算CK_PREDIV0的值,在芯片手册中找到RCU_CFG1寄存器的位/位域配置表,如下图:
位域[7:4]为PREDV1输入源时钟分频,范围1-16,选择2分频,即RCU_PREDV1_DIV2;位域[3:0]为PREDV0输入源时钟分频,范围1-16,选择3分频,即RCU_PREDV0_DIV3;位域[11:8]为PLL1时钟倍频因子,范围8-16、20,选择9倍频,即RCU_PLL1_MUL9,此时根据公式可以得到CK_PREDIV0=8/2*9/3=12MHz。
接下来找到RCU_CFG0寄存器的位/位域配置表,如下图:
RCU_CFG0寄存器的位域[21:18]和位[29]为PLL源时钟的倍频因子,范围2-14、6.5、16-32,选择10倍频,即RCU_PLL_MUL10,由公式得到CK_PLL=12x10=120MHz。至此,通过锁相环将8MHz的外部晶振倍频到120MHz,系统时钟配置完毕。
三.验证
在主函数定义一个全局变量(也可以定义一个局部变量加断点),用来获取各个时钟,在Keil中debug,添加watch1窗口中可以看到想要获取的时钟的值,代码如下:
uint32_t temp_freq;
int main(void)
{
temp_freq = rcu_clock_freq_get(CK_SYS);
rcu_periph_clock_enable(RCU_GPIOC); //使能GPIOC时钟
gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);//GPIO初始化
TIMER1_Config(); //定时器初始化
while(1)
{
}
}
watch1窗口的值如下图:
变量temp_freq的值为0x07270E00,换算为十进制就是120000000,即系统时钟为120MHz。其实这种方式检验的并不准确,获取到的这个数值取决于在第3步中取消注释了哪一行,即使在第4步的时钟配置函数中你没有正确配置两个寄存器的值。最好的方式还是配置一个定时器,配置中断函数(比如:翻转一个IO口的电平)用示波器检验时钟的准确性。