The 8051


 
<-Previous
   
  The 8051 Timers
   
  The basic 8051 has two on-chip timers that can be used for timing durations or for counting external events.
   
  Interval timing allows the programmer to perform operations at specific instants in time. For example, in our LED flashing program the LED was turned on for a specific length of time and then turned off for a specific length of time. We achieved this through the use of time delays. Since the microcontroller operates at a specific frequency, we could work out exactly how many iterations of the time delay was needed to give us the desired delay.
  However, this is cumbersome and prone to error. And there is another disadvantage; the CPU is occupied, stepping through the loops. If we use the on-chip timers, the CPU could be off doing something more useful while the timers take on the menial task of keeping track of time.
   
   
  The Timers' SFRs
  The 8051 has two 16-bit timers. The high byte for timer 1 (TH1) is at address 8DH while the low byte (TL1) is at 8BH.
  The high byte for timer 0 (TH0) is at 8CH while the low byte (TL0) is at 8AH.
   
  Both timers can be used in a number of different modes. The programmer sets the timers to a specific mode by loading the appropriate 8-bit number into the Timer Mode Register (TMOD) which is at address 88H.
   
   
  Timer Mode Register
   
 
TMOD
Bit Name Timer Description
7 Gate 1 Gate bit; when set, timer only runs while INT-bar is high. This bit is used in conjunction with interrupts and will be dealt with later.
6 C/T-bar 1 Counter/timer select bit; when set timer is an event counter, when cleared timer is an interval timer.
5 M1 1 Mode bit 1
4 M0 1 Mode bit 0
3 Gate 0 Gate bit; when set, timer only runs while INT-bar is high.
2 C/T-bar 0 Counter/timer select bit; when set timer is an event counter, when cleared timer is an interval timer.
1 M1 0 Mode bit 1
0 M0 0 Mode bit 0
  The functions of the 8-bits of TMOD are described in the above table. The top four bits are for timer 1 and the bottom four bits have the exact same function but for timer 0.
  The Gate bits are used in conjunction with interrupts and will be dealt with at a later stage. For the moment we can take it that bits 7 and 3 are always cleared.
  As mentioned above, the timers can be used for counting external events or for timing intervals. If you wish the timer to be an event counter you set the corresponding C/T-bar bit. Similarly, if you wish it to be an interval timer you reset the corresponding C/T-bar bit.
  There are two mode bits (M1 and M0) for each timer. The table below describes their function.
   
 
M1 M0 Mode Description
0 0 0 13-bit timer mode (this mode exists simply to keep the 8051 backwards compatible with its predecessor, the 8048, which had a 13-bit timer) - we will not be using mode 0.
0 1 1 16-bit timer mode
1 0 2 8-bit auto-reload mode
1 1 3 Split timer mode - this mode will be dealt with at a later stage
  There are four timer modes, set by the bits M1 and M0. Mode 0 is not commonly used. Mode 3 we will deal with later.
   
  Mode 1 - 16-bit mode
  The high byte (THx) is cascaded with the low byte (TLx) to produce a 16-bit timer. This timer counts from 0000H to FFFFH - it has 216 (65,536) states. An overflow occurs during the FFFFH to 0000H transition, setting the overflow flag (to be dealt with shortly).
   
  Mode 2- 8-bit auto-reload mode
  The timer low byte (TLx) operates as an 8-bit timer (counting to FFH) while the high-byte holds a reload value. When the timer overflows from FFH, rather than starting again from 00H, the value in THx is loaded into TLx and the count continues from there.
   
 
  A diagrammatic representation of mode 1 and mode 2. (The 8051 Microcontroller, 3rd Edition - I. Scott MacKenzie)
   
  Timer Control Register
   
 
TCON
Bit Symbol Bit Address Description
7 TF1 8FH Timer 1 overflow flag; set by hardware upon overflow, cleared by software.
6 TR1 8EH Timer 1 run-control bit; manipulated by software - setting starts timer 1, resetting stops timer 1.
5 TF0 8DH Timer 0 overflow flag; set by hardware upon overflow, cleared by software.
4 TR0 8CH Timer 0 run-control bit; manipulated by software - setting starts timer 0, resetting stops timer 0.
3 IE1 8BH The bottom four bits of TCON are used in conjunction with interrupts - they will be dealt with at a later stage.
2 IT1 8AH
1 IE0 89H
0 IT0 88H
  The top four bits of TCON are the only ones that interest us at the moment since the bottom four are used with interrupts.
   
  Bit 7 and bit 5 are the timer overflow flags (TFx). The overflow flag is set by the hardware once an overflow occurs. For example, if timer 0 is in mode 1 (16-bit mode) then, during the state transition from FFFFH to 0000H the overflow flag TF0 is set by the hardware.
   
  Bit 6 and bit 4 are the timer run-control bits (TRx). A timer is started by setting TRx and stopped by clearing TRx.
   
  The TCON register is bit addressable because the programmer needs ease of access to the individual bits. You may wish to test the contents of TF1, to see when timer 1 overflows. Or you may wish to stop timer 0 without effecting timer 1, as shown below. Clearing TR0 does not effect the other seven bits of TCON.
 
