Need help understand Nick Gammon's frequency counting code

Nick has some very cool frequency counting methods over at

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

with work really well out of the gate, but I am struggling to take those methods and turn them into stand alone functions. What I'd like to do is read the period of a somewhat variable input signal multiple times, and from that derive an average elapsedTime reading.

Further down that page, Nick has several examples that count based on input from hardware interrupts, or the input capture unit on D8, but they all have virtually identical code in their main loop:

void loop () 
  {

  if (!triggered)    // wait till we have a reading
    return;
 
  unsigned long elapsedTime = finishTime - startTime;
  
  Serial.print ("Took: ");
  Serial.print (float (elapsedTime) * 62.5e-9 * 1e6);  // convert to microseconds
  Serial.println (" uS. ");
  
  delay (500);

  prepareForInterrupts ();   
}   // end of loop

My first stumbling block is the if (!triggered) return; delay method. I'm used to seeing return used to end & send data back from a function that's called by the main loop, but in this case we still are in the main loop - so where does that return go? I understand the if (!triggered) condition, I just don't understand how this makes the program wait for input.

The other thing I'm struggling to understand is why prepareForInterrupts (); only seems to work if it is called from the very end of void loop(). If I move it to the front of the loop, or anywhere else in the loop, everything stops working. My goal is to read the period/frequency multiple times and then take an average value, but if I execute prepareForInterrupts (); another time, in advance of taking a second or third reading of elapsedTime everything stops.

Again my goal is to to turn the contents of that main loop example, into a separate function that can be called multiple times in a row from the main loop.

Any guidance is appreciated.

why not leave his loop and just execute it the number of times you want but instead of printing the variable each time put it in an array you increment each time it executes and once it reads those number of times you average the array and print it. Use a for statement.

My first stumbling block is the if (!triggered) return; delay method. I'm used to seeing return used to end & send data back from a function that's called by the main loop, but in this case we still are in the main loop - so where does that return go? I understand the if (!triggered) condition, I just don't understand how this makes the program wait for input.

It returns from loop back to main, which dutifully calls loop again. So it causes us to just sit there looping really fast and checking that flag until it is true.

Again my goal is to to turn the contents of that main loop example, into a separate function that can be called multiple times in a row from the main loop.

No, that's not a good goal. You wouldn't call this three times in a row like that. You might save up three readings, but you wouldn't block and wait for one. You keep doing your other jobs while that is running.

You're thinking like a story. Code that tells a story the loop does this thing and then after it finishes does the next thing and after that finishes does the next thing and when it's all done ends. That's blocking code and it works at first to learn the language but it gets too hard to handle real quick once the story gets complicated.

So instead you gotta think like a checklist. Your loop is just a big checklist that can at any given point in time tell you what the next step is and take one small step and move on. Lots of little processes taking one small step and letting the next one go. And in that way, many things can happen at the same time in your code.

Learning to think like a checklist instead of a story is a big lesson, but it is one you must learn if you want to get good at this. See this link about doing many things at once for more inspiration.

@rogertee

"why not leave his loop and just execute it the number of times you want?"

That is easy to do with the code as it stands, but this becomes problematic when you want to add this to some other piece of code - like for instance a datalogger which is already dependant on a huge number of things that are happening in the main loop. Those other things cant be repeated 3 times just for one input reading. But Nick's code seems to only let you make one reading after prepareForInterrupts (); gets called, and that can only be called at the very end of the main loop.

@Delta_G

"It returns from loop back to main, which dutifully calls loop again."

Thank you so much! The gap in my understanding was not realizing there existed an execution level outside the core loop. Somehow in my head that was a "special" loop with nothing below it. In hindsight I realize that's a Noob mistake, but its exactly the kind of conceptual gap I run into as I teach myself these concepts. Once I got the idea that the main loop is just an ordinary function like any other, except that it gets relaunched 'automatically", Nicks method of "returning" as a delay makes perfect sense.

It also gives me something to chew on as I try to make that whole process discrete enough to call from another programs main loop. Though it would be blocking, I just need some other kind of delay to check for the value of the triggered variable.

I will read through that thread you suggested to see if I can manage this in more effective non-blocking way, though it might take me a while to digest it all.

Thank you both for your suggestions!

Once I understood the way the return-delay worked, it was pretty easy to trigger the frequency reading with a separate function:

void getClockticks() {
  // set up for interrupts
  prepareForInterrupts ();
  while (!triggered) {
    //do nothing and wait till triggered becomes true
  }
}

which gets called in the main loop by

getClockticks();
long elapsedTime1 = finishTime - startTime;

This can now be done from the main loop as many times as I wish. I am currently reading the period of the input pulses three times, and then taking the average as the final.

That's cool as long as your code has absolutely nothing else to do. I would have let the loop keep spinning and let that code update as needed until it got three triggers and calculated a frequency.

I will graduate to non-blocking code eventually, but I'm still in the process of upgrading my biological cpu.

BTW this post was the result of my tinkering with Nicks Frequency counting code:

A bit silly, but it was kind of fun to see how well I could make it work. Still chewing on the rising/falling hysteresis issue...

When I'm finally brave enough to set some fuses, I will retry the method with the 328's internal clock to see if that works better.