The 8051 and its derivatives provide a number of hardware interrupts that may be used for counting, timing, detecting external events, and sending and receiving data using the serial interface. The standard interrupts found on an 8051 are listed in the following table:
Interrupt Number |
Interrupt Description |
Address |
0 |
EXTERNAL 0 |
0003h |
1 |
TIMER/COUNTER 0 |
000Bh |
2 |
EXTERNAL 1 |
0013h |
3 |
TIMER/COUNTER 1 |
001Bh |
4 |
SERIAL PORT |
0023h |
unsigned int interruptcnt;
unsigned char second;
void timer0 (void) interrupt 1 using 2
{
if (++interruptcnt == 4000) /* count to 4000 */
{
second++; /* second counter */
interruptcnt = 0; /* clear int counter */
}
}
The interrupt attribute takes as an argument an integer constant in the value range 0 to 31. Expressions with operators are not allowed, and the interrupt attribute is not allowed in function prototypes. The interrupt attribute affects the object code of the function as follows:
The contents of the Special Function Registers ACC, B, DPH, DPL, and PSW, when required, are saved on the stack at the function invocation time.
All working registers that are used in the interrupt function are stored on the stack if a register bank is not specified with the using attribute.
The working registers and special registers that were saved on the stack are restored before exiting the function.
The function is terminated by the 8051 RETI instruction.
The following sample program shows you how to use the interrupt attribute and what the code generated to enter and exit the interrupt function looks like. The using function attribute is also used in the example to select a register bank different from that of the non-interrupt program code. However, because no working registers are needed in this function, the code generated to switch the register bank is eliminated by the optimizer.
.
.
.
stmt level source
1 extern bit alarm;
2 int alarm_count;
3
4
5 void falarm (void) interrupt 1 using 3 {
6 1 alarm_count *= 2;
7 1 alarm = 1;
8 1 }
.
.
.
ASSEMBLY LISTING OF GENERATED OBJECT CODE
; FUNCTION falarm (BEGIN)
0000 C0E0 PUSH ACC
0002 C0D0 PUSH PSW
; SOURCE LINE # 5
; SOURCE LINE # 6
0004 E500 R MOV A,alarm_count+01H
0006 25E0 ADD A,ACC
0008 F500 R MOV alarm_count+01H,A
000A E500 R MOV A,alarm_count
000C 33 RLC A
000D F500 R MOV alarm_count,A
; SOURCE LINE # 7
000F D200 E SETB alarm
; SOURCE LINE # 8
0011 D0D0 POP PSW
0013 D0E0 POP ACC
0015 32 RETI
; FUNCTION falarm (END)
.
.
.
In the example above, note that the ACC and PSW registers are saved at offset 0000h and restored at offset 0011h. Note also the RETI instruction generated to exit the interrupt.
The following rules apply to interrupt functions.
No function arguments may be specified for an interrupt function. The compiler emits an error message if an interrupt function is declared with any arguments.
Interrupt function declarations may not include a return value. They must be declared as void (see the above examples). The compiler emits an error message if any attempt is made to define a return value for the interrupt function. The implicit int return value, however, is ignored by the compiler.
The compiler recognizes direct invocations of interrupt functions and summarily rejects them. It is pointless to invoke interrupt procedures directly, because exiting the procedure causes execution of the RETI instruction which will affect the hardware interrupt system of the 8051 chip. Because no interrupt request on the part of the hardware existed, the effect of this instruction is indeterminate and usually fatal. Do not call an interrupt function indirectly through a function pointer.
The compiler generates an interrupt vector for each interrupt function. The code generated for the vector is a jump to the beginning of the interrupt function. Generation of interrupt vectors can be suppressed by including the NOINTVECTOR control directive. In this case, you must provide interrupt vectors from separate assembly modules. Refer to the INTVECTOR and INTERVAL control directives for more information about the interrupt vector table.
The C51 compiler allows interrupt numbers within the range 0 to 31. Refer to your 8051 derivative document to determine which interrupts are available.
If floating-point operations are executed in the interrupt program, the state of the floating-point register routines must be saved. Saving these registers is not necessary when no other program sections execute floating-point operations. You may use the fpsave and fprestore routines from the C51 standard library to save and restore the floating-point state. For example:
#include <math.h>
struct FPBUF intsave;
float x, y, z;
void intfunc (void) interrupt 1 using 2
{
fpsave (&intsave);
x = y * z;
fprestore (&intsave);
}
Refer to the Library Reference chapter for additional information about these routines.
Functions that are invoked from an interrupt procedure must function with the same register bank as the interrupt procedure. When the NOAREGS directive is not explicitly specified, the compiler may generate absolute register accesses using the register bank selected for that function (by the using attribute or by the REGISTERBANK control). Unpredictable results may occur when a function assumes a different register bank than the one currently selected. See the section entitled, Register Bank Access, earlier in this chapter for more information.