First, the answer to the original question:
Q) How much code in an ISR is too much?
A) Exactly how much code
you choose to put in the ISR is between you and your application, your hardware and your specification. The while( true ) Sleep(); implementation is as valid as forgoing interrupts altogether. As for some sort of limit: If it breaks your application or gets in the way debugging or makes the code unreadable then it's too much code.
In general.
ISRs can be tricky to debug so keeping them simple is a good idea.
ISRs divert flow from the rest of your code so keeping the time spent in an ISR to a minimum is
generally a good idea.
ISRs are inefficient when using a high level language, there are quite a few instructions to be executed between the occurrence of the interrupt signal and the start of your code and possibly a similar number of instructions to be executed when the ISR returns.
Lets look at some PIC16 timer code as an example.
void interrupt isr(void)
{
// *************** Your Code **************
if( PIR1bits.TMR1IF ) {
TMR1H = 231; // 50ms = 59286 @ 1:8 divider
TMR1L = 150; // 50ms @ 1:8
........
20: void interrupt isr(void)
21: {
0004 00FE MOVWF 0x7E
0005 0E03 SWAPF STATUS, W
0006 1283 BCF STATUS, 0x5
0007 1303 BCF STATUS, 0x6
0008 00A1 MOVWF 0x21
0009 0804 MOVF FSR, W
000A 00A2 MOVWF 0x22
000B 080A MOVF PCLATH, W
000C 00A3 MOVWF 0x23
000D 087F MOVF 0x7F, W
000E 00A4 MOVWF 0x24
000F 118A BCF PCLATH, 0x3
0010 2EDA GOTO 0x6DA
0011 158A BSF PCLATH, 0x3
0012 2C87 GOTO 0x487
// *************** Your Code **************
22: if( PIR1bits.TMR1IF ) {
06DA 1C0C BTFSS PIR1, 0x0
06DB 2EDD GOTO 0x6DD
06DC 2EDE GOTO 0x6DE
06DD 2F41 GOTO 0x741
23: TMR1H = 231; // 50ms = 59286 @ 1:8 divider
06DE 30E7 MOVLW 0xE7
06DF 008F MOVWF TMR1H
24: TMR1L = 150; // 50ms @ 1:8
06E0 3096 MOVLW 0x96
06E1 008E MOVWF TMR1L
That is an additional fifteen instructions of preamble on every interrupt plus whatever appears at the end of the ISR. This is not the end of the world but if all you are doing is setting a flag, that flag = true statement balloons from two instructions to more than seventeen. Worth remembering.
Also note in the above code the timer counter (TMR1H, TMR1L) reset appears as close to the start as possible, this simplifies our following maths. It most certainly appears before any additional conditional statements. Why?
The timer count begins when the timer counter is set and the interrupt occurs when the count wraps around to zero, let's call this time Ti. There is interrupt latency Tl, the preamble shown above Tp and the time it takes your code to reach the counter reset Tr.
You want an interrupt time of Ti but you actually get a time of Ti + Tl + Tp + Tr. If this discrepancy is important to you then your reset counter value can be adjusted: ResetCounterValue = CalculatedResetCounterValue - Tl - Tp - Tr. (For PIC16 at least, your micro may count down instead of up, or reload the counter automatically which invalidates most of this discussion as it is then immune to all code overhead, or do something entirely unique).
Now consider the case where the timer counter reset code appears at the bottom of your interrupt code. If the code is free of all conditionals Tr is a larger value but still constant. If, however, the code contains one of more conditional statements Tr becomes variable which is not good if your goal is a precise and repeatable timer interrupt. In the simple implementation of this latter case the best you can hope for is an interrupt time Ti
between the end of one interrupt and the start of the next. This may be what you need, especially if the time to execute you code is greater than your interrupt time, but it is not the way to count out absolute time.
Here's an similar example i wrote a while back for the wiki
http://www.eevblog.com/wiki/index.php?title=Embedded_Programming_Tips_and_Tricks_for_Beginners#Repeating_Code_Using_Timer_Overflow_Interrupts
Nice work
In light of the preceding discussion it's worth noting the there are 100us between interrupts, the interrupts themselves occur at intervals of (100 + overhead)us where overhead = the time for 0, 1, 2 or 3 conditional statements to execute + the fixed code time.
Not a criticism but I would have gone for
#define complete 0
#define Restart1000ms 10000
#define Restart100ms 1000
#define Restart10ms 100
volatile uint16_t clk1000ms = Restart1000ms; // initializing them to restart(1) insures there is an initial delay cycle when
volatile uint16_t clk100ms = Restart100mst; // the code first starts, otherwise they would all happen at
volatile uint8_t clk10ms = Restart10ms; // once during mcu poweron, which may not be desirable.
ISR (TIMER0_OVF_vect)
{
// Timer clock is 1mhz, timer is 8bit.
// Now we set the timer register to 156 so it takes 100 timer clocks to overflow.
// This will mean the interrupt code executes at 1mhz/100 = 10000Hz
TCNT0 = 156; // subtract ISR overhead from this value for more precise 100us interrupts
if ( clk1000ms != complete ) {
clk1000ms--;
}
if ( clk100ms != complete ) {
clk100ms--;
}
if ( clk10ms != complete ) {
clk10ms--;
}
}
void main(void)
{
if ( clk1000ms == complete ) {
clk1000ms = Restart1000ms;
// put code here to run every second
}
........
... and yes, those reset counts may be out by 1.
If you want to get funky:
#define complete 0
#define clk1000ms_Restart 10000
#define clk100ms_Restart 1000
#define clk10ms_Restart 100
#define RestartClock( clk ) clk = clk##_Restart
volatile uint16_t clk1000ms = clk1000ms_Restart; // initializing them to restart(1) insures there is an initial delay cycle when
volatile uint16_t clk100ms = clk100ms_Restart; // the code first starts, otherwise they would all happen at
volatile uint8_t clk10ms = clk10ms_Restart; // once during mcu poweron, which may not be desirable.
void main(void)
{
if ( clk1000ms == complete ) {
RestartClock( clk1000ms );
// put code here to run every second
}
........
But fancy defines are often frowned upon these days and funky is not so good for tutorials