You write an ISR as you would write a program function. ISR's, however, must be treated somewhat differently by the compiler and we must therefore have a way to tell the compiler that a specific function is an ISR and not a regular function. Basically, there are three things we need to do in writing an ISR. These things are:
Initialization: Initialization of a hardware
interrupt is done in the function init()
. The basic
things that must be done are
init()
function. This function is called
at the start of any program you write.
void init(void){ asm(" sei"); CONFIG = 0x04; BAUD=BAUD38K; SCCR1 = 0x00; SCCR2 = 0x0C; _Time=0; TMSK2 = 0x0D; TMSK1 |= OC4I; TFLG1 |= OC4F; TOC4 = TCNT + 256; asm(" cli"); }The first and last line of this function are assembly language instructions that disable/enable interrupt handling. Essentially, the first instruction
asm("sei")
sets the I bit in the condition code
register, thereby disabling any interrupts. This prevents
us from catching an interrupt while we're trying to
initialize the system. The last command asm(" cli")
clears the I bit, thereby re-enabling interrupt handling.
The next 4 instructions after the sei
instruction
are used to disable the watchdog timer (CONFIG=0x04
)
and to setup the asynchronous serial interface (SCI).
Setting up the SCI involves declaring the agreed upon baud
rate (BAUD=BAUD38K
) and setting the appropriate
values in the SCI's control registers, SCCR1
and
SCCR2
.
The following code segment from init()
is specific
to the initialization of the output compare 4 interrupt.
_Time = 0; TMSK2 = 0x0D; TMSK1 |= OC4I; TFLG1 |= OC4F; TOC4 = TCNT + 256;The first instruction zeros a global variable
_Time
.
This global variable is a counter that keeps track of the
number of times OC4han
has been executed. It will
be incremented each time OC4han
is executed. Due to
its global nature, this variable is accessible by all
functions in your program. So all of your functions can use
_Time
as a variable holding the current real-time.
The second instruction (TMSK2 = 0x0D
) sets the rate
at which TCNT
is incremented. In particular, this
choice will increment TCNT
once very 407
nanoseconds. The instruction TMSK1 |= OC4
arms the
output compare 4 interrupt by setting bit OC4I
. The
instruction TFLG1 |= OC4F
acknowledges any
previously received OC4 interrupts, thereby paving the way
for your program to catch the next OC4 event. Finally, we
set the output compare register TOC4
to the next
time we want the OC4 event to occur. This new deadline is
obtained by taking the current value of TCNT
and
adding 256 to it. So the next OC4 event should occur
Interrupt Handler: The interrupt handler's source
code will also be found in kernel.c
. ISR's must be
handled differently than regular functions. In particular,
the compiler needs to ensure that the return from an ISR is
handled by the rti
assembly command.
You write the ISR as if it were an ordinary function. But
you need to alert the compiler that this function is an
interrupt handler. This alert is provided through a
pre-compiler direction known as a pragma
. The
actual code in kernel.c
is shown below:
#pragma interrupt_handler OC4han() void OC4han(void){ TOC4 = TOC4 + 256; _Time = _Time+1; TFLG1 |= OC4; }
The first line
#pragma interrupt_handler OC4han()tells the compiler that the following function is to be treated like an interrupt handler. The body of
OC4han
has only three statements. In general, we
want interrupt handlers to be extremely short. In
particular, the line
TOC4 = TOC4 + 256;resets the output compare register
TOC4
to the next
time we want this interrupt to be generated. The next line
of code
_Time = _Time + 1;increments a global variable
_Time
. The final
line of OC4han()
is
TFLG1 |= OC4;This line acknowledges the interrupt by setting the appropriate bit in
TFLG1
to one.
Interrupt Vector: The final thing we need to do is
declare the interrupt vector associated with our interrupt
handler. This is accomplished using another pragma
.
The code you would need to provide is:
extern void OC4han(); #pragma abs_address:0xffe2; void(* OC4_handler[])()={ OC4han }; #pragma end_abs_addressThe program used here tells the compiler to install the interrupt handler at the absolute address
0xFFE2
.
This absolute address is the interrupt vector for output
compare 4 event (refer back to the earlier table of
interrupt vectors). With these lines of code we've tied our
ISR OC4han
to the OC4 event.