Two Functions Run at Same Time

Hi All,

I would like to have two functions (rollMenu() and sweep()) run at the same time, if that is possible. Or likely I’ll have to find another way?

The first function rollMenu() will be running at startup. All it does is move a cursor around an LCD when a twiddle knob is turned. This function works by setting a parameter value and calling LCDp() to send the cursor to the current parameter value. menu() just writes some labels to the LCD. It works great!

rollMenu() is now called in the loop() function, which either calls rollMenu() or config(), depending on how a boolean mode variable is set (if mode, then rollMenu). The mode variable is set via a button initiated ISR (mode = !mode;), and that part of the code works fine too.

sweep() is a time sensitive function, so this is where I enter the thick fog…

My thought is that I will need to first remove the while (mode == 1) from both functions, and move the first menu() call somewhere else (or else it makes the LCD flicker with each loop). Then I might add the sweep() function call to the end of rollMenu(). I suspect the drawback will be that sweep() may take several seconds, even minutes to complete. I do not know what happens if the twiddle knob is turned during this time - maybe the counts are buffered as one or just not counted at all, and then rollMenu() would step through so fast and back to sweep() that that it would not longer be functional.

void rollMenu() {
  menu();
  LCD.print("?c2");                  // set cursor: 0=off, 2=underline, 1=highlight
  delay(100);
  LCDp();
  long newPosition = myEnc.read();
  oldPosition = newPosition;
  while (mode == 1) {
    long newPosition = myEnc.read();
    if (newPosition < oldPosition) {
      p--;
      if (p < 0) {
        p = 60;
      }
      LCDp();
    }
    if (newPosition > oldPosition) {
      p++;
      if (p > 60) {
        p = 0;
      }
      LCDp();
    }
    oldPosition = newPosition;
  }
}


void sweep() {
  tuning_word_F1 = (F1 * 0x100000000LL)/124999100L;
  tuning_word_F2 = (F2 * 0x100000000LL)/124999100L;
  tuning_word_fStep = (fStep * 0x100000000LL)/124999100L;
  while (mode == 1) {
    long timeSweepStart = micros();
    long timeStepStart = micros();
    while (tuning_word <= tuning_word_F2) {
      long timenow = micros();
      if ((timenow - timeStepStart) >= timeDwell) {
        timeStepStart = timenow;
        SetFrequency(tuning_word);
        tuning_word = tuning_word + tuning_word_fStep;
      }
    }
    tuning_word = tuning_word_F1;
  }
}

The CPU can only do one thing at a time. Your computer multi-tasks by quickly switching between tasks, but that’s not something I’d attempt on the Arduino. (Even if you have a multi-processor system, your operating system is running more processes than you have CPUs/cores.) It gets rather tricky, because you have to save all of the variables and the state of the internal CPU registers, and then return the state when you return to that process/task.

You might be able to do something with interrupts, but I’ve never used interrupts on an Arduino, so I can’t help you. And if the interrupting process takes a long time, you are back to the same problem…

So… Maybe you can call one (faster) function from inside the other (slower) function.

Or, maybe you can somehow write functions so that they do one quick-loop, and and then return… When the function gets called again, it continues with whatever it was doing from where it left-off last time. I sort-of think this is the best approach.

Well, I guess I do have to consider both ends of the spectrum WRT sweep(). That function is affected by the timeDwell variable, which is currently limited on the fast end by Arduino (or my bad programming) to around 15 us per itteration (that actually seems as fast as it will go, given all the data transfer and calculations that go on), and the slow end which is limited by the value an int variable can hold (in us), and with long dwell times nothing else is really happening so the knob and LCD updates would not affect operation there.

I guess I could do it the other way and throw in a rollMenu() call on the sweep function and see how that affects the sweep speed on the fast end.

What you're trying to do is quite straight forward and doesn't need anything clever. You just need to structure your code so that it is non-blocking. In other words it doesn't stop and wait for events to happen; instead, it tests whether an event has happened and handles it, then moves on to handle other possible events. You can see this technique demonstrated in the blink without delay example sketch - and see the basic blink sketch for the blocking equivalent. As you can see, non-blocking code is slightly more complex because you have to hold the current state explicitly in data rather than having it implied by where you are in the code, but once you get your head around the concept it's very easily to implement and scales up to enable you to carry out a huge number of concurrent activities without any of them needing to be dependent on each other.

C2: Well, I guess I do have to consider both ends of the spectrum WRT sweep(). That function is affected by the timeDwell variable, which is currently limited on the fast end by Arduino (or my bad programming) to around 15 us per itteration (that actually seems as fast as it will go, given all the data transfer and calculations that go on), and the slow end which is limited by the value an int variable can hold (in us), and with long dwell times nothing else is really happening so the knob and LCD updates would not affect operation there.

I guess I could do it the other way and throw in a rollMenu() call on the sweep function and see how that affects the sweep speed on the fast end.

As Peter Says have a look at the Blink Without Delay sketch to get an idea and then have a look at the SimpleTimer library that wraps a lot of this functionality up.

It does take some thought to be able to move from a system with blocking (typically done in a repetitive loop) or using a delay() thrown in.

Once you understand the interaction between your various components (and it will take some rewriting) it is quite easy to queue up different processes to run as and when need and a main control loop waiting for input.

Craig

Except for the while loops, I thought I was non-blocking. The sweep() function does, however, send a lot of data (SPI).

I was able to measure the time of rollMenu at 12 us with no activity on the knob. Since my fastest sweep speeds are 15 us per point, adding rollMenu to the sweep function will double my sweep times. I could go with some overhead and live with 50 us per point...

With activity on the knob, up to 7500 us. I think that will be fine as well since the user will be moving the dial to make a change, and may accept that. 7.5 ms dwells would be too long.