打印

史上最详细的gd32时钟频率设置

[复制链接]
2480|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
在调试GD32芯片时遇到了时钟配置错误的问题,仔细研究文档和网上资料,认真调试之后做了以下总结,希望能帮到同样有类似困惑的你。

**

1.认识时钟
HXTAL:高速外部时钟;
LXTAL:低速外部时钟;
IRC8M:高速内部时钟;
IRC40K:低速内部时钟;

其中:
HXTAL高速外部时钟为板子焊接的外部晶振,精度高,但同时功耗相较内部时钟也较高;
IRC8M内部高速时钟为芯片内部自带的时钟,精度较低,可以应用在对时钟要求不高的场景中;
IRC40K低速内部时钟用于独立看门狗的计数。

了解时钟分类之后,就需要确认板子所用晶振为内部晶振还是外部晶振。

**


使用特权

评论回复
沙发
renzheshengui|  楼主 | 2021-6-3 16:34 | 只看该作者
2.了解芯片系统架构
**

(1)以GD32F103RC为例,Cortex-M3架构,最高时钟频率为108MHz,其中:
AHB总线为系统时钟的1分频,即最高频率为108MHz;
APB1总线为系统时钟的2分频,即最高频率为54MHz;
APB2总线为系统时钟的1分频,即最高频率为108MHz;
上述分频并非作者杜撰,而是在上面的系统架构图上可以找到,也可以在固件库的system_gd32f10x.c中找到,如下代码段所示:

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

(2)分析系统架构图不仅仅能找到系统时钟分频的情况,更重要的是可以找到所有外设所挂载的总线(AHB、APB1、APB2)情况,由此可进一步确认外设的时钟。以TIMER2为例,其挂载在APB1总线;


使用特权

评论回复
板凳
renzheshengui|  楼主 | 2021-6-3 16:34 | 只看该作者
3.分析时钟树
分析完系统架构后,要确认具体的外设时钟就需要去分析时钟树。

假设我们的目的是找到定时器2(TIMER2)的时钟频率。
开始分析时钟树,要从左往右分析。左侧四个框为四种时钟源,选择某一时钟源。
沿线路往右分析,经过各种倍频之后得到CK_SYS即系统时钟。继续往右分析,找到标注有“TIMER1、2、3…’”之类的框,可以看出其时钟来源于CK_APB1(最大54MHz),注意 在这个框中可以看到如下内容:TIMER1,2,3,4,5,6, 11,12,13 i f(APB1
prescale =1)x1
else x 2;


这段内容的意思是:
如果APB1的分频系数为1,那么TIMER2的时钟频率=APB1时钟频率 x 1;
如果APB1的分频系数为2,那么TIMER2的时钟频率=APB1时钟频率 x 2;
由此可以算出,在GD32F103RC芯片中如果配置系统时钟CK_SYS=108MHz,则CK_APB1=54MHz,APB1
prescale =2,因此CK_TIMER2 = CK_APB1 *2=CK_SYS=108MHz;
同理,如果配置CK_SYS=72MHz,则CK_TIMER2=72MHz;


使用特权

评论回复
地板
renzheshengui|  楼主 | 2021-6-3 16:35 | 只看该作者
4.修改代码
此处以使用外部12M晶振为例。
(1)设置外部晶振
打开gd32f10x.h文件,找到如下代码段:

/* define value of high speed crystal oscillator (HXTAL) in Hz */
#if !defined  HXTAL_VALUE   
#ifdef GD32F10X_CL   
#define HXTAL_VALUE    ((uint32_t)25000000) /*!< value of the external oscillator in Hz */
#else
#define HXTAL_VALUE    ((uint32_t)12000000) /* !< from 4M to 16M *!< value of the external oscillator in Hz*/
#endif /* HXTAL_VALUE */
#endif /* high speed crystal oscillator value */

HXTAL_VALUE即板子所焊接的外部晶振的宏定义,我所用的外部晶振为12MHz,所以此处为:#define HXTAL_VALUE ((uint32_t)12000000)。如果你 的外部晶振是8MHz,那么这里就应改为:#define HXTAL_VALUE ((uint32_t)8000000)。


使用特权

评论回复
5
renzheshengui|  楼主 | 2021-6-3 16:35 | 只看该作者
(2)选定外部晶振作为系统主时钟
打开system_gd32f10x.c文件,找到如下代码段:

#include "gd32f10x.h"

/* 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 */


__SYS_OSC_CLK即系统主时钟的宏定义,因为我选用外部时钟,所以此处为:#define __SYS_OSC_CLK (__HXTAL)。


使用特权

评论回复
6
renzheshengui|  楼主 | 2021-6-3 16:36 | 只看该作者
(3)配置系统时钟大小
在system_gd32f10x.c文件中找到如下代码段:

/* select a system clock by uncommenting the following line */
/* use 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)

/* use HXTAL (XD series CK_HXTAL = 8M, CL series CK_HXTAL = 25M) */
//#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)


