ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32H743 HRTIM高分辨率定时器实战:从PWM生成到死区移相

STM32H743 HRTIM高分辨率定时器实战:从PWM生成到死区移相 1. HRTIM到底是个什么东西和普通定时器的本质区别1.1 为什么stm32h743上要单独学一遍HRTIM先说实话我在接触HRTIM之前一直觉得STM32的定时器已经够用了。高级定时器TIM1/TIM8能输出互补PWM带死区带刹车做电机控制绰绰有余。直到我把项目从F103挪到H743发现负载对PWM频率的要求从20kHz提到了几百kHz甚至MHz级别普通定时器的时钟再怎么分频也喂不饱高频率高分辨率的场景这才认真翻开H743参考手册里HRTIM那一章。HRTIM全称是High-Resolution Timer高分辨率定时器。它跟普通定时器最大的区别不是频率更高这么简单而是它在硬件层面专门为电力电子、开关电源、数字电源这类场景做了优化。比如你需要在20kHz的PWM周期里做到纳秒级的占空比调节精度普通定时器用144MHz的主频算下来一个计数周期大约是6.94纳秒听起来也还行但一旦你要同时输出6路甚至更多路带死区、带移相的PWM普通定时器就不够用了。HRTIM的时钟可以跑到480MHz配合DLLDelay Locked Loop甚至能实现大约2.6纳秒的时间分辨率这已经接近高端数字电源控制器的水平。所以如果你做的是大功率DC-DC、逆变器、PFC电路、或者对PWM时序要求极高的电机驱动H743的HRTIM几乎是绕不开的模块。反过来说如果你的项目只是随便点个LED呼吸灯、驱动个普通舵机那完全没必要碰HRTIMTIM2就够了。先搞清楚自己到底需要什么再决定学不学这个模块能省掉大量踩坑时间。1.2 HRTIM的定时器组织结构主定时器和子定时器的配合HRTIM并不是一个孤立的定时器它内部由1个主定时器Master Timer和6个子定时器Timer A到Timer F组成。我第一次看这个结构时也有点懵因为普通定时器的用法是一个人独立干活而HRTIM更像一个团队协作主定时器负责给整个系统提供基准时基各个子定时器可以独立运行也可以与主定时器同步甚至可以互相级联。每个子定时器包含两个输出通道分别叫TX1和TX2比如Timer A就是TA1和TA2这样算下来6个子定时器一共12路输出。不过要注意H743的HRTIM输出引脚并不是所有封装都全引出具体能用的通道要看封装和引脚复用表。我在实际项目里用的是LQFP176封装大部分通道都能引出但如果你用的是LQFP100的H743就得仔细核对引脚了。主定时器和子定时器之间的关系可以通过同步机制来管理。比如你把主定时器设置为PWM模式然后让Timer B跟着主定时器的周期走两个定时器的计数起点就能严格对齐。这个特性在做多路交错PWMInterleaved PWM时特别有用后面我会详细讲移相怎么实现。1.3 HRTIM能做而普通PWM做不了的事为了让你直观理解HRTIM的价值列一组对比能力普通高级定时器 TIM1/TIM8HRTIM最高计数时钟144MHzH743上480MHzDLL下约2.6ns分辨率输出通道数最多4通道互补最多12通道互补死区分辨率约7ns级别亚纳秒级可调定时器间同步需要软件干预硬件级主从同步、移相故障保护输入有但通道少多路故障输入响应更快适合场景常规电机驱动、调光数字电源、交错PFC、高频逆变当然这些参数只是纸面上的真正用起来你才会发现HRTIM的复杂度和自由度是成正比的。寄存器多、模式多、同步配置多不看参考手册直接上手很容易被绕进去。但也正因为如此一旦跑通了你能得到非常漂亮、不占CPU资源的PWM波形。2. 开工之前的准备工作时钟配置与引脚分配2.1 时钟树里最容易踩的坑ker_ck和D1CCE的分配很多人在CubeMX里看到HRTIM的时钟选项时容易忽略一个问题HRTIM的时钟源不是简单的某个总线时钟而是独立的kernel clock叫ker_ck。这个时钟默认情况下可能不是480MHz需要专门去配置。我第一版配置就吃了亏。当时在CubeMX的Clock Configuration页面里看到HRTIM的时钟是240MHz想着反正是分辨率也够用就没仔细调。结果后面做死区计算时发现寄存器里一个计数步进是4.17纳秒跟参考手册里典型配置下可达2.6ns差了一大截才发现ker_ck根本没跑到最高。正确做法是先把PLL2或者PLL3配置成输出480MHz然后把这个时钟通过RCC的D1CCEDomain 1 Clock Configuration Enable或专门的MUX选择到HRTIM的ker_ck上。在CubeMX里具体路径是Clock Configuration页面里找HRTIM的时钟源选择下拉框里通常会列出可选的PLL输出。需要注意的是如果选PLL2或PLL3你要确保这个PLL没有被别的外设独占尤其注意以太网或者USB这种对时钟也敏感的外设。你可以用这样一个简单的方法验证ker_ck是否生效读RCC-D1CCIPR寄存器里HRTIM相关的位域确认选择的是哪个时钟源再根据PLL配置手动算出理论频率。千万别只信CubeMX里显示的数字我遇到过CubeMX界面显示480MHz实际跑起来还是默认240MHz的情况最后是靠读寄存器才发现配置没真正写进去。2.2 引脚与输出通道的选择H743的HRTIM输出引脚复用关系比较绕跟普通定时器一个通道对应一个固定引脚不太一样。HRTIM的输出通道有主输出比如HRTIM_TIMA1、互补输出HRTIM_TIMA1N等引脚分布在PA、PB、PC、PE、PF等多个端口。我在项目里用的几个通道如下Timer A输出PA8HRTIM_TIMA1、PA9HRTIM_TIMA2Timer B输出PB10HRTIM_TIMB1、PB11HRTIM_TIMB2Timer C输出PC6HRTIM_TIMC1、PC7HRTIM_TIMC2插一句引脚分配这一步强烈建议用CubeMX的可视化配置去查复用不要凭记忆硬写。芯片型号不同、封装不同可用的引脚差异很大。我见过有人照着网上的例程把PC6配成TIMC1结果他那颗芯片封装小PC6根本引不出白白折腾了半天。选引脚时还要留意是否是ADC、比较器等外设的复用冲突。比如PA8同时也是MCO1的引脚如果你要用MCO输出时钟调试就会冲突。另外HRTIM的互补输出引脚带N后缀如果不用可以不接但对应的输出使能要关掉否则引脚上可能出现意外电平。2.3 我实测的CubeMX初始配置这里给出一份我实测可用的CubeMX配置步骤作为基础版打开STM32CubeMX芯片选STM32H743VIT6或者你的具体型号。时钟树里把PLL2配置成480MHz输出HRTIM的时钟源选择PLL2。在Pinout Configuration页面找到HRTIM1使能Timer A选择PWM模式。配置Timer A的周期寄存器PER和比较寄存器CMP1初始值随便填后面代码里再改。配置输出通道TA1和TA2使能主输出极性设为高电平有效。如果有死区需求在Dead-Time Insertion里设置死区时间。生成工程代码。有个小细节CubeMX的HRTIM配置界面会把很多寄存器细节藏起来比如DLL校准、突发模式、定时器间同步等默认值不一定适合你的场景。所以我习惯把CubeMX当成引脚复用生成器核心配置还是自己写寄存器这样心里有数调试排查也快。3. 核心操作用HRTIM生成PWM波的完整步骤3.1 周期寄存器与比较寄存器的关系PWM频率怎么算HRTIM的PWM频率计算公式其实跟普通定时器类似只是计数时钟更高PWM频率 ker_ck / (PER 1) 占空比 CMP / (PER 1)假设ker_ck为480MHz你想要100kHz的PWM频率那么PER 1 480MHz / 100kHz 4800 PER 4799占空比50%的话CMP1 2400。看起来很简单对吧这里有个坑HRTIM的PER寄存器是16位的最大只能写到65535。如果你的PWM频率很低比如1kHz480MHz时钟下PER要写479999直接溢出。这也是很多人在网上问为什么HRTIM输出不了低频PWM的原因之一。解决方法是把ker_ck降下来或者用预分频器Prescaler。HRTIM每个子定时器都有独立的预分频器可以对ker_ck做1到128分频。但要注意一但用了预分频高分辨率优势就被削弱了所以尽量在满足频率的前提下选择尽量高的时钟。以我实际做的一个数字电源项目为例开关频率设定为200kHz占空比需要在0.1%到99.9%之间精调。480MHz下PER 1 480MHz / 200kHz 2400一个计数步进约2.083纳秒占空比调节精度就是1/2400约0.0417%。这个精度对于大部分电源控制需求来说已经非常高。如果你需要更高的精度可以把频率降低或者使用DLL模式进一步细分时间步长。3.2 PWM模式配置与输出极性从寄存器看输出行为HRTIM的PWM模式主要有两种常见配置Continuous模式连续计数和Up-Down模式增减计数。连续模式下计数器从0加到PER然后回0输出高电平的时间是0到CMP之间增减模式下计数器先加后减输出的中心对称PWM这在一些低谐波要求高的场景下更常见。配置输出极性时要注意HRTIM的输出极性寄存器跟普通定时器不太一样。普通定时器是低电平有效/高电平有效二选一HRTIM则是分别控制主输出和互补输出的有效电平。示波器看波形时如果发现占空比和预期反了先不要怀疑寄存器写错了检查一下极性配置有没有设反。我初始化HRTIM时习惯按这样的顺序操作先把HRTIM的全局使能关掉防止配置过程中输出毛刺。写各个子定时器的周期寄存器PER。写比较寄存器CMP1和CMP2。配置输出模式、极性、死区。配置主定时器的同步和触发。打开子定时器计数。打开输出使能。这个顺序不是随意定的先关全局使能可以保证你在配置中间状态时不会产生意外输出。等所有寄存器都配置完成后再打开计数和输出波形就是干净无毛刺的。3.3 使能输出的先后顺序为什么先开定时器后开输出很多第一次用HRTIM的人会在这里被坑到。我自己的经历是初始化代码写完后上电示波器探头夹在输出引脚上发现死活没有波形。后来一步步排查发现问题出在我先打开了输出使能然后才开启定时器计数导致输出引脚一直处于无效电平状态。HRTIM的输出使能逻辑是即使定时器在跑只要输出使能位比如TIMEREN寄存器里的TA1EN没置位引脚还是不会输出波形。反过来如果你先置位了输出使能但定时器还没开始计数引脚会输出配置的无效电平。所以在实际编码时务必要先使能定时器计数再使能输出。另外要留意HRTIM还有个全局输出使能位HRTIM_CR1里的EN位需要跟子定时器的输出使能配合。我建议的顺序是配置好所有寄存器。置位HRTIM_CR1的EN位让整个HRTIM模块开始工作。写HRTIM_TIMEREN寄存器把需要用到的定时器计数使能。最后写HRTIM_OUTEN寄存器使能具体通道的输出。3.4 一个可直接抄走的初始化代码下面这段代码是精简自我在实际项目中用的初始化函数HAL库版本基于CubeMX生成工程后可以直接参考void HRTIM_PWM_Init(void) { HRTIM_TimeBaseCfgTypeDef sTimeBaseCfg {0}; HRTIM_TimerCfgTypeDef sTimerCfg {0}; HRTIM_OutputCfgTypeDef sOutputCfg {0}; HRTIM_CompareCfgTypeDef sCompareCfg {0}; // 使能HRTIM时钟CubeMX中已配置 __HAL_RCC_HRTIM1_CLK_ENABLE(); // 复位HRTIM __HAL_RCC_HRTIM1_FORCE_RESET(); __HAL_RCC_HRTIM1_RELEASE_RESET(); // 配置Timer A时基 sTimeBaseCfg.Period 4799; // 100kHz ker_ck480MHz sTimeBaseCfg.RepetitionCounter 0; sTimeBaseCfg.Prescaler HRTIM_PRESCALERRATIO_DIV1; // 不分频 sTimeBaseCfg.Mode HRTIM_MODE_CONTINUOUS; // 连续计数模式 HAL_HRTIM_TimeBaseConfig(hhrtim1, HRTIM_TIMERINDEX_MASTER, sTimeBaseCfg); sTimerCfg.InterruptRequests HRTIM_IT_NONE; sTimerCfg.DACTrigger HRTIM_DACTRIGGER_NONE; sTimerCfg.ResetTrigger HRTIM_RESETTRIGGER_NONE; sTimerCfg.UpdateTrigger HRTIM_UPDATETRIGGER_NONE; sTimerCfg.RestartTrigger HRTIM_RESTARTTRIGGER_NONE; sTimerCfg.ResetUpdate HRTIM_TIMUPDATE_MASTER; sTimerCfg.PushPull HRTIM_TIMPUSH_PULL_NONE; sTimerCfg.FaultEnable HRTIM_TIMFAULTENABLE_NONE; sTimerCfg.AutoDelayedMode HRTIM_AUTODELAYEDMODE_NONE; sTimerCfg.BurstMode HRTIM_TIMBURSTMODE_MAIN; sTimerCfg.UpdateGating HRTIM_UPDATEGATING_INDEPENDENT; sTimerCfg.UpdateSource HRTIM_UPDATESOURCE_MASTER; sTimerCfg.PeriodicUpdate HRTIM_PERIODICUPDATE_MASTER; sTimerCfg.Output1State HRTIM_OUTPUTSTATE_DISABLED; sTimerCfg.Output1Reset HRTIM_OUTPUTRESET_NONE; sTimerCfg.Output1Set HRTIM_OUTPUTSET_NONE; sTimerCfg.Output2State HRTIM_OUTPUTSTATE_DISABLED; sTimerCfg.Output2Reset HRTIM_OUTPUTRESET_NONE; sTimerCfg.Output2Set HRTIM_OUTPUTSET_NONE; HAL_HRTIM_TimerConfig(hhrtim1, HRTIM_TIMERINDEX_TIMER_A, sTimerCfg); // 配置比较值50%占空比 sCompareCfg.CompareValue 2400; HAL_HRTIM_CompareConfig(hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_COMPAREUNIT_1, sCompareCfg); // 配置输出 sOutputCfg.Polarity HRTIM_OUTPUTPOLARITY_HIGH; sOutputCfg.SetSource HRTIM_OUTPUTSET_TIMCMP1; sOutputCfg.ResetSource HRTIM_OUTPUTRESET_TIMPER; sOutputCfg.IdleLevel HRTIM_OUTPUTIDLELEVEL_INACTIVE; sOutputCfg.IdleMode HRTIM_OUTPUTIDLEMODE_NONE; sOutputCfg.FaultLevel HRTIM_OUTPUTFAULTLEVEL_NONE; sOutputCfg.ChopperModeEnable HRTIM_OUTPUTCHOPPERMODE_DISABLED; sOutputCfg.BurstModeEntryDelayed HRTIM_OUTPUTBURSTMODEENTRY_DISABLED; sOutputCfg.BurstModeExit HRTIM_OUTPUTBURSTMODE_DISABLED; HAL_HRTIM_OutputConfig(hhrtim1, HRTIM_TIMERINDEX_TIMER_A, HRTIM_OUTPUT_TA1, sOutputCfg); // 先使能时基 HAL_HRTIM_TimeBaseStart(hhrtim1, HRTIM_TIMERINDEX_MASTER); HAL_HRTIM_TimeBaseStart(hhrtim1, HRTIM_TIMERINDEX_TIMER_A); // 再使能输出 HAL_HRTIM_OutputStart(hhrtim1, HRTIM_OUTPUT_TA1); }这里面有个关键点我配置了master时基和Timer A的时基。HRTIM的6个子定时器都可以选择跟谁同步我用的是独立运行模式。如果你想做到多通道同步后面会讲到怎么把Timer B跟Timer A对齐。注意这段代码是基础版没有处理死区、故障保护、突发模式等高级功能实际项目中你要按需补充。代码写完示波器上应该能看到100kHz、50%占空比的PWM方波了。如果看不到往下看排查部分。4. 进阶玩法用HRTIM做死区、互补输出和移相4.1 死区插入从寄存器到示波器死区简单说就是在互补PWM的高低边沿之间插入一段两边都关断的时间防止上下桥臂直通短路。做过电机驱动的人应该都懂没弄清楚死区就上电轻则烧MOSFET重则连累整个板子。HRTIM的死区配置比普通定时器灵活很多可以分别设置上升沿死区和下降沿死区而且单位是ker_ck周期。在480MHz时钟下一个计数周期大约是2.083纳秒。设定500纳秒死区死区寄存器值 500ns / 2.083ns ≈ 240写寄存器的时候比如Timer A的死区上升沿寄存器是DTR1下降沿是DTR2。HAL库里有专门的接口HAL_HRTIM_DeadTimeConfig(hhrtim1, HRTIM_TIMERINDEX_TIMER_A, sDeadTimeCfg);如果只配了一个方向的死区另一个方向不配置实际插入的效果是只有单边延迟这在某些Buck电路中也许够用但全桥/半桥电路强烈建议两边都配让波形对称减小输出谐波。用示波器看死区是最直观的验证方法把探头的CH1接主输出CH2接互补输出打开余晖显示就能看到两个波形之间有一小段都是低电平的时间那段时间就是死区。4.2 互补输出与刹车保护HRTIM的故障输入互补输出在HRTIM里就是同一个子定时器的TX1和TX1N。使能时注意对应通道对必须同时打开。光使能主输出、不使能互补输出引脚上可能输出恒定的无效电平或者完全没反应。故障输入Fault Input是HRTIM非常强大的一个特性。它支持多路外部故障信号一旦触发输出可以立即进入预设的安全电平甚至立即跳变为无效电平完全不需要CPU干预。这在电源类应用中就是保命符。我在项目里把比较器输出接到HRTIM的FLT1引脚做过压保护配置为高电平有效触发触发后输出强制为低低边关断。实测从故障信号出现到输出跳变整个过程在几十纳秒级别这是普通定时器很难做到的。如果你做的是大功率电源建议认真看一下参考手册里Fault的相关章节按照哪个故障输入对应哪个定时器、触发后输出什么电平的规则配好两次上电之间留足余量。有朋友问过故障保护触发了状态怎么恢复。HRTIM的故障标志一旦置位需要软件清除故障标志位再重新使能输出才能恢复正常PWM输出。如果不清标志位就重新使能输出仍会处于保护状态。这是个很容易误判的细节看起来像是定时器没启动其实是故障锁存了。4.3 移相PWM多路输出之间的相位关系移相在某些场合非常关键。比如交错式Buck电源两路PWM相位错开180度可以显著降低输入和输出的纹波。HRTIM做移相的核心思想是让后一路定时器的计数起点相对于前一路偏移一定的时间。在HRTIM里这种偏移是通过定时器之间的触发和复位同步实现的。以Timer A和Timer B为例如果你想让Timer B比Timer A滞后90度以周期为参照100kHz下就是2.5微秒可以用这一组寄存器配置把Master定时器配置为PWM模式周期为4800。Timer A和Timer B都设置为Master复位模式也就是Master计数到PER后同时复位Timer A和Timer B的计数器。注意由于是硬件复位同步严格来说Timer A和Timer B的上升沿是对齐的移相需要另外设置偏移量。要真正实现移相你需要在Timer B的配置里设置StartTrigger或者使用比较匹配作为触发起点。一个更常用的做法是让Timer B以Master的某个比较事件作为复位源而不是以Master周期结束作为复位源。假设Master的CMP2设置为1200对应90度相位Timer B配置为当Master CMP2匹配时复位那么Timer B的周期起点就比Timer A滞后了90度。这里贴一段寄存器操作的伪代码逻辑// 假设Master: PER4799, CMP21200 // Timer A: 复位源是Master的周期事件默认 // Timer B: 复位源配置为Master的比较2事件 sTimerCfg.ResetTrigger HRTIM_RESETTRIGGER_MASTER_CMP2; sTimerCfg.UpdateTrigger HRTIM_UPDATETRIGGER_MASTER_CMP2; HAL_HRTIM_TimerConfig(hhrtim1, HRTIM_TIMERINDEX_TIMER_B, sTimerCfg);注意ResetTrigger和UpdateTrigger要配套设置否则可能出现计数器按时序复位了但寄存器更新的周期没有同步导致波形错乱。这个细节我调了一下午才搞明白写出来给你们省点时间。5. 实测过程中的坑与排查思路5.1 问题一上电后没有波形输出这是HRTIM初学者最常见的现象。可能的原因有好几个我按概率从高到低列一下输出使能没置位HRTIM_OUTEN寄存器的TA1EN没写1定时器在跑但输出电路是关闭的。定时器没在跑检查HRTIM_TIMEREN寄存器确认对应位是1。时钟没配好ker_ck为0或者频率不对定时器计数器根本不动读寄存器会发现计数器一直停在某个值。引脚复用配错GPIO没有配置成复用功能或者复用的AF号不对。输出极性配置反了导致输出一直处于无效电平。排查思路建议这样先读HRTIM_TIMEREN确认定时器使能再看HRTIM_OUTEN确认输出使能然后读计数寄存器确认数值在变化。如果计数不变化检查时钟如果计数变化但引脚无波形检查GPIO复用和输出极性。5.2 问题二频率算出来和示波器对不上我遇到过理论计算100kHz示波器显示50kHz的情况。排查半天发现是PER写错了。我用的是HAL的TimeBaseConfigPeriod字段写的是4799但实际写入寄存器时HAL库内部是按Period - 1处理的导致实际周期比预期大了一倍。这类问题最常见的原因是CubeMX版本不同、HAL库版本不同同一个字段在不同版本里的语义略有差异。建议你遇到频率不对时直接读HRTIM的PER寄存器看实际值是多少再跟理论值对比。不要只看代码里写的参数要以寄存器实际值作为排查基准。还有一个容易忽略的原因如果你用的示波器测的是平均频率而PWM不是连续模式输出比如是单次触发或者突发模式平均频率自然跟理论值差很多。所以在排查时先确认波形是连续的、稳定的方波再谈频率是否准确。5.3 问题三HRTIM和普通定时器PWM的相位同步有朋友的项目里既有HRTIM输出高频PWM又有TIM3输出低频PWM控制继电器这类慢速设备。要求两者每次上电时相位固定。但HRTIM和普通定时器分属不同的时钟总线上电复位后各自独立计数相位关系每次都可能不同。要解决这个问题可以采用同步触发的方式让HRTIM产生一个触发事件通过硬件连线比如触发TRGO送到TIM3的同步输入把TIM3的计数器复位成预设值。H743的定时器之间是有触发矩阵的CubeMX里把它叫TRGO或者ITRx。配置好后每次HRTIM周期到来TIM3都会被硬件复位一次实现相位对齐。这个方案的好处是零CPU占用初始化一次就永久有效。缺点是要多花一点时间研究触发矩阵的映射关系因为H743触发矩阵外设比较多可编程配置不像F1系列那样固定。我的建议是先在CubeMX里把两个定时器的触发关系配出来再生成代码看寄存器定义比直接翻手册快很多。5.4 一个排查用的自查清单遇到HRTIM相关问题按以下顺序排查能把大部分问题快速锁定排查项操作判断标准时钟读RCC-D1CCIPR确认HRTIM时钟源与理论设定一致计数器读对应定时器寄存器CNT数值持续变化时基读PER寄存器与理论计算一致比较值读CMP1寄存器在0到PER之间输出使能读HRTIM_OUTEN对应通道位为1GPIO复用测引脚对地电阻读GPIO寄存器已是复用模式极性配置对比POL寄存器与预期一致故障状态读FLTIN和FLTSTATUS无故障锁存这个清单我在项目里打印出来贴在工作站旁边每次排查都能快速缩小范围。因为HRTIM寄存器多如果没有一个系统化的排查顺序靠东一榔头西一棒子地试很容易把人搞崩溃。6. 最后再分享几个实用技巧6.1 用DMA更新比较值做动态调光/调压如果你想用HRTIM做动态的PWM占空比调整比如软件控制的数字电源、渐变的LED驱动最优雅的方式是DMA比较寄存器。用普通方式在中断里改CMP寄存器也不是不行但中断响应时间、CPU负载都是问题。DMA方式可以实现真正的零CPU干预每隔N个PWM周期DMA自动从内存搬运新的比较值到CMP寄存器。具体配置思路把比较值数组放在一段连续内存里配置DMA请求源为HRTIM的更新事件目的地址是CMP寄存器地址。这样每产生一次更新事件DMA就自动写入下一个值形成连续调压/调光效果。我用这个方式做过一个200kHz的数字电源占空比按指定的曲线变化全程不占用CPU。配置DMA时特别要注意数据宽度CMP寄存器是16位的DMA传输宽度要匹配另外DMA循环模式可以省去重复配置的麻烦。6.2 PLL配置对HRTIM精度的影响HRTIM的精度说白了完全取决于ker_ck的稳定度和准确度。很多人以为用HSI内部高速振荡器就够了反正PWM是数字输出频率偏一点没关系。但在数字电源、同步整流这类场景PWM频率偏差会直接影响输出电压精度和EMI表现。我实测过用HSI时PWM频率在25摄氏度下还算准确温度一高频率能偏出去不少。H743的HSI本身精度一般如果你对频率稳定度有要求考虑用HSE外部晶振作为PLL的参考源。CTPCrystal Trim这类校准功能可以进一步微调。供电也很关键PLL的模拟电路部分如果供电纹波大输出时钟的抖动也会变大最终体现在PWM波形上就是边沿抖动。6.3 高效调试方法用逻辑分析仪代替示波器最后分享一个个人习惯。HRTIM的调试场景里我越来越依赖逻辑分析仪而不是示波器尤其是调试死区、移相、故障保护这些时序逻辑时。逻辑分析仪通道多可以同时抓TA1、TA1N、TB1、故障信号、甚至MCU的中断引脚一屏看全所有时序关系。示波器一般只有2到4个通道看多了就抓瞎。市面上的逻辑分析仪已经很便宜了采样率能到100MHz就足够看绝大部分HRTIM波形。配合上位机的解码功能还能把PWM的频率、占空比、死区时间直接测量出来比在示波器上手动加光标快得多。不过逻辑分析仪只有0和1两个电平看不了模拟特性比如振铃、过冲所以信号完整性分析还是得用示波器。两者配合调试效率能翻倍。说实话HRTIM这个外设在STM32家族里算是比较难啃的骨头之一但啃下来之后的回报也相当可观。高分辨率、灵活同步、硬件故障保护这些能力在做数字电源、高频逆变这类项目时是不可替代的。如果你照着这篇文章的思路配通了第一路PWM后面再加通道、加死区、加移相其实都是同一个套路只不过寄存器更多一点、配置更细一点罢了。最后补一句最实用的个人经验调HRTIM前一定先通读参考手册里HRTIM章节的前几页把时钟结构、定时器结构和输出结构之间的关系记在脑子里。等到实际动手的时候你会发现百分之八十的玄学问题最后都能归因到这三个结构之一的某个细节上。
返回列表