You are on page 1of 8

8051 Tutorial: Interrupts

As the name implies, an interrupt is some event which interrupts normal program execution. As stated earlier, program flow is always sequential, being altered only by those instructions which expressly cause program flow to deviate in some way. However, interrupts give us a mechanism to "put on hold" the normal program flow, execute a subroutine, and then resume normal program flow as if we had never left it. This subroutine, called an interrupt handler, is only executed when a certain event (interrupt occurs. The event may be one of the timers "overflowing," receiving a character via the serial port, transmitting a character via the serial port, or one of two "external events." The !"#$ may be configured so that when any of these events occur the main program is temporarily suspended and control passed to a special section of code which presumably would execute some function related to the event that occured. %nce complete, control would be returned to the original program. The main program never even &nows it was interrupted. The ability to interrupt normal program execution when certain events occur ma&es it much easier and much more efficient to handle certain conditions. 'f it were not for interrupts we would have to manually chec& in our main program whether the timers had overflown, whether we had received another character via the serial port, or if some external event had occured. (esides ma&ing the main program ugly and hard to read, such a situation would ma&e our program inefficient since wed be burning precious "instruction cycles" chec&ing for events that usually dont happen. )or example, lets say we have a large $*& program executing many subroutines performing many tas&s. +ets also suppose that we want our program to automatically toggle the ,-." port every time timer " overflows. The code to do this isnt too difficult. JNB TF0,SKIP_TOGGL !PL P"#0 !L$ TF0 SKIP_TOGGL : ### /ince the T)" flag is set whenever timer " overflows, the above code will toggle ,-." every time timer " overflows. This accomplishes what we want, but is inefficient. The JNB instruction consumes 0 instruction cycles to determine that the flag is not set and 1ump over the unnecessary code. 'n the event that timer " overflows, the 2,+ and 2+3 instruction require 0 instruction cycles to execute. To ma&e the math easy, lets say the rest of the code in the program requires 4! instruction cycles. Thus, in total, our code consumes $"" instruction cycles (4! instruction cycles plus the 0 that are executed every iteration to determine whether or not timer " has overflowed . 'f were in $*5bit timer mode, timer " will overflow every *#,#-* machine cycles. 'n that time we would have performed *## JNB tests for a total of $-$" instruction cycles, plus another 0 instruction cycles to perform the code. /o to achieve our goal weve spent $-$0 instruction cycles. /o 0.""06 of our time is being spent 1ust chec&ing when to toggle ,-.". And our code is ugly because we have to ma&e that chec& every iteration of our main program loop.

+uc&ily, this isnt necessary. 'nterrupts let us forget about chec&ing for the condition. The microcontroller itself will chec& for the condition automatically and when the condition is met will 1ump to a subroutine (called an interrupt handler , execute the code, then return. 'n this case, our subroutine would be nothing more than. !PL P"#0 $ TI )irst, youll notice the 2+3 T)" command has disappeared. Thats because when the !"#$ executes our "timer " interrupt routine," it automatically clears the T)" flag. 7oull also notice that instead of a normal $ T instruction we have a $ TI instruction. The 38T' instruction does the same thing as a 38T instruction, but tells the !"#$ that an interrupt routine has finished. 7ou must always end your interrupt handlers with 38T'. Thus, every *##-* instruction cycles we execute the 2,+ instruction and the 38T' instruction. Those two instructions together require - instruction cycles, and weve accomplished the same goal as the first example that required $-$0 instruction cycles. As far as the toggling of ,-." goes, our code is 9-: times more efficient; <ot to mention its much easier to read and understand because we dont have to remember to always chec& for the timer " flag in our main program. =e 1ust setup the interrupt and forget about it, secure in the &nowledge that the !"#$ will execute our code whenever its necessary. The same idea applies to receiving data via the serial port. %ne way to do it is to continuously chec& the status of the 3' flag in an endless loop. %r we could chec& the 3' flag as part of a larger program loop. However, in the latter case we run the ris& of missing characters55what happens if a character is received right after we do the chec&, the rest of our program executes, and before we even chec& $I a second character has come in. =e will lose the first character. =ith interrupts, the !"#$ will put the main program "on hold" and call our special routine to handle the reception of a character. Thus, we neither have to put an ugly chec& in our main code nor will we lose characters.

%&at 'ents !an Tri((er Interrupts, an) *&ere )o t&e+ (o,


=e can configure the !"#$ so that any of the following events will cause an interrupt.

