|Shopping Cart Download Website|
|You are here: 8051 Tools Development Board Example Code Timers|
example is best for new users before attempting to use the
No time to read all this?? Jump directly to the downloadable example code.
Understanding Timer 0Timer 0 can function in four different modes, each useful for certain types of applications, but the basic operation is the same in all modes. Registers hold a number which is incremented until it reaches a maximum value and then becomes zero. This roll-over back to zero is called timer overflow, and it is the condition that makes the timer useful.
The TF0 bit is automatically set to 1 when Timer 0 overflows. Your code can read the TF0 bit at any time to find out if the timer has overflowed. You can also configure the Timer 0 interrupt to automatically execute code when the timer overflows. When the interrupt code starts, the 8051 automatically clears TF0 back to zero. If you do not use an interrupt, your code must clear TF0 in order to be able to detect the next time Timer 0 overflows.
Timer can be started and stopped with the TR0 bit. When this bit is set to 1, the timer runs, and when its cleared to zero the timer is stopped. This allows you to create one-shot delays easily, or measure elapsed time between two events. Applicatons that need regular intervals usually just set this bit and never stop the timer.
Modes of Operation for Timer 0Timer 0 can operate in 4 different modes, each of which is useful for certain types of applications. This table briefly summarizes the modes.
Mode 0 is the simplest to use. A single 8 bit value is incremented at 57600 Hz, which results in 225 Timer 0 overflows per second (if the value is not modified by your code). Mode 0 is commonly used to execute an interrupt routine at a regular interval.
Mode 1 provides the greatest precision and longest timeout. The timer value is 16 bits and it increments at 1.8432 MHz. Mode 1 is useful for measuring elapsed time between events. It also provides the flexability for generating delays and timeouts with more precision, at the cost of additional complexity of handling 16 bits instead of 8.
Mode 2 provides short repetitive intervals. When the timer overflows, the 8 bit value is automatically reloaded with a value you define, rather than rolling over to zero. Because the value increments at 1.8432 MHz, the timeout interval is adjustable from 0.543 µs to 139 µs. Timer 1 is commonly used in mode 2 to configure the serial port baud rate. Timer 0 is not usually used in mode 2 because the timeout interval is so quick.
Mode 3 turns timer 0 into two 8 bit timers. One is incremented at 1.8432 MHz (no auto-reload), and the other is incremented by external pulses. Mode 3 is not commonly used.
How To Configure Timer 0Timer 0 is usually configured using these basic steps:
The first step is to clear the TR0 and TF0 bits. This can be done directly on the bits, or by modifying the TCON register which contains both of them. Writing directly to TCON also effects Timer 1. If Timer 1 is already in use generating baud rates, clearing TR1 will stop all serial port communication. In assembly, "ANL TCON, #0x5F" will clear TR0 and TF0 safely. In C, "TCON &= 0x5F" has the same effect. Manipulating the two bits directly takes one extra byte of code and makes the source code more readable. If you do this, clear TR0 first.
Next, you will usually set the Timer 0 mode using TMOD. Writing directly to TMOD also effects Timer 1. If Timer 1 is already generating baud rates, you should AND it with 0xF0 to set Timer 0 to mode 0, and then OR it with 1, 2 or 3 if one of those modes is desired instead of mode 0. TMOD also can configure counter options to the timer, which we won't discuss further to keep things simple.
To set the timer's initial value, you will write to TH0 and TL0 as necessary. For mode 0, TH0 is the 8-bit value. In mode 1, both are used to hold the 16 bits. In mode 2, TL0 holds the 8 bit value, and TH1 holds the automatic reload value.
If you intend to use interrupts, you will usually enable the interrupt (discussed below in the interrupt section), and then to complete the timer setup you would set TR0 to start it running. What you do with the timer after it is running depends on your application. The next section will look at some common applications and how to make use of the Timer 0 in each.
Simple Timer 0 Examples (Without Interrupts)Many simple programs do not need to use the Timer 0 interrupt. Instead, they can simply check the TF0 bit and test for events as the timer runs. This testing is called polling, and the loop within the code that does this is called the polling loop.
Polling is simpler and easier than using interrupts for applications that essentially wait for the timer to complete or do not need to perform other non-related tasks while the timer is running. It is possible to perform some work while the timer is running by placing it inside the polling loop. Generally, when the polling loop does not need to contain code to preform other tasks unrelated to the timer activity, polling is usually the best approach to using Timer 0. The examples in this section show how polling is done in a few useful Timer 0 applications. In each case, we will briefly look at the limitations of the simpler polling approach.
Elapsed Time MeasurementTo measure the time that elapses between two events, Timer 0 is configured in 16 bit mode, with an initial value of zero, and it is started when the first event occurs. The polling loop must check for the second event, and for overflow on the timer. At each overflow, a 16 bit variable is incremented, allowing a total time measurement of 4294967296 cycles, or approximately 2330 seconds with 2 µs resolution (the typical execution time of the polling loop).
This example uses the serial port's RI bit (reception of a byte) as the event. It can easily be modified to test port pins to measure time between hardware-level events.
Most of the time, the two conditional branches at the top of the polling loop will execute over and over. So in most cases, Timer 0 will be stopped within 4 cycles (2 µs) of the occurance of the second event. However, if the RI bit becomes set while the 16 bit increment code is executing, several mode cycles can elapse, leading to an additional error in the measurement.
The worst case error is the longest path executed by the polling loop between tests of the stop condtition. Though this error is small (and can be made smaller with some careful re-writing of the polling loop), it can not be avoided. Worse yet, if some other work must be done within the polling loop, the normal and longest execution paths between tests of the stop event will grow, thereby increasing the typical and worst-case measurement errors.
Later, we will revisit this elapsed time measurement using interrupts. The interrupt approach will allow this potential error to be eliminated (and allow other code unrelated code to execute while the timer is counting), but at the cost of a much more complex and difficult implementation.
Timeout Waiting For Input DataWhen receiving data from an external source, it is commonly required to implement a timeout. If the data does not arrive after some allowed time, the wait is abandoned and some other action is taken. Perhaps a request is retransmitted or some error message is sent to a user. This simple example waits for the user to type 10 characters. The timeout for the first character is 5 seconds, and then 1 second for each character thereafter. If the user does not respond within this time, a timeout message is printed.
TODO: explain how this code works
TODO: polling vs interrupts....
TimekeepingTODO: intro and explain how the code works
TODO: this code restructured as subroutines
TODO: pitfalls of the polling approach
How Interrupts Work And Typical Issues Using ThemThe timer interrupt is an 8051 hardware feature where the CPU will execute special code, called an interrupt service routine, when the overflow flag bit is set by the timer. The advantage is that your program does not need to constantly check that flag as it runs, as the three polling examples above must do. Another advantage is that the interrupt service routine executes almost immediately in responds to the timer overflow, rather than with a delay determined by how often the polling can check the flag.
However, these advantages do not come for free. The intrrupt service routine code must be written very carefully to avoid interfering with the main program, and great care must be used when exchanging data between the interrupt routine and the main program. Interrupts should be avoided when simple polling is adaquete.
To define a timer0 interrupt routine in assembly, simply using an ORG directive to place the code at location 0x200B. The 8051 will jump to 0x000B, and the code inside PAULMON2 has an LJMP instruction which jumps to 0x200B in your code. In some cases, another LJMP is placed at 0x200B to jump to your code, if it is not possible to place the interrupt service routine at 0x200B.
Using C language, an interrupt service routine is declared using a special "interrupt" keyword. SDCC requires that the interrupt routine or its function prototype must be included in the same file as the main() function.
TODO: Properly saving context.
TODO: Enabling, disabling, priority, and latency
TODO: Atomic access to shared data issues
Timer 0 Examples Using Interrupts
Accurate Elapsed Time Measurement
Implementing Many "Software" Timers