Unwanted Extra Milliseconds using while loop for timing

So I’m working on a project were I need to generate a pulse with a variable frequency and duration, frequency ranges from 20Hz to 200Hz, and duration around 250us. The code is also watching button inputs, controlling an LCD, outputting serial communication, and reading analog inputs, I’d also like to keep outputting I2C open as a possibility.

After each pulse the code calculates the millis() and microseconds till the next pulse by adding the calculated microsecond delay to the previous time of pulse. Then the code does the less time sensitive things, like query buttons and sending out communications (this leaves anywhere from 5 to 14ms before next pulse). Followed by a while loop that keeps querying the button states till it is in the last millisecond before pulse, then a while loop to freeze it up on the last millisecond, and then finally a microsecond delay.

void loop()
{
   if (conFlag & B00000001){
     conButtonCheck();//Only Stores Button State
     conButtonReact();//Compairs/Debounces/and reacts to buttons
     timeCalcNext();  //Calculates timeNPulseMs and timeNPulseUs    
     conButtonCheck();
     serialSend(0,timeNPulseMs); //Currently debugging, but still needed
     serialSend(1,timeNPulseUs); //These two lines only store information to be sent     
     conButtonCheck();
     if (disFlag & B10000000){disUpdateMenu();} // Updates Display if needed
     conButtonCheck();
     serialSend(4,2); //Sends stored information via serial
     conButtonCheck();
   ///Coarse Millis Delay loop
     while (millis() < timeNPulseMs){
       delayMicroseconds(400);
       conButtonCheck();}
   ///Fine Millis Delay Loop
     while (millis() <= timeNPulseMs){}
   ///Microsecond Delay  
     delayMicroseconds(timeNPulseUs);
   ///Pulse
     digitalWrite(12,HIGH);
     delayMicroseconds(rVals[7]);
     digitalWrite(12,LOW);
          
     timeLPulseMs = timeNPulseMs;
     timeLPulseUs = timeNPulseUs;
}  
 
void conButtonCheck(){
  bI[bICurrent]=~(PIND >> 3);
  bICurrent++;
}

The issue is, looking at the output on an oscilloscope, my pulses have the right frequency, but random pulses shift, or are delayed by 1 or 2 milliseconds (about 5%-20% of the pulses). The timing is always off by milliseconds and not microseconds. So for 70Hz, I get mostly 14.286ms periods and few 15.286ms and even 16.286ms periods, and since the next pulse is based off when the last was suppose to happen, and equal number of 13.286ms and 12.286ms respectively. The button querying isn’t long enough to cause issues, even if it was, I’d expect the microseconds to be off too.

Is this due to system related internal interrupts? I tried turning off interrupts, but the code just froze up, was that due to the microsecondDelay function. Can I turn off only the interrupts not needed for the delay functions? Or is there a way to let the system do the routines that are messing up my timing at another point?

Thanks for any inputs.

There is an interrupt that fires off every millisecond to -- you guessed it -- update the value returned by millis(). If you really need to calculate time differences and to keep interrupts off, you can configure a timer and then read the value from the counter directly. That would mean it rolls over less than every second (depending on how you configure it) but it might be enough.

Alternatively, you could try turning off interrupts and using micros() instead of millis(). That might let you be able to do the same thing.

You still need the interrupts for micros(), for the overflows.

There will always be a bit of jitter with this sort of thing, although a millisecond sounds like a lot. Looking at your code, though, it looks like you might be giving yourself millisecond resolution, not microsecond resolution:

while (millis() <= timeNPulseMs){}

You are probably better off using a hardware timer in one of the PWM modes, using side A as the amount to count to (the frequency) and side B as the pulse width (duty cycle). Example on this page:

http://www.gammon.com.au/forum/?id=11504