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.

[参考译文] MSP430G2553:有助于在两个边沿上切换计时器捕捉;当前设计偶尔会反转图形

Guru**** 2540720 points
Other Parts Discussed in Thread: MSP430G2553

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/987199/msp430g2553-help-with-switching-timer-capture-on-both-edges-current-design-occasionally-reverses-pattern

器件型号:MSP430G2553

大家好、谢谢观看。 我正在尝试实现一个连接到第二个 PWM 引脚的按钮按压释放、该引脚使用 WDT 来对按钮进行去抖。 结果应为:BTN Down、PWM On;BTN Up PWM Off。 该系统还必须捕获按钮状态更改的时序、以准确确定按钮的加/减时间( 本演示代码中未显示 TA1CCR2到变量的捕获)。 在尝试实现这一目标时、我有几个问题、但当前设置的最大问题是按钮状态与 PWM 行为之间的关系在多次按下按钮后会逆转。 首先、系统按照所述工作、但在几个按钮按下/释放周期后、I PWM 在按钮被释放时打开、然后在更多的时间后再切换回

我在这个论坛上读过关于提出和清除 CCIE 标志的时机的良好讨论、但我的应用似乎不需要像这些示例中那样进行高度准确的计数。 如果我能够在10 - 40ms 的范围内获得准确的时序、那就好了。 我在一个很好地处于 WDT "超时"范围内的示波器上看到按钮反弹以进行去抖、因此看起来效果很好。

那么、我的问题是如何实现这种相对简单的方案。 我使用 MSP430G2553 Timer1_A3在连接到按钮的引脚 P2_5上进行捕获、并使用引脚 P2_6上的 Timer0_A3进行 PWM 输出。 相关问题包括...

我是否可以使用 PxREN 设置内部上拉电阻(数据表显示、当配置为 I/O 时、这适用于引脚)? 我现在有一个外部上拉电阻器。

我是否需要停止计时器才能切换触发中断的边沿(TA1CTL)?

我是否必须在 TA1CCTL2中复位 COV?

是否允许使用 PxIN 和 BTN_KEY 测试引脚的状态? 正如目前使用的那样、这似乎不能按预期工作。

最后、在调试时、我注意到调试器报告、当我在 CM_1和 CM_2之间切换时、TA1CCTL2的 CMx 位为 CM_3、尽管我可以看到我正在正确切换 CCIE 位。 为什么会这样呢?

感谢您的任何想法、下面是我的代码...

#include <msp430.h> 
#define BTN_KEY BIT5   //attach key to P2_5 TA1.2
#define BUZZER  BIT6  // Buzzer -> P2_6
void PortsInit(void);
void InitBuzzer(void);
volatile char edge = 0; //used to toggle CCR2 edge detect

