RT1176 PWM启动失败 我使用PWM+故障+QTimer来实现电机脉冲控制,但PWM3子模块0的PWM_A通道偶尔无法启动,在第一个高电平之后就一直保持高电平,后续脉冲不再出现。检查后发现,PWM的“运行”位没有正确设置。即使后来在程序中添加了重复启动操作,这种异常情况仍然发生。 Re: RT1176 PWM startup failed 你好@liu626 ,
你用的是定制板还是EVK板?如果你使用的是 EVK,你对它进行过任何修改吗?
能否分享一下您使用的PWM3配置?
您是使用示例作为参考吗?如果是,是哪一个?
如果尝试使用仅包含 PWM3 的项目来重现该问题,问题是否仍然存在?
这种情况是否只发生在 PWM3 子模块 0 PWM_A 通道上?其他PWM模块或子模块是否也出现过这种情况?
是否还有其他任务或中断会操作 PWM3 寄存器,从而影响或覆盖运行位?
此致, 巴勃罗 Re: RT1176 PWM startup failed 您好,我使用的是定制电路板。我没有举任何例子。这是在项目正式开发过程中发现的问题。我配置了六个PWM通道用于脉冲控制。只有这个通道出现了问题,其他子模块都没有出现此类问题。我检查后发现,只有这个通道使用了 PWM3 模块。未检测到其他干扰因素。以下是我的配置。 静态 axis_ctrl_t g_axes[AXIS_NUM] = { { 。ID= AXIS_X1,.name= "X1", .pwmBase= PWM1,.pwmModule= kPWM_Module_0,.pwmChannel= kPWM_PwmA, .tmrBase= TMR3,.lowCh= kQTMR_Channel_2,.highCh= kQTMR_Channel_3, .tmrInputsrc=kQTMR_ClockCounter2InputPin,.cascadePcs= 6U, 故障编号= 0U,.hwExactSupported= true,.outTrigMask= kPWM_ValueRegisterMask_3, }, { 。ID= AXIS_X2,.name= "X2", .pwmBase= PWM2,.pwmModule= kPWM_Module_0,.pwmChannel= kPWM_PwmA, .tmrBase= TMR2,.lowCh= kQTMR_Channel_0, .highCh= kQTMR_Channel_1, .tmrInputsrc=kQTMR_ClockCounter0InputPin,.cascadePcs= 4U, 故障编号= 0U,.hwExactSupported= true,.outTrigMask= kPWM_ValueRegisterMask_3, }, { 。ID= AXIS_Y,.name= "Y", .pwmBase= PWM3,.pwmModule= kPWM_Module_0,.pwmChannel= kPWM_PwmA, .tmrBase= TMR3,.lowCh= kQTMR_Channel_0, .highCh= kQTMR_Channel_1, .tmrInputsrc=kQTMR_ClockCounter0InputPin,.cascadePcs= 4U, 故障编号= 0U,.hwExactSupported= true,.outTrigMask= kPWM_ValueRegisterMask_3, }, { 。ID= AXIS_Z,.name= "Z", .pwmBase= PWM4,.pwmModule= kPWM_Module_0,.pwmChannel= kPWM_PwmA, .tmrBase= TMR1,.lowCh= kQTMR_Channel_0, .highCh= kQTMR_Channel_1, .tmrInputsrc=kQTMR_ClockCounter0InputPin,.cascadePcs= 4U, 故障编号= 0U,.hwExactSupported= true,.outTrigMask= kPWM_ValueRegisterMask_3, }, { 。ID= AXIS_EX1,.name= "EX1", .pwmBase= PWM1,.pwmModule= kPWM_Module_1,.pwmChannel= kPWM_PwmA, .tmrBase= TMR1,.lowCh= kQTMR_Channel_2,.highCh= kQTMR_Channel_3, .tmrInputsrc=kQTMR_ClockCounter2InputPin,.cascadePcs= 6U, 故障编号= 1U,.hwExactSupported= true,.outTrigMask= kPWM_ValueRegisterMask_3, }, { 。ID= AXIS_EX2,.name= "EX2", .pwmBase= PWM2,.pwmModule= kPWM_Module_1,.pwmChannel= kPWM_PwmA, .tmrBase= TMR2,.lowCh= kQTMR_Channel_2,.highCh= kQTMR_Channel_3, .tmrInputsrc=kQTMR_ClockCounter2InputPin,.cascadePcs= 6U, 故障编号= 1U,.hwExactSupported= true,.outTrigMask= kPWM_ValueRegisterMask_3, }, };static void APP_Init_PWM_QTMR(void) { pwm_config_t pwmConfig; pwm_fault_param_t faultConfig; qtmr_config_t qtmrConfig; PWM_GetDefaultConfig(&pwmConfig); pwmConfig.pairOperation= kPWM_Independent; pwmConfig.reloadLogic = kPWM_ReloadImmediate; PWM_FaultDefaultConfig(&faultConfig); faultConfig.faultLevel = true; faultConfig.enableCombinationPath= false; faultConfig.faultClearingMode= kPWM_手动安全; faultConfig.recoverMode = kPWM_NoRecovery; QTMR_GetDefaultConfig(&qtmrConfig); CLOCK_EnableClock(kCLOCK_Qtimer1); CLOCK_EnableClock(kCLOCK_Qtimer2); CLOCK_EnableClock(kCLOCK_Qtimer3); PWM_StopTimer(PWM1, 0x0FU); PWM_StopTimer(PWM2, 0x0FU); PWM_StopTimer(PWM3, 0x0FU); PWM_StopTimer(PWM4, 0x0FU); pwm_fault_input_filter_param_t faultFilter; 故障过滤器.故障过滤器周期= 255U; faultFilter.faultFilterCount= 7U; faultFilter.faultGlitchStretch= false; /* 记录每个PWM实例上已配置的故障通道,避免重复设置 */ uint16_t pwm1FaultDone = 0U, pwm2FaultDone = 0U, pwm3FaultDone = 0U, pwm4FaultDone = 0U; for (uint8_t i = 0U; i < AXIS_NUM; i++) { axis_ctrl_t *ax = &g_axes[i]; PWM_Init(ax->pwmBase, ax->pwmModule, &pwmConfig); PWM_SetupFaults(ax->pwmBase, (pwm_fault_input_t)ax->faultNum, &faultConfig); /* 故障提示(每个 PWM 实例的每个故障通道只设置一次) */ { uint16_t *faultDone; 如果 (ax->pwmBase == PWM1) faultDone = &pwm1FaultDone; 否则如果 (ax->pwmBase == PWM2) faultDone = &pwm2FaultDone; 否则如果 (ax->pwmBase == PWM3) faultDone = &pwm3FaultDone; 否则 faultDone = &pwm4FaultDone; uint16_t faultBit = (uint16_t)(1U << ax->faultNum); 如果 ((*faultDone & faultBit) == 0U) { PWM_SetupFaultInputFilterExt(ax->pwmBase, (pwm_fault_channels_t)ax->faultNum, &faultFilter); *faultDone |= faultBit; } } /* 故障时输出低电平 */ ax->pwmBase->SM[ax->pwmModule].OCTRL &= ~(PWM_OCTRL_PWMAFS_MASK | PWM_OCTRL_PWMBFS_MASK); APP_PWM_Unmap_Selected_Fault(ax); APP_PWM_ClearFault_Safe(ax); ax->pwmBase->SM[ax->pwmModule].INIT = 0U; ax->pwmBase->SM[ax->pwmModule].VAL0 = 0U; ax->pwmBase->SM[ax->pwmModule].VAL1 = 1U; ax->pwmBase->SM[ax->pwmModule].VAL2 = 0U; ax->pwmBase->SM[ax->pwmModule].VAL3 = 0U; ax->pwmBase->SM[ax->pwmModule].VAL4 = 0U; ax->pwmBase->SM[ax->pwmModule].VAL5 = 0U; ax->pwmBase->SM[ax->pwmModule].TCTRL = PWM_TCTRL_OUT_TRIG_EN(ax->outTrigMask); APP_PWM_Disable_Output(ax); 如果 (ax->hwExactSupported) { qtmrConfig.primarySource= ax->tmrInputSrc; QTMR_Init(ax->tmrBase, ax->lowCh, &qtmrConfig); QTMR_Init(ax->tmrBase, ax->highCh, &qtmrConfig); ax->tmrBase->CHANNEL[ax->lowCh].CTRL = TMR_CTRL_CM(kQTMR_PriSrcRiseEdge) | TMR_CTRL_PCS(ax->tmrInputSrc); ax->tmrBase->CHANNEL[ax->highCh].CTRL = TMR_CTRL_CM(kQTMR_CascadeCount) | TMR_CTRL_PCS(ax->cascadePcs); APP_QTMR_Disable_Low_OFLAG_Output(ax); QTMR_DisableInterrupts(ax->tmrBase, ax->lowCh, 0xFFU); QTMR_DisableInterrupts(ax->tmrBase, ax->highCh, 0xFFU); QTMR_ClearStatusFlags(ax->tmrBase, ax->lowCh, 0xFFU); QTMR_ClearStatusFlags(ax->tmrBase, ax->highCh, 0xFFU); } ax->phase = kAxisIdle; ax->armed = false; ax->running = false; ax->done = true; #if HARD_PWM_STATE_GUARD_ENABLE APP_StateGuard_Reset(ax); #endif } /* 使能 QTMR 中断 */ NVIC_SetPriority(TMR1_IRQn, 2U); NVIC_SetPriority(TMR2_IRQn, 2U); NVIC_SetPriority(TMR3_IRQn, 2U); 启用IRQ(TMR1_IRQn); 启用IRQ(TMR2_IRQn); 启用IRQ(TMR3_IRQn); }static bool APP_PWM_Config_Pulse(axis_ctrl_t *axis, uint32_t highCnt400M, uint32_t lowCnt400M) { pwm_clock_prescale_t 预分频; uint16_t periodTicks; uint32_t totalCnt400M = highCnt400M + lowCnt400M; 如果 ((axis == NULL) || (totalCnt400M == 0U)) 返回 false; 如果 (!APP_PWM_SelectPrescaler_FromPeriodCnt400M(totalCnt400M, &prescale, &periodTicks)) 返回 false; uint32_t highTicks32 = (uint32_t)(((uint64_t)periodTicks * (uint64_t)highCnt400M + ((uint64_t)totalCnt400M / 2ULL)) / (uint64_t)totalCnt400M); 如果 (highTicks32 == 0U) highTicks32 = 1U; 如果 (highTicks32 >= periodTicks) highTicks32 = (uint32_t)periodTicks - 1U; uint32_t riseTicks32 = 0U; uint32_t fallTicks32 = highTicks32; #if HARD_PWM_LOW_START_PHASE_ENABLE uint32_t lowTicks32 = (uint32_t)periodTicks - highTicks32; 如果 (lowTicks32 >= (2U * HARD_PWM_LOW_START_PHASE_MIN_TICKS)) { uint32_t desiredLeadCnt400M = highCnt400M; uint32_t minLeadCnt400M = (uint32_t)HARD_PWM_LOW_START_PHASE_MIN_US * 400U; 如果 (desiredLeadCnt400M < minLeadCnt400M) desiredLeadCnt400M = minLeadCnt400M; uint32_t desiredLeadTicks32 = (uint32_t)(((uint64_t)periodTicks * (uint64_t)desiredLeadCnt400M + ((uint64_t)totalCnt400M / 2ULL)) / (uint64_t)totalCnt400M); 如果 (desiredLeadTicks32 < HARD_PWM_LOW_START_PHASE_MIN_TICKS) desiredLeadTicks32 = HARD_PWM_LOW_START_PHASE_MIN_TICKS; uint32_t maxRiseByHalfTicks32 = lowTicks32 / 2U; uint32_t postGuardTicks32 = (uint32_t)(((uint64_t)periodTicks * (uint64_t)(HARD_PWM_VAL3_POST_LOW_GUARD_US * 400U) + ((uint64_t)totalCnt400M / 2ULL)) / (uint64_t)totalCnt400M); 如果 (postGuardTicks32 < HARD_PWM_VAL3_POST_LOW_GUARD_MIN_TICKS) postGuardTicks32 = HARD_PWM_VAL3_POST_LOW_GUARD_MIN_TICKS; uint32_t maxRiseByPostGuardTicks32 = (lowTicks32 > postGuardTicks32) ? (lowTicks32 - postGuardTicks32):0U; uint32_t selectedRiseTicks32; 如果 (maxRiseByHalfTicks32 >= desiredLeadTicks32) { selectedRiseTicks32 = desiredLeadTicks32; 如果 (selectedRiseTicks32 > maxRiseByHalfTicks32) selectedRiseTicks32 = maxRiseByHalfTicks32; } 否则如果(maxRiseByPostGuardTicks32 >= desiredLeadTicks32) { selectedRiseTicks32 = desiredLeadTicks32; 如果 (selectedRiseTicks32 > maxRiseByPostGuardTicks32) selectedRiseTicks32 = maxRiseByPostGuardTicks32; } 别的 { selectedRiseTicks32 = maxRiseByPostGuardTicks32; } 如果 (selectedRiseTicks32 >= HARD_PWM_LOW_START_PHASE_MIN_TICKS) { riseTicks32 = selectedRiseTicks32; fallTicks32 = riseTicks32 + highTicks32; } } #endif 如果 (fallTicks32 >= (uint32_t)periodTicks) fallTicks32 = (uint32_t)periodTicks - 1U; uint16_t riseTicks = (uint16_t)riseTicks32; uint16_t fallTicks = (uint16_t)fallTicks32; PWM_SetPwmLdok(axis->pwmBase, APP_PwmModuleMask(axis), false); uint16_t ctrl = axis->pwmBase->SM[axis->pwmModule].CTRL; ctrl &= (uint16_t)(~PWM_CTRL_PRSC_MASK); ctrl |= PWM_CTRL_PRSC(预分频); axis->pwmBase->SM[axis->pwmModule].CTRL = ctrl; axis->pwmBase->SM[axis->pwmModule].INIT = 0U; axis->pwmBase->SM[axis->pwmModule].VAL0 = 0U; axis->pwmBase->SM[axis->pwmModule].VAL1 = (uint16_t)(periodTicks - 1U); 如果 (axis->pwmChannel == kPWM_PwmA) { axis->pwmBase->SM[axis->pwmModule].VAL2 = riseTicks; axis->pwmBase->SM[axis->pwmModule].VAL3 = fallTicks; } 否则如果 (axis->pwmChannel == kPWM_PwmB) { axis->pwmBase->SM[axis->pwmModule]。VAL4 = riseTicks; axis->pwmBase->SM[axis->pwmModule].VAL5 = fallTicks; } axis->pwmBase->SM[axis->pwmModule].TCTRL = PWM_TCTRL_OUT_TRIG_EN(axis->outTrigMask); PWM_SetPwmLdok(axis->pwmBase, APP_PwmModuleMask(axis), true); PWM_SetPwmLdok(axis->pwmBase, APP_PwmModuleMask(axis), true); axis->pwmConfigValid = true; axis->cachedHighCnt400M = highCnt400M; axis->cachedLowCnt400M = lowCnt400M; 返回 true; } Re: RT1176 PWM startup failed 你好@liu626 ,
您能否帮我找出配置问题,并测试一下仅初始化 PWM3 时问题是否仍然存在?
在查看了您分享的配置后,为了尝试重现该行为,我有以下问题:
axis_ctrl_t 的定义是什么?
您的应用程序中是否启用了 HARD_PWM_LOW_START_PHASE_ENABLE 和 HARD_PWM_STATE_GUARD_ENABLE?
以下APP功能是做什么用的?
APP_PWM_Unmap_Selected_Fault APP_PWM_ClearFault_Safe APP_PWM_禁用输出 APP_QTMR_Disable_Low_OFLAG_Output APP_StateGuard_Reset APP_PWM_SelectPrescaler_FromPeriodCnt400M APP_PwmModuleMask 此致, 巴勃罗 Re: RT1176 PWM startup failed 感谢你的回复。我使用的 GPIO 引脚是 rt1176 的 GPIO_EMC_B1_29。“axis_ctrl_t”是与电机轴相关的定义。 * PWM 输出脉冲 -> 触发 XBAR 信号 -> 脉冲下降沿驱动 QTMR 的外部时钟输入 -> * QTMR 32 位级联计数器递减 1 -> 当计数器达到 0 时,触发 QTMR 比较中断 -> 中断内部关闭 PWM * 同时,QTMR 触发硬件故障,直接拉低 PWM 的物理输出引脚。。ID= AXIS_Y, 。姓名= "Y", /* 用于串口日志记录或断点调试,以识别 Y 轴 */ /* ================= PWM 输出资源分配 ================= */ .pwmBase= PWM3, /* Y轴使用FlexPWM3模块 */ .pwm模块= kPWM_Module_0, /* 使用 PWM3 的子模块 0(每个 PWM 有 4 个子模块,编号从 0 到 3) */ .pwm通道= kPWM_PwmA, /* 使用子模块 0 的 A 相输出引脚,对应于物理引脚 GPIO_EMC_B1_29 */ /* ================= 32 位 QTMR 级联计数资源分配 ================= */ .tmrBase= TMR3, /* Y轴使用QTMR3外设(对应于IRQ中断号TMR3_IRQn) */ 低= kQTMR_Channel_0, /* 16 位低计数器:使用 TMR3 的通道 0 */ .highCh= kQTMR_Channel_1,/* 16 位高位计数器:使用 TMR3 的通道 1 */ /* 👉 .lowCh 和 .highCh它们成对出现,在硬件层面上,它们会自动组合成一个 32 位计数器。 /* ================= 硬件级联时钟源配置(核心关键) ================= */ .tmrInputsrc=kQTMR_ClockCounter0InputPin, /* 通道 0 的时钟源:设置为“外部引脚输入 0”。 在 XBAR 配置中,PWM3 脉冲的下降沿通过 XBAR 连接到 TMR3 通道 0 的输入引脚。 因此,每次 PWM 输出脉冲时,通道 0(16 位)将执行一次减量操作。*/ .cascadePcs = 4U, /* 高 16 位通道(通道 1)的时钟源 (PCS) 配置。 在 i.MX RT 的 QTMR 寄存器中,PCS 值对应如下: 0-3 = 外部引脚输入; 4 = 通道 0 的溢出/终止事件; 5 = 通道 1 的溢出/终止事件; 6 = 通道 2 的溢出/终止事件; 7 = 通道 3 的溢出/终止事件。 这里配置为 4U,意思是:让“TMR3 的通道 1”监测“TMR3 的通道 0 的溢出事件”。 已启用 HARD_PWM_LOW_START_PHASE_ENABLE 和 HARD_PWM_STATE_GUARD_ENABLE。APP_PWM_Unmap_Selected_Fault 功能:移除与当前轴对应的故障引脚映射。调用底层 PWM_SetupFaultDisableMap 函数来禁用故障。目的是为了:在不需要硬件故障的情况下,防止意外的外部干扰导致 PWM 关闭,从而方便底层调试过程。 APP_PWM_ClearFault(原始代码函数) 功能:清除 PWM 模块的故障状态标志(axis->pwmBase->FSTS)。触发硬件故障后,必须先清除此标志,然后才能允许 PWM 重新启动。 APP_PWM_禁用输出 功能:立即停止 PWM 定时器,强制引脚输出低电平(PWM_SetPwmForceOutputToZero),并启用输出。它的目的是确保在电机启动之前,引脚不会因为浮空或处于默认状态而输出高电平,这可能会导致电机运动不规则。 APP_QTMR_Disable_Low_OFLAG_Output 功能:禁用 QTMR 的 OFLAG(状态标志)输出,并强制 OFLAG 处于低电平。它与 XBAR 硬件配合使用,其作用是切断 QTMR 输出到 PWM 故障引脚的路径,防止在启动前意外触发故障。 APP_StateGuard_Reset 功能:清除国家卫队(看门狗)的计数器和旗帜。如果启用 HARD_PWM_STATE_GUARD_ENABLE 时系统遇到长时间无脉冲变化或死锁,则此功能用于在停止或重新启动系统之前重置这些保护变量。 APP_PWM_SelectPrescaler_FromPeriodCnt400M 功能:这是一个非常核心的底层频率计算函数。它接收高电平 + 低电平的总 400MHz 计数值,并计算应设置多少个预分频器 (PRSC) 和 PWM 周期 (PERIOD) 才能放入 16 位 PWM 寄存器中。请注意:如果计算出的周期超过 65535 且除法系数超出范围,则此函数将返回 false,导致电机无法启动。 APP_PwmModuleMask 功能:将 PWM 子模块的索引号(例如,值为 0 的 kPWM_Module_0)转换为左移 N 位的位掩码。NXP 的许多 PWM 寄存器(例如 OUTEN、MCTRL)都是逐位控制的。该函数负责生成正确的二进制掩码。 Re: RT1176 PWM startup failed 我找到了原因。这是因为 cm4 内核中 PIT 中断的内部负载过重,影响了 cm7 内核的 PWM 操作。降低 PIT 中断的负载后,PWM 启动失败的问题就消失了。
記事全体を表示