Arduino Timer Conflicts
I mentioned in an earlier post that getting Robot V.2 running was more trouble than expected.
The main reason for this was an odd problem - which I mistook for a hardware issue - with the analogWrite() function in the Arduino Mega 2560-compatible board I was using.
A call to analogWrite() function using digital pin 10 appeared to crash the device. Subsequent calls to analogWrite() targetting any other PWM-capable pin were apparantly failing.
...would fail and appear to hang the processor. Doing the same thing on pin 9 wouldn't crash the program, but it wouldn't do what it was supposed to do either.
It took a weekend of troubleshooting to arrive at the conclusion that the "fix" was not to use pin 10 or pin 9 for PWM. No big deal, right? It's not like the thing is short on PWM outputs. You have 15 to choose from and the loss of a couple wasn't a show-stopper for this project.
I wrote a short test program to demonstrate the issue and confirm that it actually existed, updated my notes and went on with life.
Some while later, I decided to document the problem in this forum in hopes of possibly saving someone - you, for example - a two-day troubleshooting marathon. I should mention that I could find no direct reference to an issue like this anywhere online. Nor had the board manufacturer (OSEPP)'s tech support people ever heard of it. I was pretty sure something about the Mega 2560 was busted, and I described it in those terms to everyone I communicated with about it.
Well there's a reason why nobody has heard of this particular problem with the 2560. It doesn't exist.
What there IS, is a resource conflict between analogWrite() on pins 9 and 10, and the extensive IRremote library that enables control of an Arduino device with an infrared remote control. Both facilities use interrupts generated by timers in the 2560 chip. There is one timer for each two PWM-capable pins. Pins 9 and 10 are serviced by the same timer (TIMER2, to be precise), which also happens to be same one IRremote wants for its own purposes.
IRremote and analogWrite() thus wind up trying to use the same timer, to the detriment of both.
The same code worked properly on a more prosaic Uno R3 board because IRremote uses a different timer when it's running on that device. Digging deeper would presumably have uncovered some other timer-dependant function on the Uno board that didn't work.
I disovered all of this whilst editing the first version of this post.
I generated a very minimal sketch to demonstrate the problem and guess what? There was no problem. That was when I realised that all of the testing I'd done in the past had been done with IRremote included in the code.
The program wasn't crashing; only IRremote and analogWrite() were. I didn't twig to that because I was using an IR remote control in the test program. Lame, huh? And I used to earn my daily bread as a diagnostic service technician. Facepalm moment...
The net of it is, if you're using IRremote in your Mega 2560-based project, you can't use analogWrite() on pins 9 and 10 without altering the IRremote library code to force it to use a different timer. This is well documented in the Arduino forums.
You may not want to do this. I don't blame you. Decades of compiling Linux programs has left me with considerable distaste for modifying development system libraries. You often wind up just breaking something else and if you're not obsessive about documenting such things, you might not remember that a freshly-installed development system needs a few library hacks before your existing code will compile successfully. (A variation on this theme almost cost me a full summer's development work once upon a time, but that's another story).
If that's the approach you prefer, just don't use analogWrite() on pin 9 or 10 in a project that also uses IRremote. Don't use tone() and noTone() either, btw. As the illustrations below demonstrate, they don't play nice with IRremote either.
Timer conflicts between different device drivers come out of the closet with increasing frequency as one undertakes more complex projects with Arduinos. Usually they can be resolved but sometimes you get into a Rubik's Cube situation where the only alternative to throwing the thing against the wall is to remove some functionality from the project.
If you're lucky, the conflict will show up when you try to compile a program. The linker will usually generate an error message that you can search online for with surprisingly copious results.
Otherwise, as in my case, your program will appear to have compiled successfully but will be afflicted with some obscure and probably hard to diagnose dysfunctionality.
The moral of the story is that an understanding of the hardware resources at your disposal - particularly with regard to timers and interrupts - is highly desirable for aspiring advanced Arduino coders. At the very least, know that trouble of this sort is a clear and present danger when working with peripherals such as sonar pingers, servos, and remote controls.
A bit of common sense with regard to testing and troubleshooting doesn't hurt either.
Thirty-five years ago, interrupt and I/O conflicts were high on the suspect list with problems like this. If memory serves, the original IBM PC had eight interrupt channels, several of which were reserved by the system and accomodating the requirments of all the expansion boards one might want to shove into a well-equipped system, was sometimes just not possible. Today, with lavishly-equipped PC southbridge chips and do-everything development environments, they're about the last thing we suspect.
If this post saves you from repeating an adventure of the sort described here, I'll be happy.
Bruce Grant's Web Page is licensed under a Creative Commons Attribution 4.0 International License.
Based on a work at http://www.wbga.ca..