资讯详情

【GD32】系统时钟解析

一 时钟控制单元

1. HXTAL:4-32高速外部时钟MHz外部振荡器可以为系统提供更准确的主时钟。具有特定频率的晶体必须靠近两个HXTAL引脚。必须根据所选振荡器调整与晶体连接的外部电阻和电容。

2. IRC8M:高速内部8MHz时钟,内部8MHz RC振荡器时钟,简称IRC8M时钟,拥有8MHz设备上电后的固定频率CPU默认选择的时钟源是IRC8M时钟。

3. IRC28M:高速内部28MHz时钟,内部28MHz RC振荡器时钟 (IRC28M) 固定频率28MHz,专门用作ADC时钟。

4. IRC48M:高速内部48MHz时钟,内部48M RC振荡器时钟(IRC48M) 48有固定频率MHz,用作USB时钟或者PLL时钟源。

5. LXTAL:低速外部时钟,LXTAL晶体是一个32.768kHz低速外部晶体或陶瓷谐振器。它为实时时钟电路提供了低功耗确的时钟源。

6. IRC40K:低速内部时钟,IRC40K RC振荡器时钟扮演低功耗时钟源的角色,其时钟频率约为40 kHz,为独立看门狗定时器和实时钟电路提供时钟。

7. PLL:相环,内锁相环PLL输入参考频率为4~32MHz的时钟基准2 ~64倍频可提供16~108 MHz时钟输出。

二 系统时钟架构

以GD32F330系列为例,Cortex-M最高时钟频率为84架构MHz。

通过固件库system_gd32f10x.c可知:

    /* HXTAL is stable */     /* AHB = SYSCLK */     RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;     /* APB2 = AHB/2 */     RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;     /* APB1 = AHB/2 */     RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;

AHB总线为系统时钟的1分频,最高频率为84MHz;APB1总线为系统时钟的2分频,即最高频率为42MHz;APB2总线为系统时钟的2分频,即最高频率为42MHz;

此外,通过时钟架构图,您可以清楚地了解芯片外设所挂载的时钟总线(AHB、APB1、APB2),以TIMER例如,挂载是APB1总线,USART0挂载APB2总线。

三 时钟树分析

① 以HXTAL为例,外部HXTAL时钟源为系统提供时钟频率。

② PLLPRESEL选择外部晶振作为PLL时钟源,经过PREDV分频,PLLSEL选择外部晶振作为PLL时钟源,经过PLL倍频得到CK_PLL,再通过SCS选择PLL时钟源作为系统时钟。

③ CK_SYS通过分频获得系统时钟CK_AHB,即为AHB总线时钟频率。

④ 以TIMER2为例,TIMER2挂载APB1总线,假如APB1分频系数为1,则TIMER2频率为AHB,否则TIMER2频率为AHB/(APB1分频系数/2)。

⑤ 以USART0为例,USART0挂载APB2总线,通过APB2分频得到CK_APB直接提供2频率USART0使用。

四 时钟初始化

1. 在startup_gd32f3x0.s在文件中找到以下代码段,进入SystemInit()接口

/* reset Handler */ Reset_Handler   PROC                 EXPORT  Reset_Handler                     [WEAK]                 IMPORT  SystemInit                 IMPORT  __main                 LDR     R0, =SystemInit                 BLX     R0                 LDR     R0, =__main                 BX      R0                 ENDP

2. 是RCU相关寄存器的初始化system_gd32f3x0.c找到以下代码,输入system_clock_config()函数。

