关于GD32F205RCT6的时钟配置问题

GD32F20x系列的时钟配置


前言

第一次使用兆易芯片写完验证程序调试时发现时钟不对,摸索了一下才搞清楚,分享出来供大家参考。调试所用到的芯片是GD32F205RCT6,系统时钟源选择外部高速时钟hxtal,时钟源配置为120MHz。

一、GD32F20x的时钟树

该芯片有四个时钟源,我选择了HXTAL(外部高速时钟),硬件方面采用了一个8MHz的无源晶振。下图为官方文档中的时钟树:

GD32F20x时钟树
按照官方demo中的时钟配置配置代码,在时钟树中表现出来就是图中橙色线的走向,也就是时钟源选择了HXTAL,PLL选择了PPL1(HXTAL产生了PLL1)作为输入时钟源。官方demo的时钟配置(以120MHz为例)代码如下:

static void system_clock_120m_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;       //AHB不分频
    /* APB2 = AHB/1 */
    RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;      //APB2不分频
    /* APB1 = AHB/2 */
    RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;      //APB1两分频

    /* CK_PLL = (CK_PREDIV0) * 10 = 120 MHz */ 
    RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLSEL );
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL10);
    
    /* CK_PREDIV0 = (CK_HXTAL) / 5 * 12 /5 = 12 MHz */ 
    RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
    RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL12 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV5);

    /* enable PLL1 */
    RCU_CTL |= RCU_CTL_PLL1EN;
    /* wait till PLL1 is ready */
    while((RCU_CTL & RCU_CTL_PLL1STB) == 0U){
    }
    
    /* 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)){
    }
}

二、时钟配置

1.设定外部时钟为系统时钟主振荡频率

打开“system_gdf20x.c”文件,找到下边的代码,将宏定义“#define __SYS_OSC_CLK (__IRC8M)”修改为“#define __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 */

2.设置外部晶振频率

上段代码的第二句中,在“(HXTAL_VALUE)”上右键跳转到其定义(位于gd32f20x.h),将HXTAL_VALUE的值改为你所使用的外部晶振频率,此处使用8MHz晶振,修改后的代码如下:

#define HXTAL_VALUE    ((uint32_t)8000000) /*!< value of the external oscillator in Hz */

3.选择一个系统时钟

打开“system_gdf20x.c”文件,找到下边的代码,选择哪一个时钟就将哪一段代码取消注释,此处选择外部高速时钟120MHz,代码如下:

* select a system clock by uncommenting the following line */ //通过取消以下注释来选择一个系统时钟
/* use IRC8M */
//#define __SYSTEM_CLOCK_IRC8M                    (uint32_t)(__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)
//#define __SYSTEM_CLOCK_120M_PLL_IRC8M           (uint32_t)(120000000)

/* use HXTAL */
//#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)
#define __SYSTEM_CLOCK_120M_PLL_HXTAL           (uint32_t)(120000000)

4.修改时钟配置

打开“system_gdf20x.c”文件,找到下边的代码,源代码是基于25MHz的外部晶振进行配置的,此处我们把它改成8MHz晶振,配置时钟的核心就是配置RCU_CFG0和RCU_CFG1这两个寄存器的值。修改后的代码如下:

/* CK_PLL = (CK_PREDIV0) * 10 = 120 MHz */ 
    RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLSEL );
    RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL10);
    
    /* CK_PREDIV0 = (CK_HXTAL) / 2 * 9 /3 = 12 MHz */ 
    RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);
    RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL9 | RCU_PREDV1_DIV2 | RCU_PREDV0_DIV3);

代码说明:
按照计算公式,首先要计算CK_PREDIV0的值,在芯片手册中找到RCU_CFG1寄存器的位/位域配置表,如下图:
RCU_CFG1位/位域配置
位域[7:4]为PREDV1输入源时钟分频,范围1-16,选择2分频,即RCU_PREDV1_DIV2;位域[3:0]为PREDV0输入源时钟分频,范围1-16,选择3分频,即RCU_PREDV0_DIV3;位域[11:8]为PLL1时钟倍频因子,范围8-16、20,选择9倍频,即RCU_PLL1_MUL9,此时根据公式可以得到CK_PREDIV0=8/2*9/3=12MHz。
接下来找到RCU_CFG0寄存器的位/位域配置表,如下图:
RCU_CFG0位/位域配置
RCU_CFG0寄存器的位域[21:18]和位[29]为PLL源时钟的倍频因子,范围2-14、6.5、16-32,选择10倍频,即RCU_PLL_MUL10,由公式得到CK_PLL=12x10=120MHz。至此,通过锁相环将8MHz的外部晶振倍频到120MHz,系统时钟配置完毕。


