1.前言
硬件:GD32F450 最高时钟频率200MHZ(外部晶振8MHZ)
软件:KEIL(V5.35)
固件包:GD32F4xx_Firmware_Library_V3.2.0
2.时钟树
时钟配置大概流程如下图红线指示,GD32F470的最高频率可以到240MHZ,GD32F450最高频率可以通过PLL倍频到200MHZ。
预分频器可以配置AHB/APB2/APB1域的时钟频率。AHB/APB2/APB1域的最高时钟频率分别为200MHZ/100MHZ/50MHZ。
3.软件配置
3.1 修改晶振频率
在system_gd32f4xx.c中,需要根据实际使用的外部晶振频率修改__HXTAL的值,也就是HXTAL_VALUE的值,具体在gd32f4xx.h中修改。
#include "gd32f4xx.h"/* system frequency define */
#define __IRC16M (IRC16M_VALUE) /* internal 16 MHz RC oscillator frequency */
#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */
//#define __SYS_OSC_CLK (__IRC16M) /* main oscillator frequency */
//没有用到
#define __SYS_OSC_CLK (__HXTAL) /* main oscillator frequency *//* select a system clock by uncommenting the following line */
//#define __SYSTEM_CLOCK_IRC16M (uint32_t)(__IRC16M)
//#define __SYSTEM_CLOCK_HXTAL (uint32_t)(__HXTAL)
//#define __SYSTEM_CLOCK_120M_PLL_IRC16M (uint32_t)(120000000)
//#define __SYSTEM_CLOCK_120M_PLL_8M_HXTAL (uint32_t)(120000000)
//#define __SYSTEM_CLOCK_120M_PLL_25M_HXTAL (uint32_t)(120000000)
//#define __SYSTEM_CLOCK_168M_PLL_IRC16M (uint32_t)(168000000)
//#define __SYSTEM_CLOCK_168M_PLL_8M_HXTAL (uint32_t)(168000000)
//#define __SYSTEM_CLOCK_168M_PLL_25M_HXTAL (uint32_t)(168000000)
//#define __SYSTEM_CLOCK_200M_PLL_IRC16M (uint32_t)(200000000)
#define __SYSTEM_CLOCK_200M_PLL_8M_HXTAL (uint32_t)(200000000)
//#define __SYSTEM_CLOCK_200M_PLL_25M_HXTAL (uint32_t)(200000000)
gd32f4xx.h,实际使用的是8MHZ晶振,如果使用的是其他频率的晶振,需要对应修改。很多时候串口通信异常就是时钟频率不对导致的。如果上面的宏定义里面没有自己想要的频率,可以参考官方写好的时钟配置函数来设置PLL_M/PLL_N/PLL_P/PLL_Q。
#ifndef GD32F4XX_H
#define GD32F4XX_H#ifdef cplusplusextern "C" {
#endif /* define GD32F4xx */
#if !defined (GD32F450) && !defined (GD32F405) && !defined (GD32F407)/* #define GD32F450 *//* #define GD32F405 *//* #define GD32F407 */
#endif /* define GD32F4xx */#if !defined (GD32F450) && !defined (GD32F405) && !defined (GD32F407)#error "Please select the target GD32F4xx device in gd32f4xx.h file"
#endif /* undefine GD32F4xx tip *//* define value of high speed crystal oscillator (HXTAL) in Hz */
#if !defined (HXTAL_VALUE)
#define HXTAL_VALUE ((uint32_t)8000000)
#endif /* high speed crystal oscillator value *//* define startup timeout value of high speed crystal oscillator (HXTAL) */
#if !defined (HXTAL_STARTUP_TIMEOUT)
#define HXTAL_STARTUP_TIMEOUT ((uint16_t)0x0fff)//0x0800
#endif /* high speed crystal oscillator startup timeout */
3.2 时钟初始化
打开 __SYSTEM_CLOCK_200M_PLL_8M_HXTAL 宏定义,也可以配置成168MHZ,根据实际情况来配置。系统默认选择的是内部时钟的那个宏定义。
说明: __SYS_OSC_CLK 这个宏定义改不改都没关系,没有用到这个宏。
/* system frequency define */
#define __IRC16M (IRC16M_VALUE) /* internal 16 MHz RC oscillator frequency */
#define __HXTAL (HXTAL_VALUE) /* high speed crystal oscillator frequency */
//#define __SYS_OSC_CLK (__IRC16M) /* main oscillator frequency */
//没有用到
#define __SYS_OSC_CLK (__HXTAL) /* main oscillator frequency *//* select a system clock by uncommenting the following line */
//#define __SYSTEM_CLOCK_IRC16M (uint32_t)(__IRC16M)
//#define __SYSTEM_CLOCK_HXTAL (uint32_t)(__HXTAL)
//#define __SYSTEM_CLOCK_120M_PLL_IRC16M (uint32_t)(120000000)
//#define __SYSTEM_CLOCK_120M_PLL_8M_HXTAL (uint32_t)(120000000)
//#define __SYSTEM_CLOCK_120M_PLL_25M_HXTAL (uint32_t)(120000000)
//#define __SYSTEM_CLOCK_168M_PLL_IRC16M (uint32_t)(168000000)
//#define __SYSTEM_CLOCK_168M_PLL_8M_HXTAL (uint32_t)(168000000)
//#define __SYSTEM_CLOCK_168M_PLL_25M_HXTAL (uint32_t)(168000000)
//#define __SYSTEM_CLOCK_200M_PLL_IRC16M (uint32_t)(200000000)
#define __SYSTEM_CLOCK_200M_PLL_8M_HXTAL (uint32_t)(200000000)
//#define __SYSTEM_CLOCK_200M_PLL_25M_HXTAL (uint32_t)(200000000)
#ifdef __SYSTEM_CLOCK_IRC16M
uint32_t SystemCoreClock = __SYSTEM_CLOCK_IRC16M;
static void system_clock_16m_irc16m(void);
#elif defined (__SYSTEM_CLOCK_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_HXTAL;
static void system_clock_hxtal(void);
#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC16M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_IRC16M;
static void system_clock_120m_irc16m(void);
#elif defined (__SYSTEM_CLOCK_120M_PLL_8M_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_8M_HXTAL;
static void system_clock_120m_8m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_120M_PLL_25M_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_120M_PLL_25M_HXTAL;
static void system_clock_120m_25m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_168M_PLL_IRC16M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_168M_PLL_IRC16M;
static void system_clock_168m_irc16m(void);
#elif defined (__SYSTEM_CLOCK_168M_PLL_8M_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_168M_PLL_8M_HXTAL;
static void system_clock_168m_8m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_168M_PLL_25M_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_168M_PLL_25M_HXTAL;
static void system_clock_168m_25m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_200M_PLL_IRC16M)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_200M_PLL_IRC16M;
static void system_clock_200m_irc16m(void);
#elif defined (__SYSTEM_CLOCK_200M_PLL_8M_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_200M_PLL_8M_HXTAL;
static void system_clock_200m_8m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_200M_PLL_25M_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_200M_PLL_25M_HXTAL;
static void system_clock_200m_25m_hxtal(void);#endif /* __SYSTEM_CLOCK_IRC16M *//* configure the system clock */
static void system_clock_config(void);/*!\brief setup the microcontroller system, initialize the system\param[in] none\param[out] none\retval none
*/
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 */#endif/* Reset the RCU clock configuration to the default reset state ------------*//* Set IRC16MEN bit */RCU_CTL |= RCU_CTL_IRC16MEN;RCU_MODIFY/* Reset CFG0 register */RCU_CFG0 = 0x00000000U;/* Reset HXTALEN, CKMEN and PLLEN bits */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();
}
在SystemInit()函数最后调用时钟配置函数 system_clock_config(),外部晶振为无源晶振。
#elif defined (__SYSTEM_CLOCK_200M_PLL_8M_HXTAL)
/*!\brief configure the system clock to 200M by PLL which selects HXTAL(8M) as its clock source\param[in] none\param[out] none\retval none
*/
static void system_clock_200m_8m_hxtal(void)
{uint32_t timeout = 0U;uint32_t stab_flag = 0U;/* enable HXTAL */RCU_CTL |= RCU_CTL_HXTALEN;//HXTAL_STARTUP_TIMEOUT 默认为0x0800,需要加大等待晶振稳定的时长,改为0x0fff/* 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){}}RCU_APB1EN |= RCU_APB1EN_PMUEN;PMU_CTL |= PMU_CTL_LDOVS;/* HXTAL is stable *//* AHB = SYSCLK */RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;/* APB2 = AHB/2 */RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;/* APB1 = AHB/4 */RCU_CFG0 |= RCU_APB1_CKAHB_DIV4;/* Configure the main PLL, PLL_M = 8, PLL_N = 400, PLL_P = 2, PLL_Q = 9 */ RCU_PLL = (8U | (400U << 6U) | (((2U >> 1U) - 1U) << 16U) |(RCU_PLLSRC_HXTAL) | (9U << 24U));/* enable PLL */RCU_CTL |= RCU_CTL_PLLEN;/* wait until PLL is stable */while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){}/* Enable the high-drive to extend the clock frequency to 200 Mhz */PMU_CTL |= PMU_CTL_HDEN;while(0U == (PMU_CS & PMU_CS_HDRF)){}/* select the high-drive mode */PMU_CTL |= PMU_CTL_HDS;while(0U == (PMU_CS & PMU_CS_HDSRF)){} /* select PLL as system clock */RCU_CFG0 &= ~RCU_CFG0_SCS;RCU_CFG0 |= RCU_CKSYSSRC_PLLP;/* wait until PLL is selected as system clock */while(0U == (RCU_CFG0 & RCU_SCSS_PLLP)){}
}
避坑指南:在使用GD32F450的时候,时钟配置时,等待外部时钟稳定,HXTAL_STARTUP_TIMEOUT,这个宏默认为0x0800,有时候外部晶振在这个超时时间内没有稳定,会导致程序卡死。我这边将这个宏定义修改为0x0FFF,超时时间延长一倍,等待外部晶振稳定。
在gd32f4xx.h中修改HXTAL_STARTUP_TIMEUP这个宏的值,默认为0x0800。
/* define GD32F4xx */
#if !defined (GD32F450) && !defined (GD32F405) && !defined (GD32F407)/* #define GD32F450 *//* #define GD32F405 *//* #define GD32F407 */
#endif /* define GD32F4xx */#if !defined (GD32F450) && !defined (GD32F405) && !defined (GD32F407)#error "Please select the target GD32F4xx device in gd32f4xx.h file"
#endif /* undefine GD32F4xx tip *//* define value of high speed crystal oscillator (HXTAL) in Hz */
#if !defined (HXTAL_VALUE)
#define HXTAL_VALUE ((uint32_t)8000000)
#endif /* high speed crystal oscillator value *//* define startup timeout value of high speed crystal oscillator (HXTAL) */
#if !defined (HXTAL_STARTUP_TIMEOUT)
#define HXTAL_STARTUP_TIMEOUT ((uint16_t)0x0fff)//0x0800
#endif /* high speed crystal oscillator startup timeout */
如果外部晶振使用的是有源晶振,需要修改函数system_clock_200m_8m_hxtal(),在函数最前面添加rcu_osci_bypass_mode_enable(RCU_HXTAL)函数。
#elif defined (__SYSTEM_CLOCK_200M_PLL_8M_HXTAL)
/*!\brief configure the system clock to 200M by PLL which selects HXTAL(8M) as its clock source\param[in] none\param[out] none\retval none
*/
static void system_clock_200m_8m_hxtal(void)
{uint32_t timeout = 0U;uint32_t stab_flag = 0U;rcu_osci_bypass_mode_enable(RCU_HXTAL);//外部有源晶振/* enable HXTAL */RCU_CTL |= RCU_CTL_HXTALEN;//HXTAL_STARTUP_TIMEOUT 默认为0x0800,需要加大等待晶振稳定的时长,改为0x0fff/* 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){}}RCU_APB1EN |= RCU_APB1EN_PMUEN;PMU_CTL |= PMU_CTL_LDOVS;/* HXTAL is stable *//* AHB = SYSCLK */RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;/* APB2 = AHB/2 */RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;/* APB1 = AHB/4 */RCU_CFG0 |= RCU_APB1_CKAHB_DIV4;/* Configure the main PLL, PLL_M = 8, PLL_N = 400, PLL_P = 2, PLL_Q = 9 */ RCU_PLL = (8U | (400U << 6U) | (((2U >> 1U) - 1U) << 16U) |(RCU_PLLSRC_HXTAL) | (9U << 24U));/* enable PLL */RCU_CTL |= RCU_CTL_PLLEN;/* wait until PLL is stable */while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){}/* Enable the high-drive to extend the clock frequency to 200 Mhz */PMU_CTL |= PMU_CTL_HDEN;while(0U == (PMU_CS & PMU_CS_HDRF)){}/* select the high-drive mode */PMU_CTL |= PMU_CTL_HDS;while(0U == (PMU_CS & PMU_CS_HDSRF)){} /* select PLL as system clock */RCU_CFG0 &= ~RCU_CFG0_SCS;RCU_CFG0 |= RCU_CKSYSSRC_PLLP;/* wait until PLL is selected as system clock */while(0U == (RCU_CFG0 & RCU_SCSS_PLLP)){}
}
3.3 时钟频率测试
可以使用rcu_clock_freq_get()函数来获取时钟
//系统时钟测试SYS_clk = rcu_clock_freq_get(CK_SYS);//200000000AHB_clk = rcu_clock_freq_get(CK_AHB);//200000000APB1_clk = rcu_clock_freq_get(CK_APB1);//50000000APB2_clk = rcu_clock_freq_get(CK_APB2);//100000000