void SystemInit(void) { #if (defined(GD32F350))     RCU_APB2EN |= BIT(0);     CMP_CS |= (CMP_CS_CMP1MSEL | CMP_CS_CMP0MSEL); #endif /* GD32F350 */     if(((FMC_OBSTAT & OB_OBSTAT_PLEVEL_HIGH) != OB_OBSTAT_PLEVEL_HIGH) &&             (((FMC_OBSTAT >> 13) & 0x1) == SET)) {         FMC_KEY = UNLOCK_KEY0;         FMC_KEY = UNLOCK_KEY1 ;         FMC_OBKEY = UNLOCK_KEY0;         FMC_OBKEY = UNLOCK_KEY1 ;         FMC_CTL |= FMC_CTL_OBER;         FMC_CTL |= FMC_CTL_START;         while((uint32_t)0x00U != (FMC_STAT & FMC_STAT_BUSY));         FMC_CTL &= ~FMC_CTL_OBER;         FMC_CTL |= FMC_CTL_OBPG;         if((FMC_OBSTAT & OB_OBSTAT_PLEVEL_HIGH) == OB_OBSTAT_PLEVEL_NO) {             OB_SPC = FMC_NSPC;         } else if((FMC_OBSTAT & OB_OBSTAT_PLEVEL_HIGH) == OB_OBSTAT_PLEVEL_LOW) {             OB_SPC = FMC_LSPC;         }         OB_USER = OB_USER_DEFAULT & ((uint8_t)(FMC_OBSTAT >> 8));         OB_DATA0 = ((uint8_t)(FMC_OBSTAT >> 16));         OB_DATA1 = ((uint8_t)(FMC_OBSTAT >> 24));         OB_WP0 = ((uint8_t)(FMC_WP));         OB_WP1 = ((uint8_t)(FMC_WP >> 8));         while((uint32_t)0x00U != (FMC_STAT & FMC_STAT_BUSY));         FMC_CTL &= ~FMC_CTL_OBPG;         FMC_CTL &= ~FMC_CTL_OBWEN;         FMC_CTL |= FMC_CTL_LK;     }     /* FPU settings */ #if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U)     SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); /* set CP10 and CP11 Full Access */ #endif      /* enable IRC8M */     RCU_CTL0 |= RCU_CTL0_IRC8MEN;     while(0U == (RCU_CTL0 & RCU_CTL0_IRC8MSTB)) {     }     RCU_CFG0 &= ~(RCU_CFG0_SCS);     RCU_CTL0 &= ~(RCU_CTL0_HXTALEN | RCU_CTL0_CKMEN | RCU_CTL0_PLLEN | RCU_CTL0_HXTALBPS);      /* reset RCU */     RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | \                   RCU_CFG0_ADCPSC | RCU_CFG0_CKOUTSEL | RCU_CFG0_CKOUTDIV | RCU_CFG0_PLLDV);     RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF4 | RCU_CFG0_PLLDV); #if (defined(GD32F350))     RCU_CFG0 &= ~(RCU_CFG0_USBFSPSC);     RCU_CFG2 &= ~(RCU_CFG2_CECSEL | RCU_CFG2_USBFSPSC2); #endif /* GD32F350 */      RCU_CFG1 &= ~(RCU_CFG1_PREDV | RCU_CFG1_PLLMF5 | RCU_CFG1_PLLPRESEL);     RCU_CFG2 &= ~(RCU_CFG2_USART0SEL | RCU_CFG2_ADCSEL);     RCU_CFG2 &= ~RCU_CFG2_IRC28MDIV;     RCU_CFG2 &= ~RCU_CFG2_ADCPSC2;     RCU_CTL1 &= ~RCU_CTL1_IRC28MEN;     RCU_ADDCTL &= ~RCU_ADDCTL_IRC48MEN;     RCU_INT = 0x00000000U;     CU_ADDINT = 0x00000000U;

    /* configure system clock */
    system_clock_config();

#ifdef VECT_TAB_SRAM
    nvic_vector_table_set(NVIC_VECTTAB_RAM, VECT_TAB_OFFSET);
#else
    nvic_vector_table_set(NVIC_VECTTAB_FLASH, VECT_TAB_OFFSET);
#endif
}

3. 选择系统时钟频率84MHZ,在system_gd32f3x0.c找到如下代码,进入system_clock_84m_hxtal()。

static void system_clock_config(void)
{
#ifdef __SYSTEM_CLOCK_8M_HXTAL
    system_clock_8m_hxtal();
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
    system_clock_72m_hxtal();
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2)
    system_clock_72m_irc8m();
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC48M_DIV2)
    system_clock_72m_irc48m();
#elif defined (__SYSTEM_CLOCK_84M_PLL_HXTAL)
    system_clock_84m_hxtal();
#elif defined (__SYSTEM_CLOCK_84M_PLL_IRC8M_DIV2)
    system_clock_84m_irc8m();
#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL)
    system_clock_96m_hxtal();
#elif defined (__SYSTEM_CLOCK_96M_PLL_IRC8M_DIV2)
    system_clock_96m_irc8m();
#elif defined (__SYSTEM_CLOCK_96M_PLL_IRC48M_DIV2)
    system_clock_96m_irc48m();
#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)
    system_clock_108m_hxtal();
#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M_DIV2)
    system_clock_108m_irc8m();
#else
    system_clock_8m_irc8m();
#endif /* __SYSTEM_CLOCK_8M_HXTAL */
}

4. 根据系统时钟频率,去配置其他外设时钟源,在system_gd32f3x0.c找到如下代码。

