8051 – Timers/Counters

Hi, guys in our previous tutorials we have seen LED, Switch, LCD interfacing. Today we are going to see the 8051 Timer Counter Tutorial.

8051 Timer Counter Tutorial


8051 has two Timers/Counters, which are Timer0/Counter0 and Timer1/Counter1. Both timer/counter is sixteen-bit Timer/Counter. Does anybody have any doubt? No doubt? But I have one doubt. I said 8051 is an 8-bit controller and both timers are 16-bit timers. How? well. I will explain you later. So both timers can be used 4-modes. Before that, I will explain the Timer and Counter operations.


First, let us consider a simple 8-bit counter. Since this is a modulo-8 set up we are concerned with 256 numbers in the range 0 to 255 (28 =256). The counter will count in a continuous sequence as follows:

Hex             Decimal

00h                  0

01h                  1

02h                  2

.                         .

.                         .

FEh                 254

FFh                 255

00h                 0             ——–> here the counter overflows to zero 

01h                  1

02h                  2

Supposing we were to initialize this Timer/Counter with a number, say 252, then the counter would overflow after just four-event pulses, i.e:

FCh                 252

FDh                 253

FEh                 254

FFh                 255

00h                 0             ——–> here the counter overflows to zero 

01h                  1

02h                  2

An 8-bit counter can count 255 events before overflow and overflows on the 256th event. When initialized with a predefined value of say 252 it overflows after counting just four events. Thus the number of events to be counted can be programmed by pre-loading the counter with a given number value.


The 8051 internally divides the processor clock by 12. If a 12 MHz processor clock is used then a 1 MHz instruction rate clock or a pulse once every microsecond, is realized internally within the chip. If this 1-microsecond pulse is connected to a Timer/Counter input, in place of an event input, then the Timer/Counter becomes a timer which can delay by up to 255 microseconds. There is a clear difference between a timer and a counter. The counter will count events, up to 255 events before overflow, and the timer will count time pulses, thus creating delays up to 255 microseconds in our example.

To be precise we would refer to the counter as an event counter and we would refer to the timer as an interval timer.

If the timer is initialized to zero it will count 256 microseconds before overflow. If the timer is initialized to a value of 252, for example, it will count just 4 microseconds before overflow. Thus this timer is programmable between 1 microsecond and 256 microseconds.

Before programming, we should know the Timer Registers. 8051 has 5 registers for timer operation.

Registers used

  • TMOD (Timer Mode Register)
  • TCON (Timer Control Register)
  • THx (Timer High Register) (x means weather 0 or 1. It indicates timer 0 or Timer 2)
  • TLx (Timer Low Register)  (x means weather 0 or 1. It indicates timer 0 or Timer 2)

TMOD Register

Timer 1 

Timer 0
Gate C/T M1 M0 Gate C/T M1 M0

Gate: This bit indicates How to control the timer whether by software, or hardware. If we set this by 0 we can start and stop the timer by software(coding). If we set 1 timer is controlled by hardware. we have the dedicated pin in Port 0.

C/T: This bit used to select timer or counter. 0 for Timer and 1 for the counter.

M0, M1: These bits are used to select the timer modes like below.

M1      M0                     Mode

0            0           Mode 0: 13 bit mode (seldom used).

0             1           Mode 1: 16-bit mode

1             0           Mode 2: 8-bit mode (with auto-reload feature)

1             1            Mode 3: 8-bit split timer



Explanation of Function




When this bit is set the timer will only run when INT1 (P3.3) is high. When this bit is clear the timer will run regardless of the state of INT1.




When this bit is set the timer will count events on T1 (P3.5). When this bit is clear the timer will be incremented every machine cycle.




Timer mode bit (see below)




Timer mode bit (see below)




When this bit is set the timer will only run when INT0 (P3.2) is high. When this bit is clear the timer will run regardless of the state of INT0.




When this bit is set the timer will count events on T0 (P3.4). When this bit is clear the timer will be incremented every machine cycle.




Timer mode bit (see below)




Timer mode bit (see below)


