RT1176 PWMの起動に失敗しました 私はPWM + Fault + 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 こんにちは、カスタム回路基板を使用しました。私は具体的な例を挙げませんでした。これは、プロジェクトの正式な開発過程で発見された問題だった。パルス制御用に6つのPWMチャネルを設定しました。このチャネルだけが問題を起こし、他のサブモジュールでは同様の問題はありませんでした。調べたところ、このチャネルだけがPWM3モジュールを使っていることがわかりました。他に干渉因子は検出されなかった。以下は私の設定です。 static 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、 .faultNum= 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、 .faultNum= 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、 .faultNum= 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、 .faultNum= 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、 .faultNum= 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、 .faultNum= 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.enableCombinationalPath= 偽; faultConfig.faultClearingMode= kPWM_ManualSafety; 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; faultFilter.faultFilterPeriod= 255U; faultFilter.aultFilterCount= 7U; faultFilter.faultGlitchStretch= 偽; /* 记录每个 PWM 实例上已配置的 faultチャネル,避免重复设置 */ 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 实例的每个 faultチャネル 只设一次) */ { uint16_t *faultDone; if (ax->pwmBase == PWM1) faultDone = &pwm1FaultDone; else if (ax->pwmBase == PWM2) faultDone = &pwm2FaultDone; else if (ax->pwmBase == PWM3) faultDone = &pwm3FaultDone; else faultDone = &pwm4FaultDone; uint16_t faultBit = (uint16_t)(1U << ax->faultNum); if((*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); if (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->カスケードPcs); 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->位相 = kAxisIdle; ax->armed = false; ax->running=false; ax->done = 真; #if HARD_PWM_STATE_GUARD_ENABLE APP_StateGuard_Reset(斧); #endif } /* 使能 QTMR 中断 */ NVIC_SetPriority(TMR1_IRQn、2U); NVIC_SetPriority(TMR2_IRQn、2U); NVIC_SetPriority(TMR3_IRQn、2U); EnableIRQ(TMR1_IRQn); EnableIRQ(TMR2_IRQn); EnableIRQ(TMR3_IRQn); }静的ブールAPP_PWM_Config_Pulse(axis_ctrl_t *軸、 uint32_t highCnt400M、 uint32_t 低Cnt400M) { pwm_clock_prescale_tプリスケール; uint16_t期間ダックス; uint32_t totalCnt400M = highCnt400M + lowCnt400M; もし((軸 == NULL) ||(totalCnt400M == 0U))Return false; もし(!APP_PWM_SelectPrescaler_FromPeriodCnt400M(totalCnt400M、およびprescale、&periodTicks)) Return 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 LeadCnt400M = 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); if (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); if(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 RiseTicks32; もし (maxRiseByHalfTicks32 >= desiredLeadTicks32) { selectedRiseTicks32 = desiredLeadTicks32; if(selectedRiseTicks32 > maxRiseByHalfTicks32) selectedRiseTicks32 = maxRiseByHalfTicks32; } そうでなければ(maxRiseByPostGuardTicks32 >= desiredLeadTicks32) { selectedRiseTicks32 = desiredLeadTicks32; if (selectedRiseTicks32 > maxRiseByPostGuardTicks32) selectedRiseTicks32 = maxRiseByPostGuardTicks32; } そうでなければ { selectedRiseTicks32 = maxRiseByPostGuardTicks32; } if (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); もし(軸>pwmチャンネル== 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(軸>pwmBase、APP_PwmModuleMask(軸)、真); PWM_SetPwmLdok(軸>pwmBase、APP_PwmModuleMask(軸)、真); axis->pwmConfigValid = true; 軸>キャッシュドHighCnt400M = highCnt400M; axis->cachedLowCnt400M = lowCnt400M; 真を返す; } Re: RT1176 PWM startup failed こんにちは、 @liu626 さん。
設定を分離して、PWM3だけを初期化しても問題が続くかテストするのを手伝ってもらえますか?
ご提供いただいた設定を確認し、その動作を再現するために、以下の質問があります。
axis_ctrl_t の定義は何ですか?
アプリケーションでHARD_PWM_LOW_START_PHASE_ENABLEとHARD_PWM_STATE_GUARD_ENABLEは有効になっていますか?
以下のアプリ機能はどのような働きをしますか?
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 には 0 から 3 までの 4 つのサブモジュールがあります) */ .pwmChannel= kPWM_PwmA, /* サブモジュール0のフェーズAの出力ピンを使用します。これは物理ピンGPIO_EMC_B1_29に対応します。 */ /* ================= 32 ビット QTMR カスケード カウント リソース割り当て ================= */ .tmrBase= TMR3, /* Y軸はQTMR3ペリフェラル(IRQ割り込み番号TMR3_IRQnに対応します)を使用します */ .lowCh= 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 機能:これは周波数計算の非常に重要な基本機能です。これは、High + Lowの合計400MHzカウント値を受け取り、16ビットPWMレジスタに収まるように設定すべきプリスケーラ(PRSC)とPWM周期(PERIOD)の数を計算します。ご注意ください:計算された周期が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起動時の不具合が解消しました。
View full article