Delay() gives wrong time delay

Below in the function oneMillisecondDelay, delay(1) does generate a one millisecond delay, but in the function tenMillisecondDelay, delay(10) generates only a two millisecond delay.
I am using a scope to see what is happening

void setup()
{
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(5, INPUT_PULLUP);
pinMode(6, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);
pinMode(8, OUTPUT);
pinMode(9, INPUT_PULLUP);
pinMode(10, OUTPUT);
}//endof setup

// the loop function runs over and over again forever
void loop()
{
volatile int val = 1;
volatile unsigned int PatsDelay = 0x00;

val = digitalRead(9);
if (val == 0)
{

//tenMicrosecondDelay();

if (digitalRead(0) == 0) PatsDelay = 0xFE;
if (digitalRead(1) == 0) PatsDelay = 0xFD;
if (digitalRead(2) == 0) PatsDelay = 0xFB;
if (digitalRead(3) == 0) PatsDelay = 0xF7;
if (digitalRead(4) == 0) PatsDelay = 0xEF;
if (digitalRead(5) == 0) PatsDelay = 0xDF;
if (digitalRead(6) == 0) PatsDelay = 0xBF;
if (digitalRead(7) == 0) PatsDelay = 0x7F;

switch (PatsDelay)
{
  case 0xFE:
    oneMicrosecondDelay();
  break;

  case 0xFD:
    tenMicrosecondDelay();
  break;

  case 0xFB:
    hundredMicrosecondDelay();
  break;

  case 0xF7:
    oneMillisecondDelay();    
  break;

  case 0xEF:
    tenMillisecondDelay(); 
  break;

  case 0xDF:
    hundredMillisecondDelay(); 
  break;

  case 0xBF:
    oneSecondDelay(); 
  break;

  case 0x7F:
    tenSecondDelay(); 
  break;
 }//endof switch

}//endof if val == 0
}//endof main

void oneMicrosecondDelay()
{
asm volatile("cli");
PORTB = PORTB | 0x01;
asm("nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n nop\n");
PORTB = PORTB & 0xFE;
asm volatile("sei");

//flash LED for 1/2 second
PORTB = PORTB | 0x04;
delay(500);
PORTB = PORTB & 0xFB;

delay(3000);
}

void tenMicrosecondDelay()
{
asm volatile("cli");
PORTB = PORTB | 0x01;
delayMicroseconds(10);
PORTB = PORTB & 0xFE;
asm volatile("sei");

//flash LED for 1/2 second
PORTB = PORTB | 0x04;
delay(500);
PORTB = PORTB & 0xFB;

delay(3000);
}

void hundredMicrosecondDelay()
{
asm volatile("cli");
PORTB = PORTB | 0x01;
delayMicroseconds(100);
PORTB = PORTB & 0xFE;
asm volatile("sei");

//flash LED for 1/2 second
PORTB = PORTB | 0x04;
delay(500);
PORTB = PORTB & 0xFB;

delay(3000);
}

void oneMillisecondDelay()
{
asm volatile("cli");
PORTB = PORTB | 0x01;
delay(1);
PORTB = PORTB & 0xFE;
asm volatile("sei");

//flash LED for 1/2 second
PORTB = PORTB | 0x04;
delay(500);
PORTB = PORTB & 0xFB;

delay(3000);
}

void tenMillisecondDelay()
{
asm volatile("cli");
PORTB = PORTB | 0x01;
delay(10);
PORTB = PORTB & 0xFE;
asm volatile("sei");

//flash LED for 1/2 second
PORTB = PORTB | 0x04;
delay(500);
PORTB = PORTB & 0xFB;

delay(3000);
}

void hundredMillisecondDelay()
{
asm volatile("cli");
PORTB = PORTB | 0x01;
delay(100);
PORTB = PORTB & 0xFE;
asm volatile("sei");

//flash LED for 1/2 second
PORTB = PORTB | 0x04;
delay(500);
PORTB = PORTB & 0xFB;

delay(3000);
}

void oneSecondDelay()
{
asm volatile("cli");
PORTB = PORTB | 0x01;
PORTB = PORTB | 0x04;//LED on
delay(1000);
PORTB = PORTB & 0xFE;
PORTB = PORTB & 0xFB;//LED off
asm volatile("sei");

delay(3000);
}

void tenSecondDelay()
{
asm volatile("cli");
PORTB = PORTB | 0x01;
PORTB = PORTB | 0x04;//LED on
delay(10000);
PORTB = PORTB & 0xFE;
PORTB = PORTB & 0xFB;//LED off
asm volatile("sei");

delay(3000);
}

