5 years, 10 months ago.

Frequency measure using interupts?

The frequency range I am interested in is 100kHz-200kHz. I am using mbed APIs to communicate with my PC. The code I have written is for an STM32L053C8 discovery board. I am able to measure the frequency only till 97kHz and at 97 kHz I am recieving a huge error in value of about 1 kHz. At 98 kHz the value mysteriously drops to zero. I am clueless as to why this happening. I need a more accurate and a frequency measure till 200 kHz of range. Please forgive me if I have made some silly errors as I am very new to this.

Freq measure

#include "mbed.h"
#define GATE_TIME   1 // Gate time (period of time to count pulses within) in seconds


Serial pc(PA_9,PA_10);
InterruptIn         input(PB_10);  // input line
Timeout             timeout;
volatile bool       measuringEnabled = false;
volatile uint64_t   counter;
volatile uint64_t   freq=0;

//ISR to count pulses
void onPulse(void) {
    if (measuringEnabled)
        counter++;
}
 
// ISR to stop counting
void stopMeasuring(void) {
    measuringEnabled = false;
}
 
// Initializes counting
void startMeasuring(void) {
    counter = 0;
    timeout.attach(callback(&stopMeasuring), GATE_TIME);
    measuringEnabled = true;

int main(void) {
    input.rise(callback(&onPulse)); // assign an ISR to count pulses
    while (1) {
        startMeasuring();
        while (measuringEnabled);   // wait until the measurement has completed
        if(GATE_TIME < 2)
            wait(2 - GATE_TIME);    // wait at least one second before printing
            freq=counter;
            printf("   %u   \n",freq);
        }
}

1 Answer

5 years, 10 months ago.

First there are some formatting problems in what you posted. Brackets are missing. startMeasuring() is missing body end bracket (line 28). The If on line 34 does not have opening bracket making only that first line conditional.

This basic approach can work for low frequencies but there is an upper limit on the frequency you can measure this way. Looks like this board is 32MHz or 31.25ns per clock tick. The system will need some hundreds of clock ticks between every rising edge interrupt in order to do the ISR context switch. The rtos needs some CPU time every now and then and main itself needs some CPU time.

The control logic seems like it could be a lot simpler. If I were doing this I would have only the one interrupt for counting rising edges. Then in the main body I would use either the free us timer or a custom Timer to track time. Each loop something like:

// clear counter
// reset timer or snapshot us timer at start of loop
// wait(1 second) - counter ISR is doing it's thing here
// capture copy of counter
// capture timer value time_base = timer.read()
// frequency = counter_copy / time_base

It wouldn't surprise me if at 100kHz the failure you're seeing is simply because the CPU can't keep up. I saw someone doing this same pulse counting on an Arduino at I think 8MHz and it failed above 50kHz - so same type of range.

You might be able to setup one of the hardware timers to do this more efficiently without involving the CPU and make it usable with higher frequencies. You would have to dig into the micro reference manual and then use the HAL driver layer to configure the timer.

Graham