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.

[参考译文] MSP430F2617:具有4ms 延迟问题的 PWM 输出

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1438789/msp430f2617-pwm-output-with-4ms-delay-issues

器件型号:MSP430F2617
主题中讨论的其他器件:DRV8955

工具与软件:

您好!

我会尽力解释我的最终目标、但我可能无法正确解释、因此请耐心等待。  

我将尝试使用 MSP430上的一个奇异引脚、生成"延迟"PWM 信号、其中信号开始处于高电平(3.3V)并持续4ms、然后开始根据我所需的频率和占空比产生脉冲。 这将基于来自外部源的 GPIO 输入生成。

我的方法是:

1)设置计时器 B (对应于 P4.3)

-在向上(MC_1)模式下设置 CCR0和 CCR3

-通过与 CCIE 或操作比较/控制寄存器来初始化它们的中断

-初始化相应的引脚到 GPIO 引脚

2)设置计时器 A (仅使用此计时器计数到4ms)

-设置 CCR0以保持值32000、@8MHz DCOCLK 这应该等于4ms

-按照与 Timer B 相同的方式初始化中断

3)设置所有相应的 GPIO、在本例中、我使用的是 P6.5 (是的、我知道、我应该已经将这个信号路由到 P1或 P2、以便我可以使用硬件中断、但我是通过软件轮询来实现的、这可能是我无法使其正常工作的问题)

-将 P6.5初始化为下拉的 GPIO 输入

4)在我的 main 中、使用 while (1)创建一个无限循环、  

if (TRIGGER_INPUT)->使用 P4OUT |= BIT3将 P4.3设置为高电平->激活计时器 A ->在计时器 A IRQ 中、使用 P4SEL |= BIT3将 P4.3设置为 PWM 引脚->激活计时器 B ->在 TB 中激活 CCR0和 CCR3的 IRQ 之间触发器。

这似乎不起作用,或者,更确切地说,它似乎"有时"起作用。

让我们简单介绍一下:我将尝试使用此 PWM 信号作为 DRV8955的输入、DRV8955是一款电机驱动芯片、具有4个 PWM 输入和4个 PWM 输出。 出于某种原因、在输出上实际 PWM 之前、我似乎看不到带有"延迟"的初始 GPIO 脉冲。 此外-无论出于何种原因、我的初始延迟仅为1.5ms 而不是4ms、我不清楚原因。 下面是将时钟设置为8MHz 的代码:

DCOCTL = CALDCO_8MHZ;//更改 DCO = MCLK = 8 MHz
BCSCTL1 = CALBC1_8MHZ | XTS;//基本时钟系统控制寄存器

任何有关如何正确执行此操作的见解都很棒! 我可以张贴我的整个代码以及去任何细微差别,但我不想使这篇文章太多的一篇文章。

提前感谢您、这一直是我目前处理的一个问题、已有近两个星期、没有任何希望。  

