jitter in squarewave output

Hello,
I'm using an Arduino board to generate three square waves having a specific phase relationship. The output has a significant amount of jitter when viewed on a scope. It appears that the HIGH duration and LOW duration do not remain fixed. I've tried the code on an Uno, Due, and Mega2560 board with the same results. The code is very short and I've included it below.

Code summary: The program generates three outputs: a square wave with a 6ms period (about 165Hz), a 30us pulse in the middle of the 165Hz HIGH period, and a 2nd 30us pulse in the middle of the 165Hz LOW period. The two 30us pulses can have as much as 5us of jitter. I'm using this to test a circuit card that takes in an analog signal (I'm substituting the 165Hz square wave for the analog signal at the moment) and samples it during the positive peak and the negative peak.

What could be causing the timing to be inconsistent?

Thanks,
Mike

//defines d0 - d2 as the digital outputs on pins D0 thru D2
const int d3 = 3; //d0 is the "analog" output to the Test Card's analog input.
const int d4 = 4; // sampling signal 1
const int d5 = 5; // sampling signal 2

int p; // delay const used in the timing of the analog signal
int q; // delay const used in the timing of the 30us sampling signalssignals

void setup() {
pinMode(d3, OUTPUT);
pinMode(d4, OUTPUT);
pinMode(d5, OUTPUT);

p = 1500; // length of 1/4 of square wave representing the analog signal in microseconds
q = 50; // length 0f sampling pulse in microseconds

digitalWrite(d3, LOW); // presets analog signal LOW
digitalWrite(d4, LOW); // presets sampling signal 1 LOW
digitalWrite(d5, LOW); // presets sampling signal 2 LOW
}

void loop()
{
digitalWrite(d3, HIGH); // sends analog signal HIGH
delayMicroseconds(p); // pause 1500us
digitalWrite(d4, HIGH); // sends sampling signal 1 HIGH
delayMicroseconds(q); // pauses 30us
digitalWrite(d4, LOW); // sends sampling signal 1 LOW
delayMicroseconds(p); // pauses 1500us
digitalWrite(d3, LOW); // analog signal goes LOW. End of one half of the cycle
delayMicroseconds(p); // pause 1500us
digitalWrite(d5, HIGH); // sends sampling signal 2 HIGH
delayMicroseconds(q); // pauses 30us
digitalWrite(d5, LOW); // sends sampling signal 2 LOW
delayMicroseconds(p); // pauses 1500us, end of loop. End of one full cycle. Repeats.
}

take a look at the docs for delayMicros

Mark

delayMicros should be fairly accurate for anything between 3 and 16000+ microseconds.
@zim42, please post a photo of the waveform as viewed on the scope so everyone can understand what you mean by jitter. If you mean it is not exactly on the timing you are wanting, it is very likely due to digitalWrite(). Using digital write, calls a function, and uses at least two macros to have a universally portable construct at the cost of accuracy in timing. Direct Port Manipulation will eliminate a lot of the inconsistent timing, which can be further trimmed by subtracting a few microseconds from your delay until you get the perfect timing.

Perehama,
Thanks for your suggestion. Attached is a short video clip of the scope. The HIGH duration is programmed for 30us, but actually varies between 37 and 45us. If it was consistent I could tweak it as needed. But, you can see in the video that it is changing. This is on the Mega2560. It happens on the Uno and Due both.
-Mike

VID_20180720_132356.mpg (826 KB)

zim42:
Perehama,
Thanks for your suggestion. Attached is a short video clip of the scope. The HIGH duration is programmed for 30us, but actually varies between 37 and 45us. If it was consistent I could tweak it as needed. But, you can see in the video that it is changing. This is on the Mega2560. It happens on the Uno and Due both.
-Mike

If you need that much precision, you may have to resort to assembler. But, it's not as bad as it might be.

for(int i = 0; i<600; i++) {
  __asm__ ( "nop;");
}

The only assembler here is the no-operation instruction, and really the only reason that is there is to keep the compiler from optimising the loop away. Each iteration of the loop should take a consistent number of processor cycles, and hence a consistent amount of time. You'll need to experiment with the number.

If you need to shave it even more precisely, then you may want to do the entire sequence in assembler, including using a port write rather than using digitalWrite() to toggle the pins.

A know a bloke who used a technique like this to talk to a game controller, which also used a protocol involving microsecond pulses.

Thank you Paul,

I'll check that out!

-Mike