13-bit Time Mode (Mode 0)

Timer mode “0” is a 13-bit timer. This is a relic that was kept around in the 8051 to maintain compatibility with its predecessor, the 8048. Generally the 13-bit timer mode is not used in new development.

When the timer is in 13-bit mode, TLx will count from 0 to 31. When TLx is incremented from 31, it will “reset” to 0 and increment THx. Thus, effectively, only 13 bits of the two timer bytes are being used: bits 0-4 of TLx and bits 0-7 of THx. This also means, in essence, the timer can only contain 8192 values. If you set a 13-bit timer to 0, it will overflow back to zero 8192 machine cycles later.

Again, there is very little reason to use this mode and it is only mentioned so you won’t be surprised if you ever end up analyzing archaic code which has been passed down through the generations (a generation in a programming shop is often on the order of about 3 or 4 months).

16-bit Time Mode (Mode 1)

Timer mode “1” is a 16-bit timer. This is a very commonly used mode. It functions just like 13-bit mode except that all 16 bits are used.

TLx is incremented from 0 to 255. When TLx is incremented from 255, it resets to 0 and causes THx to be incremented by 1. Since this is a full 16-bit timer, the timer may contain up to 65536 distinct values. If you set a 16-bit timer to 0, it will overflow back to 0 after 65,536 machine cycles.

8-bit Time Mode (Mode 2)

Timer mode “2” is an 8-bit auto-reload mode. What is that, you may ask? Simple. When a timer is in mode 2, THx holds the “reload value” and TLx is the timer itself. Thus, TLx starts counting up. When TLx reaches 255 and is subsequently incremented, instead of resetting to 0 (as in the case of modes 0 and 1), it will be reset to the value stored in THx.

For example, let’s say TH0 holds the value FDh and TL0 holds the value FEh. If we were to watch the values of TH0 and TL0 for a few machine cycles this is what we see:


Machine Cycle TH0 Value TL0 Value
1 FDh FEh
2 FDh FFh
3 FDh FDh
4 FDh FEh
5 FDh FFh
6 FDh FDh
7 FDh FEh

As you can see, the value of TH0 never changed. In fact, when you use mode 2 you almost always set THx to a known value and TLx is the SFR that is constantly incremented.

What is the benefit of auto-reload mode? Perhaps you want the timer to always have a value from 200 to 255. If you use mode 0 or 1, you’d have to check-in code to see if the timer had overflowed and, if so, reset the timer to 200. This takes precious instructions of execution time to check the value and/or to reload it. When you use mode 2 the microcontroller takes care of this for you. Once you’ve configured a timer in mode 2 you don’t have to worry about checking to see if the timer has overflowed nor do you have to worry about resetting the value–the microcontroller hardware will do it all for you.

The auto-reload mode is very commonly used for establishing a baud rate which we will talk more about in the Serial Communications chapter.

Split Timer Mode (Mode 3)

Timer mode “3” is a split-timer mode. When Timer 0 is placed in mode 3, it essentially becomes two separate 8-bit timers. That is to say, Timer 0 is TL0, and Timer 1 is TH0. Both timers count from 0 to 255 and overflow back to 0. All the bits that are related to Timer 1 will now be tied to TH0.

While Timer 0 is in split mode, the real Timer 1 (i.e. TH1 and TL1) can be put into modes 0, 1 or 2 normally–however, you may not start or stop the real timer 1 since the bits that do that are now linked to TH0. The real timer 1, in this case, will be incremented every machine cycle no matter what.

The only real use I can see of using split timer mode is if you need to have two separate timers and, additionally, a baud rate generator. In such case you can use the real Timer 1 as a baud rate generator and use TH0/TL0 as two separate timers.

TCON Register

Finally, there is one more SFR that controls the two timers and provides valuable information about them. The TCON SFR has the following structure:

As you may notice, we have only 4 timer defined of the 8 bits. That is because the other 4 bits of the SFR don’t have anything to do with timers, they have to do with Interrupts and they will be discussed in the chapter that addresses interrupts.

