A better solution
A better solution to the problems outlined is to use timer-based interrupts as a means of invoking functions at particular times.
Timer-based interrupts and interrupt service routines
As we saw in Chapter 1, an interrupt is a hardware mechanism used to notify a proces- sor that an ‘event’ has taken place: such events may be internal events or external events. Altogether the core 8051 / 8052 architecture supports seven interrupt sources:
● Three timer/counter interrupts (related to Timer 0, Timer 1 and – where available – Timer 2)
● Two UART-related interrupts (note: these share the same interrupt vector, and can be viewed as a single interrupt source)
● Two external interrupts
In addition, there is one addition interrupt source over which the programmer has minimal control:
● The ‘power-on reset’ (POR) interrupt
When an interrupt is generated, the processor ‘jumps’ to an address at the bottom of the CODE memory area. These locations must contain suitable code with which the microcontroller can respond to the interrupt or, more commonly, the locations will include another ‘jump’ instruction, giving the address of suitable ‘interrupt serv- ice routine’ located elsewhere in (CODE) memory.
While the process of handling interrupts may seem rather complicated, creating interrupt service routines (ISRs) in a high-level language is a straightforward process, as illustrated in Listing 13.3.
The result of running the program shown in Listing 13.3 in the Keil hardware simula- tor is shown in Figure 13.3.
Much of Listing 13.3 should be familiar. The code to set up Timer 2 in the function Timer_2_Init() is the same as the delay code discussed in Chapter 11, the two main differences being that, in this case:
1 The timer will generate an interrupt when it overflows
2 The timer will be automatically reloaded, and will immediately begin counting again We discuss both of these differences in the following subsections.
The interrupt service routine (ISR)
The interrupt generated by the overflow of Timer 2, invokes the ISR called, here, X().
The link between this function and the timer overflow is made using the Keil key- word interrupt (included after the function header in the function definition):
void X(void) interrupt INTERRUPT_Timer_2_Overflow
plus the following #define directive:
#define INTERRUPT_Timer_2_Overflow 5
To understand where the ‘5’ comes from, note that the interrupt numbers used in ISRs directly correspond to the enable bit index of the interrupt source in the 8051 IE SFR. That is, bit 0 of the IE register will be linked to a function using ‘interrupt 0’. Table 13.1 shows the link between the interrupt sources and the required interrupt numbers for the original 8051/8052.
Overall, the use of interrupts linked to timer overflows is a safe and powerful tech- nique which will be applied throughout this book.
Automatic timer reloads
As noted earlier, when Timer 2 overflows, it is automatically reloaded and immedi- ately begins counting again. In this case, the timer is reloaded using the contents of the ‘capture’ registers (note that the names of these registers vary slightly between chip manufacturers):
This automatic reload facility ensures that the timer keeps generating the required ticks, at precisely 1 ms intervals, without any software load, and without any inter- vention from the user’s program.
The ability to ‘automatically reload’ Timer 2 simplifies the use of this timer as a source of regular ticks. Note that Timer 0 and Timer 1 also have an auto-reload capability, but only when operating as an 8-bit timer. In most applications, an 8- bit timer can only be used to generate interrupts at intervals of around 0.25 ms (or less); this is not generally useful.