华大MCU之三:时钟控制器(CMU)配置记录

cathy的头像

今天在新项目(MCU 为华大 HC32F460)中不再使用外部晶振,转而要使用 HC32F460 内部的 HRC,之前在使用外部晶振时,对华大 MCU 的时钟配置有过一些了解,但是,由于使用内部晶振与使用外部晶振有些差别,今天就记录一下配置过程!没啥难的,就是单纯记录一下而已!

华大 MCU 时钟的配置,与 ST 的类似,都有很多选择,用户可以根据需要灵活选择。用户手册章节 6 时钟控制器(CMU)中的介绍已经很详细了,所以本文就重点结合代码来说明,还有就是从手册中摘录了一些配置中需要重点关注的点。

时钟控制器(CMU)

系统时钟框图是个好东西,基本配置项都一目了然了!如下图所示,左侧就是可选的时钟源。XTAL 就类似于 ST 的 HSE;XTAL32 就类似于 ST 的 LSE;HRC 就类似于 ST 的 HSI;LRC 就类似于 ST 的 LSI,至于 MRC 和 SWDTLRC 与 ST 差别就要大一些了。

“华大MCU之三:时钟控制器(CMU)配置记录"
  • 外部高速振荡器(XTAL) 晶振的频率范围:4~24MHz
  • 外部低速振荡器(XTAL32) 晶振的频率范围:32.768KHz
  • MPLL 时钟(MPLL) 输入时钟输入可选外部高速振荡器(XTAL)或者内部高速振荡器(HRC)
  • UPLL 时钟(UPLL) 输入时钟:输入可选外部高速振荡器(XTAL)或者内部高速振荡器(HRC)
  • 内部高速振荡器(HRC) 频率:16MHz 或者20MHz
  • 内部中速振荡器(MRC) 频率:8MHz
  • 内部低速振荡器(LRC) 频率:32.768KHz
  • SWDT 专用内部低速振荡器(SWDTRC)频率:10KHz

HRC

HRC 的频率可由 ICG1. HRCFREQSEL 配置成 16MHz 或者 20MHz。HRC 振荡器的优点是成本较低(无需使用外部组件)。此外,其启动速度也要比 XTAL 晶振块,但即使校准后,其精度也不及外部晶振。我本次要使用 HRC,所以本文就以 HRC 为重点关注对象,其他时钟的配置基本类似。

其中,需要重点关注 ICG 这个部分。用户手册章节 8 初始化配置(ICG)中有详细描述,这一部分貌似和 ST 的 OPT FLASH(或者叫 Option bytes)的作用差不多。重点关注的原因就是这个部分不是读写寄存器操作,而是在编写代码时,直接固化数据(通常使用编译器指令),下面是华大给出的库中的处理代码(hc32f46x_icg.c):

#if defined ( __GNUC__ ) && !defined (__CC_ARM) /* GNU Compiler */
const uint32_t u32ICG[] __attribute__((section(".icg_sec"))) =
#elif defined (__CC_ARM)
const uint32_t u32ICG[] __attribute__((at(0x400))) =
#elif defined (__ICCARM__)
__root const uint32_t u32ICG[] @ 0x400 =
#else
#error "unsupported compiler!!"
#endif
{
    /* ICG 0~ 3 */
    ICG0_REGISTER_CONSTANT,
    ICG1_REGISTER_CONSTANT,
    ICG2_REGISTER_CONSTANT,
    ICG3_REGISTER_CONSTANT,
    /* ICG 4~ 7 */
    ICG4_REGISTER_CONSTANT,
    ICG5_REGISTER_CONSTANT,
    ICG6_REGISTER_CONSTANT,
    ICG7_REGISTER_CONSTANT,
};

其中,uint32_t u32ICG[] 这个数组中就是使用编译器命令固化的数据。里面的这些宏值的配置,不得不库文件 hc32f46x_icg.h 中根据需要来修改!这里要发句牢骚,对于 hc32f46x_icg.h,ddl_config.h 有毛用! 看我下面的需求:

