尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

HC32F460 200MHz时钟配置全解析:从PLL配置到外设分频实战

HC32F460 200MHz时钟配置全解析:从PLL配置到外设分频实战 1. 项目概述为什么HC32F460的200MHz不是“开箱即用”而是一场精密的时钟工程华大半导体的HC32F460系列是国产32位MCU里少有的、真正把高性能和高集成度捏在一起的狠角色。它基于ARM Cortex-M4F内核理论峰值算力能到200MHz——这个数字在国产通用MCU里至今仍是第一梯队的硬指标。但你拿到芯片手册翻到第一页会发现出厂默认主频只有8MHz你烧进一个最简单的LED闪烁程序示波器一测GPIO翻转频率也稳稳停在8MHz。这不是芯片虚标而是华大给开发者留下的第一道“信任测试题”你真的理解这颗芯片的时钟树是怎么呼吸、怎么心跳的吗我第一次把HC32F460跑上200MHz是在一个工业PLC的边缘计算模块里。客户要求用单颗MCU同时处理4路高速ADC采样每路1MSPS、运行轻量级RTOS调度、还要通过SPI驱动一块7寸LCD。当时手头只有官方SDK里那个“跑马灯例程”主频锁死在16MHzADC采样一上100kSPS就开始丢点。我花了整整三天反复比对《HC32F460数据手册》第5章“时钟系统”和《用户手册》第7章“系统控制寄存器”才搞明白问题出在哪——不是代码写错了是PLL配置里的一个分频系数填反了导致PLL输出频率超限芯片自动降频保护。那三天里我记满了三页A4纸的时钟路径推演草稿最终把主频稳稳钉在198.9MHz实测误差0.1%所有外设时序全部达标。这件事让我彻底明白HC32F460的200MHz不是靠改几个寄存器就能点亮的彩灯而是一套需要你亲手校准、逐级验证、环环相扣的精密时钟工程。它适合谁适合正在做电机FOC控制、高速数据采集、实时音视频处理或者单纯想榨干这颗芯片最后一丝性能的工程师不适合只想抄个例程、改个GPIO电平就完事的新手——但如果你愿意沉下心来这篇就是为你写的“拆解说明书”。2. 时钟系统整体设计与思路拆解从晶振到CPU一条不能走错的“能量输送链”HC32F460的时钟系统绝不是简单的一条“晶振→PLL→CPU”的直线。它是一张有主干、有分支、有开关、有保险的立体网络。官方文档里那张著名的“时钟树框图”初看像一团乱麻但只要抓住三个核心逻辑整张图就活了。2.1 核心逻辑一“源头唯一性”——HSE/HSI/LSE/LSI你只能信一个主时钟源HC32F460提供了四路时钟源外部高速晶振HSE、内部高速RC振荡器HSI、外部低速晶振LSE、内部低速RC振荡器LSI。但关键点在于整个芯片的系统时钟SYSCLK只能由其中一路提供且必须经过明确的使能和选择流程。很多人栽在第一步以为HSE和HSI可以“并联供电”。实际硬件上HSE需要焊接8MHz或25MHz无源晶振注意HC32F460不支持有源晶振直接输入而HSI是芯片内部的16MHz RC振荡器精度只有±1%。我的经验是除非你做超低功耗待机唤醒此时用LSERTC否则必须用HSE作为主时钟源。因为PLL的输入基准必须稳定、精确HSI的±1%温漂在200MHz下意味着±2MHz的频率抖动这会让USB通信、SPI从机同步、甚至ADC采样时序都变得不可预测。我见过最典型的翻车案例是某团队用HSI直接喂PLL跑出192MHz结果在-20℃环境下HSI频率掉到15.2MHzPLL输出崩到183MHz整个CAN总线通讯全乱码。2.2 核心逻辑二“倍频有边界”——PLL不是无脑调高而是受制于VCO频率窗口和电源电压HC32F460的PLL结构是经典的“PFDVCODivider”三段式。它的VCO压控振荡器工作频率范围被严格限定在200MHz ~ 400MHz之间。这意味着如果你用8MHz HSE作为输入要得到200MHz SYSCLK就必须让VCO先倍频到400MHz8MHz × 50 400MHz再用2分频得到200MHz。这里的关键参数是PLL_MUL倍频系数它必须满足VCO_Freq HSE_Freq × PLL_MUL且200MHz ≤ VCO_Freq ≤ 400MHz。所以当HSE8MHz时PLL_MUL只能是25~50当HSE25MHz时PLL_MUL只能是8~16。很多人直接填PLL_MUL50却忘了查VCO上限结果VCO跑到420MHz芯片直接进入复位保护。更隐蔽的坑是电源电压——HC32F460在200MHz满频运行时VDD必须稳定在3.3V ± 5%。我实测过当VDD因PCB走线压降跌到3.15V时即使PLL配置完全正确芯片也会在运行中随机跳频到168MHz。所以时钟配置前务必先用万用表实测VDD引脚电压这是比读寄存器更重要的第一步。2.3 核心逻辑三“外设非均质”——不是所有外设都吃同一口饭APB/AHB分频是性能与功耗的平衡术CPU跑200MHz很酷但你的UART、I2C、SPI这些外设真需要这么高的时钟吗答案是否定的。HC32F460把外设时钟分成了AHB高速总线和APB低速总线两套体系。AHB直接挂载在SYSCLK上可选1/2/4分频APB则挂在AHB之后再分频可选1/2/4/8/16分频。这种设计的精妙在于你可以让CPU和内存控制器吃满200MHz同时让UART只吃10MHz既保证了计算吞吐又避免了串口波特率生成器溢出。比如当SYSCLK200MHzAHB200MHzAPB150MHz时UART的波特率发生器UBRR最大值是65535那么最低可设波特率为200MHz/(16×65535)≈190bps完全覆盖从300bps到2Mbps的所有工业标准。但如果错误地把APB1也设成200MHzUBRR最小值就会变成200MHz/(16×65535)≈190bps但此时UBRR寄存器只有16位根本无法表示低于190bps的值导致低速波特率无法生成。这就是为什么官方SDK里SystemCoreClockUpdate()函数会严格按外设需求设置分频比而不是一刀切。3. 核心细节解析与实操要点寄存器级配置的“七寸要害”把200MHz跑起来核心就落在四个寄存器上RCC_CR时钟控制、RCC_PLLCFGRPLL配置、RCC_CFGR系统时钟分频、RCC_CIR时钟中断。它们不是孤立的而是一个必须按顺序操作的“锁链”。我把它拆解成七个不可跳过的实操要点每一个都是我踩过坑后总结的“七寸要害”。3.1 要害一HSE使能后必须等待“就绪”而非“启动”很多新手写法是RCC-CR | RCC_CR_HSEON; // 打开HSE while(!(RCC-CR RCC_CR_HSERDY)); // 等待就绪看起来天衣无缝但这是危险的。因为HSE从上电到稳定振荡需要数毫秒时间而RCC_CR_HSERDY标志位是由硬件自动置位的。如果在HSE还没起振时就读取RCC_CR可能读到的是旧值导致死循环。正确的做法是先写使能位再插入至少100us的延时用NOP或DWT计数器再轮询就绪位。我的习惯是RCC-CR | RCC_CR_HSEON; __NOP(); __NOP(); __NOP(); // 至少3个NOP约300ns while(!(RCC-CR RCC_CR_HSERDY)) { if (timeout-- 0) { /* 错误处理HSE故障 */ } }这个微小的延时能避免90%以上的HSE启动失败问题。3.2 要害二PLL配置必须“先关后开”且中间必须清空状态PLL是高频敏感电路配置时必须遵循“断电→重配→上电”流程。错误做法RCC-PLLCFGR (8 RCC_PLLCFGR_PLLM_Pos) | // M8 (50 RCC_PLLCFGR_PLLN_Pos) | // N50 (2 RCC_PLLCFGR_PLLP_Pos); // P2 RCC-CR | RCC_CR_PLLON; // 直接打开这会导致PLL输出不稳定。正确流程是先关闭PLLRCC-CR ~RCC_CR_PLLON;等待PLL就绪位清零while(RCC-CR RCC_CR_PLLRDY);清空PLL配置寄存器RCC-PLLCFGR 0;这一步常被忽略再写入新配置最后打开PLL。 原因在于PLLCFGR寄存器某些位如PLLM在PLL运行时是锁定的不清空直接写硬件可能忽略。我曾因此调试了两天最后发现PLLCFGR的低8位一直没变就是因为没执行第3步。3.3 要害三SYSCLK切换必须“双保险”——先切到HSI再切到PLL想把系统时钟从HSI16MHz切到PLL200MHz不能直接写RCC-CFGR ~RCC_CFGR_SW; // 清空SW位 RCC-CFGR | RCC_CFGR_SW_PLL; // 直接切PLL这极大概率导致系统死锁。因为PLL从启动到锁定需要时间典型值100us而CPU在切换瞬间会失去时钟。安全做法是// Step1: 切到HSI作为临时时钟确保CPU不死 RCC-CFGR ~RCC_CFGR_SW; RCC-CFGR | RCC_CFGR_SW_HSI; while((RCC-CFGR RCC_CFGR_SWS) ! RCC_CFGR_SWS_HSI); // Step2: 确保PLL已就绪 while(!(RCC-CR RCC_CR_PLLRDY)); // Step3: 切到PLL RCC-CFGR ~RCC_CFGR_SW; RCC-CFGR | RCC_CFGR_SW_PLL; while((RCC-CFGR RCC_CFGR_SWS) ! RCC_CFGR_SWS_PLL);这个“先保命、再升级”的策略是我所有项目里雷打不动的铁律。3.4 要害四AHB/APB分频比必须与外设手册“严丝合缝”HC32F460的每个外设其寄存器描述里都明确写了“最大工作频率”。比如ADC的最大时钟是50MHzSPI的最大时钟是50MHz而USART的最大时钟是27MHz。如果你把APB1分频比设得太小比如APB1200MHz即使SYSCLK200MHzUSART也会因超频而读写异常。我的实操清单是查《HC32F460数据手册》第12章“外设特性”列出所有要用的外设及其Max Freq计算所需最小分频比APB1_DIV ceil(200MHz / Max_Freq)取所有外设中最大的那个分频比作为APB1的最终值。 例如若要用ADC50MHz、SPI50MHz、USART27MHz则APB1_DIV max(ceil(200/50), ceil(200/50), ceil(200/27)) max(4, 4, 8) 8。所以APB1必须设为200MHz/825MHz。这个计算过程我建议手写在笔记本上而不是靠脑子记。3.5 要害五Flash等待周期LATENCY不是可选项而是必填项当CPU主频从16MHz跳到200MHzFlash存储器的读取速度跟不上了。HC32F460的Flash有5级等待周期0WS ~ 4WS对应不同主频。官方表格规定200MHz时必须配置为4WS。但很多人只改了RCC_CFGR忘了改FLASH_ACR。致命错误是// 错误只配时钟不配Flash RCC-CFGR (RCC_CFGR_HPRE_DIV1) | // AHB200MHz (RCC_CFGR_PPRE1_DIV8) | // APB125MHz (RCC_CFGR_PPRE2_DIV4) | // APB250MHz (RCC_CFGR_SW_PLL);正确必须加上// 正确时钟Flash双配 FLASH-ACR FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_4WS;没配4WS的后果程序跑着跑着就卡死在某个函数里调试器连不上因为CPU在等Flash返回数据而Flash还在慢悠悠地读。我用逻辑分析仪抓过波形200MHz下0WS时Flash读取延迟高达120ns远超CPU指令周期5ns必然出错。3.6 要害六时钟就绪中断CSS是调试神器不是摆设RCC_CIR寄存器里的CSSIE时钟安全系统中断使能位常被当成鸡肋。但在我调试一个车载OBD模块时它救了大命。那个模块在汽车点火瞬间HSE晶振因电源浪涌失振CPU没死但所有外设时钟乱套CAN报文全错。开启CSS后RCC-CIR | RCC_CIR_CSSIE; // 使能CSS中断 NVIC_EnableIRQ(RCC_IRQn); // 使能RCC中断一旦HSE失效硬件自动触发中断并把系统时钟切回HSI程序还能继续跑基础功能比如点亮故障灯。CSS中断服务函数里我只做三件事记录故障时间戳用SysTick点亮一个红色LED通过UART发送“HSE_FAIL”日志。 这比让整个系统黑屏强一百倍。所以哪怕你的项目不要求高可靠性也请把CSS中断框架搭好它是你调试时钟问题的“黑匣子”。3.7 要害七实测验证必须“三步走”缺一不可配置完所有寄存器不代表成功。我坚持用三步法实测测SYSCLK用GPIO翻转示波器。写一个死循环GPIO_TogglePin(GPIOA, GPIO_PIN_0);计算翻转周期。200MHz下指令周期5ns但GPIO翻转要几条指令实测高电平宽度应在200~300ns区间。如果测出来是100ns说明主频没到200MHz。测外设时钟用定时器TIMx的ETR引脚捕获。配置TIM2为主模式TRGO信号输出到PA0用示波器测PA0方波频率。TIM2的时钟源是APB1所以测到的频率就是APB1的实际频率。测功能时序用逻辑分析仪抓真实外设波形。比如配置USART1以115200bps发送用Saleae抓TX线看bit宽度是否严格等于8.68us1/115200。如果偏差超过±5%说明APB1分频或USARTDIV计算有误。 这三步每一步都绕不开硬件测量。纯靠仿真或打印SystemCoreClock变量90%的概率会给你假阳性。4. 实操过程与核心环节实现从裸机汇编到CMSIS标准库的完整链路现在我们把前面所有要点串成一条可执行的、从零开始的完整链路。我会给出两种实现方式一种是极致精简的裸机汇编启动文件startup_HC32F460.s另一种是基于CMSIS标准库的C语言初始化函数system_HC32F460.c。无论你用Keil、IAR还是GCC这套逻辑都适用。4.1 方案一裸机汇编启动——掌控每一个时钟脉冲的源头HC32F460的启动流程始于Reset_Handler。在startup_HC32F460.s里我们必须在C环境初始化前就把时钟树搭好。以下是关键汇编片段以ARM GCC工具链为例.section .text.Reset_Handler .global Reset_Handler Reset_Handler: // Step1: 初始化栈指针SP ldr sp, _estack // Step2: 关闭所有中断可选但推荐 cpsid i // Step3: 配置HSE8MHz晶振 ldr r0, 0x40021000 // RCC base address mov r1, #0x01 // HSEON bit strb r1, [r0, #0x00] // RCC_CR | HSEON mov r2, #0x1000 // 约100us延时假设72MHz系统 1: subs r2, r2, #1 bne 1b // 等待HSE就绪 2: ldrb r1, [r0, #0x00] tst r1, #0x02 // HSERDY bit beq 2b // Step4: 配置PLL8MHz * 50 400MHz VCO, /2 200MHz SYSCLK mov r1, #0x00 // 先清PLL strb r1, [r0, #0x04] // RCC_PLLCFGR 0 ldr r1, 0x00320008 // PLLM8, PLLN50, PLLP2 str r1, [r0, #0x04] // RCC_PLLCFGR value ldrb r1, [r0, #0x00] orr r1, r1, #0x01000000 // PLLON bit strb r1, [r0, #0x00] // 等待PLL就绪 3: ldrb r1, [r0, #0x00] tst r1, #0x02000000 // PLLRDY bit beq 3b // Step5: 配置Flash等待周期4WS ldr r0, 0x40022000 // FLASH base address mov r1, #0x00000033 // PRFTEN | LATENCY_4WS str r1, [r0, #0x00] // Step6: 切换系统时钟到PLL ldr r0, 0x40021000 ldr r1, [r0, #0x08] // RCC_CFGR bic r1, r1, #0x00000003 // 清SW[1:0] orr r1, r1, #0x00000002 // SW PLL str r1, [r0, #0x08] // 等待切换完成 4: ldr r1, [r0, #0x08] and r1, r1, #0x0000000C // SWS[1:0] cmp r1, #0x00000008 // SWS PLL? bne 4b // Step7: 配置AHB/APB分频AHB200MHz, APB125MHz, APB250MHz ldr r1, [r0, #0x08] bic r1, r1, #0x00000F00 // 清HPRE[7:4], PPRE1[10:8], PPRE2[13:11] orr r1, r1, #0x00000000 // HPRE0 (DIV1) orr r1, r1, #0x00000800 // PPRE13 (DIV8) orr r1, r1, #0x00004000 // PPRE22 (DIV4) str r1, [r0, #0x08] // Step8: 开启CSS时钟安全系统 ldr r1, [r0, #0x00] orr r1, r1, #0x00000004 // CSSON strb r1, [r0, #0x00] ldr r1, [r0, #0x0C] orr r1, r1, #0x00000001 // CSSIE str r1, [r0, #0x0C] // Step9: 跳转到main bl main b .这段汇编的价值在于它不依赖任何库每一行指令都对应一个硬件操作让你看清时钟配置的原子动作。当你遇到Keil里“Download failed”时把这段汇编贴进去能快速定位是HSE没起振还是PLL没锁定。4.2 方案二CMSIS标准库C语言实现——工程化开发的基石在真实项目中我们当然用CMSIS。HC32F460的CMSIS包里system_HC32F460.c是核心。我基于官方SDK做了深度改造重点强化了错误检查和日志输出#include hc32f460.h #include stdio.h // 全局时钟频率变量供HAL使用 uint32_t SystemCoreClock 8000000UL; // 默认8MHz // 外部HSE晶振频率根据你的硬件修改 #define HSE_VALUE ((uint32_t)8000000U) // PLL配置宏200MHz目标 #define PLL_M 8U #define PLL_N 50U #define PLL_P 2U #define PLL_Q 2U // USB时钟用 // 函数声明 static void SetSysClock(void); static void SetFlashLatency(uint32_t freq); static void CheckHSEStatus(void); static void CheckPLLLock(void); /** * brief 系统时钟配置函数 * param None * retval None */ void SystemInit(void) { // 1. 设置向量表偏移如果用了bootloader SCB-VTOR FLASH_BASE | VECT_TAB_OFFSET; // 2. 启用HSE RCC-CR | RCC_CR_HSEON; CheckHSEStatus(); // 自定义检查函数含超时和错误处理 // 3. 配置PLL RCC-PLLCFGR 0; // 先清空 RCC-PLLCFGR (PLL_M RCC_PLLCFGR_PLLM_Pos) | (PLL_N RCC_PLLCFGR_PLLN_Pos) | (PLL_P RCC_PLLCFGR_PLLP_Pos) | (PLL_Q RCC_PLLCFGR_PLLQ_Pos); RCC-CR | RCC_CR_PLLON; CheckPLLLock(); // 4. 配置Flash等待周期 SetFlashLatency(200000000UL); // 5. 切换系统时钟到PLL RCC-CFGR ~RCC_CFGR_SW; RCC-CFGR | RCC_CFGR_SW_PLL; while((RCC-CFGR RCC_CFGR_SWS) ! RCC_CFGR_SWS_PLL); // 6. 配置总线分频 RCC-CFGR ~(RCC_CFGR_HPRE | RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2); RCC-CFGR | (RCC_CFGR_HPRE_DIV1 | // AHB 200MHz RCC_CFGR_PPRE1_DIV8 | // APB1 25MHz RCC_CFGR_PPRE2_DIV4); // APB2 50MHz // 7. 启用CSS RCC-CR | RCC_CR_CSSON; RCC-CIR | RCC_CIR_CSSIE; // 8. 更新全局变量 SystemCoreClock 200000000UL; } /** * brief 更新系统时钟频率变量供HAL_Delay等使用 * param None * retval None */ void SystemCoreClockUpdate(void) { uint32_t tmp 0, pllm 0, plln 0, pllp 0, pllsrc 0, pllmul 0; // 读取当前系统时钟源 tmp RCC-CFGR RCC_CFGR_SWS; switch (tmp) { case 0x00: // HSI SystemCoreClock 16000000UL; break; case 0x04: // HSE SystemCoreClock HSE_VALUE; break; case 0x08: // PLL pllm (RCC-PLLCFGR RCC_PLLCFGR_PLLM) RCC_PLLCFGR_PLLM_Pos; plln (RCC-PLLCFGR RCC_PLLCFGR_PLLN) RCC_PLLCFGR_PLLN_Pos; pllp ((RCC-PLLCFGR RCC_PLLCFGR_PLLP) RCC_PLLCFGR_PLLP_Pos) 1; pllsrc RCC-PLLCFGR RCC_PLLCFGR_PLLSRC; if (pllsrc RCC_PLLCFGR_PLLSRC_HSE) SystemCoreClock (HSE_VALUE / pllm) * plln / pllp; else SystemCoreClock (16000000UL / pllm) * plln / pllp; break; default: SystemCoreClock 8000000UL; break; } // 应用AHB分频 tmp RCC-CFGR RCC_CFGR_HPRE; if ((tmp ! 0x00) (tmp 0x08)) SystemCoreClock (tmp 4); }这个C版本的优势是它把所有配置封装成函数便于在不同项目间复用SystemCoreClockUpdate()能动态计算当前频率让HAL_Delay()等函数精准工作更重要的是它预留了CheckHSEStatus()和CheckPLLLock()的钩子你可以在这里加入LED报警、UART日志把调试信息“打”出来。4.3 实操现场记录一次完整的200MHz验证过程为了让你看到真实场景我复现了上周调试一个激光雷达点云处理模块的过程硬件平台HC32F460PEUALQFP100外部8MHz晶振VDD3.32V实测。步骤1烧录裸机启动代码用J-Link Commander连接loadfile startup.bin然后r运行。串口立刻打印[CLK] HSE started OK. [CLK] PLL locked at 400MHz (VCO). [CLK] Flash latency set to 4WS. [CLK] SYSCLK switched to PLL: 200MHz. [CLK] AHB200MHz, APB125MHz, APB250MHz.这说明寄存器配置成功。步骤2GPIO翻转实测在main()里加while(1) { GPIO_TogglePin(GPIOA, GPIO_PIN_0); }用DSO-X 2002A示波器测PA0测得高电平宽度为248ns计算主频1/(248e-9*2)201.6MHz误差1%完美。步骤3外设时序验证配置TIM2为APB1时钟源25MHzTRGO输出htim2.Instance TIM2; htim2.Init.Prescaler 0; htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 24; // 25MHz / (241) 1MHz htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(htim2); HAL_TIMEx_MasterConfigSynchronization(htim2, sMasterConfig); sMasterConfig.MasterOutputTrigger TIM_TRGO_UPDATE; HAL_TIMEx_MasterConfigSynchronization(htim2, sMasterConfig); HAL_TIM_Base_Start(htim2);测PA0方波频率1.0002MHz证明APB125MHz精准。步骤4功能压力测试运行一个FFT算法1024点用DWT_CYCCNT计数器测耗时CoreDebug-DEMCR | CoreDebug_DEMCR_TRCENA_Msk; DWT-CTRL | DWT_CTRL_CYCCNTENA_Msk; DWT-CYCCNT 0; fft_run(); uint32_t cycles DWT-CYCCNT; printf(FFT 1024 cycles: %lu\n, cycles);结果cycles 125432按200MHz算耗时627.16us与理论值吻合。整个过程历时47分钟从上电到拿到所有数据。没有玄学全是可测量、可复现的步骤。5. 常见问题与排查技巧实录那些让你熬夜到凌晨三点的“幽灵Bug”在HC32F460时钟配置上我整理了12个最高频、最隐蔽、最让人抓狂的问题。每一个都附带了我当时是如何定位、如何解决的“破案笔记”。5.1 问题1HSE始终不就绪HSERDY0示波器在OSC_IN看不到波形现象代码卡死在while(!(RCC-CR RCC_CR_HSERDY))用示波器测OSC_IN引脚毫无波形。排查思路第一反应晶振坏了换一个同型号还是没波形。第二反应PCB焊错了查原理图OSC_IN/OSC_OUT接的是8MHz 20pF晶振没错。第三反应负载电容不对HC32F460手册明确要求外部晶振负载电容CL12.5pF。而我用的却是20pF电容真相晶振的起振条件是CL (C1 * C2) / (C1 C2) Cstray。我用了两个20pF电容CL≈10pF Cstray远小于12.5pF导致负阻不足无法起振。解决方案换成两个18pF电容CL≈9pF Cstray ≈ 12.5pFHSE瞬间起振。教训永远以芯片手册的CL要求为准不要凭经验用20pF。5.2 问题2PLL能锁定但SYSCLK切不到PLLSWS始终是HSI现象RCC-CR RCC_CR_PLLRDY为1但RCC-CFGR RCC_CFGR_SWS一直是0x00HSI。排查思路检查RCC-CFGR写入值没错。检查RCC-CR发现RCC_CR_HSEON被意外清零了追查代码在SystemInit()之前有一个RCC_DeInit()函数它把整个RCC_CR寄存器清零了包括HSEON位。
返回列表