GD32F4(5):GD32F450时钟配置为200M过程分析

GD32F450时钟树分析

1. 系统环境

  • 系统:win10

  • IDE:keil5

  • 开发板:GD官方评估板GD32450Z_EVAL

  • 用户手册版本:GD32F4xx_yonghushouce_Rev2.6.pdf

  • 标准库版本:GD32F4xx_Demo_Suites_V2.5.0

2. 时钟树图

对于任何的一款MCU,熟悉它的时钟树,熟悉mcu时钟的配置过程,会修改时钟配置都是非常有必要的。

对于GD32F4来讲,它的时钟控制单元提供了一系列频率的时钟功能,包括:

  • 一个外部高速晶体振荡器时钟(HXTAL)。
  • 一个外部低速晶体振荡器时钟(LXTAL),一般接32.768K晶振。
  • 一个内部48M RC振荡器时钟(IRC48M),这个48M时钟是专门为时钟校准控制器(CTC)和USB提供时钟的。
  • 一个内部16M RC振荡器时钟(IRC16M。
  • 一个内部32K RC振荡器时钟(IRC32K)。
  • 三个锁相环(PLL)、一个HXTAL时钟监视器(CKM)、时钟预分频器、时钟多路复用器和时钟门控电路。

GD32F4系列MCU的时钟树如下图:
在这里插入图片描述

AHB、APB1、AHB2和Cortex-M4时钟都源自系统时钟(CK_SYS),系统时钟的时钟源可以选择IRC16M、 HXTAL或PLL。

系统时钟的最大运行时钟频率可以达到240MHz,但数据手册显示GD32F450最高可达到200M,GD32F470最高到达240M。

独立看门狗定时器有独立的时钟源(IRC32K),实时时钟(RTC)使用IRC32K、 LXTAL或HXTAL的分频(通过配置RCU_CFG0寄存器的RTCDIV位)作为时钟源。

3. GD32F450的时钟配置函数

首先我们先简要的叙述GD32F450的启动过程,关于GD32F450编译和启动过程的详细分析,我将另外写一篇文章,本文重点是记录官方带的demo程序是如何将GD32F450时钟配置到200M的。

startup_gd32f450.s文件中,有如下一段汇编代码:

;/* reset Handler */
Reset_Handler   PROC
                EXPORT  Reset_Handler                     [WEAK]
                IMPORT  SystemInit
                IMPORT  __main
                LDR     R0, =SystemInit  ;将SystemInit函数地址加载到R0寄存器
                BLX     R0				 ;执行SystemInit进行时钟设置,执行完后返回
                LDR     R0, =__main      ;将__main函数地址加载到R0寄存器
                BX      R0				 ;执行__main函数,不再返回,__main会再调用用户的main函数,从此进入用户编写的程序
                ENDP

和STM32的启动过程一样,芯片上电先调用Reset_Handler,Reset_Handler会调用两个函数,一个是SystemInit配置系统时钟,另一个是__main,本章重点介绍SystemInit函数;

时钟的配置,由3个函数来完成,SystemInit执行会调用system_clock_configsystem_clock_config执行在调用system_clock_200m_25m_hxtal,最后完成200M时钟的配置,具体的时钟配置函数和内部代码逻辑如下:

3.1 SystemInit函数

void SystemInit (void)
{
  /* FPU settings ------------------------------------------------------------*/
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)//默认不开起
    //浮点运算相关设置
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
    //其实这个CP10、CP11并不是真正的寄存器,它其实是协处理器的名字,ARM拥有16个协处理器,常被命名为CP0—CP15,其中CP0—CP7由厂家定义协处理功能,而CP8—CP15预留给ARM使用,CP15提供一些系统控制功能,这包括体系结构和特征识别,以及控制、状态信息和配置支持,还提供了性能监视器寄存器。CP14主要提供debug系统的控制、Thumb执行环境、字节码执行。CP10、CP11 两个协处理器一起,提供了浮点运算和向量操作,以及高级的 SIMD 指令扩展。协处理器8、9、12和13预留给ARM将来使用。因为协处理器是协助内核的,16个协处理器共提供出16*32位,基本每个位都有其相应的功能(不使用的位除外),我们平时都接触不到,因此我们平时都忽略它。
  #endif
  /* Reset the RCU clock configuration to the default reset state ------------*/
  /* Set IRC16MEN bit */
  //选择内部16M时钟  
  RCU_CTL |= RCU_CTL_IRC16MEN;
  //将时钟进行2分频,等待时钟稳定,这里会等待的久一点,
  RCU_MODIFY
  //上面程序的目的是设置当前系统内核的时钟,此时系统的时钟为8M
      
  //下面的Reset都是关闭操作,将各种时钟模块的使能全部关闭,变成失能
  /* Reset CFG0 register */
  RCU_CFG0 = 0x00000000U;

  /* Reset HXTALEN, CKMEN and PLLEN bits */
  //关闭外部高速时钟、高速时钟时钟监视器(CKM)、PLL锁相环使能
  RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);

  /* Reset PLLCFGR register */
  RCU_PLL = 0x24003010U;

  /* Reset HSEBYP bit */
  RCU_CTL &= ~(RCU_CTL_HXTALBPS);

  /* Disable all interrupts */
  RCU_INT = 0x00000000U;
         
  /* Configure the System clock source, PLL Multiplier and Divider factors, 
     AHB/APBx prescalers and Flash settings ----------------------------------*/
  //进入时钟配置,重新配置系统相关时钟
  system_clock_config();
}

