This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TMS320F280041:使用 ePWM 模块的第一个 PWM 周期出现问题

Guru**** 2692745 points

Other Parts Discussed in Thread: TMS320F280041, C2000WARE

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1597425/tms320f280041-problem-with-first-pwm-period-using-the-epwm-module

器件型号: TMS320F280041
主题: C2000WARE 中讨论的其他器件

您好 TI 社区、

我正在从事 ISR 驱动的 ePWM 实现、以驱动 C2000 MCU 上的 WS2813-Mini LED、这是 TMS320F280041(F28004x 系列、使用支持 C2000Ware DriverLib 和 syscfg 的工程)。  

目标是在 800ns 周期((TBPRD=79 @ 100MHz,向上计数模式)下为“0"(“(300ns 高电平/ 500ns 低电平)和“1"(“(500ns 高电平/ 300ns 低电平)生成一个具有精确占空比的连续比特流。

到目前为止、我实现这一目标的策略是设置一个简单的 ePWM 模块、该模块将在每个周期 (COUNTER = PERIOD) 结束时产生中断。在这个 ISR 中、我们将获取在下一个周期中使用的模块比较器 A 的 CMP 值、以实现所需的 LED 编码。

为此、我决定进行以下 EPWM 初始化:

void EPWM_init()
{
	EPWM_setEmulationMode(LED_DRIVER_BASE, EPWM_EMULATION_FREE_RUN);
	
    EPWM_setClockPrescaler(LED_DRIVER_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1);	
    EPWM_setTimeBasePeriod(LED_DRIVER_BASE, 200);	
	
    EPWM_setTimeBaseCounter(LED_DRIVER_BASE, 0);	
    EPWM_setTimeBaseCounterMode(LED_DRIVER_BASE, EPWM_COUNTER_MODE_UP);	
	
    EPWM_disablePhaseShiftLoad(LED_DRIVER_BASE);	
    EPWM_setPhaseShift(LED_DRIVER_BASE, 0);
	
    EPWM_setCounterCompareValue(LED_DRIVER_BASE, EPWM_COUNTER_COMPARE_A, 0);	
    EPWM_setCounterCompareShadowLoadMode(LED_DRIVER_BASE, EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_ZERO);	
    EPWM_setCounterCompareValue(LED_DRIVER_BASE, EPWM_COUNTER_COMPARE_B, 0);	
    EPWM_setCounterCompareShadowLoadMode(LED_DRIVER_BASE, EPWM_COUNTER_COMPARE_B, EPWM_COMP_LOAD_ON_CNTR_ZERO);
	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPB);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPB);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPA);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPB);	
    EPWM_setActionQualifierAction(LED_DRIVER_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_NO_CHANGE, EPWM_AQ_OUTPUT_ON_TIMEBASE_DOWN_CMPB);
	
    EPWM_setRisingEdgeDelayCountShadowLoadMode(LED_DRIVER_BASE, EPWM_RED_LOAD_ON_CNTR_ZERO);	
    EPWM_disableRisingEdgeDelayCountShadowLoadMode(LED_DRIVER_BASE);	
    EPWM_setFallingEdgeDelayCountShadowLoadMode(LED_DRIVER_BASE, EPWM_FED_LOAD_ON_CNTR_ZERO);	
    EPWM_disableFallingEdgeDelayCountShadowLoadMode(LED_DRIVER_BASE);	
    EPWM_enableInterrupt(LED_DRIVER_BASE);	
    EPWM_setInterruptSource(LED_DRIVER_BASE, EPWM_INT_TBCTR_PERIOD);	
    EPWM_setInterruptEventCount(LED_DRIVER_BASE, 1);
}

每次请求 LED 配置时、都会调用以下函数:  