“华大MCU之三:时钟控制器(CMU)配置记录"

有些宏值的配置无法放到 ddl_config.h 中,不得不更改库文件!然后在配置时钟时需要使用如下代码:

/* 1. 启动 HRC, MCU 启动后默认以 MRC 来工作,以下开始切换到 HRC */
    CLK_HrcCmd(Enable);

    /* 根据手册,需要等待 HRC Ready 后才可以正常使用 */
    while(Set != CLK_GetFlagStatus(ClkFlagHRCRdy));

这里有个坑需要注意,我之前在博文《华大MCU之一:HC32F460 替换 STM32F411 移植记录 》中也有说过。实际项目中,我们的程序结构多为下图所示:

“华大MCU之三:时钟控制器(CMU)配置记录"

hc32f46x_icg.c 只能放在 IAP 中!如果放到了 APP 中编译会产生错误!例如,在 ST 中,我们通常会在 APP 中来配置看门狗,但是在华大中这就行不通了!

PLL 的配置

在实际使用中,我们多数情况下需要将时钟源进行倍频,以使系统时钟达到一个较高的频率。倍频使用的器件就是 PLL。HC32F46xx 器件具有两个PLL:

“华大MCU之三:时钟控制器(CMU)配置记录"
  • MPLL 由 XTAL 或HRC 振荡器提供时钟信号,并具有三个不同的输出时钟:
    • P 分频器输出用于生成系统时钟(最高达 200 MHz)
    • 三个输出都可用于生成 USBFS、TRNG、ADC 和 I2S 时钟。
  • UPLL 三个输出亦可用于生成 USBFS、TRNG、ADC 和 I2S 时钟。

使用时注意以下三点:

1)在 HRC 或 XTAL 振荡器稳定后,再对 PLL 进行配置。

2)MPLL/UPLL 的分频系数 M、N、P、Q、R 可独立配置(系统时钟框图中 N 在哪?我也不知道!)。由于在 PLL 使能后 PLL 配置参数便不可更改,所以建议先对 PLL 进行配置,然后再使能。

3)当进入掉电和停止模式后,两个 PLL 将由硬件禁止。

基本就是对应下面的代码(这里只配置了 MPLL,文章最后的完整示例里有 UPLL的配置)了:

/* 2. 设置 PLL 的时钟源为 HRC */
    CLK_SetPllSource(ClkPllSrcHRC);

    /* 3. MPLL config (主晶振 / pllmDiv * plln / PllpDiv = 128M). */
    stcMpllCfg.pllmDiv = 16ul;
    stcMpllCfg.plln    = 256ul;
    stcMpllCfg.PllpDiv = 2ul;
    stcMpllCfg.PllqDiv = 8ul;
    stcMpllCfg.PllrDiv = 2ul;
    CLK_MpllConfig(&stcMpllCfg);
    
    /* Enable MPLL. */
    CLK_MpllCmd(Enable);

各种外设时钟

时钟的配置还有一部分就是分频出各种外设时钟(系统时钟框图的右侧输出的各种时钟),分频出各种外设时钟没有啥难的,只要保证不超过限制即可。各时钟的说明如下:

“华大MCU之三:时钟控制器(CMU)配置记录"

基本就是对应下面的代码(具体的分配系数根据自己的需求变化):

/* Set bus clk div. */
    stcSysClkCfg.enHclkDiv = ClkSysclkDiv1;   // 当前 128MHz,最大 168MHz
    stcSysClkCfg.enExclkDiv = ClkSysclkDiv2;  // 当前 64MHz,最大 84MHz
    stcSysClkCfg.enPclk0Div = ClkSysclkDiv1;  // 当前 128MHz,最大 168MHz
    stcSysClkCfg.enPclk1Div = ClkSysclkDiv2;  // 当前 64MHz,最大 84MHz
    stcSysClkCfg.enPclk2Div = ClkSysclkDiv4;  // 当前 32MHz,最大 60MHz
    stcSysClkCfg.enPclk3Div = ClkSysclkDiv4;  // 当前 32MHz,最大 42MHz
    stcSysClkCfg.enPclk4Div = ClkSysclkDiv2;  // 当前 64MHz,最大 84MHz
    CLK_SysClkConfig(&stcSysClkCfg);

