以GD32F405芯片为例介绍MCU初始化时钟的过程

时钟初始化函数入口

时钟初始化一般是在APP程序刚进入时设置,以GD32F405MCU为例在,时钟初始化函数在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

学会看时钟树

  • 时钟树中常见符号说明
    ++ 梯形,窄边代表输出端,宽边代表输入端,梯形边上的竖线对应的代表寄存器位数
    在这里插入图片描述
    GD32F405时钟树,基本外设使用的时钟部分在AHB节点后,AHB节点后根据对应的分频系数基本都能确定对应外设使用频率,一般来说主要需要理解AHB如何获取即可。

    如何获取AHB时钟频率具体说明如下图所示
    在这里插入图片描述
    通过函数理解具体实现过程,时钟频率函数如下
/*!
    \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_CTL为时钟控制寄存器,设置IRC16M时钟做为系统时钟时

  RCU_MODIFY			/*#define RCU_MODIFY      {volatile uint32_t i; \
                         RCU_CFG0 |= RCU_AHB_CKSYS_DIV2; \
                         for(i=0;i<50000;i++);}
                         设置AHB总线频率并等待执行*/
    
  /* Reset CFG0 register */
  RCU_CFG0 = 0x00000000U;			//初始化,不太理解为什么设置为后又要清空,可能官方有其他考虑

  /* Reset HXTALEN, CKMEN and PLLEN bits */
  RCU_CTL &= ~(RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN);//异或操作,将RCU_CTL_PLLEN | RCU_CTL_CKMEN | RCU_CTL_HXTALEN位置0,其他位置1 

  /* 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();			//上面主要是将时钟初始化,这个函数开始具体配置系统时钟
}

根据对应宏定义开启进入对应函数,官方给出的是进入system_clock_200m_irc16m()函数

/*!
    \brief      configure the system clock
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void system_clock_config(void)
{
#ifdef __SYSTEM_CLOCK_IRC16M
    system_clock_16m_irc16m();
#elif defined (__SYSTEM_CLOCK_HXTAL)
    system_clock_hxtal();
#elif defined (__SYSTEM_CLOCK_120M_PLL_IRC16M)
    system_clock_120m_irc16m();
#elif defined (__SYSTEM_CLOCK_120M_PLL_8M_HXTAL)
    system_clock_120m_8m_hxtal();
#elif defined (__SYSTEM_CLOCK_120M_PLL_25M_HXTAL)
    system_clock_120m_25m_hxtal();
#elif defined (__SYSTEM_CLOCK_168M_PLL_IRC16M)
    system_clock_168m_irc16m();
#elif defined (__SYSTEM_CLOCK_168M_PLL_8M_HXTAL)
    system_clock_168m_8m_hxtal();
#elif defined (__SYSTEM_CLOCK_168M_PLL_25M_HXTAL)
    system_clock_168m_25m_hxtal();
#elif defined (__SYSTEM_CLOCK_200M_PLL_IRC16M)
    system_clock_200m_irc16m();
#elif defined (__SYSTEM_CLOCK_200M_PLL_8M_HXTAL)
    system_clock_200m_8m_hxtal();
#elif defined (__SYSTEM_CLOCK_200M_PLL_25M_HXTAL)
    system_clock_200m_25m_hxtal();
#endif /* __SYSTEM_CLOCK_IRC16M */   
}
/*!
    \brief      configure the system clock to 200M by PLL which selects IRC16M as its clock source
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void system_clock_200m_irc16m(void)
{
    uint32_t timeout = 0U;
    uint32_t stab_flag = 0U;
    
    /* enable IRC16M */
    RCU_CTL |= RCU_CTL_IRC16MEN;				//设置时钟源为IRC16

    /* wait until IRC16M is stable or the startup time is longer than IRC16M_STARTUP_TIMEOUT */
    do{
        timeout++;
        stab_flag = (RCU_CTL & RCU_CTL_IRC16MSTB);
    }while((0U == stab_flag) && (IRC16M_STARTUP_TIMEOUT != timeout));//等待时钟稳定,对应寄存器置位或者超时

    /* if fail */
    if(0U == (RCU_CTL & RCU_CTL_IRC16MSTB)){
        while(1){
        }
    }
         
    RCU_APB1EN |= RCU_APB1EN_PMUEN;			//PMU时钟使能
    PMU_CTL |= PMU_CTL_LDOVS;				//PMU时钟控制寄存器高电压,应该是

    /* IRC16M is stable */
    /* AHB = SYSCLK */
    RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;			//设置AHB分频系数为1
    /* APB2 = AHB/2 */
    RCU_CFG0 |= RCU_APB2_CKAHB_DIV2;		//设置APB2分频系数为2
    /* APB1 = AHB/4 */
    RCU_CFG0 |= RCU_APB1_CKAHB_DIV4;		//设置APB1分频系数为4

    /* Configure the main PLL, PSC = 16, PLL_N = 400, PLL_P = 2, PLL_Q = 9 */ 
    RCU_PLL = (16U | (400U << 6U) | (((2U >> 1U) - 1U) << 16U) |
                   (RCU_PLLSRC_IRC16M) | (9U << 24U));		//设置锁相环的分频系数为16,PLL_N为400,PLL_P系数为2,PLL_Q系数为9,因此CK_PLL_P = ICR16*PLL_N /(PSC *PLL_P )				CK_PLL_Q = ICR16*PLL_N /(PSC *PLL_Q )				

    /* enable PLL */
    RCU_CTL |= RCU_CTL_PLLEN;		//打开PLL锁相环作为时钟输入源

    /* wait until PLL is stable */
    while(0U == (RCU_CTL & RCU_CTL_PLLSTB)){		//等待PLL锁相环设置成功
    }
    
    /* 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;							//高速晶体振荡器(HXTAL) 使能
    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)){
    }
}

只能说是大致理解,还有部分未理解,比如是如何设置PLL_P作为CK_SYS的不太理解,如果有理解的朋友可以在评论区大致讲一下,感谢

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值