在代码中高亮显示的项即为我选择配置的72MHz系统时钟,时钟来源为外部高速时钟倍频。


使用特权

评论回复
7
renzheshengui|  楼主 | 2021-6-3 16:36 | 只看该作者
(4)修改倍频系数
上述操作只是选择了72MHz作为时钟频率,但距离真正产生72MHz时钟还需要先对时钟源(高速外部时钟)进行倍频等处理。
在system_gd32f10x.c文件中找到如下代码段:

/*!
    \brief      configure the system clock to 72M by PLL which selects HXTAL(MD/HD/XD:8M; CL:25M) as its clock source
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void system_clock_72m_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;
    /* APB2 = AHB/1 */
    RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
    /* APB1 = AHB/2 */
    RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;

#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD))
    /* select HXTAL/2 as clock source */
    RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0);
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0);

    /* CK_PLL = (CK_HXTAL/2) * 18 = 72 MHz */
    RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
    RCU_CFG0 |= RCU_PLL_MUL12;//RCU_PLL_MUL18

#elif defined(GD32F10X_CL)
    /* CK_PLL = (CK_PREDIV0) * 18 = 72 MHz */
    RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL18);

    /* CK_PREDIV0 = (CK_HXTAL)/5 *8 /10 = 4 MHz */
    RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
    RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL8 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV10);

    /* enable PLL1 */
    RCU_CTL |= RCU_CTL_PLL1EN;
    /* wait till PLL1 is ready */
    while((RCU_CTL & RCU_CTL_PLL1STB) == 0){
    }
#endif /* GD32F10X_MD and GD32F10X_HD and GD32F10X_XD */

    /* 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)){
    }
}


注意该代码段中的这一段代码:

#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD))
    /* select HXTAL/2 as clock source */
    RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0);
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_CFG0_PREDV0);

    /* CK_PLL = (CK_HXTAL/2) * 18 = 72 MHz */
    RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
    RCU_CFG0 |= RCU_PLL_MUL12;//RCU_PLL_MUL18


最后一行是设置时钟源的倍频系数。
库文件默认的CK_HXTAL(高速外部时钟)为8MHz,根据计算公式:CK_PLL = (CK_HXTAL/2) * 18 = 72 MHz,得出的倍频系数为18,即RCU_PLL_MUL18。
而实际上我们用的CK_HXTAL(高速外部时钟)为12MHz,根据计算公式:CK_PLL = (CK_HXTAL/2) * 12 = 72 MHz,得出的倍频系数为12,即RCU_PLL_MUL12。因此最后一行应该为:RCU_CFG0 |= RCU_PLL_MUL12,即对时钟源倍频12倍。
至此我们真正得到的系统时钟才为72 MHz。


使用特权

评论回复
8
renzheshengui|  楼主 | 2021-6-3 16:37 | 只看该作者
5.检验配置结果
配置TIMER2驱动并查询系统时钟频率、APB1总线时钟频率、系统时钟来源。

/**
* @brief  TIMER2配置(timebase)
* 时钟频率 = 72000000 / 36000 = 2000Hz
* 时钟周期 = 1 / 2000Hz = 0.5ms
* 更新周期 = 0.5ms * 2000 = 1s
* @param  None
* @retval 返回值
*/
static void timer2_config(uint16_t Prescaler, uint32_t Period)
{
    timer_parameter_struct timer_initpara;
        rcu_periph_clock_enable(RCU_TIMER2);
       
        timer_deinit(TIMER2);
    /* initialize TIMER init parameter struct */
    timer_struct_para_init(&timer_initpara);
    /* TIMER2 configuration */
    timer_initpara.prescaler         = Prescaler - 1;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = Period;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_init(TIMER2, &timer_initpara);
       
    timer_interrupt_enable(TIMER2, TIMER_INT_UP);
    timer_enable(TIMER2);
       
    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    nvic_irq_enable(TIMER2_IRQn, IRQ_PRIO_TIMEBASE);

    sys_clk_freq = rcu_clock_freq_get(CK_SYS);
    apb1_clk_freq = rcu_clock_freq_get(CK_APB1);
    sys_clk_source = rcu_system_clock_source_get();
    if(sys_clk_source == RCU_SCSS_IRC8M)
    {
        source_type = 1;//系统时钟为高速内部时钟
    }
    else if(sys_clk_source == RCU_SCSS_HXTAL)
    {
        source_type = 2;//系统时钟为高速外部时钟
    }
    else
    {
        source_type = 3;//系统时钟为高速外部时钟倍频
    }  
}



执行程序后得到结果如下:

sys_clk_freq系统时钟频率为72MHz;
apb1_clk_freq总线APB1时钟频率为36MHz;
source_type系统时钟源为0x03即RCU_SCSS_PLL;
以上与我们期望设置的情况一致,timer2_run_time即定时器TIMER2的运行时间按配置以1s为周期增加。


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

78

主题

3858

帖子

2

粉丝