GD32F10x外部晶振配置108MHz系统时钟

嵌入式_GD32F10x外部晶振配置108MHz系统时钟



前言

由于公司更改硬件设计选择使用新的型号兆易创新国产芯片,需要把以前的软件进行移植,新板子要求新的板子使用的外部8兆晶振,系统时钟要求达到108兆,在配置过程中踩了别人的坑,在此简单记录一下。

注:本项目基于GD32F103CBT6硬件平台, 使用标准库GD32F10x_Firmware_Library_V1.0.0(提示:此库坑多、慎用!


一、时钟树与配置思路

GD32F10x使用M1内核时钟树如图所示,先根据构时钟树构想的配置思路如图上红线路径所示:

A:此处决定PREDV0的时钟源是外部时钟还是PLL1(CL型芯片才有PLL1和PLL2,时钟配置寄存器 1的第16位),我们是HD型芯片,所以配置不了该位,选择默认值0。
B:此处为PREDV0分频因子,时钟配置寄存器 0的第17位,我们选择为外部时钟2分频,得到4MHz时钟
C:此位决定PLL使用0(内部高速时钟)还是1(外部高速时钟),时钟配置寄存器 0的第16位,选择配置为1
D:PLL的倍频因子,时钟配置寄存器 0的第18到21位、29位,我们选择倍频27,4MHz*27 = 108MHz
E:系统时钟选择,决定系统用那一路时钟,时钟配置寄存器 0的第0位与第1位,我们配置为10即可

最终得到108兆系统时钟
在这里插入图片描述

二、时钟配置过程

● 1.在gd32f10x.h文件中修改HSE_VALUE宏的值为:8000000

#if !defined  HSE_VALUE    
#ifdef GD32F10X_CL   
#define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
#else 
#define HSE_VALUE    ((uint32_t)8000000) /* !< From 4M to 16M *!< Value of the External oscillator in Hz*/
#endif /* HSE_VALUE */
#endif

● 2.在system_gd32f10x.c文件中修改需要的系统时钟频率宏为:SYSCLK_FREQ_108MHz

代码如下(示例):

/* Uncomment the corresponding line to configure system clock that you need  */ 
/* The clock is from HSE oscillator clock  */
//#define SYSCLK_FREQ_HSE    HSE_VALUE
//#define SYSCLK_FREQ_24MHz  24000000
//#define SYSCLK_FREQ_36MHz  36000000
//#define SYSCLK_FREQ_48MHz  48000000
//#define SYSCLK_FREQ_56MHz  56000000 
//#define SYSCLK_FREQ_72MHz  72000000
//#define SYSCLK_FREQ_96MHz  96000000
#define SYSCLK_FREQ_108MHz  108000000

/* Uncomment the corresponding line to configure system clock that you need  */ 
//#define SYSCLK_FREQ_48MHz_HSI  48000000 
/* The clock is from HSI oscillator clock  */
//#define SYSCLK_FREQ_72MHz_HSI  72000000
//#define SYSCLK_FREQ_108MHz_HSI  108000000

然后在system_gd32f10x.c文件中你就可以看到108兆时钟配置函数

static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
  SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
  SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
  SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
  SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
  SetSysClockTo56();  
#elif defined SYSCLK_FREQ_72MHz
  SetSysClockTo72();
#elif defined SYSCLK_FREQ_96MHz
  SetSysClockTo96();
#elif defined SYSCLK_FREQ_108MHz				//根据宏自动选择该配置函数
  SetSysClockTo108();
#elif defined SYSCLK_FREQ_48MHz_HSI
  SetSysClockTo48HSI();
#elif defined SYSCLK_FREQ_72MHz_HSI
  SetSysClockTo72HSI();
#elif defined SYSCLK_FREQ_108MHz_HSI
  SetSysClockTo108HSI();
#endif
 
}

● 3.在system_gd32f10x.c 文件中修改SetSysClockTo108();
一般情况下是不用修改该函数的,但是我直接使用时踩了个坑,时钟频率不对,然后返回来检查寄存器时分析了这个函数,这里简单说一下作为个人笔记,大佬可以自动跳过。
最主要是这里:
RCC->GCFGR |= (uint32_t)( RCC_GCFGR_PLLPREDV_HSE_DIV2 | RCC_GCFGR_PLLSEL_HSE | RCC_GCFGR_PLLMF27);
RCC_GCFGR_PLLPREDV_HSE_DIV2:这里配置时钟树图的 B处
RCC_GCFGR_PLLSEL_HSE:这里配置时钟树图的 C处
RCC_GCFGR_PLLMF27:这里配置时钟树图的 D处
检查了一遍根据寄存器,显示的值都没问题,但调试到选择则PLL作为系统时钟这句就有问题:
RCC->GCFGR |= (uint32_t)RCC_GCFGR_SCS_PLL;
通过函数RCC_GetClocksFreq(&RCC_Clockstruct)抓出来的时钟频率全是乱的

代码如下(示例):

