hi,I used a custom circuit board. I didn't refer to any examples. This was a problem that was discovered during the official development of the project. I configured six PWM channels for pulse control. Only this channel had problems, while no such issue occurred on the other sub-modules. I checked and found that only this channel was using the PWM3 module. No other interfering factors were detected. Below is my configuration.
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 = false;
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.faultFilterCount = 7U;
faultFilter.faultGlitchStretch = false;
/* 记录每个 PWM 实例上已配置的 fault channel,避免重复设置 */
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 channel 只设一次) */
{
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;
}
}
/* Fault 时输出低电平 */
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->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);
EnableIRQ(TMR1_IRQn);
EnableIRQ(TMR2_IRQn);
EnableIRQ(TMR3_IRQn);
}static bool APP_PWM_Config_Pulse(axis_ctrl_t *axis,
uint32_t highCnt400M,
uint32_t lowCnt400M)
{
pwm_clock_prescale_t prescale;
uint16_t periodTicks;
uint32_t totalCnt400M = highCnt400M + lowCnt400M;
if ((axis == NULL) || (totalCnt400M == 0U)) return false;
if (!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);
if (highTicks32 == 0U) highTicks32 = 1U;
if (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;
if (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;
if (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 selectedRiseTicks32;
if (maxRiseByHalfTicks32 >= desiredLeadTicks32)
{
selectedRiseTicks32 = desiredLeadTicks32;
if (selectedRiseTicks32 > maxRiseByHalfTicks32)
selectedRiseTicks32 = maxRiseByHalfTicks32;
}
else if (maxRiseByPostGuardTicks32 >= desiredLeadTicks32)
{
selectedRiseTicks32 = desiredLeadTicks32;
if (selectedRiseTicks32 > maxRiseByPostGuardTicks32)
selectedRiseTicks32 = maxRiseByPostGuardTicks32;
}
else
{
selectedRiseTicks32 = maxRiseByPostGuardTicks32;
}
if (selectedRiseTicks32 >= HARD_PWM_LOW_START_PHASE_MIN_TICKS)
{
riseTicks32 = selectedRiseTicks32;
fallTicks32 = riseTicks32 + highTicks32;
}
}
#endif
if (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(prescale);
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);
if (axis->pwmChannel == kPWM_PwmA) {
axis->pwmBase->SM[axis->pwmModule].VAL2 = riseTicks;
axis->pwmBase->SM[axis->pwmModule].VAL3 = fallTicks;
} else if (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;
return true;
}