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.

Frequency counter 600KHz

Hi I'm George, I want to make a query.
I am making a frequency counter, the way I do it is:
I configure Timer0 in Free Running mode.
I configure Timer1 as event counter , rising edge, whenever it detects an edge, an interrupt is generated that keeps the count of Timer0 to make the period calculation .
My firmware, it calculates the frequency, but only makes up to 600kHz, when the count more than 800kHz, stops working.
The clock of my system is set to 80MHz.
The variable that contains the frequency, I check from the debugger.

This is the code:

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "stdlib.h"
#include <math.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/systick.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "utils/uartstdio.h"


//Con este firmware lo maximo que pude medir aceptable fueron 600KHz

// function prototypes
void init_timer0(void);
void init_timer1(void);
void Timer0_int(void);
void contador(void);

unsigned long cuenta_vieja;
unsigned long cuenta_nueva=0;
unsigned long cuenta;
float periodo,frecuencia;

int main()
{
		// Configure system clock at 80 MHz.
	    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL| SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
	    SysCtlDelay(3);
	    IntMasterEnable();

	    init_timer0();
	    init_timer1();

	    TimerEnable(TIMER0_BASE, TIMER_A);
	    TimerEnable(TIMER1_BASE, TIMER_A);


	  while(1)
	    {

		  periodo=(((float)cuenta)/80000000);
		  frecuencia=1/periodo;

	    }

}

void init_timer0(void)	// Free Running Timer
{
	 SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);			//habilito el TIMER0
	 SysCtlDelay(3);
	 TimerConfigure(TIMER0_BASE,  TIMER_CFG_PERIODIC);		//configuro como periodico
	 TimerLoadSet(TIMER0_BASE, TIMER_A, 0xfffffffe);		//cargo el valor de conteo 2^16-1
	 TimerEnable(TIMER0_BASE, TIMER_A);				//habilito el timer A
}

void init_timer1(void)   // Contador de flancos
{

    SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
    TimerConfigure(TIMER1_BASE, (TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_B_CAP_TIME_UP));  // esto me permite contar 2^32 pulsos
    TimerControlEvent(TIMER1_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);	//configuro por flanco de subida
    TimerLoadSet(TIMER1_BASE, TIMER_A,0xfffffffe );
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIOPinConfigure(GPIO_PB4_T1CCP0);
    GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_4);
    IntRegister(INT_TIMER1A, contador);
    TimerIntClear(TIMER1_BASE, TIMER_CAPA_EVENT);
    TimerIntEnable(TIMER1_BASE, TIMER_CAPA_EVENT);
    IntEnable(INT_TIMER1A);

}
void contador(void)	// entra aca cada vez que recibe un flanco
{
	TimerIntClear(TIMER1_BASE, TIMER_CAPA_EVENT);
    cuenta_vieja=cuenta_nueva;
    cuenta_nueva=TimerValueGet(TIMER0_BASE, TIMER_A);
    cuenta=abs(cuenta_nueva-cuenta_vieja);
}

Someone can help me find the problem?

