How to check timer accuracy

I have set up an interrupt timer, using a prescaler of 8 to give 0.5 microsecond resolution. I have the interrupt set to start at 1000 microseconds and decrease by 10 microseconds per step.

I'm struggling to understand if I have everything correct, as when checking against "micros()" I'm seeing a fluctuation in the time the interrupt runs. I don't know whether it's truly running off, or whether it's just down to error introduced by the "cli(); sei();" blocks stopping the micros timer.

Here's the sketch:

volatile bool doStep = false;

bool not_output = true;
unsigned long start, step, elapsed, t_start, t_end;
int uSecond_delay = 2000;
int steps = 0;
int target = 50;
String output [50];

void setup(){

	Serial.begin(9600);
	while( !Serial ){
		;
	}

	Serial.println("Setup..");

	noInterrupts();

	TCCR1A = 0;
	TCCR1B = 0;

	TCNT1  = 0;

	TCCR1B |= B00000010;
	TIMSK1 |= B00000010;	

	OCR1A = uSecond_delay;

	interrupts();

	start = micros();
	t_start = start;
}

ISR(TIMER1_COMPA_vect){

	TCNT1 = 0;
	doStep = true;
}

void loop() {

	if( doStep && steps < target ){

		step = micros();
		elapsed = step - start;
		start = step;

		uSecond_delay -= 20;

		output[ steps ] = elapsed;
		steps++;

		noInterrupts();
		
		doStep = false;
		OCR1A = uSecond_delay;
		
		interrupts();
	}

	if( steps == target && not_output ){

		t_end = micros();

		not_output = false;

		for( int i = 0; i < target; i++ ){

			Serial.println( output[i] );
		}

		Serial.print( "Avg: " );
		Serial.println( (t_end - t_start) / target );
	}
}

The first few lines from the output:

Setup..
1000
996 // should be 990
980
972 // should be 970
960
952 // should be 950

as you can see, it's not giving the round numbers expected. Can anyone shed any light on whether this is correct? I only need to be sure the actual interrupt routine is running on time, I don't need to log the time accurately but right now I'm unsure where I stand.

Thanks!

micros() has a 4 microsecond resolution

See micros() - Arduino Reference

Thank you for the information, I hadn't realised that. I'll just have to trust the interrupt is correct then.

cli();
TCCR1B = B00000000; // clear timer register from arduino use
TCCR1A = B00000000; // same as above
TCNT1  = 0; // zero the count
OCR1A = 1999; // set top value of count
TIMSK1 |= B00000010; // enable output compare A match interrupt
TCCR1A = B01000000; // set OC1A (PB1) to toggle on match
TCCR1B = B00001010; // set CTC and prescalar
sei();
DDRB |= B00000010; // set PB1 to output

This will toggle PB1 high 500 times and low 500 times, giving you a 500Hz waveform, which can be seen on an oscilliscope.

Yeah when you need one, an oscilloscope is a nice thing to have.

Same with a logic analyzer.

Say, did you know that wokwi.con lets you build and test quite a bit of a new project without touching any hardware?

My new toy. Delighted to find it has, wait for it, a logic analyzer you can drag out and set up.

Perhaps this would allow you to develop more confidence in the correctness and accuracy of your program.

a7

Thanks for the tip, that looks interesting. I don't have any hardware for analysing these things, so that simulator might be very useful.

basically the take away for you is that OCR1A should be set to 1999 for the prescalar set to give you a 1 millisecond interrupt fire... not 2000

I see, yes it's zero indexed which I missed. Thanks. To be honest I'm a bit out of my depth, your initial reply went over my head. I appreciate the explanation.

I have used the wowki simulator, it's really good. Surprised it is accessible without an account. The problem is I don't know if the results I am seeing is correct.

Using OCR1A = 1999 (for 1microsecond), in PulseView I get a consistent pulse of 1001.47 uSeconds. I'm not sure if this is being affected by the simulation, the time it takes to set the pin state or something else?

Here's the code I'm using in wowki (on a mega board, with the logic analyser attached to pin 53):

void setup() {
  
  noInterrupts();

  pinMode( 53, OUTPUT );

	TCCR1A = 0;
	TCCR1B = 0;

	TCNT1  = 0;

	TCCR1B |= B00000010;
	TIMSK1 |= B00000010;	

	OCR1A = 1999;

	interrupts();
}

void loop() {}

volatile bool state = false;
ISR(TIMER1_COMPA_vect){

	TCNT1 = 0;
    state = !state;
	digitalWrite(53, state);
}

I ran the code from @Perehama using the same simulator on an Uno, and that gives a 999.93us pulse, so I'm guessing I am missing something here.

Thanks for the help.

I figured it out, I was being lazy and setting the pin inside the ISR. I changed it to set a flag and then set the pin state in the loop and I'm now getting a 999.936us pulse which is close enough for me :slight_smile:

1 Like

As you found, no.

So far my experience with wokwi has led me away from assuming it is at fault…

They even simulate switch bounce in the pushbuttons.

My confidence is approaching the same I have in modern compilers, which to a first approximation are never the problem.

I haven't looked into every corner of wokwi, and probably couldn't, but it seems to be built on a mercilessly accurate simulation engine of some kind.

I haven't looked into how one would select particular IDE versions, tool chains and library versions or other ubtleties like that

There is a responsive community on the discord. I think this thing can only get better.

One thing I would like is a way to control real time - watching things for minute or hours can be a drag, it seems like one could have a speed control on that and move things along a bit when desirable.

a7

It certainly seems good, obviously with no experience of it I did have a bit of doubt but as is usually the case the fault was with my code.

Having the logic analyser on there is great, I can see it being a very helpful tool - thanks again for the tip!

Use CTC mode and use OCR1A toggle, as I first instructed. This achieves the same thing, a state toggle on one of the pins, without all the pin lookup and mcu clock cycles... as it's built into the timer hardware, parallel to the mcu. From there, knowing you've got it almost right, you can tune your gate using precision/variable caps or a TXCO etc. to get more precise. You will only ever get as accurate as your crystal oscillator.

Forgive my ignorance, would it be possible to alter the "delay" on each hit with that approach? From what I saw running your code it produces a fixed frequency output. I need to be able to calculate and change the time for the next interrupt on each event.

As an aside, I tried your code on a Mega 2560 using pin 52 which according to the reference is PB1 yet nothing was output. The code worked on the Uno. Where would I look to see what needs altering between the boards?

whenever i've had to set up hardware timers, i usually verify it by counting some number of events and looking at a wall clock with a second hand. for example, there should be some # of events in 5 minutes

I actually have a clock like that! And have done the same. Man does it get old fast slow soon enough.

I also use the counter setting on an instrument I have, either when a longer period of personal observation is untenable or the events are too frequent to count manually.

One way or another, always. :expressionless:

a7

USB logic analyzers can be had for very little money and make checking this sort of thing easy: https://www.sparkfun.com/products/15033

Yes. I have an older one and have gotten some mileage out of it.

As for making things easy, though, I have to say dragging a virtual one onto the desktop and clicking to wire it up is kinda hard to beat.

When it suffices.

a7

I don't have a Mega2560, but I am guessing that you want to change

DDRB |= B00000010; // set PB1 to output

to

DDRB |= B00100000; // set PB5 to output

This is the output for OCR1A based on this pinout.