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.

[参考译文] MSP430FR2355:在接近溢出时触发两次 TB0CCR1中断

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1205531/msp430fr2355-tb0ccr1-interrupt-triggered-twice-when-close-to-overflow

器件型号:MSP430FR2355

大家好、

我在使用计时器 B 进行频率输入捕获时偶然发现了一个问题。

定时器 B0配置为捕获/比较连续模式。 MCLK = SMCLK = 24MHz。

我正在使用 eComp 0进行外部应用频率捕获。

代码在定制硬件和 EXP-MSP430FR2355 Launchpad 上进行测试:

#include <msp430.h>
#include <stdint.h>
#include <stdbool.h>
#include <intrinsics.h>

static inline void init_clk(void);
static inline void init_ecomp(void);
static inline void init_timer(void);
static inline void timer_start(void);
static inline void callback_period_finished(const uint16_t t);

volatile uint32_t     captureTrace[100] = { 0 };
volatile uint_fast8_t cntPeriods        = 0;
volatile uint_fast8_t cntOverflow       = 0;

static inline void init_clk(void) {
    WDTCTL = WDTPW | WDTHOLD;// stop watchdog timer
    PM5CTL0 &= ~LOCKLPM5;    // Disable the GPIO power-on default high-impedance
                             // mode to activate previously configured port settings
                             // Configure reference
    PMMCTL0_H = PMMPW_H;     // Unlock the PMM registers
    PMMCTL2 |= INTREFEN;     // Enable internal reference
    while (!(PMMCTL2 & REFGENRDY))
        ;// Poll till internal reference settles

    FRCTL0 = FRCTLPW | NWAITS_2;// FRAM waitstate for usage with 24MHz
    P2SEL1 |= BIT6 | BIT7;      // P2.6~P2.7: crystal pins
    do {
        CSCTL7 &= ~(XT1OFFG | DCOFFG);// Clear XT1 and DCO fault flag
        SFRIFG1 &= ~OFIFG;
    } while (SFRIFG1 & OFIFG);// Test oscillator fault flag

    __bis_SR_register(SCG0); // disable FLL
    CSCTL3 |= SELREF__XT1CLK;// Set XT1CLK as FLL reference source

    CSCTL1 = DCORSEL_7;   // DCO Range = 24MHz
    CSCTL2 = FLLD_0 + 731;// DCOCLKDIV =  32768Hz*(n+1) = SMCLK

    __delay_cycles(3);
    __bic_SR_register(SCG0);                 // enable FLL
    CSCTL4 = SELMS__DCOCLKDIV | SELA__XT1CLK;// Set ACLK = XT1CLK = 32768Hz
                                             // DCOCLK = MCLK and SMCLK source
    CSCTL5 |= DIVM_0 | DIVS_0;               // MCLK = DCOCLK
                                             // SMCLK = MCLK

    __delay_cycles(1000);// Allow clock system to settle
}

static inline void init_ecomp(void) {
    // Select eCOMP input function on P1.0
    P1SEL0 |= BIT0;
    P1SEL1 |= BIT0;

    CP0CTL0 = CPPSEL_0;          // Select C0 as input for V+ terminal
    CP0CTL0 |= CPNSEL1 | CPNSEL2;// Select DAC as input for V- terminal
    CP0CTL0 |= CPPEN | CPNEN;    // Enable eCOMP input
    CP0DACCTL |= CPDACEN;        // Select VCC as ref and enable DAC
    CP0DACDATA |= CPDACBUF1_31;  // CPDACBUF1=On-chip VREF(1V5) *31/64
    CP0CTL1 |= CPEN | CPMSEL_1;  // Turn on eCOMP, in low power mode
}

static inline void init_timer(void) {
    // Timer0_B1 Setup
    TB0CCTL1 = CM_1 | CCIS__CCIB | SCS | CAP | CCIE;
    // Capture rising edge,
    // Use CCI1B=internal,
    // Synchronous capture,
    // Enable capture mode,
    // Enable capture interrupt
}

static inline void timer_start(void) {
    TB0CTL = TBCLR;
    TB0CTL &= ~TBIFG;
    TB0CCR1 = 0;
    TB0CTL &= ~COV | ~CCIFG;
    TB0CTL = TBSSEL__SMCLK | MC__CONTINUOUS | TBIE | ID_0 | CNTL__16;// Start timer in continuous mode, enable interrupt
}

static inline void callback_period_finished(const uint16_t t) {
    captureTrace[cntPeriods] = (uint32_t)(cntOverflow * 65535 + t);
    cntOverflow              = 0;
}

int main(void) {

    init_clk();
    init_ecomp();
    init_timer();
    __bis_SR_register(GIE);

    timer_start();
    while (1)
        ;
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER0_B1_VECTOR
__interrupt void Timer0_B1_ISR(void)
#elif defined(__GNUC__)
void __attribute__((interrupt(TIMER0_B1_VECTOR))) Timer0_B1_ISR(void)
#else
#error Compiler not supported!
#endif
{
    switch (__even_in_range(TB0IV, TB0IV_TBIFG)) {
        case TB0IV_TB0CCR1: {
            //period finished
            const uint16_t t = TB0CCR1;
            TB0R             = 0;
            TB0CTL &= ~CCIFG;
            callback_period_finished(t);
            ++cntPeriods;
        } break;
        case TB0IV_TB0CCR2: break;
        case TB0IV_TB0IFG:
            //overflow occured
            ++cntOverflow;
            break;
        default:
            break;
    }

    if (cntPeriods > 99) {
        //for Debug
        _nop();
        cntPeriods = 0;
    }
}

在这两种情况下都观察到相同的行为:当应用外部频率366Hz 时、即捕获寄存器应保持值65535 (24MHz/65535 ~= 366Hz)时、TB0CCR1寄存器中的值存在很大差异。

我注意到、当调试中断标志时、CCIFG 在读取 TB0CCR1时被复位(行109)、并在写入0到 TB0R 时(行110)直接被置位。

旗不应该保持复位? 计时器可能会出现问题吗?

期待任何建议,并提前感谢!

Br、

Jonathan

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

    "多少钱"?

    您正面临 CCR1:CCIFG 和 TB0CTL:TBIFG 之间的根本冲突、因为 ISR 不会在零时间内运行。 如果两个事件发生的时间非常接近(例如20个时钟左右)、TB0IV 将按优先级顺序而不是按时间顺序显示它们、因此没有系统的方法来判断哪一个首先发生。 这使得"计数溢出"方法来扩展频表的范围相当困难。

    您可以通过分频计时器时钟来在低端获得一些范围。 这将削减高端系统、但运行 ISR 的成本已经限制了这一点(症状:捕获溢出)。 类似地、由于 ISR 成本的关系、将 TB0R 复位为零会引入不准确(表现为漂移)、这在高端最为明显 您可能需要一点时间来考虑对于您的应用而言、最实用的范围是什么。