CLR TR0
   
   
  Initialising the Timers
  The timers are initialised (ie; put into a particular mode of operation) by moving the appropriate value into the TMOD register. For example, if you wished to use timer 0 as a 16-bit interval timer and timer 1 as an 8-bit auto-reload event counter you would load the following value into TMOD.
   
 
  bit value note
Timer 1 7 0 gate (set when using interrupts, reset otherwise)
6 0 C/T-bar (0 because timer 1 is to be an interval timer)
5 0 mode bits (01 for mode 1 - 16-bit)
4 1
Timer 0 3 0 gate (set when using interrupts, reset otherwise)
2 1 C/T-bar (1 because timer 0 is to be an event counter)
1 1 mode bits (10 for mode 2 - 8-bit auto-reload)
0 0
  The instruction for initialising the timers as above would therefore be:
 
MOV TMOD, #16H
   
  (0001 0110bin = 16H)
   
   
  Starting and Stopping the Timers
  The run/control bits in the TCON register are used for starting and stopping the timers. As shown in the TCON table above, bit 6 is the run control bit for timer 1 while bit 4 is the run control bit for timer 0. The following two instructions start timer 0 and timer 1 respectively.
   
 
SETB TR0
SETB TR1
   
  Setting the run control bit starts the timer. To stop the timer, you clear the corresponding run control bit
   
 
CLR TR0; stop timer 0
CLR TR1; stop timer 1
   
   
  Reading the Timers on the Fly
  Reading the current value of a timer when it is not running is quite easy. You simply move the high byte and the low byte to the desired memory location (usually a register).
 
MOV R7, TH0; move the hi-byte of timer 0 into R7
MOV R6, TL0; move the lo-byte of timer 0 into R6
   
  However, to read the contents of a timer while it is running (ie; on the fly) poses a problem. Let's say we put timer 0's current value into R7 and R6, as shown above. The problem arises when the value in the lo-byte goes from FFH to 00H immediately after we read the high byte.
  For example, let's say the high-byte is 08H and the low-byte is FFH. The controller moves the high byte into R7. Then, before it gets a chance to read the low byte, it changes from FFH to 00H. The controller then moves this value into R6, getting an overall 16-bit reading of 0800H when the value should have been 08FFH.
  The solution is to read the high byte, then read the low byte, then read the high byte again. If the two readings of the low-byte are not the same repeat the procedure. The code for this method is detailed below.
 
 
 
tryAgain:
MOV A, TH0
MOV R6, TL0
CJNE A, TH0, tryAgain; if the first reading of the hi-byte (in A) is not equal

to current reading in the hi-byte (TH0) try again

MOV R7, A; if both readings of the hi-byte are the same move the first reading into R7

- the overall reading is now in R7R6

   
   
  Clock Sources
  As discussed earlier, both timers can be used either as interval timers or event counters. The only difference between the timer as an interval timer and an event counter is the clock source. The timers are triggered by a negative edge to their clock inputs.
   
  When the timer is operating as an interval timer it is being clocked by the internal clock (this internal clock, usually 12MHz, is actually divided by twelve to yield a reasonable clock frequency for most applications - therefore, the internal clocking signal is usually 1MHz).
   
  When the timer is operating as an event counter it is triggered by an external source connected to pin T0 (port 3, pin 4) for timer 0 and pin T1 (port 3, pin 5) for timer 1. In this way the timer can count events rather than keep track of time.
  For example, if a sensor on a conveyor belt system produces a negative edge everytime an item on the belt passes by, this sensor could be connected to pin T0. If timer 0 were then initialised as an event counter and started, everytime an item passed in front of the sensor, a negative edge would be generated on pin T0, thus triggering timer 0 which would proceed to the next stage in its count sequence. In this way, timer 0 is counting the number of items passing by on the conveyor belt.
   
  Generating Pulse Trains
  We can very effectively use the timers where exact timing conditions are necessary. For example, if we need to generate a 4KHz pulse train on pin 5 of port 1, we could use either timer 0 or timer 1 in 8-bit auto-reload interval timing mode.
  Let's say we decide to use timer 0. We must first work out the value that must be placed in the TMOD register to put timer 0 into the desired mode.
   
 
  bit value note
