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.

External Frequency Measurement

Other Parts Discussed in Thread: SIMPLICITI

I'm using an MSP430 F2274 on the RF2500 kit, and I'd like to be able to measure the frequency/speed of an external signal, like that from a function/signal generator. The resources on this forum and elsewhere indicated that the best way to do this was by using a Timer's capture function, and so I have written a rough draft of code (given below) to do so:

double freq[2];
int i = 0;

__interrupt void Timer_A (void);

void main (void)
{
P2SEL |= 0x08; // P2.3 (Ext. pin 6) set as TACCI1B
P2DIR &= ~0x08; // P2.3 set as input

BCSCTL1 = CALBC1_1MHZ; // Set SMCLK to 1 MHz
DCOCTL = CALDCO_1MHZ;

TACCTL0 |= CM1 + CCIS1 + CAP + SCS + CCIE;
// Rising Edge, Input 1, Capture, Synchronize, IE
TACTL |= TASSEL_2 + MC_2; // SMCLK, Continuous Mode
// Whenever input signal hits rising edge, timer value is copied into TACCR0
// and interrupt flag is set.

_BIS_SR(LPM0_bits + GIE);
}

/*-------------------------------------------
* Timer A interrupt service routine
*-----------------------------------------*/
#pragma vector = TimerA0_VECTOR
__interrupt void Timer_A (void)
{
// Flag cleared automatically
freq[i++] = TACCR0;
if (i == 2)
{
i = 0;
TACCTL0 &= ~CCIE;
LPM0_EXIT;
}
}