时钟切换

无论是华大还是 ST,都有一个章节来专门介绍时钟的切换。在系统启动以及进出低功耗时,都有可能需要进行时钟切换,如果没有正常的切换,可能导致无法启动。时钟切换必须严格遵循手册中给出的切换步骤!

在系统复位后,默认系统时钟为 MRC。通过设定寄存器 CMU_CKSW 切换时钟源,切换步骤参照时钟源切换。只有在目标时钟源已稳定的状态下,才可以从一个时钟源切换到另一个时钟源。

“华大MCU之三:时钟控制器(CMU)配置记录"

时钟切换时需要正确配置 Flash/SRAM 的等待周期,防止系统时钟频率大于 Flash/SRAM 的最大动作频率。这个也是很重要的,用惯了 ST 标准库的人可能对这个部分比较陌生,因为 ST 的工具会根据我们配置的频率自动为我们生成这部分的处理,手动移植时,就必须要关注这部分的配置。Flash/SRAM 的等待周期如下图所示:

“华大MCU之三:时钟控制器(CMU)配置记录"

关于这部分在用户手册 CPU 时钟和 FLASH 读取时间之间的关系 中有详细的步骤描述。

最终配置

最终,一个完整的配置时钟的函数如下所示:

/**
  * @brief System Clock Configuration
  * @retval None
  */