thank you very much.

  • We cannot find your expectations/desires - beyond the fact that 600KHz is, "Too slow."

    Your mention of desired rate and accuracy requirement would prove helpful - would it not?
  • First reply (above) while sitting on tarmac - waiting for flight clearance.   Now - landed - more time for detail.

    Would it not prove useful to break your program into constituent parts - and determine how (each) impacts your, "speed of execution?"   It may be that one bad "actor" (program function) "eats" the great bulk of your execution time.   Turning your attention to that function makes great sense - does it not?

    This method of, "Divide & conquer" speeds, simplifies & enhances your diagnostic efforts.   Attacking the "whole" - rather than far smaller, specific functional parts - most always takes longer & proves less than optimal...

  • Hello Jorge,

    As CB mentions, a bit more information would be helpful to evaluate what the problem might be. Some key things to let us know are what exactly is failing at frequencies >600KHz and what happens at frequencies >800KHz? Have you looked at the variables and the calculated count to get some idea of what might be happening in your frequency calculations? I suspect there are some overflow issues going on.

    If rollover is an issue in your calculations, you may want to consider using both counter in Timer0 to create a 32 bit counter instead of 16 bits. Also, you might want to have a look at the time you spend in your ISR and if this is causing you to miss any edges when capturing the counts.
  • Vendor's Chuck raises good points - especially the suggestion to switch to 32 bit counter & insure that ISR is (very) short & sweet. (fast executing)

    Also helpful would be poster's confirmation of the accuracy of those (600KHz) counter measures. How were those measured - were they truly accurate? (i.e. compared well against some "external standard.")

    That 600KHz "cut-off" - if truly accurate - likely shines "great light" on your program limitation. (it should be possible to determine (via simple calculation) what "blows up" when input signal arrives at/above 600KHz...)

    On a more general note - basing your timed result upon, "Back to back" signal edges (usually) proves sub-optimal.   (your error sources become more impactful via that single edge detection)   Instead - waiting for a "good" binary count such as 8 or even 16 signal edges - rather than just one - will greatly lessen signal irregularities and much reduce the impact of your calculation's error sources.  (i.e. the greater (real) time duration elapsed (by allowing 8 or 16 edges) will "absorb" many/most of your calculation's "time based" errors...)

  • Thanks for your interest.

    Yes of course. Sorry, I should have clarified that.
    I would like to measure at least up to 5 MHz or maybe more.

    I'm sure that is how you say "Divide and conquer". I'll try to pay more attention to those functions.

    Chuck Davenport

    Chuck.
    Frequency calculations were made based on the value taken by the variables

    cuenta_vieja = cuenta_nueva;
    cuenta_nueva = TimerValueGet (TIMER0_BASE, TIMER_A);

    And the difference of them let me do the calculation with tick like 1/80MHz. In addition the frequency is measured by the signal generator. The generator is not very good but is okay.

    The next test will increase the timer to 32bit.
    Thanks

    cb1-

    The method you mensionas, it seems to me that this very good, I will implement this method together with the Chuck proposal .
    Soon I will test and show you the results.
    Thank you very much everyone for your cooperation.

    Jorge

  • So - while I'm not sure that speed is possible - the suggestion to "break your program into constituent parts" and measure (time) each - surely provides great insight into those functions, "Most in need of speed-up."

    Looking at a massive "whole" is far less effective than the "Divide/Conquer" approach - and should be your next step...
  • Hello everyone,

    I implemented the method of counting several pulses (8) before calculating the frequency. As expected, a more stable result.

    However, this could not solve the problem.

    After that, I set up Timer2 free periodically and when I could measure takes to get in and out of the interrupt routine.

    To calculate the time: T = 146/80 * 10 ^ 6 = 1,825 * 10 ^ 6 1 / T = 547945.2 Hz = 547.9KHz

    The code for the ISR that gives me that value is:

    void contador(void)	// Interrupt Handler
    {
    	mark1=TimerValueGet(TIMER2_BASE, TIMER_A);
    	TimerIntClear(TIMER1_BASE, TIMER_CAPA_EVENT);
    	counter++;
    	cuenta_vieja=cuenta_nueva;
    
    	if(counter==8)
    	{
    		cuenta_nueva=TimerValueGet(TIMER0_BASE, TIMER_A);
    		cuenta=abs(cuenta_nueva-cuenta_vieja);
    
    		update=1;
        	counter=0;
    	}
    	mark2=TimerValueGet(TIMER2_BASE, TIMER_A);
    }

    By doing several tests, always I get the same result. So I can understand why I can not measure beyond 550Khz.

    Now, the question is ... how to make the interruption delay less time?

    The following code is minimal delay and 7.75 * 10 ^ -7s, giving me a maximum of 1.29 MHz freq .....

    void contador(void)	// Handler interrupt
    {
    	mark1=TimerValueGet(TIMER2_BASE, TIMER_A);
    	TimerIntClear(TIMER1_BASE, TIMER_CAPA_EVENT);
    	update=1;
    	mark2=TimerValueGet(TIMER2_BASE, TIMER_A);
    }

    I can not think how I can do to make it faster this routine ....
    Any suggestions?
    Thank you very much!!

  • Thank you - though your results are not completely satisfying you have gained some improvement.

    If time allows - I'll load your code and run under our "pro" IDE (IAR) which includes a "burden-free" cycle counter - and will quickly/easily enable the measurement of your code's execution time.

    I would note that our group prefers to employ one timer as an "input signal edge counter" (which avoids your "counter++;") and a second (independent timer) as a, "time-gate" (i.e. the time duration in which the edge counter is enabled - i.e. allowed to count.)

    Might you investigate this method (two, separate timers - each dedicated to specific task). You enable the edge counter immediately after launching your duration timer (or together - if that's possible) and then disable the edge counter when your "count" reaches 8. (per your code, above)

    Note that firm/I employ ARM M4 (and M7) from multiple vendors - the design strategy above relies upon your MCU's edge counter to interrupt upon the recognition of the "8th pulse's arrival." (often it's best to preload such counter w/"8" and then have the edge counter, "count down" and enter the edge counter's interrupt when it hits zero.) Upon that "edge counter" interrupt you immediately "freeze/halt" the separate, duration timer. The content of that duration time - divided by 8 - reveals the "period" of your input signal...    

    Note: I cannot recall if this process is fully embodied w/in this vendor's TM4C devices - when such "hardware" capability exists w/in the MCU's Timer - best results are achieved.   (i.e. highest possible input frequency capture obtained...)