Timer 1 7 0 since we are not using timer 1 we can put all four upper bits to 0
6 0
5 0
4 0
Timer 0 3 0 gate (set when using interrupts, reset otherwise)
2 0 C/T-bar (0 because timer 0 is to be an interval timer)
1 1 mode bits (10 for mode 2 - 8-bit auto-reload)
0 0
  Therefore, the value to be placed in TMOD is 02H.
   
  Mode 2 is the 8-bit auto-reload mode. The low byte of the counter behaves like an 8-bit counter. The high-byte is used for storing the reload value. When an overflow occurs (ie; state transition from FFH) the timer does not revert to zero. Rather, the value in the high-byte is placed in the low-byte and the sequence begins again from there.
  If the high-byte contains 00H, then the timer behaves exactly like an 8-bit timer, counting continuously from 00H to FFH and back to 00H again.
  As an interval timer, the clocking signal arrives every 1us (microsecond). The 12MHz system clock is divided by 12 and then fed to the timer. Therefore, the timer's clocking signal is 1MHz, resulting in a negative edge every 1us (time = 1/frequency).
  If the high byte contains 00H, the counter goes through the full 256 states (00H to FFH). Therefore, an overflow occurs every 256us.
  We require a 4KHz pulse train on pin 5 of port 1; in other words, P1.5 must toggle every 125us (4KHz signal - one cycle every 250us - one half cycle every 125us).
  We need to load the high byte with a suitable value to result in an overflow every 125us. To find the value, we subtract the required time from the maximum time (255us - 125us = 131). Changing this value to HEX gives 83H.
   
  The code for generating the pulse train is detailed below:
   
 
MOV TMOD, #02H; set up timer 0 as 8-bit auto-reload interval timer
MOV TH0, #83H; put reload value into timer 0 hi-byte
SETB TR0; start timer 0
waitForOverflow:
JNB TF0, $; repeat this line while timer 0 overflow flag is not set
CLR TF0; timer 0 overflow flag is set by hardware on transition from FFH - the flag must be reset by software
CPL P1.5; complement (invert) pin 5 on port 1 - this instruction toggles the specified bit
JMP waitForOverflow; go back and wait for overflow again
   
  Note: with the above program, P1.5 is at 1 for the same length of time that it's at 0 (125us each), therefore the waveform has 50% duty cycle.
   
   
  A One Second Delay
  One second is a very long time for a microcontroller. If the microcontroller has a system clock frequency of 12 MHz then the longest delay we can get from either of the timers is 65,536 usec. To generate delays longer than this, we need to write a subroutine to generate a delay of (for example) 50 ms and then call that subroutine a specific number of times. To generate a 1 second delay we would call our 50 ms delay 20 times.
  In the example below timer 1 is used. There is no particular reason for this; timer 0 could have been used.
  The value placed in the timer before it is started is 65,536 - 50,000 = 15,536. To get a delay of 50 ms (or 50,000 us) we start the timer counting from 15,536. Then, 50,000 steps later it will overflow. Since each step is 1 us (the timer's clock is 1/12 the system frequency) the delay is 50,000 us.
   
 
using 0
...
MOV TMOD, #10H; set up timer 1 as 16-bit interval timer
...
 
fiftyMsDelay:
CLR TR1 ; stop timer 1 (in case it was started in some other subroutine)
MOV TH1, #3CH
MOV TL1, #0B0H ; load 15,536 (3CB0H) into timer 1
SETB TR1 ; start timer 1
JNB TF1, $; repeat this line while timer 1 overflow flag is not set
CLR TF1; timer 1 overflow flag is set by hardware on transition from FFFFH - the flag must be reset by software
CLR TR1 ; stop timer 1
RET
 
oneSecDelay:
PUSH PSW
PUSH AR0 ; save processor status
MOV R0, #20 ; move 20 (in decimal) into R0
loop:
CALL fiftyMsDelay ; call the 50 ms delay
DJNZ R0, loop ; 20 times - resulting in a 1 second delay
POP AR0
POP PSW ; retrieve processor status
RET
 
 
<-Previous

Copyright (c) 2005-2006 James Rogers