Avoiding jitter in 8us pulse

Hi there!

I'm trying to manage pulses few to hunderds microseconds and I want them little precise. I'm not able to lower the jitter to some very small value.

The simple example code is here:

void setup()
{
pinMode(9, OUTPUT);
	 
// initialize Timer1
cli(); // disable interrupts
TCCR1A = 0;  //cleaning Timer1 registers
TCCR1B = 0;

OCR1A = 15; //setting value for comparison 15*8* 62ns = ~8us

TCCR1B |= (1 << WGM12); // turn on CTC mode

TCCR1B |= (1 << CS11); //  8 prescaler

TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt

sei(); // enable interrupts
}

ISR(TIMER1_COMPA_vect) // interrupt service routine 
{
  cli();
  PINB = 0x02;  // Toggle Pin 9  each passtrough
  sei();
}
 
void loop()
{
  // nothing here...
}

In this case, pulse lenght is about 8us and the jitter is +/- 2-3 clock cycles. In other situations, when the prescaler for example is 1, it is much worse. And sometimes the timer calculates a pulse lenght which differs up to 60% of pulse lenght, but this is rare (1/1000).

My question is: Am I doing something wrong? Is there a way to have the timing more precise? I cannot use the phase correct PWM mode, because I need to change the pulse lenght ( by changing OCR1A value) in the program.

The jitter scope is attached.

Many thanks for advices.

edit: the board is arduino Ethernet, 16MHz clock

I would think a 4-6 cycle jitter is pretty good using interrupts. Even with nothing in loop() the latency servicing the interrupt will vary.

If there is nothing else to do I would poll the appropriate timer flag, or start thinking about using some external hardware.


Rob

I think Nick Gammon did some pretty thorough timing measurements and showed that it took about 4uS to service an interrupt.

Jitter might also occur from the micros() timer which runs all the time.
I saw this recently when I was testing sending out a bunch of bytes via SPI in response to a clock pulse. I had to disable interrupts so the bytes could go out in sync and not be interrupted by the micros() timer.

CrossRoads:
I think Nick Gammon did some pretty thorough timing measurements and showed that it took about 4uS to service an interrupt.
http://www.gammon.com.au/forum/?id=11488

Jitter might also occur from the micros() timer which runs all the time.
I saw this recently when I was testing sending out a bunch of bytes via SPI in response to a clock pulse. I had to disable interrupts so the bytes could go out in sync and not be interrupted by the micros() timer.

Cool, could you post the code that disables just the timer0 interrupts? I always that it would be useful to have a function named stopTimer0(); and restartTimero(); to deal with issues like you had. I know this would just be setting or clearing a single bit in a timer0 register, but wrapping it into a user function might make it easier to remember how to do it.

Lefty

Thank you for replies.

The code is more complex so it is no possible to poll the timer flag for all the time. It is no important if the pulse setted to 100us will be +/- 20us, so the time for servicing an interrupt is not crucial. But it is imprortant the pulse will be always the same lenght. I thought the processor will service the interrupt always by the same cycles count.

Disabling Timer0 which uses the micros() by
TCCR0A = 0;
TCCR0B = 0;

reduced the jitter to strictly +/- 1 clock cycle. Not perfect but big improve!

Where the +/-1 clock difference is came from ??

rotyka:
Thank you for replies.

The code is more complex so it is no possible to poll the timer flag for all the time. It is no important if the pulse setted to 100us will be +/- 20us, so the time for servicing an interrupt is not crucial. But it is imprortant the pulse will be always the same lenght. I thought the processor will service the interrupt always by the same cycles count.

Disabling Timer0 which uses the micros() by
TCCR0A = 0;
TCCR0B = 0;

reduced the jitter to strictly +/- 1 clock cycle. Not perfect but big improve!

Where the +/-1 clock difference is came from ??

Most likely just the inherent jitter in the 16MHz crystal clock oscillator that clocks the AVR chip?

Lefty