Timer " %verflow. Timer $ %verflow. 3eception>Transmission of /erial 2haracter. 8xternal 8vent ". 8xternal 8vent $.

'n other words, we can configure the !"#$ so that when Timer " %verflows or when a character is sent>received, the appropriate interrupt handler routines are called. %bviously we need to be able to distinguish between various interrupts and executing different code depending on what interrupt was triggered. This is accomplished by 1umping to a fixed address when a given interrupt occurs.

Interrupt Fla( Interrupt -an)ler .))ress 8xternal " '8" Timer " Timer $ /erial T)" T)$ 8xternal $ '8$ """-h """(h ""$-h ""$(h

3'>T' ""0-h

(y consulting the above chart we see that whenever Timer " overflows (i.e., the T)" bit is set , the main program will be temporarily suspended and control will 1ump to """(H. 't is assumed that we have code at address """(H that handles the situation of Timer " overflowing.

Settin( /p Interrupts
(y default at powerup, all interrupts are disabled. This means that even if, for example, the T)" bit is set, the !"#$ will not execute the interrupt. 7our program must specifically tell the !"#$ that it wishes to enable interrupts and specifically which interrupts it wishes to enable. 7our program may enable and disable interrupts by modifying the '8 /)3 (A!h .
Bit Na0e Bit .))ress : * # 9 0 $ " 8A 5 5 8/ 8T$ 8B$ 8T" 8B" A)h A8h A@h A2h A(h AAh A4h A!h 1planation o2 Fun3tion ?lobal 'nterrupt 8nable>@isable Andefined Andefined 8nable /erial 'nterrupt 8nable Timer $ 'nterrupt 8nable 8xternal $ 'nterrupt 8nable Timer " 'nterrupt 8nable 8xternal " 'nterrupt

As you can see, each of the !"#$s interrupts has its own bit in the '8 /)3. 7ou enable a given interrupt by setting the corresponding bit. )or example, if you wish to enable Timer $ 'nterrupt, you would execute either. 4O5 I ,608& or S TB T1 (oth of the above instructions set bit - of '8, thus enabling Timer $ 'nterrupt. %nce Timer $ 'nterrupt is enabled, whenever the T)$ bit is set, the !"#$ will automatically put "on hold" the main program and execute the Timer $ 'nterrupt Handler at address ""$(h. However, before Timer $ 'nterrupt (or any other interrupt is truly enabled, you must also set bit : of '8. (it :, the ?lobal 'nterupt 8nable>@isable, enables or disables all interrupts simultaneously. That is to say, if bit : is cleared then no interrupts will occur, even if all the other bits of '8 are set. /etting bit : will enable all the interrupts

that have been selected by setting other bits in '8. This is useful in program execution if you have time5critical code that needs to execute. 'n this case, you may need the code to execute from start to finish without any interrupt getting in the way. To accomplish this you can simply clear bit : of '8 (2+3 8A and then set it after your time5criticial code is done. /o, to sum up what has been stated in this section, to enable the Timer $ 'nterrupt the most common approach is to execute the following two instructions. S TB T1 S TB . Thereafter, the Timer $ 'nterrupt Handler at "$(h will automatically be called whenever the T)$ bit is set (upon Timer $ overflow .

Pollin( Se7uen3e
The !"#$ automatically evaluates whether an interrupt should occur after every instruction. =hen chec&ing for interrupt conditions, it chec&s them in the following order.

8xternal " 'nterrupt Timer " 'nterrupt 8xternal $ 'nterrupt Timer $ 'nterrupt /erial 'nterrupt

This means that if a /erial 'nterrupt occurs at the exact same instant that an 8xternal " 'nterrupt occurs, the 8xternal " 'nterrupt will be executed first and the /erial 'nterrupt will be executed once the 8xternal " 'nterrupt has completed.

Interrupt Priorities
The !"#$ offers two levels of interrupt priority. high and low. (y using interrupt priorities you may assign higher priority to certain interrupt conditions. )or example, you may have enabled Timer $ 'nterrupt which is automatically called every time Timer $ overflows. Additionally, you may have enabled the /erial 'nterrupt which is called every time a character is received via the serial port. However, you may consider that receiving a character is much more important than the timer interrupt. 'n this case, if Timer $ 'nterrupt is already executing you may wish that the serial interrupt itself interrupts the Timer $ 'nterrupt. =hen the serial interrupt is complete, control passes bac& to Timer $ 'nterrupt and finally bac& to the main program. 7ou may accomplish this by assigning a high priority to the /erial 'nterrupt and a low priority to the Timer $ 'nterrupt. 'nterrupt priorities are controlled by the IP /)3 ((!h . The ', /)3 has the following format.
Bit Na0e Bit .))ress : * 5 5 5 5 1planation o2 Fun3tion Andefined Andefined

