The advice of doing very little or as little as possible in an ISR is
generally good advice but it is recommending a solution based on an
assumed set of circumstances and limitations.
Doing time consuming things from an Interrupt function is definitely possible
and there are situations where it makes sense and is useful.
That said there are some reasons for the "do as little as possible" advice particularly
in an AVR Arduino environment since time consuming processing simply
can't be done with the same ease as doing it from non interrupt level.
There are 2 main reasons to keep an ISR short and sweet.
- Blocking/delaying other interrupts
- Reentrancy issues.
Both of these issues can be solved and often are in other environments.
Often times they can even be worked around on the AVR as well, but they are impossible
to fully solve on the AVR in a generic way particularly when using Arduino.
And solving these is not something
that could be done from an Arduino sketch and would come with limitations.
The first issue is a quite obvious: Blocking other interrupts
It relates to the simplistic interrupt handling inside the AVR.
The AVR does not have multiple interrupts levels so while the handling of
simultaneous interrupt sources are prioritized,
there is only a single interrupt level. What that means is that by default, once an interrupt happens,
no other interrupt can happen until that ISR runs and leaves.
If your ISR takes longer than the time until something else wants to interrupt like say the timer interrupt,
you delay the processing of that interrupt.
With Arduino, the timer interrupt wants to run every 1ms.
Writing to the LCD is VERY slow. Even a single character can take more than 1ms
particularly on something like i2c.
A clear, or HOME takes 20+ ms.
This problem can be easily solved by simply having the ISR
re-enable interrupts in the ISR routine. Sounds easy enough.....
However, doing that causes the 2nd problem to rear it's ugly head.
The second issue is not always as obvious: Reentrancy
ISR Reentrancy has 2 basic problems.
- RAM/stack usage.
- code/resource re-entrancy
Reentrancy problem: RAM/Stack usage
Yes an AVR ISR can re-enable interrupts to allow other ISRs to run,
but depending on the type of interrupt, its frequency and the duration of the ISR code
that re-enables the interrupts (i.e. a long/slow ISR routine)
that may not work since the that same ISR itself might be re-entered and if it occurs enough times,
eventually causes a stack over flow.
At a minimum it can cause things to get processed out of order.
Consider a case were an interrupt occurs every 5ms. If that ISR triggers something that writes
to the LCD (which takes longer than 5ms) then the LCD processing can't complete before another interrupt
happens (which interrupts the previous LCD write operation).
In that case, "re-ordering" would occur, since the second ISR LCD update would happen before the
first completed.
If this continues to happen, you could end up with a stack overflow,
since each ISR and call to code that writes to the LCD uses up stack space, eventually the stack
is overflowed and the processor will crash.
On the other hand if you know that ISR is a slow ISR or never nests (can't occur again until the ISR leaves)
then sure you can re-enable interrupts and take as long as you want inside the ISR and it would
not have a problem with stack overflow. (it still migh have code/resource re-entrancy issues)
Reentrancy problem: code/resource reentrancy
This is the not so obvious part of reentrancy.
Most code is not written to be re-entrant. That means that the code cannot
properly handle more than a single instance running at one time.
While not truly possible on a single processor like the AVR, it logically happens
when the code is called from an ISR.
Since the ISR interrupts whatever is running it can interrupt anything.
Suppose a lcd.write() operation is in the middle of running (say from inside your loop() code)
and then an ISR interrupts it and then the ISR processing code calls lcd.write().
From a logical point of view the second lcd.write() is now running concurrently with the first lcd.write().
This has the potential to screw things up since the first lcd.write() was talking
to the LCD hardware and now some other piece of code is going talk to that same
piece of hardware, perhaps before it is ready.
This is a problem all over in the Arduino code because the core code and libraries
are simply not designed for re-entrance.
So even if your code is capable of re-entrancy, it may call an Arduino core code
function that is not.
Now if there is the need to write to the LCD an ISR as part of some "last resort" debugging
mechanism and you can closely control how it is done (i.e. like perhaps never call the LCD code anywhere
but inside the ISR) It is definitely possible and might be useful.
--- bill