【前言】

GD32由于没有象STM32等有图形化配置的界面,所以学习了解它的时钟是非常有必要的。GD32H759系列的时钟配置是在官方提供的CMSIS\Source目录下面的system_gd32h7xx.c文件中配置系统时钟。通过数据手册与该例程,基本了解了GD32H759_EVAL开发板的时钟配置。

1、经查看原理图开发板上板载了25M高速晶振,示例中大都是使用外部高速时钟运行程序。

164025poevgtolz0eatuog

在system_gd32h7xx.c中的第60行配置了总线高速时钟为600M

164025g4iit9sti7s5iesg

  • GD32H759在上电复位后,汇编首先调用systemini进行系统时钟的复位与配置。

164025vdn46ktizjemgznj

  • systeminit函数是在system_gd32h7xx.c中定义。其内容如下:
void SystemInit(void)

  • {

  • /* 使能内部64M时钟 */

  • RCU_CTL |= RCU_CTL_IRC64MEN;

  • // 等待64MHz RC 振器器稳定标志位

  • while(0U == (RCU_CTL & RCU_CTL_IRC64MSTB)) {

  • }

  • RCU_APB4EN |= RCU_APB4EN_SYSCFG;

  • SYSCFG_SRAMCFG1 &= ~SYSCFG_SRAMCFG1_TCM_WAITSTATE;

  • RCU_CFG0 &= ~RCU_CFG0_SCS;

  • /* 复位时钟控制寄存器 */

  • /* reset HXTALEN, CKMEN, PLL0EN, PLL1EN, PLL2EN, PLLUSB0 and PLLUSB1 bits */

  • RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLL0EN | RCU_CTL_PLL1EN | RCU_CTL_PLL2EN | RCU_CTL_HXTALBPS);

  • RCU_ADDCTL1 &= ~(RCU_ADDCTL1_PLLUSBHS0EN | RCU_ADDCTL1_PLLUSBHS1EN | RCU_ADDCTL1_LPIRC4MEN);

  • /* reset CFG0, CFG1, CFG2, CFG3 registers */

  • RCU_CFG0 &= ~(RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC | RCU_CFG0_APB3PSC | RCU_CFG0_APB4PSC | RCU_CFG0_AHBPSC |

  • RCU_CFG0_I2C0SEL | RCU_CFG0_SCS | RCU_CFG0_RTCDIV);

  • RCU_CFG1 &= ~(RCU_CFG1_HPDFSEL | RCU_CFG1_TIMERSEL | RCU_CFG1_PERSEL |

  • RCU_CFG1_CAN0SEL | RCU_CFG1_CAN1SEL | RCU_CFG1_CAN2SEL |

  • RCU_CFG1_RSPDIFSEL | RCU_CFG1_USART0SEL | RCU_CFG1_USART1SEL | RCU_CFG1_USART2SEL | RCU_CFG1_USART5SEL | RCU_CFG1_PLL2RDIV);

  • RCU_CFG2 &= ~(RCU_CFG2_SAI2B1SEL | RCU_CFG2_SAI2B0SEL | RCU_CFG2_SAI1SEL | RCU_CFG2_SAI0SEL |

  • RCU_CFG2_CKOUT0SEL | RCU_CFG2_CKOUT1SEL | RCU_CFG2_CKOUT0DIV | RCU_CFG2_CKOUT1DIV);

  • RCU_CFG3 &= ~(RCU_CFG3_ADC01SEL | RCU_CFG3_ADC2SEL | RCU_CFG3_SDIO1SEL

  • | RCU_CFG3_I2C3SEL | RCU_CFG3_I2C2SEL | RCU_CFG3_I2C1SEL);

  • RCU_CFG4 &= ~(RCU_CFG4_EXMCSEL | RCU_CFG4_SDIO0SEL);

  • RCU_CFG5 &= ~(RCU_CFG5_SPI0SEL | RCU_CFG5_SPI1SEL | RCU_CFG5_SPI2SEL |

  • RCU_CFG5_SPI3SEL | RCU_CFG5_SPI4SEL | RCU_CFG5_SPI5SEL);

  • /* disable all interrupts */

  • RCU_INT = 0x14FF0000U;

  • RCU_ADDINT = 0x00700000U;

  • /* reset all PLL0 parameter */

  • RCU_PLL0 = 0x01002020U;

  • RCU_PLL1 = 0x01012020U;

  • RCU_PLL2 = 0x01012020U;

  • RCU_PLLALL = 0x00000000U;

  • RCU_PLLADDCTL = 0x00010101U;

  • RCU_PLLUSBCFG = 0x00000000U;

  • RCU_PLL0FRA = 0x00000000U;

  • RCU_PLL1FRA = 0x00000000U;

  • RCU_PLL2FRA = 0x00000000U;

  • /* configure system clock */

  • system_clock_config();

  • }
  • 复制代码

    在复位结束后,进行新的进钟配置system_clock_config();在system_clock_config中根据宏定义,定位到了system_clock_600m_hxtal函数,其内容如下。

    static void system_clock_600m_hxtal(void)

  • {

  • uint32_t timeout = 0U;

  • uint32_t stab_flag = 0U;

  • RCU_CTL |= RCU_CTL_HXTALEN;

  • 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) {

  • }

  • }

  • /* insert TCM wait state at 600MHz */

  • RCU_APB4EN |= RCU_APB4EN_SYSCFG; //开启SYSCFG bit(0)

  • SYSCFG_SRAMCFG1 |= SYSCFG_SRAMCFG1_TCM_WAITSTATE; //使能SRAM等待 bit(0),写1

  • /* HXTAL is stable */

  • /* AHB = SYSCLK / 2 */

  • RCU_CFG0 |= RCU_AHB_CKSYS_DIV2;

  • /* APB4 = AHB / 2 */

  • RCU_CFG0 |= RCU_APB4_CKAHB_DIV2;

  • /* APB3 = AHB / 2 */

  • RCU_CFG0 |= RCU_APB3_CKAHB_DIV2;

  • /* APB2 = AHB / 1 */

  • RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;

  • /* APB1 = AHB / 2 */

  • RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;

  • /* PLL select HXTAL, configure PLL input and output range */

  • RCU_PLLALL &= ~(RCU_PLLALL_PLLSEL | RCU_PLLALL_PLL0VCOSEL | RCU_PLLALL_PLL0RNG);

  • RCU_PLLALL |= (RCU_PLLSRC_HXTAL | RCU_PLLALL_PLL0VCOSEL | RCU_PLL0RNG_4M_8M);

  • /* PLL0P = HXTAL / 5 * 120 = 600 MHz */

  • RCU_PLL0 &= ~(RCU_PLL0_PLL0N | RCU_PLL0_PLL0PSC | RCU_PLL0_PLL0P | RCU_PLL0_PLL0R | RCU_PLL0_PLLSTBSRC);

  • RCU_PLL0 |= ((PLL0N << PLL0N_REG_OFFSET) | (PLL0PSC << PLL0PSC_REG_OFFSET) | (PLL0P << PLL0P_REG_OFFSET) | (PLL0R << PLL0R_REG_OFFSET));

  • RCU_PLLADDCTL &= ~(RCU_PLLADDCTL_PLL0Q);

  • RCU_PLLADDCTL |= (PLL0Q << PLL0Q_REG_OFFSET);

  • /* enable PLL0P, PLL0Q, PLL0R */

  • RCU_PLLADDCTL |= RCU_PLLADDCTL_PLL0PEN | RCU_PLLADDCTL_PLL0QEN | RCU_PLLADDCTL_PLL0REN;

  • /* enable PLL0 */

  • RCU_CTL |= RCU_CTL_PLL0EN;

  • /* wait until PLL0 is stable */

  • // #define RCU_CTL_PLL0STB BIT(25) /*!< PLL0 clock stabilization flag */

  • //等待 PLL0 时钟稳定标志位

  • while(0U == (RCU_CTL & RCU_CTL_PLL0STB)) {

  • }

  • // #define RCU_CKSYSSRC_PLL0P CFG0_SCS(3) /*!< system clock source select PLL0P */

  • // 系统时钟选择状态 01:选择 CK_HXTAL 时钟作为 CK_SYS 时钟源 3为11:选择 CK_PLL0P 时钟作为 CK_SYS 时钟源

  • /* select PLL0 as system clock */

  • RCU_CFG0 &= ~RCU_CFG0_SCS;

  • RCU_CFG0 |= RCU_CKSYSSRC_PLL0P;

  • /* wait until PLL0 is selected as system clock */

  • while(RCU_SCSS_PLL0P != (RCU_CFG0 & RCU_CFG0_SCSS)) {

  • }

  • }
  • 复制代码

    在system_clock_600m_hxtal函数中,首先将RCU_CTL;寄存器的外部高速时钟位使能,然后等待时钟稳定位硬件置1,如果超时,则系统挂起。

    如果外部时钟配置成功,则进入以下设置时钟:

    1、APB4使能

    2、SRAM的等待设置。

    3、AHB为系统时钟频率的1/2 即为300M

    4、APB4为AHB的时钟频率1/2——150M

    5、APB3 为AHB的时钟频率1/2——150M

    6、APB2 为AHB的时钟频率——300M

    7、APB1 为AHB的时钟频率——150M

    然后使能RCU——PULLAL的所有时钟位。

    最后全能RCU_CTL时钟寄存器的使能位,开启高速时钟,等待高速时钟稳定后就可以正式进入系统的其他应用之中。

    【读后感】

    从时钟的学习中,我初步了解了GD32H759的时钟配置的流程,虽然对GD32的各个时钟寄存器没有充分的掌握,但是可以在实际的应用中,知道如何去查找资料,在以后的各个外设的配置中,如果使用分频、计算速度的配置等。