三.验证

在主函数定义一个全局变量(也可以定义一个局部变量加断点),用来获取各个时钟,在Keil中debug,添加watch1窗口中可以看到想要获取的时钟的值,代码如下:

uint32_t temp_freq;
int main(void)
{
  	temp_freq = rcu_clock_freq_get(CK_SYS);
	rcu_periph_clock_enable(RCU_GPIOC);                                //使能GPIOC时钟
	gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);//GPIO初始化
	TIMER1_Config();                                                   //定时器初始化	
	while(1)
  {
  }
}

watch1窗口的值如下图:

变量temp_freq的值为0x07270E00,换算为十进制就是120000000,即系统时钟为120MHz。其实这种方式检验的并不准确,获取到的这个数值取决于在第3步中取消注释了哪一行,即使在第4步的时钟配置函数中你没有正确配置两个寄存器的值。最好的方式还是配置一个定时器,配置中断函数(比如:翻转一个IO口的电平)用示波器检验时钟的准确性。

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是基于GD32F205芯片的CAN通信代码示例: 首先需要在代码中包含以下头文件: ``` #include "gd32f20x.h" #include "gd32f20x_can.h" ``` 然后需要初始化CAN模块: ``` /* CAN0 and CAN1 default configuration */ can_parameter_struct can_parameter; can_parameter_struct_struct_init(&can_parameter); can_parameter.time_triggered = DISABLE; can_parameter.auto_bus_off_recovery = ENABLE; can_parameter.auto_wake_up = ENABLE; can_parameter.auto_retrans = ENABLE; can_parameter.receive_fifo_number = CAN_FIFO0; can_parameter.transmit_fifo_order = DISABLE; can_parameter.working_mode = CAN_NORMAL_MODE; can_parameter.resync_jump_width = CAN_BT_SJW_1TQ; can_parameter.time_segment_1 = CAN_BT_BS1_11TQ; can_parameter.time_segment_2 = CAN_BT_BS2_4TQ; can_parameter.prescaler = 3; can_init(CAN0, &can_parameter); can_init(CAN1, &can_parameter); ``` 接下来需要配置CAN的过滤器: ``` /* CAN0 filter configuration */ can_filter_parameter_struct can_filter_parameter; can_filter_parameter.filter_number = 0; can_filter_parameter.filter_mode = CAN_FILTERMODE_MASK; can_filter_parameter.filter_bits.higher_bits = 0x0000; can_filter_parameter.filter_bits.lower_bits = 0x0000; can_filter_parameter.filter_mask_bits.higher_bits = 0x0000; can_filter_parameter.filter_mask_bits.lower_bits = 0x0000; can_filter_parameter.filter_fifo_number = CAN_FIFO0; can_filter_parameter.filter_enable = ENABLE; can_filter_init(&can_filter_parameter); ``` 接着可以启动CAN模块: ``` /* enable CAN0 */ can_enable(CAN0, ENABLE); /* enable CAN1 */ can_enable(CAN1, ENABLE); ``` 可以使用以下函数发送CAN消息: ``` /* transmit CAN message */ can_trasnmit_message_struct transmit_message; transmit_message.tx_sfid = 0x321; transmit_message.tx_efid = 0x0000; transmit_message.tx_ft = CAN_FT_DATA; transmit_message.tx_ff = CAN_FF_STANDARD; transmit_message.tx_dlen = 8; transmit_message.tx_data[0] = 0x11; transmit_message.tx_data[1] = 0x22; transmit_message.tx_data[2] = 0x33; transmit_message.tx_data[3] = 0x44; transmit_message.tx_data[4] = 0x55; transmit_message.tx_data[5] = 0x66; transmit_message.tx_data[6] = 0x77; transmit_message.tx_data[7] = 0x88; can_message_transmit(CAN0, &transmit_message); ``` 可以使用以下函数接收CAN消息: ``` /* receive CAN message */ can_receive_message_struct receive_message; can_message_receive(CAN0, CAN_FIFO0, &receive_message); ``` 以上是基于GD32F205芯片的CAN通信代码示例。请根据具体需求进行修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值