retrolefty:
Most likely just the inherent jitter in the 16MHz crystal clock oscillator that clocks the AVR chip?

I don't mean phase shift of the sloping edge, but shift of the edge by whole cycle. It is few orders higher than the oscillator jitter.

But may I easily turn off Timer0? It breaks my serial communication ...

edit:

CrossRoads:
I think Nick Gammon did some pretty thorough timing measurements and showed that it took about 4uS to service an interrupt.
http://www.gammon.com.au/forum/?id=11488

it explains well that Timer0 is needed to Serial communication. But when I stop the timer in whole code, it doesn't affect the jitter anyway. It is still +/- 4 cycles, which is terrible.

Isn't there a mode for the timers in which the timer sets or clears a pin without needing to call an interrupt routine?

...R

Indeed, you don't have to use interrupt, look in datasheet:
Table 16-1.
Compare Output Mode, non-PWM
COM1A1/COM1B1 COM1A0/COM1B0

Table 16-2.
Compare Output Mode, Fast PWM (1)
COM1A1/COM1B1 COM1A0/COM1B0

rotyka:
Disabling Timer0 which uses the micros() by
TCCR0A = 0;
TCCR0B = 0;

reduced the jitter to strictly +/- 1 clock cycle. Not perfect but big improve!

Where the +/-1 clock difference is came from ??

Interrupt handling is between instructions, not all instructions are 1 cycle - make sense?

BTW - better to disable just the interrupt on timer0 then analogWrite () will still work on pins 5 and 6:

  TIMSK0 = 0x00 ;
[quote]could you post the code that disables just the timer0 interrupts?

I think it was just the cli; sei; kind of thing.
Issued one to dis-able interrupts, did my SPI blasting of data, did the other to re-enable interrupts. My 43 bytes of data then took about 45uS to send out very smoothly.

From Nick's page:

Disable interrupts - If you need to disable interrupts you can "clear" the interrupt flag like this:

noInterrupts (); // or ...

cli (); // clear interrupts flag

Enable interrupts - You can enable interrupts with the function call "interrupts" or "sei" like this:

interrupts (); // or ...

sei (); // set interrupts flag

Either method has the same effect, using "interrupts" / "noInterrupts" is a bit easier to remember which way around they are.

The default in the Arduino is for interrupts to be enabled. Don't disable them for long periods or things like timers won't work properly.

CrossRoads:

[quote]could you post the code that disables just the timer0 interrupts?

I think it was just the cli; sei; kind of thing.
Issued one to dis-able interrupts, did my SPI blasting of data, did the other to re-enable interrupts. My 43 bytes of data then took about 45uS to send out very smoothly.

From Nick's page:

Disable interrupts - If you need to disable interrupts you can "clear" the interrupt flag like this:

noInterrupts (); // or ...

cli (); // clear interrupts flag

Enable interrupts - You can enable interrupts with the function call "interrupts" or "sei" like this:

interrupts (); // or ...

sei (); // set interrupts flag

Either method has the same effect, using "interrupts" / "noInterrupts" is a bit easier to remember which way around they are.

The default in the Arduino is for interrupts to be enabled. Don't disable them for long periods or things like timers won't work properly.

I'm aware of how, when, and why of enabling and disabling globally all interrupts, I was just wondering if you knew the values and register name to just enable or disable the timer0 that is put into service by the arduino init function.

Lefty

I do not.

Is going to sleep an option?

"The timer is also set up to generate an interrupt, which has the sole purpose of waking the processor up from sleep, so it can draw each line with exactly the same delay after the pulse. If it wasn't asleep, there would be a variation of two to three clock cycles (since an interrupt cannot occur during a single instruction) and this gives very bad-looking "jitter" on the screen."

Looks like it would be, if Nick did that to fix the problem it would be ok. So how would that go?

  1. start pulse
  2. start timer
  3. go to sleep
  4. wake up
  5. stop pulse

1 and 2 could be swapped.

As there are latencies involved you would have to adjust the timer value to get the 8uS.


Rob