尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STM32G431霍尔传感器模式联动PWM定时器失效排查:从XOR到TRGO的寄存器级调试

STM32G431霍尔传感器模式联动PWM定时器失效排查:从XOR到TRGO的寄存器级调试 1. 问题现象与背景霍尔信号进去了PWM却没反应先说说我为什么会碰这个问题。最近在做一个基于STM32G431KB的无刷直流电机BLDC驱动器电机端装了三路霍尔传感器用来反馈转子位置。控制策略不复杂霍尔信号决定换相时序PWM定时器负责输出六路互补PWM驱动半桥。板子打样回来后我先把霍尔传感器模式也就是很多人说的TIMER XOR模式调通用调试器看寄存器三路霍尔信号的状态、边沿、计数器的变化全部正常。但问题来了PWM定时器就是不动输出引脚始终是固定电平好像触发信号根本没送过去。这个坑我折腾了两天翻遍了参考手册和论坛最后发现原因不止一个而且很多坑是官方文档和CubeMX不会直接告诉你的。所以这篇博文我就把这次排查过程完整记录下来从原理到配置再到寄存器级调试尽可能把“为什么没触发”这个问题讲清楚。如果你也在用G431的霍尔传感器模式去同步PWM定时器这篇内容应该能帮你省下不少时间。先交代一下硬件环境主控STM32G431KBLQFP32封装主频170MHz霍尔传感器三路接到TIM1的CH1、CH2、CH3引脚PWM输出TIM8高级定时器控制三相半桥开发环境STM32CubeMX 6.x STM32CubeG4固件库 IAR不过这里要先说清楚一个概念也是很多人理解偏差的地方所谓“TIMER XOR模式”在STM32的参考手册里其实不叫XOR模式准确名称是“霍尔传感器模式”Hall Sensor Mode。它做的事情是把TIM1的CH1、CH2、CH3这三个通道的输入信号通过一个异或门合并成一个信号再送到定时器的输入捕获单元。所以你在CubeMX里找不到叫XOR的选项正确的选项名称就是Hall Sensor Mode或者Combined PWM Mode。搞清楚这个概念后面查问题才能有的放矢。2. 霍尔传感器模式到底做了什么一句话讲透XOR的底层逻辑2.1 硬件级异或TI1、TI2、TI3如何变成一路信号STM32G431KB的高级定时器TIM1和TIM8都支持这个功能。打开参考手册搜“Hall Sensor”或者“XOR”关键字你会找到这样的描述当TIMx_CR2寄存器的TI1S位Timer Input Selection置1时TIMx_CH1、CH2、CH3三个引脚上的信号会被内部异或门合并然后作为TIMx_CH1通道的输入信号。也就是说后面所有的捕获、触发、比较逻辑看到的都只是三路信号的异或结果而不是某一单独通道的信号。这里有个容易误解的点CH2和CH3的引脚仍然有输入功能能检测电平但它们不再参与独立的输入捕获通道逻辑。所以你如果用HAL的捕获回调去读CH2、CH3的边沿在这个模式下是得不到期望结果的。我自己最开始调的时候也是先单独测了三路霍尔信号都正常然后开了霍尔模式就发现CH2的捕获回调再也没触发过——后来才意识到三路信号已经合并成一路了。2.2 XOR信号与PWM定时器的联系方式TRGO与ITR的桥梁霍尔传感器模式的输出结果不会自动送到另一个定时器。它只是改变了TIM1内部输入通道的输入源。要让PWM定时器比如TIM8跟着霍尔信号同步需要走一条完整的链路TIM1的XOR信号产生跳变沿TIM1的输入捕获单元检测到边沿后根据配置可以产生捕获事件或者更新事件TIM1的主模式Master Mode Selection把某个事件映射到TRGOTrigger Output引脚或者内部触发线ITRxTIM8作为从定时器通过内部触发输入ITR0、ITR1等接收到这个TRGO信号TIM8配置为外部时钟模式1External Clock Mode 1或者触发模式Trigger Mode把这个信号作为时钟或者触发源TIM8开始计数输出PWM。问题往往就出在第3步和第4步的连接关系上。比如TIM1的TRGO要送到TIM8TIM8的ITR输入编号是几这个在参考手册的“TIMx internal trigger connection”表里有明确说明但CubeMX图形化界面不一定能帮你做对后面我会详细讲。2.3 为什么用XOR信号来同步BLDC控制的真实需求可能有人会问直接把三路霍尔信号分别接到三个定时器通道在中断里做换相不是更直观吗为什么要多此一举搞异或原因在于控制精度和CPU占用。BLDC控制中不管电机转速多快霍尔信号的边沿都代表一个确定的转子位置六个扇区的切换都必须精确发生在正确的电角度。如果用中断轮询或者软件判断中断响应延迟、代码执行时间抖动都会导致换相角度偏移转速越高越明显。而硬件XOR加定时器同步可以让PWM定时器在霍尔边沿到来时硬件自动完成周期重载、计数清零或者捕获新位置完全不需要CPU干预。这就是硬件定时器同步的价值。我在实际测试中也验证了这一点电机转速到3000rpm时霍尔信号频率大约600Hz6极对、每圈6个扇区软件中断方案在负载突变时换相误差明显变大切换到硬件同步方案后波形干净了很多。3. 排查一PWM定时器的时钟源根本没配置为外部触发3.1 我的初始配置为什么看起来全对但实际上错了第一次配置TIM8时我的思路是这样的TIM1作为主定时器工作在霍尔传感器模式下捕获霍尔信号的边沿TIM8作为从定时器产生三相互补PWM。为了让霍尔边沿能驱动TIM8我把TIM8的Slave Mode设为External Clock Mode 1触发源选的是ITR0然后启动TIM8。代码如下// TIM8 从模式配置 TIM_SlaveConfigTypeDef sSlaveConfig {0}; sSlaveConfig.SlaveMode TIM_SLAVEMODE_EXTERNAL1; sSlaveConfig.InputTrigger TIM_TS_ITR0; sSlaveConfig.TriggerPolarity TIM_TRIGGERPOLARITY_RISING; sSlaveConfig.TriggerFilter 0; if (HAL_TIM_SlaveConfigSynchro(htim8, sSlaveConfig) ! HAL_OK) { Error_Handler(); }看起来没问题对吧外部时钟模式1就是让TIM8的计数器在检测到ITR0上升沿时加1。但实测发现TIM8的CNT寄存器纹丝不动。问题在哪在ITR0对应的内部触发源上。我把TIM8的ITR0当成了TIM1的TRGO但这是错的。我查了参考手册的触发连接表G431系列上TIM8的ITR0连的是TIM1的TRGO这确实没错。但关键是我之前把TIM1的TRGO输出配置成了更新事件Update Event而TIM1在霍尔传感器模式下只有输入捕获事件或触发事件才能反映霍尔信号变化。如果我配置的是更新事件那TIM1的更新事件完全可能因为计数器配置不当而没有产生。3.2 正解TIM1的主模式输出到底应该选什么这里我才意识到霍尔传感器模式下要让边沿信号有效传递应该把TIM1的Master Mode设为Trigger Output也就是TRGO由触发事件产生而不是Update。具体操作是配置TIM1的CR2寄存器的MMS[2:0]位段// 选择 TRGO 由触发事件产生 htim1.Instance-CR2 ~TIM_CR2_MMS; htim1.Instance-CR2 | TIM_CR2_MMS_2; // MMS100Trigger如果你用的是HAL库可以在初始化TIM1的MasterConfig时设置sMasterConfig.MasterOutputTrigger TIM_TRGO_TRIGGER;这一点在CubeMX里不太好发现因为CubeMX的“Master Output Trigger”是个下拉框里面有很多选项Enable、Update、OC1、OC1REF、OC2、OC2REF……默认是Update必须改成Trigger才有意义。提示想搞清楚MMS字段每个取值对应哪个TRGO源找参考手册的“TIMx_CR2 register”章节看MMS位段描述。这是硬件调试的必修课。改完之后我再测TIM8的CNT发现它会在每个霍尔边沿加1了PWM也开始输出了。没有从模式配置时序、触发信号链接这层理解光看CubeMX配置很难发现问题。3.3 外部时钟模式1和触发模式的区别什么时候该用哪个排查过程中我还发现了另外一个知识点。TIM8的从模式除了External Clock Mode 1还可以选Trigger Mode触发模式。这两种模式容易搞混它们的差别非常大外部时钟模式1SMS111每个触发边沿计数器加1。适合用来记录事件数量或者作为外部事件计数器。触发模式SMS110触发边沿只是把计数器的工作状态从暂停切换到运行之后计数器继续按照内部时钟PSC/ARR计数。适合用外部信号来控制PWM输出什么时候开始干活。回到BLDC场景你更可能是想让TIM8在霍尔边沿到来时重新开始一轮PWM周期而不是每个霍尔边沿只计一次数。所以我最终用的是Trigger Mode配合PWM生成模式效果是霍尔信号一来TIM8的计数器从0重新开始输出对齐到新的换相位置。如果你用的是外部时钟模式1你会发现PWM周期和霍尔边沿频率完全一样输出根本不是正常的PWM波形。4. 排查二内部触发源ITR连接关系一个极易踩的坑4.1 ITR0、ITR1、ITR2、ITR3到底对应哪个定时器STM32的定时器内部触发连接不是所有ITR输入都对应同一个主定时器。不同的从定时器ITR编号对应的主定时器不同。以STM32G431KB为例TIM8的触发输入连接是这样的不同芯片可能有差异务必以对应型号参考手册的表格为准从定时器ITR0ITR1ITR2ITR3TIM1TIM5TIM2TIM3TIM4TIM8TIM1TIM2TIM3TIM4注意看TIM8的ITR0才是TIM1。如果你不小心选了ITR1那连的是TIM2搞半天肯定没有信号到达。我在第一次配置时用的是CubeMX的STSEL下拉框它默认给的是ITR0看起来没错。但如果你是手动写寄存器或者从别的项目拷贝代码这一步非常容易出错务必对照表格确认。另外并非所有定时器都支持霍尔传感器XOR模式。TIM1和TIM8可以但TIM2、TIM3、TIM4这些通用定时器没有这个功能。G431的HRTIM高分辨率定时器里面也有类似功能的输入选项但配置方式不同不是同一个寄存器。4.2 手动寄存器配置的写法主从定时器链路打通的标准代码为了避免各位抄错我把完整的寄存器级配置流程贴出来。这里假设TIM1是霍尔传感器模式主定时器TIM8是PWM从定时器。第一步配置TIM1的霍尔传感器模式// 使能 TIM1 时钟已经在RCC阶段完成 // 1. 开启 TI1S 位启用 Hall Sensor XOR 模式 TIM1-CR2 | TIM_CR2_TI1S; // 2. 配置 CH1 为输入捕获模式直接映射到 TI1 TIM1-CCMR1 ~TIM_CCMR1_CC1S; TIM1-CCMR1 | TIM_CCMR1_CC1S_0; // CC1S01TI1 // 3. 配置 CH1 输入滤波和预分频 // IC1F0不滤波IC1PSC0不分频 TIM1-CCMR1 ~TIM_CCMR1_IC1F; TIM1-CCMR1 ~TIM_CCMR1_IC1PSC; // 4. 使能捕获设置上升沿触发 TIM1-CCER ~TIM_CCER_CC1P; TIM1-CCER | TIM_CCER_CC1E; // 5. 配置主模式TRGO 由触发事件产生 TIM1-CR2 ~TIM_CR2_MMS; TIM1-CR2 | TIM_CR2_MMS_2; // MMS100Trigger // 6. 使能 TIM1 TIM1-CR1 | TIM_CR1_CEN;第二步配置TIM8的触发模式// 1. 配置从模式触发模式SMS110输入触发源为 ITR0对应TIM1 TIM8-SMCR ~TIM_SMCR_SMS; TIM8-SMCR | (0x6 TIM_SMCR_SMS_Pos); // 2. 配置触发源选择TS000ITR0 TIM8-SMCR ~TIM_SMCR_TS; TIM8-SMCR | (0x0 TIM_SMCR_TS_Pos); // 3. 配置触发极性为上升沿如果信号边沿反了可以换下降沿 TIM8-SMCR ~TIM_SMCR_ETP; TIM8-SMCR | TIM_SMCR_ETP; // 根据实际极性调整 // 4. 配置 PWM 模式1向上计数输出极性高有效 TIM8-CCMR1 ~TIM_CCMR1_OC1M; TIM8-CCMR1 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; // PWM mode 1 TIM8-CCMR1 | TIM_CCMR1_OC1PE; // 预装载使能 TIM8-CCER ~TIM_CCER_CC1P; TIM8-CCER | TIM_CCER_CC1E; // 使能输出 // 5. 设置 ARR 和 CCR1 TIM8-ARR 999; // PWM 周期 TIM8-CCR1 500; // 占空比 50% // 6. 更新事件加载预装载寄存器 TIM8-EGR | TIM_EGR_UG; // 7. 使能 TIM8 TIM8-CR1 | TIM_CR1_CEN;注意一个细节TIM8必须先使能从模式再使能计数器顺序反了会导致第一个触发边沿丢失。4.3 一个奇特的案例ITR触发边沿极性对不上的排查继续分享一个实战中踩到的坑。霍尔传感器的输出电平不同电机驱动器设计可能不同有的高电平对应N极有的高电平对应S极。对TIM1来说三种霍尔信号经过异或后边沿方向取决于此刻三个输入的具体状态组合。如果主定时器输出的TRGO边沿与你从定时器设置的触发极性不一致从定时器就永远收不到有效触发。我在测试这个现象时一度怀疑是ITR连接的问题后来用调试器观察TIM8的SMCR寄存器发现ETP位触发极性设的是上升沿但霍尔信号的异或结果在高转速下上升沿和下降沿不对称霍尔元件安装位置偏差导致。这时候只认上升沿可能会丢事件。一个稳妥做法是把TIM8的触发极性设为“任意边沿”或者选择一个合适的霍尔传感器信号源保证边沿质量。G431的高级定时器还支持把ETP设为高电平或低电平触发如果你的霍尔信号是电平方式比如电机停止时转子位置固定在某区可以改成电平触发试试。5. 排查三霍尔输入信号质量与滤波参数触发失效的隐形杀手5.1 霍尔信号为什么会有毛刺安装、上拉、干扰三座大山这是很多人忽略的一点。霍尔传感器本身是开漏输出需要外部上拉电阻。如果你上拉电阻选得太大比如100kΩ信号的上升沿会变得很缓在边沿附近产生振铃。而STM32定时器的输入捕获是数字电路对电平跳变有迟滞要求如果信号在阈值附近来回抖动就会产生多个触发脉冲或者根本识别不到有效边沿。我在调试中发现用示波器连接霍尔输出引脚能看到明显的抖动和振铃尤其是电机启动瞬间电流大的时候。后来给每路霍尔信号加了RC低通滤波1kΩ串联10nF对地并且把上拉电阻从10kΩ换成了4.7kΩ波形干净了很多。这里建议调试时先不接电机用信号发生器或手动拨动霍尔元件测试XOR模式是否正常排除EMI干扰因素。5.2 输入捕获滤波参数IC1F怎么选不是越大越好TIM1的输入捕获单元自带一个可编程滤波器IC1F4位宽度可设1~15作用是对输入信号进行N次采样确认滤除小于N个时钟周期的毛刺。这个参数在CubeMX和HAL库中都可以配置。但这里有个两难滤波太深会引入额外延迟滤波太浅又滤不掉毛刺。我的经验是从IC1F0开始调用示波器看TIM1的捕获事件计数是否稳定如果出现多计数或者漏计数再逐渐增大。对于低速电机霍尔信号频率低于1kHzIC1F4到6基本够用高速电机10kHz以上IC1F建议不要超过2否则延迟会导致换相角度偏移。实际测试中我发现IC1F对上升沿的影响比下降沿更明显。因为霍尔信号的开漏输出上升沿靠上拉电阻慢慢拉高本身就比下降沿缓。所以如果滤波器设置过大可能出现“只触发下降沿不触发上升沿”的假象看起来就像PWM定时器没被触发。5.3 三路霍尔信号电平一致性异或结果的隐藏问题TIM1霍尔传感器模式异或门是三路输入的布尔异或。如果三路霍尔信号不是严格的方波或者存在相位偏差异或结果可能产生非常窄的脉冲。这个窄脉冲能不能被TIM1捕获到取决于输入滤波和预分频器的设置。有时候霍尔信号本身没问题但异或后产生的脉冲宽度小于定时器的最小捕获脉宽也会丢事件。我建议排查时用示波器直接看TIM1_CH1引脚也就是合并后的XOR信号的波形而不是只看单路霍尔信号。如果XOR信号出现很窄的毛刺或异常脉冲先处理霍尔信号本身再谈定时器配置。这一点在做霍尔传感器模式调试时很重要因为XOR信号在芯片内部你没法直接量测只能通过比对三个输入信号的波形来推测。6. 排查四CubeMX配置里容易埋雷的地方尤其注意从模式6.1 CubeMX中TIM8的Slave Mode选项你真的选对了吗在CubeMX中进入TIM8配置界面Slave Mode下拉框有几个选项Disabled、External Clock Mode 1、External Clock Mode 2、Reset Mode、Gated Mode、Trigger Mode、External Clock Mode 1 Trigger Mode。很多人在这里纠结我也有过。不同的功能对应不同的Slave Mode没有选对触发链路就不通。如果你想让PWM定时器在霍尔边沿到来时重新计数一次并且每个霍尔信号周期内完整输出一个PWM周期建议选择Trigger Mode同时把主定时器的TRGO映射为Trigger事件。这样做的效果是霍尔边沿触发了TIM8的计数器启动之后按你配置的ARR和PSC跑完整个PWM周期如果下个霍尔边沿还没来计数器会继续循环跑如果提前来了计数器会重新开始。这里我测试了两种工作场景电机恒速运行时霍尔信号周期稳定使用Trigger Mode确实能让PWM和霍尔信号对齐波形很漂亮。电机加速或减速时霍尔信号周期变化较大使用Trigger Mode可能会导致PWM周期不稳定因为ARR没有动态调整。这种情况下通常会用“Reset Mode”复位从模式让TIM8的计数器在霍尔边沿到来时立即清零强制对齐到0。但Reset Mode必须配合主定时器的OCxREF或更新事件作为TRGO源不能随便配。6.2 最容易忽视的一步TIM1的TRGO信号需要真正“拉出去”前面说过TIM1的Main Output TriggerMMS位决定了TRGO输出的是哪个内部事件。但还有一个隐含条件在STM32G4系列中TIM1和TIM8属于高级控制定时器它们的TRGO通常要经过一个额外的开关才能输出到内部触发网络。检查RM0440手册可以看到TIM1的TRGO在内部触发网络里的使能条件往往要和TIMx_CR2的MMS配合。如果你在CubeMX配置时只设置了定时器参数但忘记在“Mapping”里把TRGO映射出来内部触发链路可能根本不通。比较常见的现象是单独看TIM1的所有寄存器都正常但TIM8就是等不到信号。我在实际调试中用调试器watch TIM8的SMCR寄存器发现SMS位即使设成了Trigger Mode但因为没有正确的TRGO输出状态一直没变化。6.3 HAL库函数初始化顺序先开后从还是先从后主HAL库的初始化顺序也影响触发链路。我建议先初始化从定时器TIM8再初始化主定时器TIM1或者两个都初始化完后再开始使能。因为如果主定时器先跑起来霍尔信号边沿已经产生了不少但从定时器还没配置好这些触发事件就丢掉了。反过来从定时器先初始化主定时器后使能这样主定时器一开始输出边沿从定时器就能立即响应。CubeMX生成的代码里初始化顺序按照你在图形界面中的添加顺序排列。你可以在Main.c中调整MX_TIM1_Init()和MX_TIM8_Init()的调用顺序或者在两个Init之后、启动之前把初始化状态都准备好再依次调用HAL_TIM_PWM_Start()和HAL_TIM_Base_Start()。这看起来是个细节但在实际调试中确实遇到因为顺序不对导致第一个霍尔边沿丢失的情况。另外一个容易忽略的是HAL_TIM_Base_Start_IT()和HAL_TIM_PWM_Start()的区别。如果你用的是PWM输出模式必须调用HAL_TIM_PWM_Start()使能PWM输出只调用HAL_TIM_Base_Start()不会输出PWM波形。7. 调试方法利器寄存器级定位触发链路的完整思路7.1 从简到繁的排查顺序用调试器和示波器把链路一分为三遇到这种“主定时器数据正常但从定时器不触发”的问题我的排查顺序一般分三步第一步确认主定时器TIM1的霍尔传感器模式是否真的生效。读寄存器TIM1-CR2的TI1S位是否为1读TIM1-SMCR的SMS位确认主定时器是否配置为从模式霍尔传感器模式通常还需要把主定时器配置为Reset Mode或者Gated Mode视CubeMX版本而定。如果TI1S1但SMS位被清0异或功能可能没完全生效。第二步确认TRGO输出是否正常。在调试器中观察事件或者用一个不用的定时器比如TIM6作为辅助观测把TIM6的触发源设为TIM1的TRGO看TIM6的计数器是否动作。如果TIM6能计数说明TIM1的TRGO已经产生了有效的内部触发事件问题出在TIM8这一侧。如果TIM6也不动那问题在主定时器侧。第三步确认从定时器TIM8的触发源配置。读TIM8-SMCR的TS位确认指向ITR0读SMS位确认从模式不是Disabled读CR1的CEN位确认计数器已使能。把这三个寄存器的值记录下来和期望值一一比对基本能定位90%的问题。7.2 我实际用到的寄存器检查表格我把当时排查用的寄存器检查项整理成一张表方便你照着查检查项寄存器/位段期望值说明TIM1 霍尔模式使能TIM1-CR2.TI1S1启用CH1/CH2/CH3异或输入TIM1 TRGO源TIM1-CR2.MMS100Trigger让TRGO由触发事件产生TIM1 计数器使能TIM1-CR1.CEN1主定时器运行TIM8 从模式TIM8-SMCR.SMS110Trigger Mode从模式配置TIM8 触发源TIM8-SMCR.TS000ITR0TIM8的ITR0对应TIM1TIM8 触发极性TIM8-SMCR.ETP根据信号极性上升/下降沿匹配TIM8 计数器使能TIM8-CR1.CEN1从定时器运行TIM8 PWM输出使能TIM8-CCER.CC1E1使能通道输出这张表我每次调试定时器同步问题时都会用。贴一张在屏幕旁边对着寄存器一个个看效率极高。7.3 示波器怎么测三条探头的接法示波器在定位这类问题时也是必需品。我推荐三条探头通道1接霍尔传感器的一路输出比如Hall A观察原始信号质量。通道2接TIM1的CH1引脚但注意这个引脚在霍尔模式下输出的是异或信号吗不对CH1引脚仍然是霍尔信号输入芯片内部才有异或。所以通道2没法直接测到XOR输出但你可以测TIM1的TRGO引脚需要把TRGO映射到某个引脚如果有引出的话。通道3接TIM8的PWM输出引脚观察是否出波形。如果你的板子没有把TRGO引出来可以用调试器观察TIM1的捕获/更新事件计数来间接判断。总之示波器和调试器配合把信号链路分成“霍尔信号→TIM1异或→TIM1 TRGO→TIM8触发→PWM输出”五段一段段确认问题基本能锁定。8. 终极经验总结霍尔传感器模式PWM定时器的五个关键检查点8.1 检查点一主定时器MMS有没有选Trigger这是最容易被忽略的地方。CubeMX默认MMS是Update但霍尔传感器模式下TRGO必须由触发事件也就是捕获事件或者更新事件产生。如果你选的是Update且更新事件不产生后续链路全部白搭。另外要注意G4系列的高级定时器有两个主模式输出选项一个是MasterOutputTriggerMMS另一个是BreakOutputMOE。MOE位如果没有使能高级定时器的PWM输出引脚可能呈高阻态这也是一个容易踩的坑。调试时如果发现PWM引脚没有输出检查一下TIM8-BDTR寄存器的MOE位是否为1。8.2 检查点二ITR编号有没有对应错TIM8的ITR0是对应TIM1的TIM1的ITR0对应的是TIM5不要搞混。如果你在CubeMX里选“ITR0”要看当前配置的是哪个从定时器不能一概而论。我建议每次做定时器同步时都手动查一遍参考手册的触发连接表不要凭记忆。8.3 检查点三霍尔传感器模式启动了但CubeMX没把从模式自动配好CubeMX中配置TIM1的霍尔传感器模式它会自动把TIM1设为从模式但不会自动配置TIM8。所以你必须去TIM8的配置页里手选从模式。如果你用的是HAL库还需要手动调用HAL_TIM_SlaveConfigSynchro()来初始化从模式。这个API的调用顺序在HAL库的文档里没有特别强调但在实际工程中一定在HAL_TIM_PWM_Start()之前调用否则会报断言失败或者配置不生效。8.4 检查点四输入滤波和预分频导致的事件丢失我前面说过IC1F参数过大会导致边沿被过滤掉。另外TIM1的输入捕获预分频器IC1PSC是配置成不分频还是2分频、4分频、8分频如果霍尔信号频率很高且预分频设置过大会丢失事件。低速电机一般不需要分频但高速电机如果发现TIM1的捕获计数不对可以考虑把预分频调大然后根据分频系数在软件里换算。调试技巧先用一个固定频率的信号发生器接到TIM1_CH1验证捕获链路是否正常。这时候把预分频和滤波都设为0用信号发生器依次输出100Hz、1kHz、10kHz方波看捕获计数值是否符合预期。如果这些基本测试都通过再接霍尔传感器。8.5 检查点五PWM输出极性、死区与霍尔信号换相逻辑的配合最后即使PWM定时器能被触发换相逻辑和PWM极性如果不对电机依然转不好。霍尔传感器模式的三个输入信号经过异或后只能告诉定时器“转子位置发生了变化”但具体是哪个扇区、哪一路PWM应该开通还需要软件根据三路霍尔信号的状态来判断。所以我通常的做法是TIM1做霍尔传感器XOR模式负责产生位置变换触发事件TIM8负责产生PWM信号另外再配置一个通用定时器比如TIM2做输入捕获同时捕获三路霍尔信号的状态在捕获中断里做换相表查询。硬件同步和软件判断并行这样既保证了PWM周期的对齐精度又能准确换相。9. 一个完整的实战示例TIM1霍尔传感器模式同步TIM8 PWM输出9.1 系统初始化的完整流程下面这个示例不是理论推演是我在实际项目中跑通的完整配置。主定时器TIM1工作在霍尔传感器模式从定时器TIM8工作在Trigger Mode输出三相互补PWM。先初始化TIM8void MX_TIM8_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig {0}; TIM_SlaveConfigTypeDef sSlaveConfig {0}; htim8.Instance TIM8; htim8.Init.Prescaler 0; htim8.Init.CounterMode TIM_COUNTERMODE_UP; htim8.Init.Period 9999; // 100kHz PWM at 170MHz, 但实际由触发控制 htim8.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim8.Init.RepetitionCounter 0; htim8.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; HAL_TIM_PWM_Init(htim8); // 从模式Trigger Mode触发源 ITR0TIM1 sSlaveConfig.SlaveMode TIM_SLAVEMODE_TRIGGER; sSlaveConfig.InputTrigger TIM_TS_ITR0; sSlaveConfig.TriggerPolarity TIM_TRIGGERPOLARITY_RISING; sSlaveConfig.TriggerFilter 0; HAL_TIM_SlaveConfigSynchro(htim8, sSlaveConfig); // PWM 通道配置 sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 5000; sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim8, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(htim8, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_ConfigChannel(htim8, sConfigOC, TIM_CHANNEL_3); // 死区设置例如 1us sBreakDeadTimeConfig.OffStateRunMode TIM_OSSR_ENABLE; sBreakDeadTimeConfig.OffStateIDLEMode TIM_OSSI_ENABLE; sBreakDeadTimeConfig.LockLevel TIM_LOCKLEVEL_OFF; sBreakDeadTimeConfig.DeadTime 100; sBreakDeadTimeConfig.BreakState TIM_BREAK_DISABLE; sBreakDeadTimeConfig.BreakPolarity TIM_BREAKPOLARITY_HIGH; sBreakDeadTimeConfig.BreakFilter 0; sBreakDeadTimeConfig.AutomaticOutput TIM_AUTOMATICOUTPUT_DISABLE; HAL_TIMEx_ConfigBreakDeadTime(htim8, sBreakDeadTimeConfig); }再初始化TIM1void MX_TIM1_Init(void) { TIM_SlaveConfigTypeDef sSlaveConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; TIM_IC_InitTypeDef sConfigIC {0}; htim1.Instance TIM1; htim1.Init.Prescaler 0; htim1.Init.CounterMode TIM_COUNTERMODE_UP; htim1.Init.Period 0xFFFF; htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter 0; htim1.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_IC_Init(htim1); // 霍尔传感器模式设置 CH1/CH2/CH3 异或 TIM1-CR2 | TIM_CR2_TI1S; // 输入捕获通道1映射到 TI1 sConfigIC.ICPolarity TIM_INPUTCHANNELPOLARITY_RISING; sConfigIC.ICSelection TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler TIM_ICPSC_DIV1; sConfigIC.ICFilter 0; HAL_TIM_IC_ConfigChannel(htim1, sConfigIC, TIM_CHANNEL_1); // 主模式TRGO 由触发事件产生 sMasterConfig.MasterOutputTrigger TIM_TRGO_TRIGGER; sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_ENABLE; HAL_TIMEx_MasterConfigSynchronization(htim1, sMasterConfig); // 启动输入捕获中断方式 HAL_TIM_IC_Start_IT(htim1, TIM_CHANNEL_1); }最后在主函数中启动int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM1_Init(); MX_TIM8_Init(); // 优先启动主定时器但要确保从定时器已初始化完成 HAL_TIM_Base_Start(htim1); HAL_TIM_PWM_Start(htim8, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim8, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim8, TIM_CHANNEL_3); while (1) { // 换相逻辑放在 TIM1 的捕获中断中处理 } }9.2 中断里做什么换相逻辑和PWM输出的联动TIM1的捕获中断函数中读取CCR1寄存器的值可以知道霍尔信号XOR后的边沿与上次边沿之间的时间差这个时间差值反映了电机的瞬时转速。根据三路霍尔信号的状态组合去查询换相表然后修改TIM8各个通道的CCR值实现换相。void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM1) { uint16_t hall_state ReadHallState(); // GPIO读取三路霍尔 uint16_t pwm_tab[6][3]; // 换相表根据电机设计定义 uint8_t sector GetSectorFromHall(hall_state); // 调整 TIM8 三个通道的占空比 __HAL_TIM_SET_COMPARE(htim8, TIM_CHANNEL_1, pwm_tab[sector][0]); __HAL_TIM_SET_COMPARE(htim8, TIM_CHANNEL_2, pwm_tab[sector][1]); __HAL_TIM_SET_COMPARE(htim8, TIM_CHANNEL_3, pwm_tab[sector][2]); } }这里要注意HAL_TIM_IC_CaptureCallback是在中断上下文执行的代码要尽量精简。不要把耗时操作比如浮点运算、printf日志放在里面否则会影响霍尔信号的响应。我建议把换相结果先写到缓冲区在主循环中统一更新或者至少用volatile变量把计算量最小化。9.3 启动顺序的终极方案避免首次触发丢失经过反复测试我发现最稳妥的启动顺序是初始化TIM8从定时器但从模式先不使能。初始化TIM1主定时器开启霍尔传感器模式。调用HAL_TIM_SlaveConfigSynchro()配置TIM8的从模式。启动TIM8的PWM输出。最后启动TIM1。为什么这样排因为从定时器必须先配置好接收触发信号的状态如果主定时器先启动霍尔产生的第一个、第二个边沿可能已经过去了而从定时器还在初始化中触发事件就会丢失。即使后来启动了从定时器也等不到第一个边沿只能等下一个霍尔边沿。在某些电机位置这个等待时间可能很长表现就是电机启动延迟几十毫秒甚至更久。10. 常见问题速查表照着查少走弯路下面这张表是我把这次调试过程中遇到的所有问题整理出来的包含问题现象、可能原因、解决方向。你可以直接截图保存作为排查参考。问题现象可能原因解决方向TIM8计数器不动从模式未配置检查SMCR的SMS和TS位TIM8计数器不动TRGO源不对检查TIM1的CR2的MMS位TIM8计数器不动ITR编号不对对照参考手册触发连接表TIM8计数器一次只加1使用了外部时钟模式1改为Trigger Mode或Reset ModeTIM8输出引脚无波形MOE位未使能检查TIM8-BDTR寄存器的MOE位TIM8输出PWM但周期不对ARR被触发源覆盖检查从模式配置和主定时器频率霍尔信号正常但捕获计数异常输入滤波太深减小ICF或ICPS霍尔信号正常但捕获计数异常三路霍尔电平不一致检查上拉电阻和信号质量霍尔信号有毛刺上拉电阻过大减小上拉电阻或加RC滤波频繁进入HardFaultHAL初始化顺序不对调整TIM8初始化在TIM1之前霍尔事件丢失导致PWM失步触发极性不匹配修改SMCR的ETP位如果你遇到的是表中没有的情况建议先用调试器确认TIM1和TIM8的SMCR、CR2、CCER、BDTR这几个寄存器的值逐一截图对比基本能定位到问题。11. 关于G431与F103/其他系列的区别参考价值说明顺便提一下STM32G431KB的霍尔传感器模式原理上和F103系列的TIM1/TIM8是类似的。但有一个区别需要注意G4系列的内核主频更高170MHz定时器时钟预分频配置和F103可能不同另外G4系列的TIM1/TIM8支持更多的主模式选项主模式寄存器和F103也有细微差别。所以如果你之前是在F103上调通的代码移植到G431时务必重新检查CR2的MMS配置和SMCR的触发源选择。我这次就是从F103的项目迁移过来的一开始以为代码可以直接用结果在这上面栽了跟头希望各位引以为戒。还有一点G4系列部分型号带HRTIM高分辨率定时器它也能处理霍尔传感器模式但配置方式和TIM1/TIM8完全不同而且HRTIM的寄存器更复杂。我的建议是能用TIM1/TIM8解决就不要轻易上HRTIM除非你的PWM频率需要精确到几十皮秒级否则TIM1/TIM8的精度定时器时钟周期约5.88ns170MHz已经足够绝大多数电机控制场景。12. 写在最后的一个小技巧最后分享一个我从这次调试中总结的小技巧如果怀疑触发链路不通最快的验证方法是利用DAC或者一个GPIO引脚把某个内部事件“引”出来观察。比如配置一个普通GPIO在TIM1的捕获中断里翻转这个GPIO用示波器看这个引脚的频率就知道霍尔信号XOR后的边沿频率是多少。再在TIM8的更新中断里翻转另一个GPIO就能看到TIM8是否真的在跑。这种方法比单纯看寄存器直观得多尤其在调试电机控制时序时两个引脚的相位关系一眼就能看明白。我这次就是把这两个“探针引脚”加到了板子上后来很多时序问题都是通过观察它们的波形定位的。硬件调试不是只能靠逻辑分析仪简单GPIO加示波器就是最实用的工具。希望这篇内容对你有帮助祝你的霍尔传感器模式PWM调试一次通过。
返回列表