Best Method for running expensive code every N milliseconds

I am working on a quadrocopter control library. I wish to have all of the quadrocopter functions happen 'in the background' so that the user doesn't have to manually update the quadrocopter in the loop(){} section of the code. Here is the goal:

Quadrotor quad;
void setup() {
quad.Init(length, mass, thrust, capacity);
//Anything else they want to initialize
}

void loop() {
//this section is clear
//they can do anything they want here
//and it won't impact quadrotor performance
}

I was able to get it working using timer interrupts every 10 milliseconds, but all serial communication failed once it was enabled. I am assuming that serial writes use interrupts, and interrupts are blocked during the handling of another. The main update code currently takes about 5-6 milliseconds to run, so there isn't a lot of leeway with the timing. I am willing to run it slightly less often, but probably no less than 50-60 times per second. There are also other methods that will be called about 5-10 times per second, and one that is called once per second.

Here are the requirements for a solution:

It must work with serial in and out! A software-based serial is acceptable, but ONLY if it is very fast! I have a 10hz GPS module that will send a lot of data through UART, plus two-way communication with a secondary processor.

It must run every N milliseconds regardless of whatever code the user puts in their loop(){} function. The interrupt idea solved this problem.

It must be transparent to the user. I want the user to initialize the quadrotor object, and then forget about it. They can access public methods on the quadrotor to get information and set parameters, but all of the crucial math must happen without them knowing. Quadrocopters are inherently unstable and will crash if the control methods aren't called.

I can post code if you want, but there is A LOT so far and I'm not even done! I'm open to any ideas that will work, and will try anything out as soon as it's suggested.

Thanks! -Eric

I'd look into having an interrupt service routine for your 10ms timer that re-enables interrupts when it runs, so the serial communication can still happen. Have a look at the "Nested Interrupts" section here:

http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

If you re-enable interrupts in your every-10-milliseconds ISR you have to be very careful to get out of the ISR within 10ms else you will re-interrupt yourself and the code could get stuck this way. It would be better to disable the 10ms timer interrupt as soon as you enter the ISR, then re-enable it right before you exit.

-- The Rugged Motor Driver: two H-bridges, more power than an L298, fully protected

EricMiddleton:
I was able to get it working using timer interrupts every 10 milliseconds, but all serial communication failed once it was enabled. I am assuming that serial writes use interrupts, and interrupts are blocked during the handling of another.

That’s true. However the interrupt merely lets it start another transfer. In other words, the interrupt is when the sending buffer is empty, and that lets the code put another byte into the buffer. I haven’t timed it, but I would guess around 10 to 20 uS.

A software-based serial is acceptable, but ONLY if it is very fast!

It won’t be, so you can forget that.

I’m inclined to agree with RuggedCircuits. You could conceivably re-enable interrupts in the timer ISR, but for safety turn off timer interrupts while you do so (the timer can be left running).

6 mS sounds a lot, make sure you are using minimal data sizes where possible (eg. byte instead of int, where practical). Maybe you can unwind loops, use table lookups, etc.

Also don’t overdo volatile variables. If the variable is only used in the interrupt, it doesn’t have to be volatile.

The interrupt takes a long time because I'm using a lot of floating point math, which is all emulated. I might eventually dabble with fixed point math, but I'll stick with the floating point for now.

I tried your suggestion of enabling all other interrupts in the handler, and it worked perfectly! Thank you very much!