What is the minimum period over which loop() is executed?

I’ve been trying to determine if I need to add debouncing code to my project. In my investigations I noticed that the user code seems to execute at an period (approximately every 1ms). This is much greater than I would anticipate with a 120MHz processor. Below is the code I flashed to the Photon.

void loop() {
    
    ButtonOut = digitalRead(ButtonPIN);

    if(ButtonOut == HIGH && ButtonLast == LOW) {
        ButtonCount = ButtonCount + 1;
        ButtonLast = HIGH;
    } else if (ButtonOut == LOW) {
        ButtonLast = LOW;
    }
    
    if (SerialTimer < millis()) {
        SerialTimer = millis()+1000;
        PotOut = analogRead(PotPIN);
        Serial.print("Button Count = ");
        Serial.print(ButtonCount);
        Serial.print(" , Level = ");
        Serial.print(PotOut);
        Serial.print(" , duration(us) = ");
        LoopPeriod = EndTime-StartTime;
        Serial.println(LoopPeriod);
    }

    StartTime = EndTime;
    EndTime = micros();

}

This results in the following serial terminal output:

Button Count = 8 , Level = 693 , duration(us) = 1000
Button Count = 9 , Level = 697 , duration(us) = 1000
Button Count = 9 , Level = 695 , duration(us) = 997
Button Count = 11 , Level = 694 , duration(us) = 1000

First, do I have an error in my test case? If I understand it correctly in the loop() immediately before the serial transmission, only five statements are evaluated ButtonOut, IF(FALSE), IF(FALSE), StartTime, and EndTime. At most this should generate a few dozen assembly instructions.

Second, is there a documentation source I should be reading about the interaction between loop() and the system processes that would contain this information? I have reviewed https://docs.particle.io/reference/firmware/core/#automatic-mode and searched for “loop” in the documentation.

Most importantly, the topic question: What is the minimum period over which loop() is executed on the Photon in its default mode of operation (ie., SYSTEM_MODE(AUTOMATIC), etc.)?

Finally, What other configuration settings and/or use methodologies should I look at to find cases where the execution rate would be fast enough, I might need to debounce switches. Again assuming we’re using Automatic mode and just the web IDE, nothing fancy. I’m using the Photon for a College Freshman EE course and I’m certain my students will find some way to break it! :smile:

Thanks for your time,
David Orser

1 Like

There are several threads dealing with this or similar questions
e.g.
Timing of the calls to loop()
Spark Core Execution Speed (deals with Core but the general idea stays the same)

If you have more time critical tasks you’d have to use ***other SYSTEM_MODE***s or interrupts or both, till FreeRTOS arrives in the Particle universe.

What do you have in mind?
Fast enough for what?
What kind of switch would you debounce that bounce less than 1ms?
How do you intend to debounce?

For my understanding even with a loop() frequency of 1000Hz (or even 200Hz as for the Core) debouncing is advisable.

Thanks for the links, those are perfect (question 2). It does look like my code is executing nearly as fast as possible (question 1). I flashed the same program on Core and got ~5ms for the execution time.

And in summary to the main question:
1.) The Core has a minimum period of execution of the loop() function of 5ms
2.) The Photon has a minimum period of execution of the loop() function of 1ms

Both of these are in standard modes (online, SYSTEM_MODE(AUTOMATIC), etc.)

As for the need for debouncing (Final question), yes, there are plenty of switches that can bounce longer than 2 or 10ms (2xLoopPeriod). (Un?)Fortunately my current switch seems to bounce for substantially less than 2ms, thus making it very hard to show students how they might need to debounce ;). I need to do some more investigation with an expanded set of switches. However, I’ll probably split that conversation off into a different thread as it is ancillary to the topic posted.

Thanks for the feedback,
David

1 Like

Hi @Phoenix

Just one more point–if you turn the cloud “off” with a different system mode, those numbers get fast by 3 orders of magnitude. Just a thought.

We are building a beta of multithreading to be released next week (hopefully…this is hard stuff!) this will free the loop execution time from anything the system is doing so should make for high frequency loop execution even when the cloud is connected.

However, there is only one MCU, so even with a fast loop execution in the best case, there may be times the system has to time slice between active threads. For very quick events like a fast button debounce, it’s possible this is still missed by a loop.

Rather than polling the button state in a tight loop (which is wasteful of resources and not very precise), I suggest you look at using GPIO interrupts via attachInterrupt to drive the debounce logic, so no transitions are missed.

2 Likes

I, too, am wondering about bouncing. In the code below, when I press the button, I get many lines of output. I told my students yesterday that I thought it was due to bouncing … but is it really due to bouncing, or just the loop being called multiple times, or a combination thereof? Is there anyway to tell, without an oscilliscope (not that I’d know what to do with one :sunglasses: )

void loop() {
    if(digitalRead(switchy)==LOW /*&& Time.now() - previousTime >= 1*/){ // button pressed
        digitalWrite(led,HIGH);
        Serial.println(Time.now());
        // Particle.publish("Testing-Meh",String(Time.now()));
       // Serial.println(eventName);
        //Serial.println(data);
        previousTime = Time.now();
    } else {
        digitalWrite(led,LOW);
    }

}

I’d say that’s multiple interations of loop()

If you don’t want to use edge triggered interrupts, there are two common aproaches to tackle that thing

void loop()
{
  static int previousState = -1;
  int currentState = digitalRead(switchy);

  // only act on change of state
  if (!currentState && currectState != previousState)
  {
    previousState = currentState;
    ...
  }
}

// or

void loop()
{
  if(!digitalRead(switchy))
  {
    ...
    // trap the flow till button is released
    while(!digitalRead(switchy)) Particle.process();
  }
}

or the cheap cop out to add a delay longer than the typical press period.

Button bounce is usually in the one digit millisecond range (if at all).

To tell if your switch bounces you’d best use a tight loop and pinReadFast() where you count the changes of state for one press or release over a given periode.
My own tests with standard but quality switches gave me always very little bounce.