Is it a bad idea to run *everything* in a single interrupt (PID, serial handling etc)

Status
Not open for further replies.

howiemnet

Active member
So: I've got a Teensy doing PID at 2KHz, triggered by an interrupt.

I need to handle communications as well - reading new setpoints into one of my buffers, and sending out requests for more data when necessary.

Ideally, I'd like to stick this in the same interrupt as the PID. There are no timeconsuming operations; all the serial comms is handled byte by byte so I'm never waiting for anything.

Any gotchas here? Any reason I can't have an empty loop() function? I like the simplicity of having this sort of thing:

Code:
void myInterruptHandler() {
   calcPID();  // do this at the start of each cycle so we don't get jitter
   handleIncomingSerialByte();  // only ever handle one byte per cycle
   checkBuffersAndSendRequestForMoreDataIfNecessary();
   prepareNextSetpoint();       // this interpolates the next setpoint 
                                //  from the buffered data
}

Now I think about it, perhaps I should just stick everything in the main loop instead, and rely on this:
Code:
void loop() {
    if (myElapsedMicros > 500) {
      myElapsedMicros -= 500);
      calcPID etc etc etc

.... Hmm.

Suppose my question is, for the least jittery timing, does it make a difference, interrupt driven, vs an ordinary loop with elapsedMicros providing timing?

***** UPDATE *****

Arrgg... I *knew* there was a reason I wanted to do it on an interrupt: if I have a load of Teensys hooked up together, I'll need to have one generate a clock pulse and the rest can do all their PID stuff (as above) in an interrupt. Makes sense to have them all work on an interrupt, then, just that the master Teensy will be using a timer, and the rest will be watching for a clock pin.

So - any gotchas stuffing everything in an interrupt?
 
Last edited:
If the code in your ISR executes fast enough so that it doesn't slow down other important parts of your system, an empty loop() is fine. If there are *no* other parts in your system, you can probably do it that way.

BUT as your code evolves (and it probably will), you should consider setting a flag in the ISR, like so:

Code:
static bool update = false;
void myInterruptHandler() {
  update = true;
}

void loop() {
  if(update)
  {
    update = false;
    calcPID();  // do this at the start of each cycle so we don't get jitter
    handleIncomingSerialByte();  // only ever handle one byte per cycle
    checkBuffersAndSendRequestForMoreDataIfNecessary();
    prepareNextSetpoint();       // this interpolates the next setpoint 
                                //  from the buffered data
  }
}
That way other ISRs have a better chance of executing with low latency. You can also do something in the ISR and some more in the main loop. The problem with this approach (as above, with a bool) is that you might miss "double" flags, when the code handling the flag doesn't execute before the ISR is called a second time.
 
The main gotcha with putting *everything* into an interrupt that runs periodically is your code must always fully complete its work, in all cases, before the interrupt occurs again.

For fairly simple projects, where you do essentially the same thing every interrupt, this can work very well.

For more complex projects, and especially ones where you must communicate with other systems and deal with errors, timeouts and other possible problems, anticipating all cases and assuring proper timing can become quite difficult. When you or someone else needs to add another feature to the code months or years later, such a rigid timing requirement can be easily forgotten or overlooked, even if it's well documented.
 
Cool. I think there should be enough slack (at 96MHz) to do all I need to do in less than the 500us I've got, but I'll test the hell out of it.

Am I likely to hit any issues with any of the housekeeping interrupts if the processor spends much of its time in my interrupt? I don't really know what other interrupts are set up in the background on Arduinos and Teensys. I'm guessing at:

- UART
- millis() and micros()

The UART I need to work, but I don't care much about latency (I'm buffering things and need to receive only around 150 bytes/sec).
Micros() and millis() I don't need.

Anything else I should know about?

(Your comments about compexity and timing edge-case dangers are valid and noted. This is a complex project, but I'm trying to keep all the non-linear stuff on the Mac, keeping the Teensy end of things as simple and streamlined as possible. Watch me eat my words in 6 months time)
 
The main gotcha with putting *everything* into an interrupt that runs periodically is your code must always fully complete its work, in all cases, before the interrupt occurs again.

Yes but that's true for every peridocally task, regardless if it is running in an interrupt or not..
Technically, the new interrupt will be "chained" to the old one if the old was too slow. This means it will be called as soon as possible after the old one in this case.
 
Status
Not open for further replies.
Back
Top