I’m making a quadcopter with redundant flight control systems. The quad has 8 motors (2 on each arm) and 3 flight controllers. I’m trying to use the Arduino mega as a “voter” to determine if the flight controllers agree and which signals to send to the motors. Therefore, I have to interpret 24 PWM signals.
The code uses pin change interrupts, and I need all 24, but pin change interrupt pins 11-15 don’t correspond to any pins on the Arduino (based on the pinout diagram for the ATmega2560); they’re pins on the microcontroller that don’t have any breakout points. I solved that problem by connecting the individual microcontroller pins to some unused digital pins’ breakout points and scraping away the traces to those breakout points.
It seems to work fine when I remove channels 16-24, but the data are just crazy when I try to use all the channels. All the pulse lengths should be about 1000us when the motors aren’t spinning (2000us for 100% rpm), but when the quad is disarmed, these are the data I’m getting:
(In the image, channel 9 has a bad connection and that's why it's all zeroes)
The project "Read PWM, Decode RC Receiver Input, and Apply Fail-Safe" by Joop Brokking indicates that the ISRs take more time when you use more channels. The trend he described suggests that each ISR would take around 31us when using all 8 channels. Is that too much?
I suspect that the Arduino just isn’t fast enough to be up to the task, but I can’t figure out how to prove that, and I’m really at a loss for ideas.
If anyone has any insight into my problem it would be greatly appreciated. I’m a high school student working on a difficult science fair project.
At a quick glance, the interrupt service routines look quite "heavy". What level of accuracy do you require in the delivered result ?
What frequency is the PWM signal ? Around 50Hz ?
The frequency is 50hz and I think an accuracy of about 4us seems reasonable -- it doesn't need to be perfect because the Arduino's job is just to determine if the signals are diverging too much.
Also, I just graphed the seemingly random data I posted and found some patterns that may or may not be at all significant: PWM data graphed - Google Sheets
At least in the first 8 channels, the trend in the data appears to be a somewhat regular triangle (maybe somewhat sawtooth) wave. I think that this is a symptom of the error accumulating. I think that the Arduino is getting further and further behind the point at which the signal goes low until it misses that pulse's fall and triggers after the next pulse's fall.
Is this evidence that the Arduino isn't fast enough for the job? What alternatives might I be able to implement if I can't use a Mega?
Your printing is relatively slow and not synchronized with anything. That is, you are seeing snapshots at loop speed and the loop is being constantly interrupted by the incoming data. Further, as far as I see, you are not clearing out the old timer values so, if a channel does not receive new data, you will continue to display the old data.
However, you also have the potential problem that you are reading 4 byte timers which may be updated during the time you are reading them. The Mega is an 8 bit processor so its “atomicity” is units of one byte. The usual solution is to suspend interrupts, make a copy of the variable, restore interrupts, then process, in your case print, the variable. Here, you have to be careful not to suspend them for too long otherwise you will miss seeing changes in the data.
If you do start altering the timer variables in the loop(), say to reset them, you will also have to declare these as volatile.
A 32bit processor may be better in this case, however, it is not clear that the problem is also one of processor capacity.
This is all very good advice and I really appreciate it. I'm new to Arduino coding so my implementation of your ideas might be terrible. Anyway, tried to implement all of your suggestions in this code:
I couldn't figure out a way to suspend the interrupts, but I made them conditionally not do anything depending on the value of the variable "enableinterrupts." I don't know if that achieves the effect you were intending.