Chris O'Byrne - YAVRTOS

Interrupt Service Routines


Defines

#define TASK_ISR(vector, do_task_switch)
 The macro for ISRs.

Detailed Description

Interrupt Service Routines are functions that are called when an interrupt occurs, and are defined with the TASK_ISR() macro.

Define Documentation

#define TASK_ISR ( vector,
do_task_switch   ) 

The macro for ISRs.

The arguments are

  • vector the ISR vector
  • do_task_switch something that evaluates non-zero if the ISR should trigger a task switch. This could be a constant, a function call, a macro, anything. Note that the tick interrupt must, by definition, trigger a task switch. It is highly recommended that this macro always evaluates to non-zero - the only time where it would be safe for it to evaluate to zero is if it does absolutely nothing to any task, semaphore, mailbox or mutex (i.e. if it doesn't do anything that could un-suspend a suspended task).
See the example application and how do I write an ISR for examples of usage.

The WinAVR™ ISR() macro can also be used to define ISRs - however, when using ISR() as opposed to TASK_ISR(), you cannot make use of any YAVRTOS API call from within your ISR, and your ISR will not (necessarily) be using the system stack.

Taking this macro apart line-by-line, we have

 // The standard way of calling an ISR
 void vector(void) __attribute__ ((signal,naked,__INTR_ATTRS));
 // The ISR itself
 void vector(void) {
        // Save the entire CPU context to the stack (which could be a task stack or the system stack)
        save_cpu_context();
        // Global interrupts were enabled just before this ISR was launched (otherwise it would not have
        // launched), and are disabled upon entry into the ISR. Set the interrupt enable bit at the location of
        // the saved status register on the saved stack so that when we restore the CPU context, interrupts will
        // be re-enabled.
        *(((uint8_t*)SP)+1) |= _BV(SREG_I);
        // Now, we have either interrupted a task or interrupted another ISR. system.interrupted_task will
        // be non-zero if a task has already been interrupted - i.e. if we have interrupted an ISR
        // Note that task_switch() also sets system.interrupted_task - any ISRs that manage to run during
        // the brief period when task_switch() enables interrupts should not subsequently run task_switch()!
        if (system.interrupted_task) { // We have interrupted an ISR (or we have interrupted task_switch())
                // Execute the macro to see if we should do a task switch
                if (do_task_switch) {
                        // The macro may have enabled interrupts - disable them again
                        cli();
                        // Set bit 1 of system.interrupted_task - the bit that signals that a task switch is
                        // required. We don't do the task switch just yet, as we have interrupted another ISR,
                        // so we need to return to that ISR first. The ISR that interrupted the task will be the one
                        // to actually perform the task switch (see below)
                        // If we have actually interrupted task_switch(), then setting bit 1 of system.interrupted_task
                        // will have no effect - but we were doing a task switch anyway!
                        system.interrupted_task = 3;
                } else {
                        // Task switch not required (this time). Make sure that interrupts are still disabled
                        cli();
                }
        } else { // We have interrupted a task
                // Save the stack pointer
                current_task->sp = (uint8_t *)SP;
                // Switch to the system stack
                SP = (uint16_t) system.stack_top;
                // Set the system.interrupted_task so that subsequent ISRs will know that they have interrupted
                // an ISR, not a task. This has the side-effect of disabling switch_task() - all API calls check
                // system.interrupted_task and do NOT perform a task switch if it is non-zero.
                system.interrupted_task = 1;
                // Execute the macro and see if we should do a task switch
                if (do_task_switch) {
                        // The macro could have enabled interrupts - disable them
                        cli();
                        // Signal to ourselves that we need a task switch - bit 1 of system.interrupted_task is set
                        // when a task switch is required
                        system.interrupted_task = 3;
                } else {
                        // Make sure that interrupts are disabled
                        cli();
                }
                // At this point, any ISRs that interrupted us have finished.
                // Now, if a task switch is required, perform it!
                if (system.interrupted_task & 2) {
                        // switch_task() will (eventually) reset system.interrupted_task, and never "returns"
                        switch_task();
                }
                // A task switch is not required - restore the stack pointer so that we return to the task
                SP = (uint16_t) current_task->sp;
                // We are about to return to the task - reset system.interrupted_task
                system.interrupted_task = 0;
        }
        // Return to whatever we were doing before this ISR was called
        restore_cpu_context();
        __asm__ volatile ("ret" ::);
 }


YAVRTOS and YAVRTOS documentation Copyright © 2007-2008 Chris O'Byrne. Email - chris <at> obyrne <dot> com