My intention would be to take two (or 3 or 4) consecutive rising edge mesaurements of the input signal, and then do some calculations (not shown in the code yet) to find the frequency/speed of that signal. However, as far as I can tell, the above code will be constantly checking the input signal for rising edges, and will therefore be constantly getting the next Timestamp, which I do not want to do. My question is: How can I modify/add to the code above so that it stays in LPM0 for, say, 0.5 seconds, then wakes up to take the timestamp of 2  or 3 or 4 consecutive rising edge measurements and calculate the frequency, and then goes back into LPM0 for another 0.5 s, and so on?

  • Why doN't you use teh hardware where the hardware can do the job?

    Depending on the input frequency there are two approaces: counting the time or counting the pulses.

    If the frequency is high, you feed the pulses into the external tiemr clock input and let the tiemr count the pulses. Then use a second timer to set a gating interval. And read the tiemr count at start and end of the interval to see how many pulses came in during the interval. Works for up to 65535 pulses per interval.

    If the frequenc yis low, then le the timer count clock pulses and use one or two capture/compare units in capture mode to generate an interrupt at the rising (and maybe falling, for the duty cycle) edge of the incoming signal. The differene in the two captured timer values (effectively 16 bit timerstamps) gives you the amount of timer ticks between two edges. And therefore the frequency. Works for up to 65535 timer ticks per period (minimum frequency) and a minimum time per period that is larger than the execution time for the interrupt handling (maximum frequency). And requires only one timer. :)

    The second one is actually what you tried in your code.

    What you need to do is to add some code in you rmain loop that configures the timer to produce a suitable delay. I suggest using CCR1 for the edge capturing and CCR0 for the delay creation. Also, depending on the input signal frequency, the timer might need to be clocked by sow ACLK for the delay and fast SMCLK for the edge tracking.

    Or you (ab)use the watchdog timer in interval mode for the delay. In the WDT ISR you enable the CCR0 interrupt and the CCR0 ISR already disables it after two captures and exits LPM0, letting the main thread calculate the frequency befoe rgoing into LPM0 again.

  • In the full version of the code I am developing that is a modification of the SimpliciTI wireless sensor monitor code, I have left Timer A in the same function it had originally (it is set up to use the ACLK sourced from the VLO, and with a TACCR0 value of 12000, so it sets a flag ~once/second that was then used to take measurements from the ADCs before re-setting the flag). I believe the frequency should be high enough that the first method you mentioned, using one timer as a pulse counter and the other timer as a gating interval, will work. Correct me if I'm wrong, but I should be able to use Timer A for the gating interval of 1 second as it's already doing, and then just set Timer B to increment a value each time it captures a rising edge, right?

    Assuming that is the case, how should I modify the Timer B setup and interrupt to serve that purpose? I was thinking something like the following: I set up Timer B into capture mode, have the Timer A interrupt copy the pulsecounts value into the array(commandwrite) I plan to send wirelessly and reset the pulsecounts value to 0, and have the Timer B interrupt simply increment the pulsecounts value. However, I have a feeling that I'm missing something else that I would need to add to the code to make it work, and would appreciate your input:

    Setup

    int pulsecounts = 0;

    // ACLK, Divider of 1, Continuous mode, Timer B clear, Interrupt enabled
    TBCTL = TBSSEL_1 + ID_0 + MC_2 + TBCLR + TBIE;
    // Rising edge, CCIxB input, Sync capture, Capture mode, Interrupt enabled
    TBCCTL0 = CM_1 + CCIS_1 + SCS + CAP + CCIE;

    P4SEL = 0x08; // P4.3 (Ext. Pin 8) set as CCI0B input
    P4DIR &= ~0x08; // P4.3 (Ext. Pin 8) set as CCI0B input

    Interrupts:

    #pragma vector=TIMERA0_VECTOR
    __interrupt void Timer_A (void)
    {
    sSelfMeasureSem = 1;
    commandwrite[3] = pulsecounts;
    pulsecounts = 0;
    __bic_SR_register_on_exit(LPM3_bits); // Clear LPM3 bit from 0(SR)
    }


    #pragma vector = TIMERB0_VECTOR
    __interrupt void Timer_B (void)
    {
    pulsecounts++;
    }


  • Hi Jens-Michael, 

    I ended up testing the code above with a signal generator, and it does work pretty well, in that pulsecounts ends up giving a good approximate value of the frequency since it's counting the number of pulses in 1 second. 

    However, I have a follow-up question that I was hoping you could answer: the value of pulsecounts(which should be equivalent to the frequency) varied somewhat widely from the actual frequency being fed in and captured. For example, I hooked up the signal generator to an oscilloscope as well, and the frequency of the signal being generated was 104 Hz. However, pulsecounts went as high as 109 during one test I conducted. Is the reason for this due to variation in the VLO frequency (due to temperature, etc) causing the gating interval to be slightly longer than 1 second, or is there another issue that needs to be rectified/considered?

  • There is a 5% difference between 104 and 109. I wonder how accurate is the 1 second interval you used? The VLO frequency varies from chip to chip between 4 kHz and 20 kHz. That is many many times bigger than 5%. And since it is very low power, it is very sensitive to noise. Can you check the VLO frequency with the scope?

  • To set the 1-second interval, I just set TACCR0 = 12000, but given the large frequency range that you give for the VLO, I guess it would make sense for there to be some variation. I don't have a lot of experience with this, so would you mind telling me how I could check the VLO frequency using the scope? Thanks!

  • I have to warn you, since VLO is inaccurate and subject to noise, it is not suitable to be used as an accurate time reference. The result of your frequency measurement can only be as good as the reference. You can show VLO on the scope, but that will not improve your measurement. You need to have a better reference to get better results.

    Anyway.

    You are using VLO to drive ACLK. If the P2.0 is not being used for something else, you could put ACLK on P2.1

    Alternatively, since you are using TimerA to divide ACLK by 12000, you could also use either of the following pins to show the waveform after that division: P1.1, P1.2, P1.3, P1.5, P1.6, P1.7, P2.2, P2.3, or P2.4

    Tell me which pin is not being used and can be used for the scope, and I will tell you what to do.

  • That's a fair point - I guess the best choice in that case would be to use the SMCLK with one of the predefined MHz clocks as the reference, though that would mean moving down to LPM0 instead of LPM3. In any case, I would still like to check the VLO frequency as well.

    Currently, none of the pins are being used (save those connected to the LEDs and button, and the one being used for the input capture) since I'm still developing the code, but as I'm using RF2500 boards, I would prefer to be able to do the measurements via one of the extension pins on the board. 

  • You can route ACLK to P2.0 by: "P2DIR |= BIT0; P2SEL |= BIT0; "

**Attention** This is a public forum