【GD32】系统时钟解析

这篇具有很好参考价值的文章主要介绍了【GD32】系统时钟解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一 时钟控制单元

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

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

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

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

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

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

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

二 系统时钟架构

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

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

 通过固件库的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),以TIMER2为例,挂载的是APB1总线,USART0挂载APB2总线。

三 时钟树分析

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

 

① 以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_APB2频率直接提供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;
    RCU_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决定。

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

2.PREDV分频系数

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

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

3. PLLSEL选择器

由RCU_CFG0寄存器的第16位决定

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

 

4. PLL倍频系数

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

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

七 仿真验证

gd32使用内部时钟源,GD32学习笔记,单片机,stm32,嵌入式硬件

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

参考文章:

http://t.csdn.cn/JHan5

http://t.csdn.cn/k0m3W文章来源地址https://www.toymoban.com/news/detail-604431.html

到了这里,关于【GD32】系统时钟解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GD32F470 移植STM32F429工程 Keil调试笔记

    keil版本:5.25 安装 GigaDevice.GD32F4xx_DFP.3.0.4.pack Keil.STM32F4xx_DFP.2.15.0.pack 1、原项目为STM32F429 工程,切换到GD32F470 只需在 Options for Target\\\"“对话框的Device菜单中选中“GD32F470II”,重新编译即可,一般不会有编译错误。 2、将项目工程在切换回STM32F429,在 Options for Target”\\\"对话框的D

    2024年02月09日
    浏览(51)
  • GD32F303高级定时器输出互补PWM-开发笔记

    ◼ 总通道数:4; ◼ 计数器宽度:16位; ◼ 时钟源可选:内部时钟,内部触发,外部输入,外部触发; ◼ 多种计数模式:向上计数,向下计数和中央计数; ◼ 正交编码器接口:被用来追踪运动和分辨旋转方向和位置; ◼ 霍尔传感器接口:用来做三相电机控制; ◼ 可编程

    2024年02月09日
    浏览(62)
  • GD32使用CS1237通过ADC采集温度

    读和写的命令 注:需要注意PGA的放大倍数,不要超出了CS1237的输入的范围,要不然会输出最大值。 CS1237.h文件 CS1237.c文件 ADC值转温度文件(使用的PT1000) 主函数调用方法 1.在C1237_Init初始化函数中设置好CS1237的工作模式和设置DO和SCLK接脚; 2.调用C1237_ReadResult函数,设置DO和

    2024年02月03日
    浏览(34)
  • 【GD32单片机】GD32工程构建,快速上手GD32

    之前在学校接触最多的是STM32单片机,但出来工作后发现,GD32或MM32单片机却是经常能接触到的,虽然学习资料和生态没有STM32好,但基本芯片内外设资源却差不多,开发起来大同小异。 在开始构建工程之前需要去GD32的官网下载一些资料; 打开官网 https://www.gigadevice.com.cn/ 选

    2024年02月03日
    浏览(52)
  • STM32/GD32学习指南-踩坑之(一)外部晶振配置,初始化失败,不起振

    GD32使用外部有源晶振和无源晶振的问题,型号为GD32 F450 一、GD32配置使用外部晶振 1.使用外部无源晶振 找到startup_gd32f450_470.s汇编文件,找到SystemInit()函数跳转进去 在底部找到system_clock_config()函数,再次跳转进去 选中宏定义:__SYSTEM_CLOCK_200M_PLL_IRC16M,跳转,如图 将内部时钟

    2024年02月13日
    浏览(52)
  • 嵌入式_GD32使用宏开关进行Debug串口打印调试

    串口Debug是一种将数据通过串口发送的方法。通过使用printf函数,我们可以将需要发送的数据格式化为字符串,并通过串口发送出去。在C语言中,通常使用串口发送数据的函数为printf函数,但是需要将标准输出重定向到串口。 本文详细的介绍了如何重定向printf输出到串口输出

    2024年02月14日
    浏览(50)
  • 【GD32F427开发板试用】+使用USBFS轻松实现HID键盘应用

    本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者: 不锈钢铁侠 最近有项目需要用到键盘自动输入功能,提升工作效率。故使用该开发板实现自定义输入内容并通过按键控制自动通过usb输出。 在官方GD32F4xx_Firm

    2024年02月12日
    浏览(39)
  • [GD32F4]基于GD32固件库移植cherryusb[STM32F4]

    [GD32F4]基于GD32固件库移植cherryusb[STM32F4] 使用开发板是淘宝买的不知名开发板,没什么好说的,具体的型号是GD32F450VET6。 使用的cherryusb版本是0.9.0版本。 使用的GD32官方固件库版本是:GD32F4xx_Firmware_Library_V3.0.4 cherryusb最牛的地方在于抛弃掉所有的依赖,只需要知道芯片的usb中断

    2024年02月06日
    浏览(52)
  • GD32F4(9):GD32f4出现上电不工作,必须按复位程序才能跑起来

    绘制一个gd32450的pcb板子,结果烧录程序后发生下面事情: 上电程序不能正常启动或者偶尔可以正常启动一次,很随机。 当上电后程序不启动的时候,我再按一下mcu的reset按键,程序就能正常启动了。 当我debug调试的时候,回回都能正常启动,根本定位不到问题 首先在板子里

    2023年04月08日
    浏览(72)
  • GD32 看门狗

      独立看门狗的原理:设定一个重载值。赋值计数器。每来一个脉冲计数值减减。如果计数值减到0。还没有去喂狗就会产生复位。所以在计数值在0~重载值范围必须要喂一次狗。  在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,

    2024年02月07日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包