华大 MCU 之三 时钟控制器(CMU)配置记录
今天在新项目(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 时钟。
使用时注意以下三点:
- 在 HRC 或 XTAL 振荡器稳定后,再对 PLL 进行配置。
- MPLL/UPLL 的分频系数 M、N、P、Q、R 可独立配置(系统时钟框图中 N 在哪?我也不知道!)。由于在 PLL 使能后 PLL 配置参数便不可更改,所以建议先对 PLL 进行配置,然后再使能。
- 当进入掉电和停止模式后,两个 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,最大 168MHzstcSysClkCfg.enExclkDiv = ClkSysclkDiv2; // 当前 64MHz,最大 84MHzstcSysClkCfg.enPclk0Div = ClkSysclkDiv1; // 当前 128MHz,最大 168MHzstcSysClkCfg.enPclk1Div = ClkSysclkDiv2; // 当前 64MHz,最大 84MHzstcSysClkCfg.enPclk2Div = ClkSysclkDiv4; // 当前 32MHz,最大 60MHzstcSysClkCfg.enPclk3Div = ClkSysclkDiv4; // 当前 32MHz,最大 42MHzstcSysClkCfg.enPclk4Div = ClkSysclkDiv2; // 当前 64MHz,最大 84MHzCLK_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_USBstc_clk_upll_cfg_t stcUpllCfg;#endifMEM_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,最大 168MHzstcSysClkCfg.enExclkDiv = ClkSysclkDiv2; // 当前 64MHz,最大 84MHzstcSysClkCfg.enPclk0Div = ClkSysclkDiv1; // 当前 128MHz,最大 168MHzstcSysClkCfg.enPclk1Div = ClkSysclkDiv2; // 当前 64MHz,最大 84MHzstcSysClkCfg.enPclk2Div = ClkSysclkDiv4; // 当前 32MHz,最大 60MHzstcSysClkCfg.enPclk3Div = ClkSysclkDiv4; // 当前 32MHz,最大 42MHzstcSysClkCfg.enPclk4Div = ClkSysclkDiv2; // 当前 64MHz,最大 84MHzCLK_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;//48MstcUpllCfg.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 之三 时钟控制器(CMU)配置记录相关推荐
- 华大 MCU 之一 HC32F460 替换 STM32F411 移植记录
更新 2020年 10 月 21 日,将驱动库更新到了最新版 1.1.1 2020年 10 月 20 日,MCU 由原来的 HC32F460KCTA 更换为 HC32F460KETA 简介 目前, ...
- 华大 MCU 之七 DMA 导致 SPI 异常停止的原因分析、DMA 配置的那些坑
缘起 在最近的项目测试中发现,SPI 通信总是莫名其妙的失败,查看寄存器发现 SPI 已经被停止了.根据手册,SPI 在异常情况下会被强制停止(SPI 的使能为被清零),而根据波形显示通信过程没有 ...
- 华大 MCU 之四 使用问题记录
在最近使用华大 MCU 时,遇到了不少坑,这里记录一下,以方便在以后升级驱动库!其中,有些问题仅仅是在由 ST 切换到 华大之后不太适于的问题,有些是驱动库的更改问题! 如果您发现我说的问题是 ...
- 华大 MCU 之五 SPI 从机 DMA 模式 配置(不能正常接收问题处理)
最近有个需求是需要使用 华大 MCU(HC32F460) 的 SPI 作为从机来接收数据,无奈搞了两天死活不可用.配置完 SPI 的从机模式后,只要启动主机端的发送就出现如下图所示的错误: 下面是 ...
- MCAL-GTM之时钟管理CMU
文章目录 前言 时钟管理单元CMU CMU时钟图 GTM全局时钟分频 Cmu CFGU时钟分配 产生的时钟示例图: Cmu FXU时钟分配 Cmu EGU时钟分配 总结 前言 项目需要配置PWM信号采 ...
- 活动目录系列之三---域控制器常规卸域
活动目录系列之三---域控制器常规卸域 在介绍正题之前,先补充很多人提出的一个疑问,就是什么情况下计算机名系统不让更改,其实很简单,当在域情况下,用非管理员登陆域,即计算机名就不让更改,运行---CM ...
- 华大 MCU 之六 SEGGER Embedded Studio 及 Ozone 使用 Jlink 调试
首先说一下,我之前的开发流程是:VSCode 编辑代码 + Keil 编译及调试.Keil 的调试功能虽然很强大,但是多数功能需要配合 ARM 自家的 ULINKpro 才可以用,例如 Perfo ...
- 华大单片机HC32L130/HC32L136红外端口配置
华大单片机HC32L130/HC32L136红外端口配置 HC32L130一共有三个引脚可以配置成红外38.4KHZ输出引脚 #define PIN_PWM_IR GpioPortB, GpioPin ...
- MCU集成-时钟复位控制
时钟 首先我们需要知道我们生成时钟的需求如下. 然后知道设计要点 dft可控:表示在dft模式下,我们选择外部的测试时钟而不是芯片内部的pll的时钟.这个外部的时钟可以被dft工程师控制. dft隔离 ...
最新文章
- 用python的matplotlib画标准正态曲线
- uniapp中实现每次点击左侧菜单右边区域都从顶部开始
- 【工具】PC端调试手机端 Html 页面的工具
- C++知识总结(2)--字符串和数组
- 9.3 LSMW程序创建操作手册 第5 6 7步
- 首次!阿里达摩院将Pure Transformer 应用于目标重识别ReID!
- 在 eclipse 中设置每行的字数
- 南邮计算机科学与技术专业排名,南京邮电大学王牌专业有哪些
- sigmoid 激励函数
- 金蝶EAS,序时簿ListUI只允许选择一行或至少选择一行记录
- 数据挖掘:数据(数据的基本统计描述)
- A reference for learning process
- Centos 通过 Nginx 和 vsftpd 构建图片服务器
- 无心剑中译狄兰·托马斯《不要温顺地走进那个良夜》
- 关于科技将如何塑造旅行和流动性的七个预测
- 如何进行支付功能的测试
- Unity5新版Shader模板源码解析
- 小程序canvas画画板签字版,touchmove时卡顿的问题(根本原因是因为vue语法中page.data导致视图层和逻辑层的频繁通讯导致)
- pluseaudio 的设置声卡
- Linux ALSA声卡驱动之七:录音(Capture) 调用流程
热门文章
- 阿里云朱照远:AI打开新视界 8K时代已来!
- 直接让浏览器下载文件而不打开
- 【C++学习】String类的基本用法
- SQL Server 2000从入门到精通3
- 推荐 7 个 Github 上近 200k Star 的计算机学习资源,练好前端内功的秘籍!
- 2:IDEA生成springboot项目,修改启动图标和网页端口
- SpringBatch批处理框架入门(一)
- vue elementui el-select通过@Change触发事件
- lombok pom.xml依赖
- golang管道channel的遍历和关闭:应该使用for...range来遍历