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 interrupt triggered twice when close to overflow

Part Number: MSP430FR2355


Hi folks,

I stumbled upon an issue when using timer B for frequency input captures.

Timer B0 is configured in capture/compare, continous mode. MCLK = SMCLK = 24 MHz.

I'm using eComp 0 for external applied frequency capture.

Code is tested both on custom HW and on 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;
    }
}

In both cases same behaviour is observed: When external frequency 366 Hz is applied, which happens to be the frequency where the capture register should hold the value 65535 (24MHz/65535 ~= 366 Hz), the values in the TB0CCR1 register vary a lot.

I noticed, when debugging the interrupt flags, CCIFG is reset when reading TB0CCR1 (line 109) and gets directly set when writing 0 to TB0R (line110).

Shouldn't the flag stay resetted? May there be an issue with the timer?

Looking forward to any suggestions and thanks in advance!

BR,

Jonathan

  • How much is "a lot"?

    You're facing a fundamental race between CCR1:CCIFG and TB0CTL:TBIFG, since ISRs don't run in zero time. If the two events happen very close (say 20 clocks or so) together, TB0IV will present them in priority order, not chronological order, so there's no systematic way to tell which one happened first. This makes the "counting overflows" method of extending your frequency meter's range fairly difficult.

    You can gain some range at the low end by dividing the timer clock. This will cut your high end, but the cost of running your ISR already limits that (symptom: capture overruns). Similarly, resetting TB0R to zero introduces an inaccuracy (in the form of drift) due to the cost of the ISR which will be most noticeable at the high end. You might take a moment to consider what the most realistically useful range is for your application.

**Attention** This is a public forum