| |
 |
The 8051
|
|
|
|
| |
|
| |
|
| |
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
|
| |
| |
|
|
|
Copyright (c) 2005-2006 James Rogers
|
|