How can I use a timer to count 5 seconds?
Submitted By: Craig Steiner FAQ Last Modified: 07/10/06
- Timers are useful, but they are fast. With a standard 8052 running at 12MHZ, the 16-bit timer--which takes the most time of any timer to overflow--will still overflow about 15.26 times per second; that's about .06 seconds per overflow. To use a timer to count relatively long periods of times--anything over .06 seconds--will require a little extra code.
This is the pefect situation for an interrupt. The idea is to cause the timer to overflow at a known rate and then count how many times it overflows. For example, .06 is not a fun number to work with since it is not evenly divisible by a full second. Rather, it'd be useful to configure the program such that the timer overflows every .05 seconds. Then, if it has overflowed 20 times you know one second has passed. This can be done in various ways, but the following code is offered as one solution:
TCOUNT EQU 40h ;This is an address in IRAM ALARM EQU 00h ;This is an address of BIT memory ORG 0000h LJMP MAIN ORG 000Bh T0_INTERRUPT: MOV TL0,#0B0h ;Set TL0 to 0B0h MOV TH0,#3Ch ;Set TH0 to 3Ch DJNZ TCOUNT,T0_EXIT ;Decrement TCOUNT, if not zero exit interrupt SETB ALARM ;Set ALARM to indicate the preset time has passed T0_EXIT: RETI ;Return from the interrupt MAIN: MOV TMOD,#01h ;Set timer 0 to 16-bit mode MOV TH0,#03Ch ;Initialize TH0 to overflow every .05 seconds MOV TL0,#0B0h ;Initialize TL0 to overflow every .05 seconds SETB ET0 ;Enable timer interrupt 0 SETB EA ;Enable all interrupts MOV TCOUNT,#20 ;Set TCOUNT to 20 to wait for 1 second SETB TR0 ;Turn timer 0 on JNB ALARM,$ ;Cycle until ALARM is set (1 second later)
The above code starts at MAIN by initializing timer 0 to 0000h (TH0/TL0) and setting timer 0 to 16-bit mode. The code then enables the timer 0 interrupt such that the T0_INTERRUPT code will be called every time T0 overflows from FFFFh back to 0000h. The code then sets TCOUNT to 20 (decimal). This is a count-down counter that, when it reaches zero, will indicate that the specified number of overflows (20) have ocurred. Timer 0 is then started by setting the TR0 bit and the program cycles indefinitely at the JNB instruction until the ALARM bit is set.
The T0_INTERRUPT is then called every .05 seconds. When it is called, it resets TL0 and TH0 such that timer 0 is reloaded with the value 3CB0h. 3CB0h = 15,536 in decimal. The timer will have to be incremented exactly 50,000 to count from 15,536 up to 65,535 and subsequently overflow back to 0000h. At 12MHZ on a standard 8052, it will take .05 seconds for the timer to count 50,000 cycles, thus this has the effect of reseting our timer to overflow in another .05 seconds.
The next line in the interrupt, DJNZ, decrements the TCOUNT register. This will happen each time the timer overflows, so it will decrement from the original value (20) down to zero. If, after decrementing the value, TCOUNT is still non-zero, it jumps to T0_EXIT and immediately exits the interrupt. If, however, TCOUNT decrements to zero--which means the full 20 cycles have passed--it sets the ALARM bit, then exits. Since the main program is indefinitely waiting for the ALARM bit to be set, you will know 1 second has passed when the JNB instruction at the end of MAIN continues to the next instruction.
This can be used exactly as written to wait up to 12.75 seconds (255/20 = 12.75). If you need to create a timer that measures/waits more than 12.75 seconds, you'd have to improve the code to have a 16-bit TCOUNT. This would involved two DJNZ instructions and two IRAM equates (perhaps TCOUNT_HI and TCOUNT_LO) in the T0_INTERRUPT code.
- NOTE #1: Technically, T0_INTERRUPT resets TL0 and TH0 in a not-so-safe way. It is generally recommended that when you change the value of TH0 and/or TL0 that you first stop the timer by clearing TR0. After you've set the two SFRs, you then restart the timer by setting TR0. In this case, it is not absolutely necessary because the value we're setting, 3CB0h, is such that there will be no nasty side effects. However, if you were resetting the timer to 3CFFh it would be absolutely necessary to stop the timer. Otherwise, you'd first write TL0 to FFh, it would immedaitely overflow back to 00h and you'd write TH0 to 3Ch--thus your timer would count up from 3C00h instead of 3CFFh giving you an extra 255 cycles.
NOTE #2: The above code will give results sufficiently acceptable for most applications. However, if you need super-accurate timing, take into account the time it takes to reset the SFRs at the beginning of the SFR. The above program will actually take very slightly longer than .05 seconds per interrupt. The extra time is the time it takes for the 8052 to execute the MOV TL0,#0B0h instruction. In a standard 8052 this takes two clock cycles, which means instead of writing B0h to TL0, you should really write B2h. This will cause the timer to require two less cycles to overflow thus compensating for the time it took to write to TL0. If you actually stop the clock as recommended in the previous note, you need to take into account the time it takes to execute all the instructions that were executed while the clock was stopped. Again, this is really only necessary if your timer must be very accurate or will be measuring hours and hours. Failure to take this small adjustment into account would cause your timer to lose about 1 second every 7 hours.
Add Information to this FAQ: If you have additional information or alternative solutions to this question, you may add to this FAQ by clicking here.