# 9 0 $ "

5 ,/ ,T$ ,B$ ,T" ,B"

5 (2h ((h (Ah (4h (!h

Andefined /erial 'nterrupt ,riority Timer $ 'nterrupt ,riority 8xternal $ 'nterrupt ,riority Timer " 'nterrupt ,riority 8xternal " 'nterrupt ,riority

=hen considering interrupt priorities, the following rules apply.


<othing can interrupt a high5priority interrupt55not even another high priority interrupt. A high5priority interrupt may interrupt a low5priority interrupt. A low5priority interrupt may only occur if no other interrupt is already executing. 'f two interrupts occur at the same time, the interrupt with higher priority will execute first. 'f both interrupts are of the same priority the interrupt which is serviced first by polling sequence will be executed first.

%&at -appens %&en an Interrupt O33urs,


=hen an interrupt is triggered, the following actions are ta&en automatically by the microcontroller.

The current ,rogram 2ounter is saved on the stac&, low5byte first. 'nterrupts of the same and lower priority are bloc&ed. 'n the case of Timer and 8xternal interrupts, the corresponding interrupt flag is cleared. ,rogram execution transfers to the corresponding interrupt handler vector address. The 'nterrupt Handler 3outine executes.

Ta&e special note of the third step. 'f the interrupt being handled is a Timer or 8xternal interrupt, the microcontroller automatically clears the interrupt flag before passing control to your interrupt handler routine. This means it is not necessary that you clear the bit in your code.

%&at -appens %&en an Interrupt n)s,


An interrupt ends when your program executes the 38T' (3eturn from 'nterrupt instruction. =hen the 38T' instruction is executed the following actions are ta&en by the microcontroller.

Two bytes are popped off the stac& into the ,rogram 2ounter to restore normal program execution. 'nterrupt status is restored to its pre5interrupt status.

Serial Interrupts

/erial 'nterrupts are slightly different than the rest of the interrupts. This is due to the fact that there are two interrupt flags. 3' and T'. 'f either flag is set, a serial interrupt is triggered. As you will recall from the section on the serial port, the 3' bit is set when a byte is received by the serial port and the T' bit is set when a byte has been sent. This means that when your serial interrupt is executed, it may have been triggered because the 3' flag was set or because the T' flag was set55or because both flags were set. Thus, your routine must chec& the status of these flags to determine what action is appropriate. Also, since the !"#$ does not automatically clear the 3' and T' flags you must clear these bits in your interrupt handler. A brief code example is in order.
INT_S $I.L: JNB $I,!- !K_TI 4O5 .,SB/F !L$ $I !- !K_TI: JNB TI, 8IT_INT !L$ TI 4O5 SB/F,6. 8IT_INT: $ TI C'f the 3' flag is not set, we 1ump to chec& T' C'f we got to this line, its because the 3' bit DwasD set C2lear the 3' bit after weve processed it C'f the T' flag is not set, we 1ump to the exit point C2lear the T' bit before we send another character C/end another character to the serial port

As you can see, our code chec&s the status of both interrupts flags. 'f both flags were set, both sections of code will be executed. Also note that each section of code clears its corresponding interrupt flag. 'f you forget to clear the interrupt bits, the serial interrupt will be executed over and over until you clear the bit. Thus it is very important that you always clear the interrupt flags in a serial interrupt.

I0portant Interrupt !onsi)eration: $e(ister Prote3tion


%ne very important rule applies to all interrupt handlers. 'nterrupts must leave the processor in the same state as it was in when the interrupt initiated. 3emember, the idea behind interrupts is that the main program isnt aware that they are executing in the "bac&ground." However, consider the following code. !L$ ! C2lear carry 4O5 .,695& C+oad the accumulator with 0#h .::! .,610& CAdd $"h, with carry After the above three instructions are executed, the accumulator will contain a value of -#h. (ut what would happen if right after the E%F instruction an interrupt occured. @uring this interrupt, the carry bit was set and the value of the accumulator was changed to 9"h. =hen the interrupt finished and control was passed bac& to the main program, the A@@2 would add $"h to 9"h, and additionally add an additional $h because the carry bit is set. 'n this case, the accumulator will contain the value #$h at the end of execution.

'n this case, the main program has seemingly calculated the wrong answer. How can 0#h G $"h yield #$h as a resultH 't doesnt ma&e sense. A programmer that was unfamiliar with interrupts would be convinced that the microcontroller was damaged in some way, provo&ing problems with mathematical calculations. =hat has happened, in reality, is the interrupt did not protect the registers it used. 3estated. An interrupt must leave the processor in the same state as it was in when the interrupt initiated. =hat does this meanH 't means if your interrupt uses the accumulator, it must insure that the value of the accumulator is the same at the end of the interrupt as it was at the beginning. This is generally accomplished with a ,A/H and ,%, sequence. )or example. P/S- .!! P/S- PS% 4O5 .,60FF& .:: .,609& POP PS% POP .!! The guts of the interrupt is the E%F instruction and the A@@ instruction. However, these two instructions modify the Accumulator (the E%F instruction and also modify the value of the carry bit (the A@@ instruction will cause the carry bit to be set . /ince an interrupt routine must guarantee that the registers remain unchanged by the routine, the routine pushes the original values onto the stac& using the ,A/H instruction. 't is then free to use the registers it protected to its hearts content. %nce the interrupt has finished its tas&, it pops the original values bac& into the registers. =hen the interrupt exits, the main program will never &now the difference because the registers are exactly the same as they were before the interrupt executed. 'n general, your interrupt routine must protect the following registers.

,/= @,T3 (@,H>@,+ ,/= A22 ( 3egisters 3"53:

3emember that ,/= consists of many individual bits that are set by various !"#$ instructions. Anless you are absolutely sure of what you are doing and have a complete understanding of what instructions set what bits, it is generally a good idea to always protect ,/= by pushing and popping it off the stac& at the beginning and end of your interrupts. <ote also that most assemblers (in fact, A++ assemblers that ' &now of will not allow you to execute the instruction. P/S- $0

This is due to the fact that depending on which register ban& is selected, 3" may refer to either internal ram address ""h, "!h, $"h, or $!h. 3", in and of itself, is not a valid memory address that the ,A/H and ,%, instructions can use. Thus, if you are using any "3" register in your interrupt routine, you will have to push that registers absolute address onto the stac& instead of 1ust saying P/S- $0. )or example, instead of ,A/H 3" you would execute. P/S- 00& %f course, this only wor&s if youve selected the default register set. 'f you are using an alternate register set, you must ,A/H the address which corresponds to the register you are using.

!o00on Pro;le0s *it& Interrupts


'nterrupts are a very powerful tool available to the !"#$ developer, but when used incorrectly they can be a source of a huge number of debugging hours. 8rrors in interrupt routines are often very difficult to diagnose and correct. 'f you are using interrupts and your program is crashing or does not seem to be performing as you would expect, always review the following interrupt5related issues.

$e(ister Prote3tion. Ea&e sure you are protecting all your registers, as explained above. 'f you forget to protect a register that your main program is using, very strange results may occur. 'n our example above we saw how failure to protect registers caused the main program to apparently calculate that 0#h G $"h I #$h. 'f you witness problems with registers changing values unexpectedly or operations producing "incorrect" values, it is very li&ely that youJve forgotten to protect registers. .L%.<S P$OT !T <O/$ $ GIST $S# For(ettin( to restore prote3te) 'alues. Another common error is to push registers onto the stac& to protect them, and then forget to pop them off the stac& before exiting the interrupt. )or example, you may push A22, (, and ,/= onto the stac& in order to protect them and subsequently pop only A22 and ,/= off the stac& before exiting. 'n this case, since you forgot to restore the value of "(", an extra value remains on the stac&. =hen you execute the 38T' instruction the !"#$ will use that value as the return address instead of the correct value. 'n this case, your program will almost certainly crash. .L%.<S 4.K S/$ <O/ POP T- S.4 N/4B $ OF 5.L/ S OFF T- ST.!K .S <O/ P/S- : ONTO IT# Asing 38T instead of 38T'. 3emember that interrupts are always terminated with the 38T' instruction. 't is easy to inadvertantly use the 38T instruction instead. However, the 38T instruction will not end your interrupt. Asually, using a 38T instead of a 38T' will cause the illusion of your main program running normally, but your interrupt will only be executed once. 'f it appears that your interrupt mysteriously stops executing, verify that you are exiting with 38T'.

You might also like