Are we talking about ms or µs?



BTW, this might be interesting to you.


//*********************************************************************************
// Version   YY/MM/DD   Description
// =======   ========   ===========
// 1.00      21/11/30   Working sketch for Atmega328/UNO
// 1.01      21/12/03   Using micros() for timing as it is more accurate

//*********************************************************************************
// Atmega328/UNO
// =============
// Note:
// PIND D0 to D7
// D0=0x01 D1=0x02 D2=0x04 D3=0x08 D4=0x10 D5=0x20 D6=0x40 D7=0x80
// PIND0   PIND1   PIND2   PIND3   PIND4   PIND5   PIND6   PIND7
//
// PINB D8 to D13
// D8=0x01 D9=0x02 D10=0x04 D11=0x08 D12=0x10 D13=0x20
// PINB0   PINB1   PINB2    PINB3    PINB4    PINB5

// PINC D14 to D19
// D14=0x01 D15=0x02 D16=0x04 D17=0x08 D18=0x10 D19=0x20
// PINC0    PINC1    PINC2    PINC3    PINC4    PINC5
//
//*********************************************************************************

//62.5ns delay
#define NOOP           asm("nop\n")                         

#define PULSE13        PINB = bit(PINB5); PINB = bit(PINB5) 

#define                diagnostics

#ifdef diagnostics
#define PULSE63_13     cli(); PINB = bit(PINB5); PINB = bit(PINB5); sei()
#define PULSE126_13    cli(); PINB = bit(PINB5); NOOP; PINB = bit(PINB5); sei()
#define PULSE189_13    cli(); PINB = bit(PINB5); NOOP; NOOP; PINB = bit(PINB5); sei()
#define PULSE252_13    cli(); PINB = bit(PINB5); NOOP; NOOP; NOOP; PINB = bit(PINB5); sei()
#define PULSE315_13    cli(); PINB = bit(PINB5); NOOP; NOOP; NOOP; NOOP; PINB = bit(PINB5); sei()
#define PULSE378_13    cli(); PINB = bit(PINB5); NOOP; NOOP; NOOP; NOOP; NOOP; PINB = bit(PINB5); sei()
#else
#define PULSE63_13     //Not defined 
#define PULSE126_13    //Not defined     
#define PULSE189_13    //Not defined     
#define PULSE252_13    //Not defined     
#define PULSE315_13    //Not defined     
#define PULSE378_13    //Not defined     
#endif

//*******************************************************************************

#define CLOSED            LOW
#define OPEN              HIGH

#define ENABLED           true
#define DISABLED          false

const byte pulsePin     = 13;
const byte LED_1        = 12;
const byte LED_2        = 8;

const byte mySwitch     = 2;

bool switchFlag         = DISABLED;

byte lastMySwitch       = OPEN;

unsigned int counter    = 0;

//timing stuff
unsigned long switchMillis;
unsigned long timeClosed;
unsigned long stateMachineMillis;


//*******************************************************************************
void setup()
{
  pinMode(pulsePin, OUTPUT);
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);

  pinMode(mySwitch, INPUT_PULLUP);

  //set up interrupt for pin 2
  attachInterrupt(digitalPinToInterrupt(mySwitch), pin_2_ISR, HIGH);

} //END of setup()

//*******************************************************************************
void loop()
{
  //to check loop() execution
  //PULSE63_13;

  //************************************
  //is it time to check the switches ?
  if (micros() - switchMillis >= 10000)
  {
    //to check 50ms TIMER duration
    //PULSE126_13;


    //restart the TIMER
    switchMillis = switchMillis + 10000;

    checkSwitches();
  }

  //************************************
  //toggle LED2 when mySwitch is CLOSED
  if (switchFlag == ENABLED)
  {
    byte num = 10;
    
    if(counter > 5)
    {
      num = 3;
    }
    
    for (byte x = 0; x < num; x++)
    {
      //to check how many iterations occur
      //PULSE252_13;

      //toggle the LED
      digitalWrite(LED_2, !digitalRead(LED_2));

    } //END of for()

    switchFlag = DISABLED;

    digitalWrite(LED_2, LOW);

  } //END of  if(switchState == CLOSED)

  //************************************
  //is it time to check the State Machine ?
  if (micros() - stateMachineMillis >= 50000)
  {
    //reset the TIMER
    stateMachineMillis = stateMachineMillis + 50000;

    //checkStateMachine();
  }

  //************************************
  //other non blocking code goes here
  //************************************

} //END of loop()


//*******************************************************************************
void pin_2_ISR()
{
  //provide an ISR hardware output trigger
  //PULSE378_13;

} //END of ISR