我所需的波形如下所示:

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

    您好!

    [报价 USERID="622603" URL"~/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1438789/msp430f2617-pwm-output-with-4ms-delay-issues "]if (TRIGGER_INPUT)->使用 P4OUT |= BIT3将 P4.3设置为高电平->激活计时器 A ->在计时器 A IRQ 中、使用 P4SEL |= BIT3将 P4.3设置为 PWM 引脚->激活计时器 B ->在 CCR0和 CCR3的 IRQ 之间触发 TB。

    在我这边、这个过程看起来是正确的。  您能展示一下您现在得到的波形吗? 我们可以更好地了解代码的哪一部分发生了故障。  

    我最好在这里发送您的代码。 我还建议先将延迟函数和 PWM 函数分离出来进行测试。 因为您无法获得正确的4ms 脉冲。 您可以禁用 Timer B 并仅测试 Timer A 功能。  

    此致、

    Cash Hao

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

    尊敬的 Cash:

    不幸的是,我的答复是比我所希望的更晚-我没有机会测试只是计时器 A

    我该怎么做呢? 我的第一个尝试是做如下:

    #include <msp430.h> 
    #include "msp430f2617.h"
    #include <init.h> //Typically initPWM, initParams, and Initialize() are all in init.h
    
    //I omitted Initalize() because all it does is initialize all unused ports as outputs
    
    
    void initPWM()
    {
        // -- Interrupt Initialization -- //
        // TA = Counter
        // TB = PWM
        TA0CCTL0 |= CCIE;        // CCIE enables interrupts when initialized (CCRA0)
        //TA0CCTL2 |= CCIE;
        TB0CCTL0 |= CCIE;        // Period register for Timer B
        TB0CCTL3 |= CCIE;        // Duty cycle register for Timer B - P4.3
        //TA0CCTL2 &= ~CCIFG;
        TA0CCTL0 &= ~CCIFG;      // Clear interrupt flag when initialized
        TB0CCTL0 &= ~CCIFG;
        TB0CCTL3 &= ~CCIFG;
    
    
        __bis_SR_register(GIE);
    }
    
    void initParams()
    {
        WDTCTL = WDTPW | WDTHOLD;       //Stop Watch Dog Timer (WDT)
    
        DCOCTL = CALDCO_8MHZ;           //Change DCO = MCLK = 8 MHz
        BCSCTL1 = CALBC1_8MHZ | XTS;    //Basic clock system control register
        //BCSCTL2 = DIVS_0;
    
    
    
        // -- GPIO Initialization --
    
        //Status LED
        P6DIR |= BIT5;                  //P6.5: Micro status LED
        set_MCU_status_lo;
    
        //DRV Fault LED
        P1DIR |= BIT4;                  //P1.4: Fault LED
        clear_DRV_fault;
    
        //nSLEEP (DRV Sleep)
        P3DIR |= BIT4;                  //P3.4: nSLEEP
        sleep_DRV;
    
    
        //VREF (Output) is P6.6 / DAC0
        //Making it a standard GPIO Out for testing
        P6DIR |= BIT6;
        P6SEL &= BIT6;
        P6OUT |= BIT6;
    
        //24V TRIG IN
        P6DIR &= ~BIT4;                 //P6.4: MSP_TRIG_IN
        P6SEL &= ~BIT4;                 //GPIO mode
        P6REN |= BIT4;                  //resistor pull up/down enabled
        P6OUT &= ~BIT4;                 //pull down - this will keep the value of P6.4 LOW until a signal arrives
    
    
        // -- PWM Initialization -- (P4.1 - P4.3 are not used currently) //
    
    
        // We have two timers - P4.0 for driving and P2.2 for counting to 4ms //
    
        //P4.0, IN1 output
        P4DIR |= BIT0;                  //P4.0: IN1
        P4SEL &= ~BIT0;
        P4OUT &= ~BIT0;                 //Default to LOW
    
    
        //P4.1, IN2 output
        P4DIR |= BIT1;                  //P4.1: IN2
        P4SEL &= ~BIT1;
        P4OUT &= ~BIT1;                 //Default to LOW
    
    
        //P4.2, IN3 output
        P4DIR |= BIT2;                  //P4.2: IN3
        P4SEL &= ~BIT2;
        P4OUT &= ~BIT2;                 //Default to LOW
    
    
        //P4.3, IN4 output
        P4DIR |= BIT3;                  //P4.3: IN4
        P4SEL &= ~BIT3;                 //Makes it a GPIO pin
        P4OUT &= ~BIT3;                 //Default to LOW
    
    
        //P2.3, Timer used for counting to 4ms
        P2DIR |= BIT3;                  //Using this timer only for counting
        P2OUT &= ~BIT3;                 //Set to low initially
    
        // Active timer settings //
        //dT = N / fPWM; N is the count below, fPWM * dT = N -> 8MHz * (time_elapsed) = Period
    
        TB0CCR0 = 200;                          //Period
        TB0CCR3 = 50;                    //Duty cycle
        // 50 / 200 = 25 % Duty
    
        TA0CCR0 = 32000;                    //Period
        //TA0CCR2 = 0;                      //Duty cycle
        // Counts for 4ms @ 8MHz MCLK
    
        //Set the timer modes
        //TA0CCTL2 |= OUTMOD_7;
        TA0CCTL0 |= OUTMOD_7;            //PWM Mode - Set, should correspond to P2.2
        TB0CCTL3 |= OUTMOD_7;            //PWM Mode - Reset/set, should correspond to P4.0
    }
    
    
    
    
    int main(void) // V1.0.1 hardware
    {
    
        Initialize();   //Initialize the MSP430, in general, see init.c for more
        initParams();   //Set the parameters for our configuration
        initPWM();
    
        
        while(1)
        {
            //read_trig_in = P6IN & BIT4
            if(read_trig_in)
            {
                set_MCU_status_hi;  //Are we on? (LED on my board)
                
                set_IN4_gpio;       //Initial output (P4.3 output)
    
                set_IN4_hi; //Start the 4ms pulse
    
                TACTL |= MC_1 + TBSSEL_2 + ID_0;    //Enable timer A
    
                //TBCTL |= MC_1 + TBSSEL_2 + ID_0;
    
            }
    
            else if((P4OUT & BIT3) && read_trig_in)
            {
                //do nothing
                __no_operation();
                //continue;
            }
    
            // Exit if our trigger is not active
            else
            {
    
                set_IN4_gpio;   //Set back to GPIO mode
                set_IN4_lo;
                clear_timer_A;
                set_MCU_status_lo;
                initPWM(); 
            }
    
            //set_IN4_lo;
        }
    
    
    }
    
    // ISRs //
    
    
    // 4ms counter //
    #pragma vector = TIMER0_A0_VECTOR
    __interrupt void ISR_TA0_CCR0(void)
    {
        if(read_trig_in)
        {
    
            set_IN4_lo; //This ends the pulse and allows me to check timing
    
    
            TA0CCTL0 &= ~CCIFG;                  //clears interrupt flag for Timer A.0
    
            //stop_timer_A;   //Do I need this?
    
            TA0CCTL0 &= ~CCIE;
            
        }
        else
        {
            clear_timer_A;
        }
    
    }
    

    抱歉、代码行很多、我认为其中很多代码可能是多余的。  

    这里设置 TA0CCR0来计数4ms 似乎无法正常工作。 你知道为什么吗? 我认为我对计时器块的理解有一点缺陷。

    这是我在4.3上获得的波形:  

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

    "输出模式2、3、6和7对输出单元0来说是无用的、因为 EQUn = EQU0。"

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

    啊、所以 CCR0似乎需要在 CCTL 中设置不同的 OUTMOD。 我也曾在 TA0CCTL0上使用 OUTMOD_1尝试过此操作、但看到的波形相同。

    此处的理念是:
    1) 1)将输出设置为高电平

    2) 2)启动计时器

    3)在计时器中断期间(经过4ms 后)、将输出置于低电平

    这似乎没有在这里发生。 有一个小的反向脉冲发生在大约1.5ms、我不清楚为什么。

    我甚至是否需要设置 OUTMOD 以仅使用计时器进行计数? 我没有使用连接到 TA0CCR0的这个特定引脚。

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

    您好!

    在调试项目时、您的代码可以命中 TA 中断例程吗?  

    因为在代码中、我没有看到您在代码中启用 GIE、像__bis_SR_register (LPM0_bits + GIE);  

    此致、

    Cash Hao

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

    我将全局中断移到了 main 的开头、以前它是在 initPWM()中。

    我能够点击 TA0和 TA1中断、我将使用它们来开始和结束4ms 脉冲。 它似乎工作正常、但仅在重新启动后的第一个周期中工作。 您能否看一下、看看您是否能知道为什么它只能运行一次? 我似乎不能想象这个。 我想它与 CCTL 寄存器中的中断启用有关。

    这里的变量 aDone 用于表示4ms 脉冲何时结束、以便每个外部触发器输入只发生一次。 我略微修改了 while 环路:  

        while(1)
        {
            //sleep_DRV;
            // Do stuff only if our trigger is active
            if(read_trig_in && (aDone == 0))
            {
                set_MCU_status_hi;  //Are we on?
    
                TACTL |= MC_1 + TASSEL_2 + ID_0 ;    //activates Timer A?
            }
    
            else if(read_trig_in && (aDone))
            {
                //do nothing
                if(aDone)
                    set_IN4_pwm;
    
                set_MCU_status_lo;
                //continue;
            }
    
            // Exit if our trigger is not active
            else
            {
    
                sleep_DRV;  //Sleep the motor driver
                set_IN4_gpio;   //Set back to GPIO mode
                set_IN4_lo;
                clear_timer_A;
                clear_timer_B;
                //__delay_cycles(1000000);
                //clear_DRV_fault;
                set_MCU_status_lo;
                aDone = 0;
                initPWM();
            }
        }

    以下是有效的 ISR:

    #pragma vector = TIMER0_A0_VECTOR
    __interrupt void ISR_TA0_CCR0(void)
    {
        if(read_trig_in)
        {
    
            set_IN4_lo;
            
    
            //stop_timer_A;   //Do I need this?
    
            TB0CTL |= MC_1 + TBSSEL_2 + ID_0;    //idk
            TA0CCTL0 &= ~CCIFG;                  //clears interrupt flag for Timer A.0
    
    
            TA0CCTL0 &= ~CCIE;
            aDone = 1;
        }
        else
        {
            clear_timer_A;
            set_IN4_gpio;
            aDone = 0;
        }
    
    }
    
    #pragma vector = TIMER0_A1_VECTOR
    __interrupt void ISR_TA0_CCR2(void)
    {
        if(read_trig_in)
        {
            set_IN4_hi;
            TA0CCTL2 &= ~CCIFG;
    
            TA0CCTL2 &= ~CCIE;
        }
        else
        {
            set_IN4_gpio;
            clear_timer_A;
            aDone = 0;
        }
    }

    谢谢!

    Sam

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

    您好!

    EMM、仅运行一次。 您可以尝试直接使用"="而不是"|=" 来 ​​为寄存器分配值。

    例如、 TB0CTL = MC_1 + TBSSEL_2 + ID_0;

    此致、

    Cash Hao

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

    不幸的是、这没有改变任何东西。 在 aDone 设置后、必须存在某个标志或某个东西我无法正确复位、我无法确定其含义。 我认为这是由于禁用 CCIE、但好像当我在 while 循环的 else{}语句中重新启用它时、它似乎不会改变这个运行方式、所以它肯定是我没有清除的其他东西。

    我认为这不是需要清除 TAIFG 的问题?

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

    我在@行14中添加了、额外清除 CCR3中断:

    #pragma vector = TIMER0_B1_VECTOR
    __interrupt void ISR_TB0_CCR3(void)
    {
        if(read_trig_in)
        {
            TB0CCTL3 &= ~CCIFG; //Clear the interrupt flag
        }
        else
        {
            clear_timer_B;
            set_IN4_gpio;
            set_IN4_lo;
            sleep_DRV;
            TB0CCTL3 &= ~CCIFG; //Clear the interrupt flag
        }
    }

    计时器 A 现在一直持续4毫秒、太棒了!

    我仍有一个与此相关的未决问题:

    有时、在 TimerA 中断清除后、我会在 PWM 启动之前得到该~900ns 脉冲。 这是间歇性的、不会每次都发生。 我真的无法判断这是由于涉及 TimerA 的时序问题还是完全是 TimerB 问题。

    我假设这与触发器输入的软件轮询方式有关、但我认为这也可以在 ISR 中解决。 这就像另一种没有在正确的时间清除标志的情况、从而导致出现这个小缺口。

    我尝试了各种组合清除 CCIFG 在 my else ()为每个 ISR ,并无法获得有关这方面的更多信息。  

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

    您好!

    这个 timerB 中断例程的用途是什么? 如果您只需要输出 PWM、则不需要使用其中断来执行该操作。  

    此致、

    Cash Hao

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

    我如何在不使用计时器中断的情况下生成 PWM? 我不确定我是否理解。 我认为整个要点是设置与 MCU 上的引脚相对应的任何 CCRx 寄存器、并且根据 OUTMOD 来确定引脚将如何根据 CCRx 和 CCR0触发中断时发生变化。

    我的理解是否不正确?

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

    您好!

    嗯、对于输出、PWM 波形不需要在中断例程中手动更改 GPIO 引脚状态。 下面是如何使用 timerA 生成 PWM 波形的示例代码。 它不需要中断例程来输出 PWM 信号。  

    https://dev.ti.com/tirex/explore/node?node=A__AD7Nu6Gz3Yuq.n7HkyOW1g__msp430ware__IOGqZri__LATEST

    此致、

    Cash Hao

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

    尊敬的 Cash:

    正确、我将针对 PWM 使用 TimerB 来实现它。 我只需在 TimerA ISR 中手动将 GPIO 引脚设置为高电平、以便获得一个脉冲。 我觉得这种方法比尝试在中断之间更改占空比更简单。 你是否建议我做一个不同的方式?

    在 TimerB 中断结束时、我还需要管理其他 GPIO。 此外、我认为您需要在每个中断中清除 CCIFG 才能退出中断、不是吗?

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

    您好!

    正确、我正是使用用于 PWM 的 TimerB 来完成这项操作。 我只需在 TimerA ISR 中手动将 GPIO 引脚设置为高电平、以便获得一个脉冲。 我觉得这种方法比尝试在中断之间更改占空比更简单。 您是否建议我采用不同的方式?

    可以使用 TimerA ISR 生成脉冲。 不过、如果您在代码中确实需要这些 ISR。 我建议您 以以下方式编写 ISR。 您将首先检查 TBIV/TAIV、并知道这里发生了什么中断。 并在特定情况下为应用程序代码添加。  您可以在器件 UG  TBIV/TAIV 寄存器中找到每种情况的定义。  此外、对  TBIV/TAIV 寄存器的任何访问、读取或写入都会自动复位最高挂起中断标志。 通过这种方法、你无需清除每个中断的 CCIFG。  

    此致、

    Cash Hao

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

    您是否可以考虑为我的用例提供一个稍微更具体的示例?

    我是否有四个独立的 ISR (TimerB0、TimerB1、TimerA0、TimerA1向量)并对相应的中断向量寄存器使用 switch 语句?
    例如:TimerB ISR 将会打开 TBIV、TimerA 将会打开 TAIV。  

    我不熟悉 TAIV 的行为、根据数据表、我有点困惑。  

    如果您能更具体地解释您的意思、将会非常有帮助。 我以前没有见过这样的例子。

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

    您好!

    好的、例如、您的代码如下所示。  

    #pragma vector = TIMER0_B1_VECTOR
    __interrupt void ISR_TB0_CCR3(void)
    {
        if(read_trig_in)
        {
            TB0CCTL3 &= ~CCIFG; //Clear the interrupt flag
        }
        else
        {
            clear_timer_B;
            set_IN4_gpio;
            set_IN4_lo;
            sleep_DRV;
            TB0CCTL3 &= ~CCIFG; //Clear the interrupt flag
        }
    }

    则可以更改为低于1。  

    #pragma vector = TIMER0_B1_VECTOR
    __interrupt void Timer_B(void)
    {
    
        switch( TBIV )
       {
         case  2: break;                          // CCR1 not used
         case  4: break;                          // CCR2 not used
         case  6:                                 // CCR3 CCIFG
             if(!read_trig_in)
             {
                 clear_timer_B;
                 set_IN4_gpio;
                 set_IN4_lo;
                 sleep_DRV;
             }
             break;
         case 14: break;                 // overflow
       }
    
    }

    对于 TBIV 中的每个中断源、您可以在 UG 表13-11中找到定义

    我希望此示例可以帮助您了解它的工作原理。

    此致、

    Cash Hao

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

    感谢您的帮助、Cash。 到目前为止、我的代码似乎是按原样工作的、但我想使用您的策略对其进行清理和改进。 它似乎更加清晰、更不易出错。 您的解决方案可能也比 if 语句更快。

    由于该主题足够长、因此我会将其标记为已解决、如果我将来需要再次讨论、则创建一个新主题。