1. 系统框图
2. 时钟树
- HXTAL:高速外部时钟,4到16M的外部高速晶体振荡器可为系统时钟提供更为精确时钟源。 HXTAL 时钟可以被直接用作系统时钟源或者PLL输入时钟。
- LXTAL:低速外部时钟,LXTAL是一个频率为32.768kHz的外部低速晶体或陶瓷谐振器。它为实时时钟电路提供一个低功耗且高精准的时钟源。
- IRC8M:高速内部时钟,内部8MHz RC振荡器时钟,简称IRC8M时钟,拥有8MHz的固定频率,设备上电后CPU默认选择其做为系统时钟源。它的精度比HXTAL时钟要差。
- IRC40K:低速内部时钟,IRC40K内部RC振荡器时钟担当一个低功耗时钟源的角色,不需要外部器件,它的时钟频率大约40kHz,为独立看门狗和实时时钟电路提供时钟。
3. 代码分析
A. 在system_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_VALUE) /* main oscillator frequency */
B. 在gd32f10x.h文件中可查看系统时钟源频率值25Mhz,GD32F10X_CL 产品:
/* 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)8000000) /* !< from 4M to 16M *!< value of the external oscillator in Hz*/
#endif /* HXTAL_VALUE */
#endif /* high speed crystal oscillator value */
C. 首先在startup_gd32f10x_cl.s启动文件中有关于系统时钟初始化的函数接口
;/* reset Handler */
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
D. 针对RCU模块进行初始化设置
/*!
\brief setup the microcontroller system, initialize the system
\param[in] none
\param[out] none
\retval none
*/
void SystemInit(void)
{
/* reset the RCC clock configuration to the default reset state */
/* enable IRC8M */
RCU_CTL |= RCU_CTL_IRC8MEN;
/* reset SCS, AHBPSC, APB1PSC, APB2PSC, ADCPSC, CKOUT0SEL bits */
RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC |
RCU_CFG0_ADCPSC | RCU_CFG0_ADCPSC_2 | RCU_CFG0_CKOUT0SEL);
/* reset HXTALEN, CKMEN, PLLEN bits */
RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN);
/* Reset HXTALBPS bit */
RCU_CTL &= ~(RCU_CTL_HXTALBPS);
/* reset PLLSEL, PREDV0_LSB, PLLMF, USBFSPSC bits */
#ifdef GD32F10X_CL
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF |
RCU_CFG0_USBFSPSC | RCU_CFG0_PLLMF_4);
RCU_CFG1 = 0x00000000U;
#else
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0 | RCU_CFG0_PLLMF |
RCU_CFG0_USBDPSC | RCU_CFG0_PLLMF_4);
#endif /* GD32F10X_CL */
#if (defined(GD32F10X_MD) || defined(GD32F10X_HD) || defined(GD32F10X_XD))
/* reset HXTALEN, CKMEN and PLLEN bits */
RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);
/* disable all interrupts */
RCU_INT = 0x009F0000U;
#elif defined(GD32F10X_CL)
/* Reset HXTALEN, CKMEN, PLLEN, PLL1EN and PLL2EN bits */
RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);
/* disable all interrupts */
RCU_INT = 0x00FF0000U;
#endif
/* Configure the System clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */
system_clock_config();
}
E. 根据所屏蔽的宏,可知系统频率为96MHz
/* 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)
/*!
\brief configure the system clock
\param[in] none
\param[out] none
\retval none
*/
static void system_clock_config(void)
{
#ifdef __SYSTEM_CLOCK_HXTAL
system_clock_hxtal();
#elif defined (__SYSTEM_CLOCK_24M_PLL_HXTAL)
system_clock_24m_hxtal();
#elif defined (__SYSTEM_CLOCK_36M_PLL_HXTAL)
system_clock_36m_hxtal();
#elif defined (__SYSTEM_CLOCK_48M_PLL_HXTAL)
system_clock_48m_hxtal();
#elif defined (__SYSTEM_CLOCK_56M_PLL_HXTAL)
system_clock_56m_hxtal();
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
system_clock_72m_hxtal();
#elif defined (__SYSTEM_CLOCK_96M_PLL_HXTAL)
system_clock_96m_hxtal();
#elif defined (__SYSTEM_CLOCK_108M_PLL_HXTAL)
system_clock_108m_hxtal();
#elif defined (__SYSTEM_CLOCK_48M_PLL_IRC8M)
system_clock_48m_irc8m();
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M)
system_clock_72m_irc8m();
#elif defined (__SYSTEM_CLOCK_108M_PLL_IRC8M)
system_clock_108m_irc8m();
#endif /* __SYSTEM_CLOCK_HXTAL */
}
F. 系统时钟分频至各外设频率值
在 static void system_clock_96m_hxtal(void)函数中,可知AHB = 96Mhz,APB2 = 96Mhz,APB1 = 48Mhz。
/*!
\brief configure the system clock to 96M 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_96m_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) * 24 = 96 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
RCU_CFG0 |= RCU_PLL_MUL24;
#elif defined(GD32F10X_CL)
/* CK_PLL = (CK_PREDIV0) * 24 = 96 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL24);
/* 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)){
}
}
其中以下代码是针对时钟源进行分频倍频处理,得到最终的AHB及APB1和APB2系统时钟。
#elif defined(GD32F10X_CL)
/* CK_PLL = (CK_PREDIV0) * 24 = 96 MHz */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4);
RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL24);
/* 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);
根据时钟树图片可知,根据选择的寄存器控制位,当HXTAL=25Mhz时,首先要经过PREDV1=5分频,然后再通过PLL1MF=8倍频,然后再通过PREDV0=10分频后,可知25Mhz/5*8/10=4Mhz,再进行24倍频可知CLK_SYS=24 * 4=96Mhz。AHB、APB1、APB2再通过相应的分频计算得出。
/* 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;
E. 最终各个外设的模块需要参考外设初始化时,是否对其时钟进行分频。