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 差别就要大一些了。
- 外部高速振荡器(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 有毛用! 看我下面的需求:
有些宏值的配置无法放到 ddl_config.h 中,不得不更改库文件!然后在配置时钟时需要使用如下代码:
/* 1. 启动 HRC, MCU 启动后默认以 MRC 来工作,以下开始切换到 HRC */ CLK_HrcCmd(Enable); /* 根据手册,需要等待 HRC Ready 后才可以正常使用 */ while(Set != CLK_GetFlagStatus(ClkFlagHRCRdy));
这里有个坑需要注意,我之前在博文《华大MCU之一:HC32F460 替换 STM32F411 移植记录 》中也有说过。实际项目中,我们的程序结构多为下图所示:
hc32f46x_icg.c 只能放在 IAP 中!如果放到了 APP 中编译会产生错误!例如,在 ST 中,我们通常会在 APP 中来配置看门狗,但是在华大中这就行不通了!
PLL 的配置
在实际使用中,我们多数情况下需要将时钟源进行倍频,以使系统时钟达到一个较高的频率。倍频使用的器件就是 PLL。HC32F46xx 器件具有两个PLL:
- 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);
各种外设时钟
时钟的配置还有一部分就是分频出各种外设时钟(系统时钟框图的右侧输出的各种时钟),分频出各种外设时钟没有啥难的,只要保证不超过限制即可。各时钟的说明如下:
基本就是对应下面的代码(具体的分配系数根据自己的需求变化):
/* 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 切换时钟源,切换步骤参照时钟源切换。只有在目标时钟源已稳定的状态下,才可以从一个时钟源切换到另一个时钟源。
时钟切换时需要正确配置 Flash/SRAM 的等待周期,防止系统时钟频率大于 Flash/SRAM 的最大动作频率。这个也是很重要的,用惯了 ST 标准库的人可能对这个部分比较陌生,因为 ST 的工具会根据我们配置的频率自动为我们生成这部分的处理,手动移植时,就必须要关注这部分的配置。Flash/SRAM 的等待周期如下图所示:
关于这部分在用户手册 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