//*******************************************************************************
void checkStateMachine()
{
  switch (counter)
  {
    //**********************
    case 0:
      //do nothing;

      break;

    //**********************
    case 1:
      PULSE315_13;

      break;

    //**********************
    case 2:
      PULSE315_13;
      PULSE315_13;

      break;

    //**********************
    case 3:
      PULSE315_13;
      PULSE315_13;
      PULSE315_13;

      break;

    //**********************
    case 4:
      PULSE315_13;
      PULSE315_13;
      PULSE315_13;
      PULSE315_13;

      break;

    //**********************
    case 5:
      PULSE315_13;
      PULSE315_13;
      PULSE315_13;
      PULSE315_13;
      PULSE315_13;

      break;

  } //END of switch...case

} //END of checkStateMachine()


//*******************************************************************************
void checkSwitches()
{
  //************************************                          m y S w i t c h
  byte currentState = digitalRead(mySwitch);

  if (lastMySwitch != currentState)
  {
    //update to the new state
    lastMySwitch = currentState;

    //*******************
    //was the switch closed ?
    if (currentState == CLOSED)
    {
      counter++;

      timeClosed = millis();

      switchFlag = ENABLED;

      //to check timing of a digitalWrite()
      //PULSE189_13;
      digitalWrite(LED_1, HIGH);
      //to check timing of a digitalWrite()
      //PULSE189_13;

    }

    //switch was released
    else
    {
      //was this a long push ?
      if (millis() - timeClosed > 2000)
      {
        //reset the counter
        counter = 0;
      }

      digitalWrite(LED_1, LOW);

    }

  } //END of if (lastMySwitch != currentState)

} //END of checkSwitches()

delay() is essentially ignored when the interrupts are turned off, at least on an AVR-based Arduino.

asm volatile("cli");
PORTB = PORTB | 0x01;
delay(100);

What are you trying to do, and why do you think it is a good idea to turn interrupts on and off?

Please use code tags when posting code.

I'm trying to generate rather precise pules to test a component by placing 200vdc across the component.

oneMicrosecondDelay() works well. ( i'm using inline assembly NOPs).

tenMicrosecondDelay() and hundredMicrosecondDelay() both work well also using the Arduino delayMicroseconds() function.

oneMillisecondDelay() works using the Arduino delay() function.

But tenMillisecondDelay() and hundredMillisecondDelay() don't work properly - as well as oneSecondDelay() and tenSecondDelay() .

I can remove cli and sei since it will have negligible affect on the longer milli second delays. However that technique works well for the micro second delays.

I understand that delay() is implemented differently than delayMicroseconds(). Maybe that is the problem

Timers are really good for that, and operate independently of the processor.

Are you referring to micros() and millis() ?

@gooberpat are you reading the replies?

You didn't say what kind of Arduino you are using.

And ppl mentioning "timers" are talking about the capabilities of the microprocessor's internal mechanisms for counting and timing.

On which you could build a career.

a7

No, those are functions. I'm talking about hardware timers, which are often used to generate precise pulses. Independent of the processor.

Learn about them by studying this excellent tutorial.

Yes, I'm reading the replies which is why I said I would try removing the code that turned interrupts off.
That doesn't explain while my ten/hundred microsecondDelay functions work precisely.

I'm using an Arduino Uno.

I'm looking at https://www.arduino.cc/reference/en/.

I don't see any timer functions or commands only the aforementioned millis() and micros() functions.
Is there a timer() function I missed?

Read the discussion offered in post #8.

They do NOT always work "precisely", because they can be interrupted. You just haven't noticed the glitches.

The code is open source, so you can look to see how it is done. It executes a certain number of processor instructions, the timing of which is known.

Hardware timers operate independently of interrupts, and so are more reliable.

If you plan on turning the interrupts on and off, you do need to understand the consequences.

1 Like

the Arduino delay() function relies on timer interrupts; you can't do a cli() and expect delay() to continue to work properly.

You might want to look at the avr-libc _delay_ms() function - it creates a cycle-counting loop, and should be very accurate.

Why is this sort of thing so poorly understood?

People who don't understand it haven't done the required reading and experimenting.

This is a technical hobby.

Many people never need to understand it. The accuracy of the existing delay() functions, even with interrupts enabled, is good enough.

And millis() and micros() should also work here (but not for a delay of less than 10 micro seconds).
The whole idea behind programming is that you do not need to know how functions work. But you do need to know which restrictions apply to a certain function.
Like with a car: you do not need to know how the engine works, but you need to know that you should not take a speed hump at 80 mph. And that you should feed your car with gasoline or diesel or electricity.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.