3.2 system_clock_config函数

static void system_clock_config(void)
{
...
#elif defined (__SYSTEM_CLOCK_200M_PLL_25M_HXTAL)
    //利用外部高速时钟(评估板带的是25M晶振),配置系统时钟为200M
    system_clock_200m_25m_hxtal();
#endif 
}

3.3 system_clock_200m_25m_hxtal函数

static void system_clock_200m_25m_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 */
    //若外部高速时钟异常,上面等待超时,则进入这里永远等待,系统会卡在while(1)里面
    if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)){
        while(1){
            //我感觉这里不合理,按理说外部时钟异常,应该触发相应的中断,并自动切换到内部高速时钟运行,而不是卡在这里。以后我有时间会把这个函数改改,现在先这样用吧。
            
        }
    }
    //运行到这里,说明外部高速时钟正常启动,下面就要按照时钟树,来配置系统和各个模块的时钟  
    
    RCU_APB1EN |= RCU_APB1EN_PMUEN;//使能APB1总线
    PMU_CTL |= PMU_CTL_LDOVS;//配置使能内部电压调节器1.2V域供电

    //时钟的配置原则应该是从内向外,对照时钟树图,就可以非常清楚的明白时钟配置过程
    /* HXTAL is stable */
    /* AHB = SYSCLK */
    RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;//系统SYS时钟到AHB总线时钟不进行分频
    /* APB2 = AHB/2 */
    RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;//APB2是AHB的2分频
    /* APB1 = AHB/4 */
    RCU_CFG0 |= RCU_APB1_CKAHB_DIV4;//APB1是AHB的4分频
	//GD32F450有3个PLL(PLL、PLLI2S、PLLSAI),这里只配置了一个PLL,
    //现在外部晶振时钟是25M,通过PSC的25分频,变成1M,然后再PLL内倍频400倍,变成400M,然后再经过P进行2分频,变成200M,传给系统时钟。同时400M还进行了Q的9分频,产生大约48M的CK48M给USB提供时钟去了。
    //当更换外部晶振的频率后的时候,通过修改PSC就可以进行匹配了
    /* Configure the main PLL, PSC = 25, PLL_N = 400, PLL_P = 2, PLL_Q = 9 */ 
    RCU_PLL = (25U | (400U << 6U) | (((2U >> 1U) - 1U) << 16U) |
                   (RCU_PLLSRC_HXTAL) | (9U << 24U));

    /* enable PLL */
    RCU_CTL |= RCU_CTL_PLLEN;//上面配置完了,就要使能PLL

    /* wait until PLL is stable */
    //等待PLL配置时钟稳定下来
    while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){
    }
    //在用户手册3.3.4章节,说如果1.2V电源域工作在高频状态下,且打开了多种功能,建议进入高驱动模式。
    /* Enable the high-drive to extend the clock frequency to 200 Mhz */
    //使能高驱动模式
    PMU_CTL |= PMU_CTL_HDEN;
    //等待PMU_CS寄存器的HDRF被置位
    while(0U == (PMU_CS & PMU_CS_HDRF)){
    }
    
    /* select the high-drive mode */
    //将LDO切换到高驱动模式
    PMU_CTL |= PMU_CTL_HDS;
    //等待PMU_CS寄存器的HDSRF被置位。进入高驱动模式
    while(0U == (PMU_CS & PMU_CS_HDSRF)){
    } 
    
    /* select PLL as system clock */
    //设置SCS[1:0],将时钟切换到刚配置好的PLL这条系统时钟线路
    RCU_CFG0 &= ~RCU_CFG0_SCS;
    RCU_CFG0 |= RCU_CKSYSSRC_PLLP;

    /* wait until PLL is selected as system clock */
    //等待PLL这条系统时钟配置能稳定给系统提供时钟
    while(0U == (RCU_CFG0 & RCU_SCSS_PLLP)){
    }
   	//到此处,系统完成了从8M到200M的切换
}

3.4 根据程序中的配置,绘制配置后的时钟路线图

最后,我们来绘制一下程序中配置的路径,其中绿色的是开始配置8M的时钟线路,红色的是配置200M的时钟线路,如下图:
在这里插入图片描述

4. 更换晶振后时钟配置

上面分析了时钟配置的完整过程,使用的是外部25M晶振,当我们更换晶振,除了修改函数宏之外,还要修改一个参数宏,具体请看我的另一篇文章:GD32F4(6):晶振引发串口乱码

  • 12
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈搭石

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值