static void system_clock_84m_hxtal(void)
{
    uint32_t timeout = 0U;
    uint32_t stab_flag = 0U;
    /* enable HXTAL */
    RCU_CTL0 |= RCU_CTL0_HXTALEN;

    /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
    do {
        timeout++;
        stab_flag = (RCU_CTL0 & RCU_CTL0_HXTALSTB);
    } while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
    /* if fail */
    if(0U == (RCU_CTL0 & RCU_CTL0_HXTALSTB)) {
        return;
    }
    /* HXTAL is stable */
    /* AHB = SYSCLK */
    RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
    /* APB2 = AHB/2 */
    RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;
    /* APB1 = AHB/2 */
    RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;

    /* PLL = HXTAL /4 * 21 = 84 MHz */
    RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF4 | RCU_CFG0_PLLPREDV); //16,27|(18,21),27,17
    RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PLLMF5 | RCU_CFG1_PREDV);     //30,31,(0,3)
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | (RCU_PLL_MUL21 & (~RCU_CFG1_PLLMF5))); //16,
    RCU_CFG1 |= (RCU_PLLPRESEL_HXTAL | RCU_PLL_PREDV4); //设置RCU_CFG1寄存器PREDV[3:0]预分频系数
    RCU_CFG1 |= (RCU_PLL_MUL21 & RCU_CFG1_PLLMF5); //RCU_CFG1_PLLMF5实际表示的是 :RCU_CFG0 的位 27,21:18
    /* enable PLL */
    RCU_CTL0 |= RCU_CTL0_PLLEN;

    /* wait until PLL is stable */
    while(0U == (RCU_CTL0 & RCU_CTL0_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. 设置外部晶振频率,在gd32f3x0.h文件,找到如下代码段,修改外部晶振为16MHZ。

    /* define value of high speed crystal oscillator (HXTAL) in Hz */
#if !defined  (HXTAL_VALUE)
#define HXTAL_VALUE    ((uint32_t)16000000)
#endif /* high speed crystal oscillator value */

2. 选择外部时钟源,在system_gd32f3x0.c文件,找到如下代码段,_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 */

3. 设置系统主频率大小,在system_gd32f3x0.c文件中找到如下代码段,选择外部时钟源通过PLL配置为84MHZ系统时钟频率。

#if defined (GD32F330)
//#define __SYSTEM_CLOCK_8M_HXTAL              (__HXTAL)
//#define __SYSTEM_CLOCK_8M_IRC8M              (__IRC8M)
//#define __SYSTEM_CLOCK_72M_PLL_HXTAL         (uint32_t)(72000000)
//#define __SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2    (uint32_t)(72000000)
//#define __SYSTEM_CLOCK_72M_PLL_IRC48M_DIV2     (uint32_t)(72000000)
#define __SYSTEM_CLOCK_84M_PLL_HXTAL           (uint32_t)(84000000)
//#define __SYSTEM_CLOCK_84M_PLL_IRC8M_DIV2    (uint32_t)(84000000)
#endif /* GD32F330 */

 4. 修改PLL分频倍频系数

修改前:系统外部晶振HXTAL默认为8MHZ,带入公式:

CK_PLL = (CK_HXTAL/2) * 21 = 84 MHz,得到PLL分频系数为2,PLL倍频系数为21

    /* PLL = HXTAL /2 * 21 = 84 MHz */
    RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF4 | RCU_CFG0_PLLPREDV);
    RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PLLMF5 | RCU_CFG1_PREDV);
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | (RCU_PLL_MUL21 & (~RCU_CFG1_PLLMF5)));
    RCU_CFG1 |= (RCU_PLLPRESEL_HXTAL | RCU_PLL_PREDV2);
    RCU_CFG1 |= (RCU_PLL_MUL21 & RCU_CFG1_PLLMF5);

    /* enable PLL */
    RCU_CTL0 |= RCU_CTL0_PLLEN;

修改后:系统外部晶振HXTAL为16MHZ,带入公式:

CK_PLL = (CK_HXTAL/4) * 21 = 84 MHz,得到PLL分频系数为4,PLL倍频系数为21,修改如下

    RCU_CFG1 |= (RCU_PLLPRESEL_HXTAL | RCU_PLL_PREDV4);

六 PLL相关寄存器配置

前面介绍时钟树的时候说过,选择PLL作为系统时钟源需要设置四个地方,PLLPRESEL选择器,PREDV分频系数,PLLSEL选择器,PLL倍频系数。RCU_CFG1寄存器决定分频系数,RCU_CFG0寄存器决定倍频系数。查看数据手册,可以知道:

1. PLLPRESEL选择器

由 RCU_CFG1寄存器的第30位 PLLPRESEL决定 ,或由RCU_CFG0寄存器的第17位PLLPREDV决定。

2.PREDV分频系数

由RCU_CFG1寄存器3-0位的PREDV[3,0]决定,同时CU_CFG0寄存器的第17位和PREDV的第0位功能相同。

3. PLLSEL选择器

由RCU_CFG0寄存器的第16位决定

 

4. PLL倍频系数

由RCU_CFG0寄存器21-18位的PLLMF[3,0],RCU_CFG0寄存器的27位 PLLMF[4],RCU_CFG1寄存器的31位 PLLMF[5]共同决定。

七 仿真验证

源码链接:GD32时钟配置代码学习工程-嵌入式文档类资源-CSDN下载 

参考文章:

http://t.csdn.cn/JHan5

http://t.csdn.cn/k0m3W

标签: gd8216电容器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台