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.

MSP430 Frequency detection Problem

Hi all,

I am a newbie to MSP430. The controller I am using is MSP430 2252.

Currently I am trying to measure input frequency of the range 40 - 70 Hz at pin 1.2 (Timer A1).

The problem is even though I vary the frequency from 40 till 70 Hz, I always end up getting values of approx 19900 in the buffer[] array.

I am using the following code from one of the forum post.

#include <msp430x22x2.h>


#define CLRBIT(dest,mask)                             ((dest) &= ~(mask))
#define CLEAR_INTERRUPT_FLAG()            (CLRBIT(TACCTL1,CCIFG))

unsigned int buffer[100];
unsigned int i=0;

unsigned int Current_Edge_Time;
unsigned int Previous_Edge_Time;

void main(void)
{
    WDTCTL = WDTPW + WDTHOLD;          // Stop WDT
   
    BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
    DCOCTL = CALDCO_1MHZ;
   
    P1DIR &= ~BIT2;                                          //Port 1.2 - Input
    P1SEL |= BIT2;
   
    TACTL = TASSEL_2 + MC_2;                    // SMCLK = 1 Mhz, continuous mode
    TACCTL1 = CM_1+CCIS_0+SCS+CAP+CCIE;
       
    _BIS_SR(GIE);   
}   
   
// Timer A1 interrupt service routine
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A (void)
{      
    Current_Edge_Time = TACCR1;
   
    if(Current_Edge_Time < Previous_Edge_Time)
    {
        buffer[i++] = ((0xFFFF - Previous_Edge_Time) + Current_Edge_Time);
    }
    else
    {
        buffer[i++] = Current_Edge_Time - Previous_Edge_Time;           
    }
    Previous_Edge_Time = Current_Edge_Time;   
   
    if (i>100)
        __no_operation();                       // PLACE BREAKPOINT HERE
   
       CLEAR_INTERRUPT_FLAG();     
}

 