static void SystemClock_Config(void)
{
    stc_clk_sysclk_cfg_t        stcSysClkCfg;
    // stc_clk_xtal_cfg_t          stcXtalCfg;      /* 配置外部 Xtal */
    // stc_clk_xtal32_cfg_t        stcXtal32Cfg;      /* 配置外部 Xtal32 */
    stc_clk_mpll_cfg_t          stcMpllCfg;
    stc_sram_config_t           stcSramConfig;
    #ifdef USE_USB
    stc_clk_upll_cfg_t          stcUpllCfg;
    #endif
    
    MEM_ZERO_STRUCT(stcSysClkCfg);
    // MEM_ZERO_STRUCT(stcXtalCfg);
    // MEM_ZERO_STRUCT(stcXtal32Cfg);
    MEM_ZERO_STRUCT(stcMpllCfg);
    MEM_ZERO_STRUCT(stcSramConfig);
    
    /* Set bus clk div. */
    stcSysClkCfg.enHclkDiv = ClkSysclkDiv1;   // 当前 128MHz,最大 168MHz
    stcSysClkCfg.enExclkDiv = ClkSysclkDiv2;  // 当前 64MHz,最大 84MHz
    stcSysClkCfg.enPclk0Div = ClkSysclkDiv1;  // 当前 128MHz,最大 168MHz
    stcSysClkCfg.enPclk1Div = ClkSysclkDiv2;  // 当前 64MHz,最大 84MHz
    stcSysClkCfg.enPclk2Div = ClkSysclkDiv4;  // 当前 32MHz,最大 60MHz
    stcSysClkCfg.enPclk3Div = ClkSysclkDiv4;  // 当前 32MHz,最大 42MHz
    stcSysClkCfg.enPclk4Div = ClkSysclkDiv2;  // 当前 64MHz,最大 84MHz
    CLK_SysClkConfig(&stcSysClkCfg);
    
    // /* XTAL的配置 */
    // /* Use Xtal as MPLL source. */
    // stcXtalCfg.enMode        = ClkXtalModeOsc;
    // stcXtalCfg.enDrv         = ClkXtalMidDrv;
    // stcXtalCfg.enFastStartup = Enable;
    // CLK_XtalConfig(&stcXtalCfg);
    // CLK_XtalCmd(Enable);

    /* 配置 HRC 经过 PLL 后作为系统时钟,而不是直接使用 HRC(HRC 是可以直接作为系统时钟) */

    /* 1. 启动 HRC, MCU 启动后默认以 MRC 来工作,以下开始切换到 HRC */
    CLK_HrcCmd(Enable);

    /* 根据手册,需要等待 HRC Ready 后才可以正常使用 */
    while(Set != CLK_GetFlagStatus(ClkFlagHRCRdy));

    /* 2. 设置 PLL 的时钟源为 HRC */
    CLK_SetPllSource(ClkPllSrcHRC);

    /* 3. MPLL config (主晶振 / pllmDiv * plln / PllpDiv = 128M). */
    stcMpllCfg.pllmDiv = 16ul;
    stcMpllCfg.plln    = 256ul;
    stcMpllCfg.PllpDiv = 2ul;
    stcMpllCfg.PllqDiv = 8ul;
    stcMpllCfg.PllrDiv = 2ul;
    CLK_MpllConfig(&stcMpllCfg);
    
    /* Enable MPLL. */
    CLK_MpllCmd(Enable);

    /* flash read wait cycle setting */
    EFM_Unlock();
    EFM_SetLatency(EFM_LATENCY_3);
    EFM_Lock();

    /* sram init include read/write wait cycle setting */
    stcSramConfig.u8SramIdx = Sram12Idx | Sram3Idx | SramHsIdx | SramRetIdx;
    stcSramConfig.enSramRC = SramCycle2;
    stcSramConfig.enSramWC = SramCycle2;
    stcSramConfig.enSramEccMode = EccMode3;
    stcSramConfig.enSramEccOp = SramNmi;
    stcSramConfig.enSramPyOp = SramNmi;
    SRAM_Init(&stcSramConfig);
    
    /* Wait MPLL ready. */
    while(Set != CLK_GetFlagStatus(ClkFlagMPLLRdy));
    
    /* Switch system clock source to MPLL. */
    CLK_SetSysClkSource(CLKSysSrcMPLL);
    
    #if (DDL_RTC_ENABLE == DDL_ON)
    // CLK_LrcCmd(Enable);     //Enable LRC   for RTC

    // /* RTC 用 xtal32 */
    // stcXtal32Cfg.enFastStartup = Disable;
    // stcXtal32Cfg.enDrv = ClkXtal32HighDrv;
    // stcXtal32Cfg.enFilterMode = ClkXtal32FilterModeFull;
    // CLK_Xtal32Config(&stcXtal32Cfg);
    // /* Startup xtal32 */
    // CLK_Xtal32Cmd(Enable);
    // /* wait for xtal32 running */
    // Ddl_Delay1ms(3000u);
    #endif

    #if DDL_USBFS_ENABLE == DDL_ON
    /* UPLL config (XTAL(当前为 6M) / pllmDiv * plln / PllpDiv = 48M). */
    stcUpllCfg.pllmDiv = 6u;
    stcUpllCfg.plln = 48u;
    stcUpllCfg.PllpDiv = 1u;//48M
    stcUpllCfg.PllqDiv = 1u;
    stcUpllCfg.PllrDiv = 1u;
    CLK_UpllConfig(&stcUpllCfg);
    CLK_UpllCmd(Enable);
    /* Wait UPLL ready. */
    while(Set != CLK_GetFlagStatus(ClkFlagUPLLRdy))
    {
        ;
    }

    /* Set USB clock source */
    CLK_SetUsbClkSource(ClkUsbSrcUpllp);
    #endif
}

参考

HC32F460系列用户手册Rev1.2.pdf

相关阅读:
华大MCU之一:HC32F460 替换 STM32F411 移植记录
华大MCU之二:USB 驱动 + FatFs 的移植使用详解

————————————————
版权声明:本文为CSDN博主「ZC·Shou」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ZCShouCSDN/article/details/117443246