static void SetSysClockTo108(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* CK_SYS, AHB, APB2 and APB1 configuration ---------------------------*/    
  /* Enable HSE */    
  RCC->GCCR |= ((uint32_t)RCC_GCCR_HSEEN);
 
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->GCCR & RCC_GCCR_HSESTB;
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  if ((RCC->GCCR & RCC_GCCR_HSESTB) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }  

  if (HSEStatus == (uint32_t)0x01)
  {
    /* AHB = CK_SYS not divided */
    RCC->GCFGR |= (uint32_t)RCC_GCFGR_AHBPS_DIV1;
      
    /* APB2 = AHB not divided */
    RCC->GCFGR |= (uint32_t)RCC_GCFGR_APB2PS_DIV1;
    
    /* APB1 = AHB is divided 2 */
    RCC->GCFGR |= (uint32_t)RCC_GCFGR_APB1PS_DIV2;

#ifdef GD32F10X_CL
    /* Configure PLLs ------------------------------------------------------*/

    /* PLL configuration: PLLCLK = PREDIV1 * 9 = 108 MHz */ 
    RCC->GCFGR &= (uint32_t)~(RCC_GCFGR_PLLPREDV | RCC_GCFGR_PLLSEL | RCC_GCFGR_PLLMF);
    RCC->GCFGR |= (uint32_t)(RCC_GCFGR_PLLPREDV_PREDIV1 | RCC_GCFGR_PLLSEL_PREDIV1 | RCC_GCFGR_PLLMF9); 

    /* PLL2 configuration: PLL2CLK = (HSE / 5) * 12 = 60 MHz */
    /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 12 MHz */
        
    RCC->GCFGR2 &= (uint32_t)~(RCC_GCFGR2_PREDV2 | RCC_GCFGR2_PLL2MF |
                              RCC_GCFGR2_PREDV1 | RCC_GCFGR2_PREDV1SEL);
    RCC->GCFGR2 |= (uint32_t)( RCC_GCFGR2_PREDV2_DIV5 | RCC_GCFGR2_PLL2MF12 |
                             RCC_GCFGR2_PREDV1SEL_PLL2 | RCC_GCFGR2_PREDV1_DIV5);
  
    /* Enable PLL2 */
    RCC->GCCR |= RCC_GCCR_PLL2EN;
    /* Wait till PLL2 is ready */
    while((RCC->GCCR & RCC_GCCR_PLL2STB) == 0)
    {
    }

#else     
    /* PLL configuration: PLLCLK = (HSE/2) * 27 = 108 MHz */
    RCC->GCFGR &= (uint32_t)((uint32_t)~(RCC_GCFGR_PLLSEL | RCC_GCFGR_PLLPREDV | RCC_GCFGR_PLLMF));
    RCC->GCFGR |= (uint32_t)( RCC_GCFGR_PLLPREDV_HSE_DIV2 | RCC_GCFGR_PLLSEL_HSE | RCC_GCFGR_PLLMF27);

#endif /* GD32F10X_CL */

    /* Enable PLL */
    RCC->GCCR |= RCC_GCCR_PLLEN;

    /* Wait till PLL is ready */
    while((RCC->GCCR & RCC_GCCR_PLLSTB) == 0)
    {
    }

    /* Select PLL as system clock source */
    RCC->GCFGR &= (uint32_t)((uint32_t)~(RCC_GCFGR_SCS));
    RCC->GCFGR |= (uint32_t)RCC_GCFGR_SCS_PLL;    

    /* Wait till PLL is used as system clock source */
    while ((RCC->GCFGR & (uint32_t)RCC_GCFGR_SCSS) != (uint32_t)0x08)
    {
    }
  }
  else
  { 
  } 
}

三、晶振故障排查

软件排查基本没问题那就只能看硬件了,晶振使用的是晶科JX-S25A系列的一个8兆贴片晶振其原理图为:在这里插入图片描述
使用示波器打一下晶振并联电阻两端都没有波形,然后测了一下并联电阻阻值只有9.8千欧,原理图为1M欧,所以阻值太小了,多半是焊错了,然后把电阻挑了继续拿示波器打,波形一下就出来了,周期为125ns,也确实是8MHz
在这里插入图片描述
后面专门去查了原因,给晶振并联一个M级电阻是为了使本来为逻辑反相器的器件工作在线性区, 以获得增益, 在饱和区不存在增益, 而在没有增益的条件下晶振不起振。简而言之,并联1M电阻增加了电路中的负性阻抗(-R),即提升了增益,缩短了晶振起振时间,达到了晶振起振更容易之目的。但是因为晶振本身阻抗是很大的,所以也可以不焊接并联电阻,如果要焊并联电阻就必须足够大,刚刚的1千欧就太小了,等效电阻小于1千欧就没有波形。
也有给晶振串联K级别电阻用来预防晶振过驱,限制振荡幅度。


总结

如有错误,欢迎指正,原创不易,转载留名!

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值