void ledDriverSendConfig(void)
{
    // Don't start new transmission if one is already in progress
    if (gLedDriver.isTransmitting)
    {
        return;
    }
    
    // Build the CMPA sequence from current configuration
    ledDriverBuildCmpaSequence();
    
    // Initialize transmission state
    gLedDriver.currentIdx = 1U;
    gLedDriver.isTransmitting = true;

    // CRITICAL: Disable motor control ISR during LED transmission
    // This prevents motor ISR from interrupting LED data stream
    Interrupt_disable(INT_ADCC_CONFIG_1); 

    // Make sure counter is frozen to prepare for first PWM period
    EPWM_setTimeBaseCounterMode(LED_DRIVER_BASE, EPWM_COUNTER_MODE_STOP_FREEZE);
    
    // Reset ePWM counter to start fresh
    EPWM_setTimeBaseCounter(LED_DRIVER_BASE, 0U);
    
    // Load first CMPA value
    EPWM_setCounterCompareValue(LED_DRIVER_BASE, 
                                EPWM_COUNTER_COMPARE_A, 
                                gLedDriver.cmpaSequence[0]);
    
    // Clear any pending interrupt flags
    EPWM_clearEventTriggerInterruptFlag(LED_DRIVER_BASE);
    
    // Enable ePWM interrupt to start transmission
    EPWM_enableInterrupt(LED_DRIVER_BASE);
    
    // Start ePWM counter
    EPWM_setTimeBaseCounterMode(LED_DRIVER_BASE, EPWM_COUNTER_MODE_UP);
}

在 ISR 例程中、我运行以下命令:

__interrupt void ledDriverISR(void)
{
    GPIO_writePin(isrLedDriver, 1);
    
    if (gLedDriver.currentIdx < gLedDriver.sequenceSize)
    {
        uint16_t cmpaValue = gLedDriver.cmpaSequence[gLedDriver.currentIdx];
        EPWM_setCounterCompareValue(LED_DRIVER_BASE, EPWM_COUNTER_COMPARE_A, cmpaValue);
        gLedDriver.currentIdx++;
    }
    else
    {
        EPWM_setTimeBaseCounterMode(LED_DRIVER_BASE, EPWM_COUNTER_MODE_STOP_FREEZE);
        EPWM_disableInterrupt(LED_DRIVER_BASE);
        Interrupt_enable(INT_ADCC_CONFIG_1);  // Re-enable motor int
        gLedDriver.isTransmitting = false;
        gLedDriver.currentIdx = 0U;
    }
    
    // Clear ePWM interrupt flag
    EPWM_clearEventTriggerInterruptFlag(LED_DRIVER_BASE);
    
    // Clear PIE interrupt flag for this group
    Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP3);
    
    // Optional: Toggle GPIO for timing measurement
    GPIO_writePin(isrLedDriver, 0);
}

我还在 ePWM(计数器=周期)中断开始时放置了一个 GPIO 切换开关。

但是、我遇到了一些问题:

  1. 问题 1:第一个占空比似乎损坏、只有在一个 PWM 周期后、模块的输出才是所需的输出。 则脉冲宽度是否为 300ns。 我得到以下波形输出:
    1. image.png
    2. 图例:红色波形= ledISR | Orange Waverform = LED 配置信号、EPWM 输出
    3. 分析:
      1. 请注意第一个周期是如何正确的 (1.548us、而不是 300ns)。
      2. 请注意、当 LED 中断被编程为在 COUNTER = PERIOD 时触发时、LED 中断似乎在占空比中间触发、这在后续周期中会发生。
    4. 有什么想法为什么会发生这种情况?
  2. 问题 2:现在我在 2us PWM 周期内工作。 所需的时间实际上是 800ns、我这样做是因为我遇到了一些与__interrupt void ledDriverISR (void) 例程相关的开销问题。 我正在考虑实现 DMA、以减少下一个 CMP 值获取操作开销。 你有什么想法? 我无法想到任何其他方法来通过 PWM 实现该目的、而没有在每个周期结束时使用中断...
     

感谢您的关注。

此致、

Martin Blochre

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Martin:

    您能否确保在 PWM 初始化后启用 TBCLKSYNC? 它应该看起来像下面的伪。

    TBCLKSYNC = 0

    初始化 PWM

    TBCLKYSNC = 1

    您是否仍观察到错误的第一个周期?

    问题 2:现在我的工作时间是 2us PWM 周期。 所需的时间实际上是 800ns、我这样做是因为我遇到了一些与__interrupt void ledDriverISR (void) 例程相关的开销问题。 我正在考虑实现 DMA、以减少下一个 CMP 值获取操作开销。 你有什么想法? 我无法想到任何其他方法来通过 PWM 实现该目的、而没有在每个周期结束时使用中断...
    [/报价]

    DMA 将减少下一个 CMP 值获取开销。 如果您有可用的 DMA、在这种情况下很适合使用。

    此致、

    Ryan Ma