GD32F450 标称 200MHz,但是在手册中又说 它是 240MHz。本文以 手册中的 240MHz 进行举例,我保险起见,产品中使用还是在 200MHz 下使用。
时钟树
手册上的时钟树图如下
GD32F450的 外部时钟源 有2个
- LXTAL 外部低速时钟源 32.768 kHz
- HXTAL 外部高速时钟源 4~32 MHz
GD32F450的 内部时钟源 有3个
- IRC32K 内部 32KHz 时钟源
- IRC16M 内部 16MHz 时钟源
- IRC48M 内部 48MHz 时钟源
重要时钟
对于 GD32F450 的使用,主要关注
- CK_SYS 系统时钟
_ - HCLK ( AHB bus,Cortex-M4,SRAM,DMA,peripherals )
- PCLK1 ( APB1 peripherals )
- CKTIMERx ( TIMER1,2,3,4,5,6,11,12,13 )
- PCLK2 ( APB2 peripherals )
- CKTIMERx ( TIMER0,7,8,9,10 )
- CK_ADCX ( ADC 0,1,2 )
主要的外设时钟还是下面这三个时钟,它们衍生出其它外设时钟。
- AHB 最高时率为 240 MHz
- APB2 最高时率为 120 MHz
- APB1 最高时率为 60 MHz
对于单片机内核,仅需要关注 HCLK 。
上述的时钟 主要还是 通过 CK_SYS 时钟衍生出来的,因此需要先配置好它,才能对接下来的 外设时钟进行配置。
GD32F450的PLL
CK_SYS 有 3 个 时钟源,如下图所示:
即,
- IRC16M 内部16MHz (默认)
- HXTAL 外部高速时钟
- PLLP 倍频时钟P
CK_SYS 要想达到240MHz,必须要通过 倍频时钟P 得到。
GD32F450有3个PLL,与CK_SYS有关的只有PLL,如下图所示
根据上图,可以明确倍频步骤了。
- 打开外部时钟源(HXTAL)
- 切换内核时钟为非PLL时钟(当前例程为选择内部16MHz时钟)
- 确定切换成功
- 关闭PLL
- 配置 PLLSEL 选择外部时钟源(鄙人的开发板外部晶振为8MHz)
- 配置输入分频系数,倍频系数N,输出P分频系数
- 使能 PLL
- 配置SCS切换时钟
- 确定切换成功
查看相关寄存器,即可进行配置了。
代码如下
uint8_t CLock_Init(void)
{
uint16_t overTimeCnt;
RCU_CTL |= RCU_CTL_HXTALEN; // 高速晶体振荡器(HXTAL)使能
overTimeCnt = 0x0fff;
do
{
overTimeCnt--;
if(!overTimeCnt)
return FAILED; // 等待超时
} while (!(RCU_CTL & RCU_CTL_HXTALSTB)); // 等待 外部晶振HXTAL 稳定
RCU_CFG0 = (RCU_CFG0 & ~RCU_CFG0_SCS) | RCU_CKSYSSRC_IRC16M; // 系统时钟 选择内部16MHz
overTimeCnt = 0x0fff;
do
{
overTimeCnt--;
if(!overTimeCnt)
return FAILED; // 等待超时
} while ((RCU_CFG0 & RCU_CFG0_SCSS) != RCU_SCSS_IRC16M); // 等待 系统时钟 选择内部16MHz
RCU_CTL &= ~RCU_CTL_PLLEN; // 关闭 PLL
overTimeCnt = 0x0fff;
do
{
overTimeCnt--;
if(!overTimeCnt)
return FAILED; // 等待超时
} while ((RCU_CTL & RCU_CTL_PLLEN)); // 等待 关闭 PLL
RCU_PLL = ( RCU_PLL & ~(RCU_PLL_PLLPSC | RCU_PLL_PLLN | RCU_PLL_PLLP | RCU_PLL_PLLSEL | RCU_PLL_PLLQ) ) | \
( 8 << 0) | ( 480 << 6) | ( 0 << 16) | RCU_PLL_PLLSEL | ( 10 << 24);
// 输入8分频 | 倍频480倍 | PLLP 2分频 | 选择外部晶振 | PLLQ 10分频
// 输入 1MHz | VCO=480MHz | PLLP=240MHz | | PLLQ=48MHz
RCU_CTL |= RCU_CTL_PLLEN; // 使能 PLL
overTimeCnt = 0x0fff;
do
{
overTimeCnt--;
if(!overTimeCnt)
return FAILED; // 等待超时
} while (!(RCU_CTL & RCU_CTL_PLLSTB)); // 等待 PLL 稳定
RCU_CFG0 = (RCU_CFG0 & ~(RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC)) | \
(0 << 4) | (5 << 10) | (4 << 13);
// AHB不分频 | APB1 4分频 | APB2 2分频
// 240MHz | 60MHz | 120MHz
RCU_CFG0 = (RCU_CFG0 & ~RCU_CFG0_SCS) | RCU_CKSYSSRC_PLLP; // 系统时钟 选择PLLP
overTimeCnt = 0x0fff;
do
{
overTimeCnt--;
if(!overTimeCnt)
return FAILED; // 等待超时
} while ((RCU_CFG0 & RCU_CFG0_SCSS) != RCU_SCSS_PLLP); // 等待 系统时钟 选择PLLP
return SUCCEED;
}
简单的方法
其实厂家已经帮我们初始化好了时钟,在 system_gd32f4xx.c 文件中 通过 选择宏定义就能 将单片机 配置成常用的时钟频率了。
其
AHB 不分频
APB1 四分频
APB2 二分频
/* 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)
//#define __SYSTEM_CLOCK_240M_PLL_IRC16M (uint32_t)(240000000)
// #define __SYSTEM_CLOCK_240M_PLL_8M_HXTAL (uint32_t)(240000000)
//#define __SYSTEM_CLOCK_240M_PLL_25M_HXTAL (uint32_t)(240000000)