Documente Academic
Documente Profesional
Documente Cultură
This tells the compiler that isr() is the function that will get called
when an interrupt occurs. Note that an interrupt service routine can
not take any arguments and does not return anything. This makes
sense because you do not call the ISR manually. It is called
automatically when an interrupt occurs.
Interrupts can be enabled and disabled. Each interrupt has an
interrupt enable bit in one of several registers. For example, the INT
pin interrupt enable bit (INTE) is found in the INTCON register as
shown below.
INTCON
7
6
GIE
PEIE
5
T0IE
4
INTE
3
RABIE
2
T0IF
1
INTF
0
RABIF
Also, before any interrupt can occur, the Global Interrupt Enable bit
must be set. This is called GIE and is also found in INTCON. PEIE is
the Peripheral Interrupt Enable bit and it must be set before a
peripheral interrupt can occur. However, the INT pin interrupt is
NOT a peripheral interrupt so this does not affect us right now.
The last thing we must decide for the external interrupt is if the
interrupt will occur on the rising or falling edge of the input signal.
This is controlled by the INTEDG bit of the OPTION_REG register. A
1 means interrupt on the rising edge, a 0 for falling edge.
OPTION_REG
7
6
RABPU INTEDG
5
T0CS
4
T0SE
3
PSA
2
PS2
1
PS1
0
PS0
Let's work from our example before. We will add a second LED to
pin RC7. We also attach a pull-down resistor to INT (RA2). Finally,
we will connect a switch between Vcc and the INT pin. If you don't
have a switch, just use a piece of wire!
#define LED1
names for our pins
PORTBbits.RB7;
#define LED1_TRIS
TRISBbits.TRISB7;
#define LED2
PORTCbits.RC7;
#define LED2_TRIS
TRISCbits.TRISC7;
//Create meaningful
}
int main()
{
TRISA = 0xFF;
TRISB = 0xFF;
TRISC = 0xFF;
ANSEL = 0x00;
ANSELH = 0x00;
LED1_TRIS = 0;
//LED1 is an output
LED2_TRIS = 0;
//LED2 is an output
INTCONbits.INTF = 0;
flag
OPTION_REGbits.INTEDG = 1;
INTCONbits.INTE = 1;
interrupt
INTCONbits.GIE = 1;
Enable
///////////////////////
// Main Program Loop //
///////////////////////
while(1)
{
LED1 = 1;
//Flash LED1
__delay_ms(500);
LED1 = 0;
__delay_ms(500);
}
return 0;
}
Above the main() routine I've declared the ISR. Since there is only
one interrupt enabled on our system, the routine just assumes that
an external interrupt has occured. If you enable more than one
interrupt, you will have to check the interrupt flags to determine
which triggered the interrupt. This can be accomplished simply with
IF statements:
void interrupt isr()
{
if(INCONbits.INTF == 1)
{
//reset the interrupt flag
INTCONbits.INTF = 0;
PORTCbits.RC7 = ~PORTCbits.RC7; //flip the bit
}
else if(...)
...
The ISR should always clear the interrupt flag that triggered the
interrupt. If you do not do this, another interrupt will occur
immediately after ISR completes. This will effectively halt the
operation of the main function. In our case, if we did not clear
INTCONbits.INTF, the interrupt would repeatedly occur even if the
switch wasn't closed again. LED2 would turn on and off extremely
quickly (several thousand times per second, depending on the
oscillator frequency and the exact number of instructions in the
ISR). LED1 would stop flashing entirely.
Once the interrupt flag has been cleared we can proceed to do
whatever it is we want when the interrupt occurs. In this case, we
are toggling LED2 as a visual indicator that the interrupt has indeed
occurred.
Interrupt Service Routines should not take long to run. Typically you
want to get in and out as fast as possible so that your main code
can continue.
Before it was mentioned that ISRs have no arguments and return
no values. If you need an ISR and the main routine to share
information, you can declare a global variable so that both routines
have access to it. However, if the ISR writes to that variable, your
main routine must be written in such a way that it can handle
sudden changes in that variable. This is necessary because an
interrupt can occur at any time.
Let's look at an example of where this could get us into trouble. In
the example code, the state of Port C is recorded each time an
interrupt occurs and the value is stored as a command. In the main
function, the commands coming in are checked to see if they are
non-zero, and if they are, they are saved as current command.
unsigned int index = 0;
char commands[8];
void interrupt isr()
{
//reset the interrupt flag
INTCONbits.INTF = 0;
index++;
if(index>=8)
index = 0;//cycle to the beginning of the array
commands[index] = PORTC;
PORT C
}
void main()
{
char currentCommand;
if(commands[index] != 0)
currentCommand = commands[index];
if(currentCommand == 1)
{
//handle command 1
}
else if(currentCommand == 2)
{
//handle command 2
}
... etc
}