Could somebody please help me out and let me know if I am doing anything wrong.

  • First, since Current_Edge_Time and co. are unsigned int, you don't need to do all the calculation you do. Just subtract previous from current and the result is the difference. You won't notice when the real result of this simplified calculation would be off by 65536. :)

    For a frequency of 40 Hz, the expected result is 25000, for 70Hz it would be14286. That you're getting always ~19900 seems to rather indicate that you're measuring 50Hz and nothing else.

    How sure are you about your source signal? I can imagine it being some sort of PWM with 50Hz frequency but varying duty cycle. You could try your code with CM_3 instead of CM_1, so you'll capture both half-waves independently, getting half the values, but maybe your 19900 is sometimes 7100+12800 and sometimes 12500+7400.

     

  • Hi, Thanks for the help. I found out what the problem was. It seems the oscillator is not stable at startup. If i put a small delay during startup, i get proper values for varying frequencies.

  • That's strange. The DCO should be stable within a few microseconds. It is not very precise but fast.  When stabilized by FLL, it takes a maximum (worst case) of 32 ms to reach the final frequency. Even this would be before the second interrupt.

     I'd be really surprised if an unstable DCO is the reason why you didn't get the erraneous readings first.

    Reading 100 values of 40-70Hz is a matter of ~2 seconds. So unless your DCO is by several magnitudes slower than it should, you should have seen proper values in your list - if not at the very beginning so at least some values later. Since you didn't, there must be other reasons.

    But well, if it works now...

  • Hi Michael,

    I think i celebrated a bit too early. You are right, it worked for a few times but now again i am back to square one.

    My input signal source is a frequency generator. I have verified using a scope, that the frequency does changes from 40 - 70 Hz (50% duty cycle). Also i tried for both the signal edges but i simply get random values from ~61 to ~16000.

    Any ideas why i still don't get proper values ? (I am using CCS version 3.1)

  • That's really strange. I guess I'll have to trust your scope, so the problem must be somewhere else. :)

    But for a 50% duty cycle, the readings should be equal pairs, if triggering on both edges (at the same time!). Also, a value of 61 is definitely not in the range of expected values.

    Where did you test the signal with your scope? At the processor pin? it almost looks like you're catching radio signals out of the air :) Maybe a broken wire? A missing GND connection? Remember, that scope GND is usually connected to earth. Frequency generator and MSP GND aren't

    Also, what are the voltage levels of the signal? It should be 0V to VCC. Some frequency generators (most, in fact) produce a symmetrical signal with positive and negative output voltage relative to GND. That will cause havoc on the inputs. You'll need to add an offset voltage, so the signal stays above GND only. It's easy to miss offset voltages on a scope (and everything looks fine while it isn't).

    If you're using the TTL output of the frequency generator, this will be 0V and 5V, which too is too much for the MSP inputs.

    What else could it be?  I don't know the 2252, but I think TIMERA1 is the right vector (not that you're reading the CCR value at the timer overflow vector) - on the 5438, the vectors are named TIMER1_A0 and TIMER1_A1, as it has two A-Timers.
    Do you really need to clear the IFG? Isn't it automatically cleared when entering the interrupt function or reading TACCR1?

    But after all, the program should do the job.

     

  • Hi Paul,

    I was able to run your code and get proper timer capture values between 40-70Hz (between ~25000  to ~14300 counts) on the F2252 device. The first result in buffer should be ignored as its previous_edge count was zero. I dont think its a problem with the firmware.

    Like Jens-Michael asked, is your input signal good? Have you checked the DC offset/ amplitude of the signal to make sure the capture happens fine?

    Here are some suggestions reg. the code as such: Use a switch statement inside the Timer ISR to handle various TAIV interrupt vectors accordingly. Also there is no need to clear the timer interrupt flag inside the ISR, it should automatically get cleared. Find attached example code for reference.

    Regards,

    Bhargavi

    #include <msp430x22x2.h>
    
    #define CLRBIT(dest,mask)                 ((dest) &= ~(mask))
    #define CLEAR_INTERRUPT_FLAG()            (CLRBIT(TACCTL1,CCIFG))
    
    unsigned int buffer[100];
    unsigned int i=0;
    unsigned int Current_Edge_Time;
    unsigned int Previous_Edge_Time;
    
    void main(void)
    {
        WDTCTL = WDTPW + WDTHOLD;           // Stop WDT
        
        BCSCTL1 = CALBC1_1MHZ;              // Set DCO to 1MHz
        DCOCTL = CALDCO_1MHZ;
        
        P1DIR |= BIT0;                      // P1.0 LED output
        P1OUT &= ~BIT0;
        
        P1DIR &= ~BIT2;                     //Port 1.2/TACCR1 - Input
        P1SEL |= BIT2;                      // Select Timer Capture option
        
        TACTL = TASSEL_2 + TACLR;           // SMCLK = 1 Mhz, continuous mode
        TACCTL1 = CM_1+CCIS_0+SCS+CAP+CCIE; // CCR1 used for timer capture
                                            // capture on rising edge, CCI0
                                            // Enable timer interrupt
        Current_Edge_Time = 0x0;
        Previous_Edge_Time = 0x0;
        i=0;
        
        TACTL |= MC_2;                      // Start Timer, Continuous mode
            
        __bis_SR_register(CPUOFF + GIE);        // LPM0, ADC10_ISR will force exit
        __no_operation();
    }    
    
    // Timer A1 interrupt service routine
    #pragma vector = TIMERA1_VECTOR
    __interrupt void TA1_ISR(void)
    {
      switch (__even_in_range(TAIV, 10))        // Efficient switch-implementation
      {
        case 0: break;
        case 2:                                 // CCR1 capture interrupt
          P1OUT ^= BIT0;                        // P1.0 toggle every rising input edge
          
          Current_Edge_Time = TACCR1;    
          if(Current_Edge_Time < Previous_Edge_Time)
          {
              buffer[i++] = ((0xFFFF - Previous_Edge_Time) + Current_Edge_Time);
          }
          else
          {
              buffer[i++] = Current_Edge_Time - Previous_Edge_Time;            
          }
          Previous_Edge_Time = Current_Edge_Time;    
          
          if (i>100)
          {
            i=0;
            __no_operation();                       // PLACE BREAKPOINT HERE
          }
          break;
        case 4: break;                          // CCR2 interrupt
        case 6: break;                          // Reserved
        case 8: break;                          // Reserved
        case 10: break;                         // Timer Overflow
      }
    }
    
    

  • For the usage of the timer Interrupt Vector register, I want to add a small code example. It is for mspgcc compiler but it should be possible to adapt it to CCE or IAR.

    interrupt(TIMER0_A1_VECTOR) timera0x_handler(void){
      __asm__ __volatile__("add   %[src]       ,r0       ":: [src] "m" (TA0IV));
      __asm__ __volatile__("reti                         "::); // NO INT
      __asm__ __volatile__("jmp   timera0_cc1_handler    "::); // CC1
      __asm__ __volatile__("jmp   timera0_cc2_handler    "::); // CC2
      __asm__ __volatile__("jmp   timera0_cc3_handler    "::); // CC3
      __asm__ __volatile__("jmp   timera0_cc4_handler    "::); // CC4
      __asm__ __volatile__("reti                         "::); // RESERVED
      __asm__ __volatile__("reti                         "::); // RESERVED
      __asm__ __volatile__("jmp   timera0_ifg_handler    "::); // IFG
    }
    interrupt(NOVECTOR) timera0_cc1_handler(void){}
    interrupt(NOVECTOR) timera0_cc2_handler(void){}
    interrupt(NOVECTOR) timera0_cc3_handler(void){}
    interrupt(NOVECTOR) timera0_cc4_handler(void){}
    interrupt(NOVECTOR) timera0_ifg_handler(void){}

    Note that the JMP instruction is used, so the handlers need to be near the jump table (+-1KB). The BR instruction would remove this limitation but requires 4 bytes instead of 2 so it will only work for the last entry in the table (ifg_handler).

  • Hi everyone, i am also facing same problem. The controller I am using is MSP430 F5438.

     I am trying to measure input frequency  at pin P4.1 (Timer B1).

    i don't know how to program...

    please help me... i am trying from last two months... please ...

    can you give me example source code..

  • Sunil,

    There is a timer capture example code present in the F51x2 device code example list (slac452) - "MSP430F51x2_td0_24.c". You could use the same code for TimerB of F5438.

    Regards,

    Bhargavi

  • Thanks for the reply..i am searching.. but i am not getting.. please can u send me the link.. 

    please..

  • Hi Bhargavi ,

    Using This reference document "2474.TimerCapture_1.c" i did timer capture at TIMERB0.1..

    Can you help me to find  the TBIV value for TBxCCR0 CCIFG interrupt.
    please..

    In  MSP430x5xx  MSP430x6xx Family User's Guide , they given TBxCCR1 CCIFG to TBxCCR6 CCIFG..
    i need TBIV value for TBxCCR0...

    in this Document "2474.TimerCapture_1.c

     switch (__even_in_range(TAIV, 10))        // Efficient switch-implementation
    {
    case 0: break;
    case 2: // CCR1 capture interrupt
    P1OUT ^= BIT0; // P1.0 toggle every rising input edge

    Current_Edge_Time = TACCR1;
    if(Current_Edge_Time < Previous_Edge_Time)
    {
    buffer[i++] = ((0xFFFF - Previous_Edge_Time) + Current_Edge_Time);
    }
    else
    {
    buffer[i++] = Current_Edge_Time - Previous_Edge_Time;
    }
    Previous_Edge_Time = Current_Edge_Time;

    if (i>100)
    {
    i=0;
    __no_operation(); // PLACE BREAKPOINT HERE
    }
    break;
    case 4: break; // CCR2 interrupt
    case 6: break; // Reserved
    case 8: break; // Reserved
    case 10: break; // Timer Overflow
    }

    This code will work for TIMERB0.1.

    but i need for TIMERB0.0

    please help me...

    thank you.

  • Sunil,

    There are two interrupt vectors for each timer module - TAxCCR0 and TAxIV. The TAxCCR0 is a dedicated interrupt vector for the Timer CCR0 block. And for rest of the blocks, interrupt flags TAxIV interrupt vector is used. So, you need to add the piece of code onto the TAxCCR0 ISR to get the capture working for TimerA0.0. For e.g. refer to msp430x54xA_tb_02.c or msp430x54xA_ta3_02.c example codes (in the F5438A code example list online) that use the TBxCCR0/TAxCCR0 interrupt vector.

    Regards,

    Bhargavi

  • Bhargavi,

    Thanks for your reply.

    I will follow your instructions.


**Attention** This is a public forum