i.MX23时钟系统深度解析:从PLL配置到动态调频的嵌入式实战
1. i.MX23时钟系统嵌入式系统的脉搏之源在嵌入式系统开发中时钟系统常常被视为一个“黑盒”——我们只需要知道它提供了多少兆赫兹的频率却很少深究其内部如何运作。然而当你需要优化系统功耗、提升性能或是解决那些难以复现的时序相关Bug时深入理解时钟系统就成了绕不开的坎。i.MX23作为一款经典的ARM9应用处理器其时钟架构设计精妙是学习嵌入式时钟管理的绝佳样本。它不仅仅是一个简单的“频率发生器”更是一个集成了锁相环PLL、可编程分频器、时钟门控和复杂时钟域管理的完整子系统。时钟系统的核心价值在于平衡性能与功耗。想象一下你的设备在播放高清视频时CPU、显示控制器和内存总线都需要高速运行而当它进入待机状态只需要一个极低频的时钟来维持实时时钟RTC和唤醒逻辑。i.MX23的时钟控制器CLKCTRL模块正是为此而生它允许软件动态地调整各个时钟域的频率甚至关闭暂时不用的时钟从而实现精细化的功耗管理。对于从事消费电子、工业控制或物联网设备开发的工程师来说掌握这套机制意味着你能从系统层面榨取出更长的电池续航和更稳定的运行表现。接下来我将结合手册内容和实际调试经验为你拆解i.MX23时钟系统的设计思路、关键寄存器操作以及那些手册上不会写的“避坑指南”。2. 时钟系统整体架构与核心设计思路要驾驭i.MX23的时钟首先得在脑子里建立起它的整体拓扑图。它的时钟源可以简化为一个树状结构树根是外部24MHz的晶体振荡器XTAL。这个24MHz的“心跳”是整个系统最原始、最稳定的频率基准。从这个根上生长出几个关键的枝干首先是直接由XTAL分频产生的各种低频时钟例如给UART、定时器用的更重要的枝干则是通过锁相环PLL生出的。i.MX23内部有一个核心的480MHz模拟PLL。你可以把它理解为一个频率乘法器它将24MHz的输入频率倍频20倍得到一个非常纯净且高频的480MHz信号。这个480MHz信号并不能直接使用因为它太高了大多数模块承受不了。因此它被送入四个独立的“相位分数分频器”Phase Fractional Divider, PFD分别产生ref_cpu、ref_emi、ref_io和ref_pix这四个关键的参考时钟。PFD的妙处在于它能进行分数分频公式是480 * (18 / FRAC)其中FRAC是一个1到35的可编程值。这意味着我们可以得到一系列非整数倍关系的精确频率比如当FRAC24时ref_cpu 480 * (18/24) 360MHz这为满足不同外设的特定频率需求提供了极大的灵活性。从这些参考时钟开始就进入了各个时钟域。最重要的三个时钟域是CPU时钟域CLK_P由ref_cpu或 XTAL 通过一个分频器产生直接驱动ARM926EJ-S核心。外部存储器接口时钟域CLK_EMI由ref_emi或 XTAL 通过分频器产生驱动SDRAM、NOR Flash等外部存储器控制器。AHB/APBH总线时钟域CLK_H通常由CLK_P分频而来驱动系统高速总线和连接其上的DMA、加密模块等。手册中特别强调了时钟频率管理的“协议”其核心思想是配置时钟必须从源头到末端逐级稳定。你不能在PLL还没锁定、输出不稳定的时候就匆忙把CPU的时钟源切换到PLL上。这好比在给高楼更换主供水管时必须先接通新水源并确认水压稳定再关闭旧管道否则就会断水。软件必须通过轮询寄存器中的BUSY或STABLE状态位来确保每一步操作都已完成且时钟稳定后再进行下一步。这个顺序一旦出错轻则导致系统运行在错误的频率上重则引发总线挂起或数据损坏。3. 核心模块详解PLL、PFD与时钟切换协议3.1 锁相环PLL的使能与锁定PLL是整个高速时钟的引擎。在i.MX23中它由HW_CLKCTRL_PLLCTRL0寄存器控制。其中最关键的是第16位的POWER位。将其置1PLL才开始上电并尝试锁定到480MHz。注意手册明确要求在将POWER位置1后必须等待至少10微秒us才能使用PLL输出的时钟。这10us就是PLL的锁定时间。在实际编程中我们通常用一个简单的延时循环来实现。更严谨的做法是查询HW_CLKCTRL_PLLCTRL1寄存器中的LOCK状态位第31位该位为1表示PLL已锁定。LOCK位的判断依赖于一个由XTAL驱动的内部计数器理论上在PLL上电约50us后该位会置起。因此一个健壮的启动代码应该先等待10us再轮询LOCK位直到其为1。HW_CLKCTRL_PLLCTRL1寄存器中还有一个FORCE_LOCK位第30位。这个位通常用于测试或调试强制启动一次锁定序列。在正常操作中我们不需要操作它。需要警惕的是这个位不是自清除的如果你设置了它必须在完成操作后手动清除。3.2 相位分数分频器PFD的配置PFD是连接PLL与各时钟域的关键桥梁。它的配置集中在HW_CLKCTRL_FRAC寄存器中。这个寄存器结构比较特殊它实际上将四个PFDCPU, EMI, IO, PIX的控制字段打包在了一个32位寄存器里每个PFD占用一个字节8位。以CPU的PFD为例它对应寄存器的最低字节。其中低6位CPUFRAC就是分频系数FRAC取值范围1-35。计算ref_cpu频率的公式就是480 * (18 / CPUFRAC) MHz。比如要得到400MHz的ref_cpu我们可以解方程480 * 18 / FRAC 400得到FRAC 21.6这不是整数。所以我们需要找一个最接近的整数值FRAC22时频率约为392.7MHzFRAC21时频率约为411.4MHz。这就需要根据CPU的实际性能需求来权衡选择。第7位CLKGATECPU是时钟门控置1则关闭该PFD的输出以省电。第6位CPU_STABLE是一个只读状态位它会在新的CPUFRAC值生效且输出稳定后翻转。手册在这里给了一个非常重要的警告HW_CLKCTRL_FRAC寄存器只支持字节访问byte access如果你使用字word或双字dword访问一次性写入整个寄存器将会触发所有四个PFD的更新序列这可能不是你想要的。例如你只想调整CPU频率却意外重置了显示像素时钟PIX PFD可能导致屏幕瞬间闪烁或黑屏。因此在操作这个寄存器时务必使用*(volatile uint8_t*)这样的指针进行字节操作。3.3 安全的时钟源切换流程手册第4.6节详细描述了将CPU或EMI时钟从XTAL源切换到PLL源的完整协议。这是一个标准操作务必遵循初始状态假设系统刚从休眠中唤醒CPU和EMI时钟由XTAL经分频后提供运行在低频模式。使能PLL设置PLLCTRL0[POWER]1。等待PLL锁定延时至少10us并建议轮询PLLCTRL1[LOCK]1。配置并使能目标PFD例如配置FRAC寄存器中的CPUFRAC字段为所需值并确保CLKGATECPU0使能输出。此时ref_cpu时钟开始以新频率运行。解除PFD时钟门控这一步在配置CPUFRAC时已经通过CLKGATECPU0完成。确保PFD输出稳定可以轮询CPU_STABLE位直到其翻转。编程目标分频器现在可以配置HW_CLKCTRL_CPU寄存器中的DIV_CPU分频字段。例如如果ref_cpu是360MHz你想让CPU运行在120MHz就设置DIV_CPU3360/3120。关键点在修改分频值前必须确保BUSY_REF_CPU位为0。切换多路复用器Bypass Off这是最后一步通过设置HW_CLKCTRL_CLKSEQ寄存器中对应的BYPASS位将CPU时钟的源从XTAL切换到ref_cpu。整个流程的核心逻辑是“先建后拆”。你必须先让新的时钟源PLL-PFD-分频后完全准备好并稳定运行最后才进行切换。反过来从高速PLL模式切回低速XTAL模式顺序则相反先切换多路复用器回XTAL再关闭PFD和PLL。4. 关键时钟域配置与寄存器实操4.1 CPU时钟CLK_P配置详解CPU时钟寄存器HW_CLKCTRL_CPU是调频的核心。它包含两个分频器一个用于XTAL源DIV_XTAL一个用于PLL/PFD源DIV_CPU。BYPASS_CPU位在CLKSEQ寄存器中决定使用哪一个。DIV_CPU(位[5:0])当使用PLL/PFD作为源时CPU频率 ref_cpu/ (DIV_CPU 1)。注意分频值不能为0。如果你想以ref_cpu的全速运行应设置为0代表分频比为1。从240MHz的ref_cpu获得60MHz CPU时钟需设置DIV_CPU 3。BUSY_REF_CPU(位28)黄金法则在尝试写入DIV_CPU或进行源切换之前必须读取此位并确保其为0。如果它为1表示时钟硬件正在同步跨时钟域的新分频值此时写入可能导致不可预测的行为。INTERRUPT_WAIT(位12)一个有趣的省电功能。当CPU进入等待中断WFI状态时如果此位置1CLK_P会被门控关闭直到中断到来。这可以显著降低待机功耗。实操示例将CPU时钟从24MHz XTAL源切换到由PLL提供的200MHz。// 1. 假设PLL已使能并锁定PFD已配置好产生400MHz的ref_cpu。 // 2. 配置DIV_CPU进行2分频得到200MHz。 HW_CLKCTRL_CPU.WR(BF_CLKCTRL_CPU_DIV_CPU(1)); // 分频值1代表2分频 (400/(11)200) // 3. 等待分频器操作完成 while (HW_CLKCTRL_CPU.RD() BM_CLKCTRL_CPU_BUSY_REF_CPU) { // 空循环等待 } // 4. 切换时钟源假设CLKSEQ寄存器中BYPASS_CPU位在bit 0 HW_CLKCTRL_CLKSEQ_SET(BM_CLKCTRL_CLKSEQ_BYPASS_CPU); // 清除BYPASS位选择PLL源4.2 总线与外部存储器时钟配置AHB/APBH总线时钟CLK_H由HW_CLKCTRL_HBUS控制。它通常由CLK_P分频而来DIV字段。这个寄存器有一个高级功能叫“自动慢速模式”Auto Slow Mode。当AUTO_SLOW_MODE使能后CLK_H默认以低速运行由SLOW_DIV设置慢速分频比。当检测到特定的总线主设备如CPU、DMA有活动时会自动切换到高速模式。你可以通过CPU_DATA_AS_ENABLE、APBHDMA_AS_ENABLE等位来选择触发高速模式的事件。这在移动设备中非常有用可以在总线空闲时自动降频省电。外部存储器接口时钟CLK_EMI的配置稍复杂由HW_CLKCTRL_EMI控制。它有一个关键模式选择位SYNC_MODE_EN。异步模式SYNC_MODE_EN0CLK_EMI独立于CLK_H拥有自己的分频器DIV_EMI和DIV_XTAL。这在需要EMI时钟与CPU时钟不同步时使用。同步模式SYNC_MODE_EN1CLK_EMI与CLK_H同步。此时HW_CLKCTRL_HBUS.DIV的分频值同时控制CLK_H和CLK_EMI。手册给出了严格的约束条件clk_p的分频值必须小于等于clk_emi的分频值且后者必须能被前者整除。例如CLK_P分频值为2即CPU频率是ref_cpu的一半那么CLK_EMI的分频值只能是2, 4, 6...等偶数。这是为了确保EMI时钟与AHB总线时钟保持确定的相位关系避免在跨时钟域数据传输时出现建立保持时间违例。配置EMI同步模式示例// 目标CPU跑在200MHz (ref_cpu400MHz, DIV_CPU1)EMI跑在100MHz且与AHB同步。 // 1. 首先确保ref_cpu和XTAL都处于活动状态。 // 2. 设置HBUS分频。CLK_H频率应为CLK_EMI的整数倍。设CLK_H100MHzCLK_P200MHz则DIV_H1 (200/(11)100)。 HW_CLKCTRL_HBUS_WR(BF_CLKCTRL_HBUS_DIV(1)); // 3. 等待HBUS分频器稳定虽然HBUS没有BUSY位但最好稍作延时。 delay_us(1); // 4. 设置EMI为同步模式并配置其分频在同步模式下此分频可能被忽略或需满足比例关系。 // 根据手册在同步模式下EMI分频值需是CPU分频值的整数倍。CPU分频值(DIV_CPU)为1EMI分频值(DIV_EMI)需为1,2,3...我们选2倍即DIV_EMI1? 注意分频寄存器值分频比-1。 // 更安全的做法是查阅更具体的同步模式编程指南。通常在设置SYNC_MODE_EN前需先配置好DIV_EMI满足比例关系。 HW_CLKCTRL_EMI_WR(BF_CLKCTRL_EMI_SYNC_MODE_EN(1)); // 5. 等待同步模式切换完成 while (HW_CLKCTRL_EMI.RD() BM_CLKCTRL_EMI_BUSY_SYNC_MODE) { // 等待 }4.3 外设时钟门控与分频对于SSP、GPMI、PIX等外设时钟控制模式类似主要涉及两个寄存器位CLKGATE时钟门控。置1则关闭该外设的时钟这是最直接的动态功耗管理手段。在初始化外设前需要确保CLKGATE0。在修改分频值DIV前也必须确保CLKGATE0。DIV分频系数。决定外设时钟频率。例如HW_CLKCTRL_SSP.DIV定义了SSP时钟相对于其参考时钟可能是ref_xtal或ref_io的分频比。BUSY状态位。在写入新的DIV值后硬件需要时间同步在此期间BUSY1软件应轮询直到BUSY0后才能进行下一步操作。一个常见的错误是忽略了操作顺序。正确流程应为1) 确保CLKGATE02) 写入新的DIV值3) 轮询BUSY位直到为04) 如果需要关闭时钟再设置CLKGATE1。如果顺序颠倒先关了时钟再改分频或者在不满足时序的情况下强行操作可能导致外设工作异常。5. 时钟系统编程的常见陷阱与调试技巧即便完全按照手册操作在实际开发中你还是会遇到各种时钟相关的问题。下面是我在多个项目中总结出的“避坑指南”。5.1 频率切换时的系统稳定性问题现象在动态切换CPU频率后系统偶尔会死机或出现数据错误。根因分析时钟切换过程中如果时序没把握好可能会产生毛刺或过短的时钟脉冲导致CPU内核或总线状态机出错。特别是从高速向低速切换时如果分频器切换瞬间产生一个周期极短的脉冲就可能违反触发器的保持时间。解决方案严格遵守“先稳后切”原则确保新时钟源PLL/PFD完全锁定且稳定新分频器配置完毕且BUSY位为0最后才切换多路选择器。引入软件延时在关键步骤间如PLL使能后、PFD配置后、分频器写入后插入足够的空指令循环或微秒级延时。手册给的10us是最小值在电源波动或温度变化时可能需要更长时间。考虑电压调整如果进行大幅升频如从100MHz跳到400MHz核心电压可能需要相应提高。i.MX23的电源管理单元PMU需要配合调整。通常芯片厂商会提供频率-电压对应表需一并配置。5.2 外设时钟门控的副作用问题现象为了省电关闭了某个暂时不用的外设如UART的时钟。当重新打开时钟并使用该外设时发现其无法正常工作或需要重新完整初始化。根因分析当时钟被门控CLKGATE1时该外设模块内部的所有寄存器可能无法保持状态或者其内部状态机被复位。重新打开时钟模块处于一个未定义或复位状态。解决方案重新初始化外设在使能外设时钟后不要假设外设保持之前的状态应重新配置其所有关键寄存器控制寄存器、中断使能等。避免频繁开关对于需要快速响应或状态需保持的外设如定时器、看门狗权衡功耗与复杂性可能选择保持其时钟开启。查阅数据手册有些外设模块在时钟门控期间会保留寄存器值这需要查阅具体模块的说明。5.3 低功耗模式下的时钟管理i.MX23支持多种低功耗模式如WAIT、STOP模式。进入这些模式前通常需要将系统时钟切换到低速的XTAL源并关闭PLL和高频PFD以节省功耗。操作流程建议将CPU、EMI等主要时钟域通过CLKSEQ切换回XTAL源。通过HW_CLKCTRL_FRAC寄存器关闭 (CLKGATE1) 各个PFD。关闭PLL (PLLCTRL0[POWER]0)。根据需要降低XTAL路径的分频比让系统运行在极低频率。执行WFI或相关指令进入低功耗状态。唤醒后必须反向执行上述过程即先开启PLL等待锁定配置PFD最后切换时钟源。唤醒过程对时序要求极高代码通常需要放在内部SRAM中执行因为此时外部SDRAM可能还未初始化或时钟不稳定。5.4 调试技巧与诊断手段当时序出现问题时如何定位是时钟配置错误检查寄存器状态首先读取所有相关的CLKCTRL寄存器确认POWER、LOCK、CLKGATE、BYPASS、DIV等字段的值与软件预期一致。特别要检查那些BUSY和STABLE位确认硬件已完成所有待处理操作。使用内部测量信号有些处理器提供可以输出到GPIO的时钟信号用于示波器测量。i.MX23可能没有直接引出但可以尝试配置某个外设如PWM输出一个与内部时钟成比例的频率间接验证。观察系统行为如果CPU频率设置过低系统响应会明显变慢如果EMI时钟设置错误可能导致内存访问失败表现为程序跑飞或数据校验错误。如果PLL未锁定系统可能根本无法从高频时钟启动。利用看门狗在调试初期特别是在修改时钟代码时启用看门狗定时器。如果因为时钟配置错误导致系统卡死看门狗可以复位系统避免手动断电。5.5 同步模式下的EMI时钟约束这是最容易出错的地方之一。在同步模式下clk_p与clk_emi的分频值必须满足整除关系。例如如果DIV_CPU设置为2即CPU分频比为3那么DIV_EMI的有效值只能是2、5、8...即分频比为3、6、9...使得clk_emi分频值是clk_p分频值的整数倍。不满足此条件就使能同步模式是导致系统挂起或内存数据错误的常见原因。在编程时最好写一个校验函数在设置SYNC_MODE_EN前检查当前DIV_CPU和计划设置的DIV_EMI是否满足整除关系。6. 实战为一个多媒体应用配置时钟假设我们正在开发一个基于i.MX23的便携式媒体播放器需求如下CPU正常菜单操作时运行在200MHz播放高清视频解码时提升至360MHz。内存EMI始终与CPU同步运行频率为CPU的一半即100MHz或180MHz。显示PIX需要产生74.25MHz的像素时钟驱动LCD。音频SAIF需要产生12.288MHz的位时钟用于44.1kHz音频播放。功耗在待机时系统应切换到24MHz XTAL时钟并关闭所有高频时钟。配置步骤与计算确定参考时钟核心PLL固定输出480MHz。CPU PFD (ref_cpu)需要支持200MHz和360MHz。根据公式480 * 18 / CPUFRAC。对于360MHzCPUFRAC 480*18/360 24。对于200MHzCPUFRAC 480*18/200 43.2取整43实际频率480*18/43 ≈ 200.93MHz误差可接受。PIX PFD (ref_pix)需要74.25MHz。PIXFRAC 480*18/74.25 ≈ 116.36超出1-35范围说明PFD无法直接生成。因此ref_pix需要先由一个PFD产生一个较高频率再通过HW_CLKCTRL_PIX.DIV分频。例如设PIXFRAC18则ref_pix 480*18/18 480MHz。然后设置PIX.DIV 480 / 74.25 ≈ 6.46取整6实际像素时钟为80MHz这不匹配。看来需要选择另一个ref_pix频率。经过计算设PIXFRAC30则ref_pix288MHz。288 / 74.25 ≈ 3.88分频比4得到72MHz分频比3得到96MHz都不精确。这说明对于非整数关系的精确视频时钟可能需要使用专用的视频PLL或更复杂的分频链i.MX23可能无法完美支持所有标准像素时钟需要查阅其LCD控制器章节看是否支持分数分频或时钟补偿。这里我们假设选择ref_pix288MHzPIX.DIV4得到72MHz时钟软件上调整显示时序参数以适应这个频率。SAIF时钟通常由ref_xtal(24MHz) 分频得到。12.288 24 * (512/1000)这需要分数分频。查看HW_CLKCTRL_SAIF寄存器其DIV字段是16位整数分频器。24MHz / 12.288MHz 1.953125不是整数。因此直接分频无法得到精确的12.288MHz可能会引入音频时钟抖动。高性能音频应用可能需要外部专用的音频时钟源。编写时钟配置函数以切换到360MHz CPU为例void switch_cpu_to_360mhz(void) { // 0. 确保当前运行在XTAL源上例如从低功耗唤醒后 // 1. 使能PLL如果尚未使能 HW_CLKCTRL_PLLCTRL0_SET(BM_CLKCTRL_PLLCTRL0_POWER); // 等待至少10us并检查LOCK位 delay_us(20); while (!(HW_CLKCTRL_PLLCTRL1_RD() BM_CLKCTRL_PLLCTRL1_LOCK)) { // 等待锁定 } // 2. 配置CPU PFD为24产生360MHz的ref_cpu并使其能 volatile uint8_t *frac_reg (volatile uint8_t*)HW_CLKCTRL_FRAC; frac_reg[0] 24; // 写入CPUFRAC字段 (Byte 0) // 注意CLKGATECPU位在Byte0的bit7需要单独操作。假设之前是关着的先清0打开。 // 更稳妥的方法是先读取整个字节修改低6位再写回。 uint8_t cpu_frac_byte frac_reg[0]; cpu_frac_byte (cpu_frac_byte 0xC0) | 24; // 保持高2位不变设置低6位为24 frac_reg[0] cpu_frac_byte; // 等待PFD稳定可选对于CPU PFD通常切换时钟源前的延时已足够 // while (!(HW_CLKCTRL_FRAC_RD() BM_CLKCTRL_FRAC_CPU_STABLE)) {} // 需根据位域调整 // 3. 配置CPU分频器。ref_cpu360MHz目标CPU360MHz则DIV_CPU0 (1分频) // 等待当前任何进行中的操作完成 while (HW_CLKCTRL_CPU_RD() (BM_CLKCTRL_CPU_BUSY_REF_CPU | BM_CLKCTRL_CPU_BUSY_REF_XTAL)) {} HW_CLKCTRL_CPU_WR(BF_CLKCTRL_CPU_DIV_CPU(0)); // 1分频 // 4. 等待分频器更新完成 while (HW_CLKCTRL_CPU_RD() BM_CLKCTRL_CPU_BUSY_REF_CPU) {} // 5. 切换时钟源到PLL假设BYPASS_CPU位为0代表使用PLL HW_CLKCTRL_CLKSEQ_CLR(BM_CLKCTRL_CLKSEQ_BYPASS_CPU); // 6. 配置EMI为同步模式并设置分频。CPU分频比为1EMI分频比需为其整数倍设为2即180MHz // 先退出自动慢速模式如果使能了 HW_CLKCTRL_HBUS_CLR(BM_CLKCTRL_HBUS_AUTO_SLOW_MODE); // 设置HBUS分频CLK_H CLK_P / (DIV1)。我们希望CLK_H180MHzCLK_P360MHz所以DIV1。 HW_CLKCTRL_HBUS_WR(BF_CLKCTRL_HBUS_DIV(1)); // 配置EMI分频器在同步模式下此值应与HBUS分频值满足比例关系。为保险设为相同值。 // 注意在设置SYNC_MODE_EN前需要确保EMI的参考时钟已稳定。 // 配置EMI PFD (ref_emi)。假设也需要180MHz则EMIFRAC 480*18/180 48超出范围最大35。 // 因此ref_emi无法直接到180MHz。在同步模式下EMI时钟由CLK_H驱动ref_emi可能不需要精确匹配。我们可以ref_emi设为一个支持的值比如360MHz (EMIFRAC24)然后依靠分频。 // 但同步模式下CLK_EMI由CLK_H分频而来似乎不直接使用ref_emi和DIV_EMI这里需要仔细看手册。 // 手册4.6节提到同步模式下EMI时钟分频器控制CLK_EMI和APBH时钟。可能意味着此时DIV_EMI字段被忽略EMI时钟完全由HBUS分频器控制。 // 保守做法在同步模式下将DIV_EMI设置为与DIV_CPU满足整数倍关系的值。 // 我们设置DIV_EMI 1 (2分频)这样ref_emi360MHz时CLK_EMI180MHz与CLK_H一致。 // 先配置EMI PFD frac_reg[2] 24; // 假设Byte2是EMIFRAC字段写入24 (480*18/24360MHz) // 清除EMI PFD门控 uint8_t emi_frac_byte frac_reg[2]; emi_frac_byte ~(1 7); // 清除CLKGATEEMI位 (假设在byte2的bit7) frac_reg[2] emi_frac_byte; // 配置EMI分频器 while (HW_CLKCTRL_EMI_RD() (BM_CLKCTRL_EMI_BUSY_REF_EMI | BM_CLKCTRL_EMI_BUSY_REF_XTAL)) {} HW_CLKCTRL_EMI_WR(BF_CLKCTRL_EMI_DIV_EMI(1)); // 2分频从360MHz得到180MHz while (HW_CLKCTRL_EMI_RD() BM_CLKCTRL_EMI_BUSY_REF_EMI) {} // 最后使能同步模式 HW_CLKCTRL_EMI_SET(BM_CLKCTRL_EMI_SYNC_MODE_EN); while (HW_CLKCTRL_EMI_RD() BM_CLKCTRL_EMI_BUSY_SYNC_MODE) {} }这段代码示例涵盖了多个关键操作但实际产品代码需要考虑更多错误处理和状态保存。最重要的是任何时钟配置函数都应在关闭中断的环境下执行因为频率切换期间定时器、调度器等都可能产生不可预知的行为。通常会将此类代码放入RAM中执行并确保执行期间不发生内存访问因为EMI时钟也在变化。