一 时钟控制单元
1. HXTAL:高速外部时钟,4到32MHz的外部振荡器可为系统提供更为精确的主时钟。带有特定频率的晶体必须靠近两个HXTAL的引脚。和晶体连接的外部电阻和电容必须根据所选择的振荡器来调整。
2. IRC8M:高速内部8MHz时钟,内部8MHz RC振荡器时钟,简称IRC8M时钟,拥有8MHz的固定频率,设备上电后CPU默认选择的时钟源就是IRC8M时钟。
3. IRC28M:高速内部28MHz时钟,内部28MHz RC振荡器时钟 (IRC28M) 有一个固定的频率28MHz,专门用作ADC时钟。
4. IRC48M:高速内部48MHz时钟,内部48M RC振荡器时钟(IRC48M) 有一个固定的频率48MHz,用作USB时钟或者PLL时钟源。
5. LXTAL:低速外部时钟,LXTAL晶体是一个32.768kHz的低速外部晶体或陶瓷谐振器。它为实时时钟电路提供一个低功耗且精确的时钟源。
6. IRC40K:低速内部时钟,IRC40K RC振荡器时钟担当一个低功耗时钟源的角色,它的时钟频率大约40 kHz,为独立看门狗定时器和实时时钟电路提供时钟。
7. PLL:锁相环,内部锁相环PLL通过对输入参考频率为4~32MHz的时钟基准2 ~64倍频,可以提供16~108 MHz的时钟输出。
二 系统时钟架构
以GD32F330系列为例,Cortex-M4架构,最高时钟频率为84MHz。
通过固件库的system_gd32f10x.c可知:
/* HXTAL is stable */
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB/2 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;
/* APB1 = AHB/2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
AHB总线为系统时钟的1分频,即最高频率为84MHz;APB1总线为系统时钟的2分频,即最高频率为42MHz;APB2总线为系统时钟的2分频,即最高频率为42MHz;
另外,通过该时钟架构图可以清楚的了解该芯片外设所挂载的时钟总线(AHB、APB1、APB2),以TIMER2为例,挂载的是APB1总线,USART0挂载APB2总线。
三 时钟树分析
① 以HXTAL为例,外部HXTAL时钟源提供系统时钟频率。
② PLLPRESEL选择外部晶振作为PLL时钟源,经过PREDV分频,PLLSEL选择外部晶振作为PLL时钟源,经过PLL倍频得到CK_PLL,再通过SCS选择PLL时钟源作为系统时钟。
③ CK_SYS系统时钟再通过分频得到CK_AHB,即为AHB总线的时钟频率。
④ 以TIMER2为例,TIMER2挂载APB1总线,假如APB1分频系数为1,则TIMER2频率为AHB,否则TIMER2频率为AHB/(APB1的分频系数/2)。
⑤ 以USART0为例,USART0挂载APB2总线,通过APB2分频得到CK_APB2频率直接提供USART0使用。
四 时钟初始化
1. 系统时钟初始化,在startup_gd32f3x0.s文件中找到如下代码段,进入SystemInit()接口
/* reset Handler */
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
2. 是RCU相关寄存器初始化,在system_gd32f3x0.c找到如下代码,进入system_clock_config()函数。
void SystemInit(void)
{
#if (defined(GD32F350))
RCU_APB2EN |= BIT(0);
CMP_CS |= (CMP_CS_CMP1MSEL | CMP_CS_CMP0MSEL);
#endif /* GD32F350 */
if(((FMC_OBSTAT & OB_OBSTAT_PLEVEL_HIGH) != OB_OBSTAT_PLEVEL_HIGH) &&
(((FMC_OBSTAT >> 13) & 0x1) == SET)) {
FMC_KEY = UNLOCK_KEY0;
FMC_KEY = UNLOCK_KEY1 ;
FMC_OBKEY = UNLOCK_KEY0;
FMC_OBKEY = UNLOCK_KEY1 ;
FMC_CTL |= FMC_CTL_OBER;
FMC_CTL |= FMC_CTL_START;
while((uint32_t)0x00U != (FMC_STAT & FMC_STAT_BUSY));
FMC_CTL &= ~FMC_CTL_OBER;
FMC_CTL |= FMC_CTL_OBPG;
if((FMC_OBSTAT & OB_OBSTAT_PLEVEL_HIGH) == OB_OBSTAT_PLEVEL_NO) {
OB_SPC = FMC_NSPC;
} else if((FMC_OBSTAT & OB_OBSTAT_PLEVEL_HIGH) == OB_OBSTAT_PLEVEL_LOW) {
OB_SPC = FMC_LSPC;
}
OB_USER = OB_USER_DEFAULT & ((uint8_t)(FMC_OBSTAT >> 8));
OB_DATA0 = ((uint8_t)(FMC_OBSTAT >> 16));
OB_DATA1 = ((uint8_t)(FMC_OBSTAT >> 24));
OB_WP0 = ((uint8_t)(FMC_WP));
OB_WP1 = ((uint8_t)(FMC_WP >> 8));
while((uint32_t)0x00U != (FMC_STAT & FMC_STAT_BUSY));
FMC_CTL &= ~FMC_CTL_OBPG;
FMC_CTL &= ~FMC_CTL_OBWEN;
FMC_CTL |= FMC_CTL_LK;
}
/* FPU settings */
#if (__FPU_PRESENT == 1U) && (__FPU_USED == 1U)
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); /* set CP10 and CP11 Full Access */
#endif
/* enable IRC8M */
RCU_CTL0 |= RCU_CTL0_IRC8MEN;
while(0U == (RCU_CTL0 & RCU_CTL0_IRC8MSTB)) {
}
RCU_CFG0 &= ~(RCU_CFG0_SCS);
RCU_CTL0 &= ~(RCU_CTL0_HXTALEN | RCU_CTL0_CKMEN | RCU_CTL0_PLLEN | RCU_CTL0_HXTALBPS);
/* reset RCU */
RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | \
RCU_CFG0_ADCPSC | RCU_CFG0_CKOUTSEL | RCU_CFG0_CKOUTDIV | RCU_CFG0_PLLDV);
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF4 | RCU_CFG0_PLLDV);
#if (defined(GD32F350))
RCU_CFG0 &= ~(RCU_CFG0_USBFSPSC);
RCU_CFG2 &= ~(RCU_CFG2_CECSEL | RCU_CFG2_USBFSPSC2);
#endif /* GD32F350 */
RCU_CFG1 &= ~(RCU_CFG1_PREDV | RCU_CFG1_PLLMF5 | RCU_CFG1_PLLPRESEL);
RCU_CFG2 &= ~(RCU_CFG2_USART0SEL | RCU_CFG2_ADCSEL);
RCU_CFG2 &= ~RCU_CFG2_IRC28MDIV;
RCU_CFG2 &= ~RCU_CFG2_ADCPSC2;
RCU_CTL1 &= ~RCU_CTL1_IRC28MEN;
RCU_ADDCTL &= ~RCU_ADDCTL_IRC48MEN;
RCU_INT = 0x00000000U;
RCU_ADDINT = 0x00000000U;
/* configure system clock */
system_clock_config();
#ifdef VECT_TAB_SRAM
nvic_vector_table_set(NVIC_VECTTAB_RAM, VECT_TAB_OFFSET);
#else
nvic_vector_table_set(NVIC_VECTTAB_FLASH, VECT_TAB_OFFSET);
#endif
}
3. 选择系统时钟频率84MHZ,在system_gd32f3x0.c找到如下代码,进入system_clock_84m_hxtal()。
static void system_clock_config(void)
{
#ifdef __SYSTEM_CLOCK_8M_HXTAL
system_clock_8m_hxtal();
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
system_clock_72m_hxtal();
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2)
system_clock_72m_irc8m();
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC48M_DIV2)
system_clock_72m_irc48m();
#elif defined (__SYSTEM_CLOCK_84M_PLL_HXTAL)
system_clock_84m_hxtal();
#elif defined (__SYSTEM_CLOCK_84M_PLL_IRC8M_DIV2)
system_clock_84m_irc8m();
#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL)
system_clock_96m_hxtal();
#elif defined (__SYSTEM_CLOCK_96M_PLL_IRC8M_DIV2)
system_clock_96m_irc8m();
#elif defined (__SYSTEM_CLOCK_96M_PLL_IRC48M_DIV2)
system_clock_96m_irc48m();
#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)
system_clock_108m_hxtal();
#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M_DIV2)
system_clock_108m_irc8m();
#else
system_clock_8m_irc8m();
#endif /* __SYSTEM_CLOCK_8M_HXTAL */
}
4. 根据系统时钟频率,去配置其他外设时钟源,在system_gd32f3x0.c找到如下代码。
static void system_clock_84m_hxtal(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable HXTAL */
RCU_CTL0 |= RCU_CTL0_HXTALEN;
/* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */
do {
timeout++;
stab_flag = (RCU_CTL0 & RCU_CTL0_HXTALSTB);
} while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL0 & RCU_CTL0_HXTALSTB)) {
return;
}
/* HXTAL is stable */
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB/2 */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;
/* APB1 = AHB/2 */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;
/* PLL = HXTAL /4 * 21 = 84 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF4 | RCU_CFG0_PLLPREDV); //16,27|(18,21),27,17
RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PLLMF5 | RCU_CFG1_PREDV); //30,31,(0,3)
RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | (RCU_PLL_MUL21 & (~RCU_CFG1_PLLMF5))); //16,
RCU_CFG1 |= (RCU_PLLPRESEL_HXTAL | RCU_PLL_PREDV4); //设置RCU_CFG1寄存器PREDV[3:0]预分频系数
RCU_CFG1 |= (RCU_PLL_MUL21 & RCU_CFG1_PLLMF5); //RCU_CFG1_PLLMF5实际表示的是 :RCU_CFG0 的位 27,21:18
/* enable PLL */
RCU_CTL0 |= RCU_CTL0_PLLEN;
/* wait until PLL is stable */
while(0U == (RCU_CTL0 & RCU_CTL0_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. 设置外部晶振频率,在gd32f3x0.h文件,找到如下代码段,修改外部晶振为16MHZ。
/* define value of high speed crystal oscillator (HXTAL) in Hz */
#if !defined (HXTAL_VALUE)
#define HXTAL_VALUE ((uint32_t)16000000)
#endif /* high speed crystal oscillator value */
2. 选择外部时钟源,在system_gd32f3x0.c文件,找到如下代码段,_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 */
3. 设置系统主频率大小,在system_gd32f3x0.c文件中找到如下代码段,选择外部时钟源通过PLL配置为84MHZ系统时钟频率。
#if defined (GD32F330)
//#define __SYSTEM_CLOCK_8M_HXTAL (__HXTAL)
//#define __SYSTEM_CLOCK_8M_IRC8M (__IRC8M)
//#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000)
//#define __SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2 (uint32_t)(72000000)
//#define __SYSTEM_CLOCK_72M_PLL_IRC48M_DIV2 (uint32_t)(72000000)
#define __SYSTEM_CLOCK_84M_PLL_HXTAL (uint32_t)(84000000)
//#define __SYSTEM_CLOCK_84M_PLL_IRC8M_DIV2 (uint32_t)(84000000)
#endif /* GD32F330 */
4. 修改PLL分频倍频系数
修改前:系统外部晶振HXTAL默认为8MHZ,带入公式:
CK_PLL = (CK_HXTAL/2) * 21 = 84 MHz,得到PLL分频系数为2,PLL倍频系数为21
/* PLL = HXTAL /2 * 21 = 84 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF | RCU_CFG0_PLLMF4 | RCU_CFG0_PLLPREDV);
RCU_CFG1 &= ~(RCU_CFG1_PLLPRESEL | RCU_CFG1_PLLMF5 | RCU_CFG1_PREDV);
RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | (RCU_PLL_MUL21 & (~RCU_CFG1_PLLMF5)));
RCU_CFG1 |= (RCU_PLLPRESEL_HXTAL | RCU_PLL_PREDV2);
RCU_CFG1 |= (RCU_PLL_MUL21 & RCU_CFG1_PLLMF5);
/* enable PLL */
RCU_CTL0 |= RCU_CTL0_PLLEN;
修改后:系统外部晶振HXTAL为16MHZ,带入公式:
CK_PLL = (CK_HXTAL/4) * 21 = 84 MHz,得到PLL分频系数为4,PLL倍频系数为21,修改如下
RCU_CFG1 |= (RCU_PLLPRESEL_HXTAL | RCU_PLL_PREDV4);
六 PLL相关寄存器配置
前面介绍时钟树的时候说过,选择PLL作为系统时钟源需要设置四个地方,PLLPRESEL选择器,PREDV分频系数,PLLSEL选择器,PLL倍频系数。RCU_CFG1寄存器决定分频系数,RCU_CFG0寄存器决定倍频系数。查看数据手册,可以知道:
1. PLLPRESEL选择器
由 RCU_CFG1寄存器的第30位 PLLPRESEL决定 ,或由RCU_CFG0寄存器的第17位PLLPREDV决定。
2.PREDV分频系数
由RCU_CFG1寄存器3-0位的PREDV[3,0]决定,同时CU_CFG0寄存器的第17位和PREDV的第0位功能相同。
3. PLLSEL选择器
由RCU_CFG0寄存器的第16位决定
4. PLL倍频系数
由RCU_CFG0寄存器21-18位的PLLMF[3,0],RCU_CFG0寄存器的27位 PLLMF[4],RCU_CFG1寄存器的31位 PLLMF[5]共同决定。
七 仿真验证
源码链接:GD32时钟配置代码学习工程-嵌入式文档类资源-CSDN下载
参考文章:
http://t.csdn.cn/JHan5文章来源:https://www.toymoban.com/news/detail-604431.html
http://t.csdn.cn/k0m3W文章来源地址https://www.toymoban.com/news/detail-604431.html
到了这里,关于【GD32】系统时钟解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!