A new piece of information in this chart is the column “bit address.” This is because this SFR is “bit-addressable.” What does this mean? It means if you want to set the bit TR1–which is the sixth bit of TCON–you can write in the program as

TR1=1; as of in the case of byte address you have to write as

TCON=0x40; this is not needed in this SFR

This has the benefit of setting the high bit of TCON without changing the value of any of the other bits of the SFR. Usually, when you start or stop a timer you don’t want to modify the other values in TCON, so you take advantage of the fact that the SFR is a bit-addressable.

THx Register & TLx Register

In these registers, the counting values are stored.

Programming of the Timer/Counter

Let’s look at how to do the following:

  • Configure the Timer/Counter as a TIMER or as a COUNTER

  • Program the Timer/Counter with a value between 0 and 255

  • Enable and disable the Timer/Counter

  • How to know when the timer has overflowed – interrupt vs. polling.

Initializing a Timer

Now that we’ve discussed the timer-related SFRs we are ready to write code that will initialize the timer and start it running. In our examples, we will discuss only Timer 0. But Timer 1 also operates the same as Timer 1.

As you’ll recall, we first must decide what mode we want the timer to be in. In this case, we want a 16-bit timer that runs continuously; that is to say, it is not dependent on any external pins.

We must first initialize the TMOD SFR. Since we are working with timer 0 we will be using the lowest 4 bits of TMOD. The first two bits, GATE0 and C/T0 are both 0 since we want the timer to be independent of the external pins. 16-bit mode is timer mode 1 so we must clear T0M1 and set T0M0. Effectively, the only bit we want to turn on is bit 0 of TMOD.


Timer 0 is now in 16-bit timer mode. However, the timer is not running. To start the timer running we must set the TR0 bit we can do that by executing the instruction.


Upon executing these two instructions timer 0 will immediately begin counting, being incremented once every machine cycle (every 12 crystal pulses).

Then the timer values have to be loaded in THx and TLx registers so that the timer runs from the mentioned value say,


TL0=0x00; this is for the 5ms

This can be calculated by using the formula,

The general formula for timer

(65536-xx)*machine cycle= Desired time

Where xx is a 16 bit no having a value of TH0 and TL0.
When the timer run in 16-bit mode with 11.0592 MHz max delay.
That can be generated is 72 ms almost. Now let say u use 11.0592MHz crystal then the Machine cycle is calculated as for every crystal value
T=1/F (for one clock pulse)

T=12/F(for twelve clock pulse= 1 machine cycle)



(65536-xx)*1.085us= 5 ms
xx=0xEE00. (This is Hex value)

Detecting Timer Overflow

Often it is necessary to just know that the timer has reset to 0. That is to say, you are not particularly interested in the value of the timer but rather you are interested in knowing when the timer has overflowed back to 0.

Whenever a timer overflows from it is the highest value back to 0, the microcontroller automatically sets the TFx bit in the TCON register. This is useful since rather than checking the exact value of the timer you can just check if the TFx bit is set. If TF0 is set it means that timer 0 has overflowed; if TF1 is set it means that timer 1 has overflowed. We can use this approach to cause the program to execute a fixed delay. say

while (TF0==0); for timer 0 overflow

Then the TF0 flag has to be manually set to 0 for the recursive purpose of the timer.

The for loop has to be included in order to call the desired time delay for a no time so that the loop can achieve the desired time delay in the range of seconds.

Say in the below example the 5ms program is executed repetitively for 200 times so that the one-second delay is achieved.


MODE 1 (Example) 16-bit Timer 0:

#include <reg51.h>

void delay();                   //5ms delay

void main()
    while(1) {

void delay()
        int i;

MODE 0 (Example) 13-bit Timer 0:


void delay()          //1ms delay
    unsigned int i;
    for(i=0;i<1000;i++) {

void main()
    while(1) {

In our next tutorial, we will see how to use the 8051 UART Tutorial.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
Bootloader Tutorials
0 0 votes
Article Rating
Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Newest Most Voted
Inline Feedbacks
View all comments
Would love your thoughts, please comment.x