int main(void)
{
	WDTCTL = WDTPW | WDTHOLD;	// stop watchdog timer
    PortsInit();
    P2DIR &= ~BTN_KEY; //Set Btn as input
    P2SEL = BTN_KEY; //Set Btn for Timer1_A CCI1A capture
    InitBuzzer();
    //Set up Timer1A
    TA1CTL = MC_2 | ID_3 | TASSEL_2 | TACLR;// | TAIE;
    // capture on falling edge, CCI1A input, synchronous cap, capture mode, ints enabled
    TA1CCTL2 = CM_2 | CCIS_1 | SCS | CAP | CCIE;
    __enable_interrupt();
    for ( ; ; ){
        // do nothing
    }
	return 0;
}
#pragma vector = WDT_VECTOR
__interrupt void WDT_ISR(void){ // use to debounce falling and rising
    IE1 &= ~WDTIE; // disable WDT interrupt
    IFG1 &= ~WDTIFG; // clear flag
    WDTCTL = WDTPW + WDTHOLD; // put WDT back in hold state
    TA1CCTL2 &= ~CCIFG; // clear the CCRO flag
    TA1CCTL2 &= ~COV; // reset COV bit if it was set
    TA1CCTL2 |= CCIE; //enable interrupts
}
#pragma vector = TIMER1_A1_VECTOR
__interrupt void TIMER1_A1_ISR (void){
    switch (__even_in_range(TA1IV, 10)){
    case 0:
        break;
    case TA1IV_TACCR2:
        //grab value of TA1CCR2 here
        TA1CCTL2 &= ~CCIE;    // disable further CCIE interrupts
        TA1CTL |= MC_0; // stop timer
        if((P2IN & BTN_KEY) == 0){ // should be here at beginning of down pulse
            edge = 1;
            TA0CTL |= MC_1; // start tone
            P2SEL |= BUZZER;
            TA1CCTL2 |= CM_1;  // trigger on rising edge
        }else{ // Should be here after down pulse finished
            edge = 0;
            TA0CTL |= MC_0; // end tone
            P2SEL &= ~BUZZER;
            TA1CCTL2 |= CM_2;  // trigger on falling edge, enable interrupts
        }
        TA1CTL |= MC_2; // restart timer
        IE1 |= WDTIE; // enable watchdog interrupt
        WDTCTL = WDT_MDLY_32;  // start and set WDT (watch dog timer) 32ms interval
        break;
    case TA1IV_TAIFG:
        break;
    default:
        for (;;){
            //Should not be possible
        }
    }
}
void PortsInit(void){
    P1OUT = 0;
    P1DIR = 0xFF;
    P2OUT = 0;
    P2DIR = 0xFF;
    P3OUT = 0;
    P3DIR = 0xFF;
}
void InitBuzzer(void){
    P2DIR |= BUZZER;
    TA0CCR0 = 0x8ec;  // period
    TA0CCR1 = 0x476;  // period/2
    TA0CTL = TACLR + TASSEL_2 +  MC_1;     // Timer -> SMCLK, Up Mode, Clear
    TA0CCTL1 |= OUTMOD_7;                  // Output mode for TA0.1 Reset/Set
}

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

    > TA1CTL |= MC_0;//停止计时器

    这不会停止计时器、因为 MC_0=0。 尝试:

    > TA1CTL &&~MC_3;//停止计时器

    --------------

    > TA1CCTL2 |= CM_1;//在上升沿触发

    类似地、这不会设置 CM=1、它会设置 CM 的低位。 (一轮后、CM=3、它保持在那里。) 请尝试:

    > TA1CCTL2 =(TA1CCTL2 &~CM_3)| CM_1;//在上升沿触发

    或者、由于(a)您对两个边沿都感兴趣、以及(b)您正在检查引脚的实际状态、因此您可能会考虑毕竟使用 CM=3。

    --------------

    检查引脚的状态是一个好主意。 不过、从战略角度来说、您应该在去抖完成后(在 WDT ISR 中)检查它、而不是在您看到第一次转换后立即检查它。

    反弹发生得非常快、而且在你观察之前、它有两次瞬变的机会。 因此、在它稳定下来后、只能在以后再看会更可靠。

    --------------

    对于您的具体问题:

    -如果您有一个外部上拉电阻器,则无需使用 REN 电阻器。

    如果您对 COV 不感兴趣,可以完全忽略它。 检查引脚可直接替换 COV 能告诉您的大部分内容。

    -您无需停止计时器即可切换边沿(CM 字段)。

    -  

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

    感谢您的快速而彻底的回复。 您在设置 CM 位时进行的校正会有所帮助。 此外、在 WDT ISR 中使用双边沿和进行检查可根据需要工作。 我看到在本例中可以使用内部上拉电阻器、并且我移除了我的外部上拉电阻器。

    我还有一个关于为 BTN_KEY (用于定时器捕捉目的)设置 P2SEL 位的问题。 在 PortsInit()中设置 P2OUT = 0和 P2DIR = 0xFF 后,我使用设置 BTN_KEY  

        P2DIR &= ~BTN_KEY; //Set Btn as input
        P2OUT |= BTN_KEY; // along with REN, set pullup
        P2REN |= BTN_KEY; //Pullup resistor
        P2SEL = BTN_KEY; //Set Btn for Timer1_A CCI1A capture
    

    这可以正常工作、但如果我要设置 P2SEL |= BTN_KEY、这也会设置 P2_6和 P2_7。 我已经查看了该处理器系列的数据表、并看到有一些关于 REN 和 SEL 之间相互作用的考虑、但这似乎是与不同的芯片有关的。 是否有更好的方法来设置这个位? 我觉得这在编译器的用户指南中、因此我将深入研究以寻找宏来定义位。

    再次感谢、我将此问题标记为已解决。

    此致、

    Nick

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

    这是一个好问题、我忽略了这个问题。 复位时、P2SEL=0xC0 (位6/7预置)以启用32kHz 振荡器(XIN/XOUT)。 这有点不寻常、但非常有用。

    这两个引脚相互关联。 要在 P2.6上执行 PWM、(P2SEL.6)您还需要关闭 P2SEL.7并将 P2.6设置为输出、以关闭振荡器。 [参考数据表(SLAS735J)表21]。 这是您最终得到的代码(使用"P2SEL=")所执行的操作、尽管您只需要执行一次。

    这两个引脚(在 F2/G2系列上)是特殊的。 对于任何其他引脚(在任何其他系列上)、使用"|="是正确的操作。

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

    有道理。 我使用的是旧 launchpad BoosterPack MK1、其蜂鸣器连接到 P2_6 (XIN)。 我应该意识到这个引脚是"特别的"。

    再次感谢、

    Nick