PIC16F877A – Timer/Counter Tutorial

This article is a continuation of the series of tutorials on the PIC16F877A Microcontroller. The aim of this series is to provide easy and practical examples that anyone can understand.

In our previous tutorial, we have seen LED Interfacing with PIC16F877A (GPIO). Now we will see the PIC16F877A Timer Tutorial. If you read this tutorial, you could generate precise delays for your microcontroller and applications. Let’s start.

PIC16F877A Timer Tutorial

As the name suggests, these are used to measure the time or generate an accurate time delay. The microcontroller can also generate/measure the required time delays by running loops, but the timer relieves the CPU from that redundant and repetitive task, allowing it to allocate maximum processing time for other tasks.

The timer is nothing but a simple binary counter that can be configured to count clock pulses (Internal/External). Once it reaches the max value, it will roll back to zero, setting up an OverFlow flag and generates the interrupt if enabled.

PIC16F877a has three timers.

  • Timer0 (8-bit timer)
  • Timer1 (16-bit timer)
  • Timer2 (8-bit timer)

All Timers can act as a timer or counter or PWM Generation. Now we will see each one by one.

To start using a timer we should understand some of the fancy terms like 8-bit/16-bit timer, Prescaler, Timer interrupts, and Focs. Now, let us see what each one really means.

As said earlier, there are both the 8-bit and 16-bit Timers in our PIC16F877A. The main difference between them is that the 16-bit Timer has a much better Resolution than the 8-bit Timer.

Prescaler is a name for the part of a microcontroller that divides the oscillator clock before it reaches logic that increases timer status.

The range of the Prescaler ID is from 1 to 256 and the value of the Prescaler can be set using the OPTION Register (we will see this register later).

As the timer increments and when it reaches its maximum value of 255 (for 8-bit timers) or 65536 (for 16-bit timers), it will trigger an interrupt and initialize itself to 0 back again. This interrupt is called as the Timer Interrupt.

This interrupt informs the MCU that this particular time has lapped.

The Fosc stands for Frequency of the Oscillator, it is the frequency of the Crystal used. The time taken for the Timer register depends on the value of Prescaler and the value of the Fosc.

Timer 0

  • The Timer0 module timer/counter has the following features:
  • 8-bit timer/counter
  • Readable and writable
  • 8-bit software programmable Prescaler
  • Internal or external clock select
  • Interrupt on overflow from FFh to 00h
  • Edge select for external clock

Registers used for Timer0

  • OPTION_REG
  • TMR0
  • INTCON

1. OPTION_REG

We perform all the necessary settings with OPTION_REG Register. The size of the register is 8 bits.

OPTION_REG Timer RegisterR = Readable bit  W = Writable bit  U = Unimplemented bit, read as ‘0’  -n = Value at POR  ‘1’ = Bit is set  ‘0’ = Bit is cleared  x = Bit is unknown

RBPU: PORTB Pull-up Enable bit (This bit is not used for timers)

1 = PORTB pull-ups are disabled
0 = PORTB pull-ups are enabled by individual port latch values

INTEDG  (This bit is not used for timers)

T0CS: TMR0 Clock Source Select bit

1 = Transition on T0CKI pin
0 = Internal instruction cycle clock (CLKO)

T0SE: TMR0 Source Edge Select bit

1 = Increment on high-to-low transition on T0CKI pin
0 = Increment on low-to-high transition on T0CKI pin

PSA: Prescaler Assignment bit

1 = Prescaler is assigned to the WDT
0 = Prescaler is assigned to the Timer0 module

PS2:PS0: Prescaler Rate Select bits

Prescaler Rate Select bitsNote: There is only one Prescaler available, which is mutually exclusively shared between the Timer0 module and the Watchdog Timer. A Prescaler assignment for the Timer0 module means that there is no Prescaler for the Watchdog Timer and vice versa.

This Prescaler is not accessible but can be configured using PS2:PS0 bits of OPTION_REG.

2. INTCON Register

INTCON Register

GIE: Global Interrupt Enable bit

1-Enables all unmasked interrupts
0-Disables all interrupts

PIE: Peripheral Interrupt Enable bit

1-Enables all unmasked peripheral interrupts
0-Disables all peripheral interrupts

TMR0IE: TMR0 Overflow Interrupt Enable bit

1-Enables the TMR0 interrupt
0-Disables the TMR0 interrupt

INTE: Not for Timers

RBIE: Not for Timers

TMR0IF: TMR0 Overflow Interrupt Flag bit

1-TMR0 register has overflowed (must be cleared in software)
0-TMR0 register did not overflow

INTF: Not for Timers

RBIF: Not for Timers

3. TMR0 Register

This is the 8-bit register that holds the timer values. For example, initially, it will be 0. It will increment by one per one clock cycle. When it reaches 255, it will trigger the TMR0IF bit in INTCON Register. Then again starts from 0.

Delay Calculation for 1 second

Delay Calculation

Here, My fclk = 11.0592MHz (You can put your board’s fclk)

Prescaler = 256 (It is based on PS0 – PS2 bits in OPTION_REG)

TMR0 = 0. (My TMR0’s value will be 0)

Desire Delay (Tout = 1 second) So Fout = 1  (Tout = 1/Fout)

Apply these values to the above formula.

Count = 11059200 / (4*256*256*1)

Count = 42.1875 (approximately 42).

Timer0 Code

In this code, a LED is connected to Port B. Those LEDs are blinking every 1 second.

#include<pic.h>

void t0delay();

void main()
{
	TRISB=0;
	OPTION_REG=0x07;   //Prescale is assigned to Timer 0, Prescaler value = 256, Fclk = 11.0592MHz
	while(1) {
		PORTB=0xff;
		t0delay();
		PORTB=0x00;
		t0delay();
	}
}

void t0delay()					// 1 second
{
	int i;
	for(i=0;i<42;i++) {
		while(!T0IF);
		T0IF=0;
	}
}				

Timer 1

The timer TMR1 module is a 16-bit timer/counter with the following features:

  • 16-bit timer/counter with two 8-Bit registers TMR1H/TMR1L
  • Readable and writable
  • software programmable Prescaler up to 1:8
  • Internal or external clock select
  • Interrupt on overflow from FFFFh to 00h
  • Edge select for external clock

Registers used for Timer1

  • T1CON
  • TMR1 (TMRIH, TMRIL)
  • PIR1

1. T1CON Register

T1CON Timer Register

T1CKPS1:T1CKPS0:Timer1 Input Clock Prescale Select bits

11 = 1:8 prescale value
10 = 1:4 prescale value
01 = 1:2 prescale value
00 = 1:1 prescale value

T1OSCEN: Timer1 Oscillator Enable Control bit

1-Oscillator is enabled
0-Oscillator is shut-off

T1SYNC: Timer1 External Clock Input Synchronization Control bit

1-Does not synchronize external clock input
0-Synchronize external clock input

TMR1CS: Timer1 Clock Source Select bit

1-External clock from pin RC0/T1OSO/T1CKI (on the rising edge)
0-Internal clock (FOSC/4)

TMR1ON: Timer1 On bit

1-Enables Timer1
0-Stops Timer1

2. TMR1 Register

Timer1 has a register called the TMR1 register, which is 16 bits in size.  Actually, the TMR1 consists of two 8-bits registers:

  • TMR1H
  • TMR1L

It increments from 0000h to the maximum value of 0xFFFFh (or 65,535 decimal). The TMR1 interrupt, if enabled, is generated on overflow which is latched in the interrupt flag bit, TMR1IF (PIR1<0>).

This interrupt can be enabled/disabled by setting/clearing the TMR1 interrupt enable bit, TMR1IE (PIE1<0>). You can initialize the value of this register to whatever you want (not necessarily “0”).

3. PIR1 Register

This register contains the Timer1 overflow flag. (TMR1IF).

TMR1IF – TMR1 overflow Interrupt Flag bit.

This flag marks the end of ONE cycle count. The flag needs to be reset in the software if you want to do another cycle count. We can read the value of the register TMR1 and write it into it.

We can reset its value at any given moment (write) or we can check if there is a certain numeric value that we need (read).

Delay Calculation for 1 second

PIC16F877A Timer Delay Calculation

Here, My fclk = 11.0592MHz (You can put your board’s fclk)

Prescaler = 1 (It is based on T1CKPS1 – T1CKPS0 bits in T1CON Register)

TMR1 = 0. (My TMR1’s value will be 0)

Desire Delay (Tout = 1 second) So Fout = 1  (Tout = 1/Fout)

Apply these values to the above formula.

Count = 11059200 / (4*1*65536*1)

Count = 42.1875 (approximately 42).

Timer1 Code

In this code, a LED is connected to Port B. Those LEDs are blinking every 1 second.

#include<pic.h>

void t1delay();

void main()
{
	TRISB=0;
	T1CON=0x01;   //Prescale value = 1:1, It using Internal clock, Timer 1 ON
	while(1) {
		PORTB=0xff;
		t1delay();
		PORTB=0;
		t1delay();
	}
}

void t1delay()
{
	int i;
	for(i=0;i<42;i++) {
		TMR1H=TMR1L=0;
		while(!TMR1IF);
		TMR1IF=0;
	}
}

Timer 2

The TImer2 module is an 8-bit timer/counter with the following features:

  • 8-bit timer/counter
  • Readable and writable
  • Software programmable Prescaler/PostScaler up to 1:16
  • Interrupt on overflow from FFh to 00h

Registers used for Timer2

  • T2CON
  • TMR2
  • PIR1
  • PR2

1. T2CON Register

T2CON Register

TOUTPS3:TOUTPS0: Timer2 Output Postscale Select bits

0000 = 1:1 postscale
0001 = 1:2 postscale
0010 = 1:3 postscale



1111 = 1:16 postscale

TMR2ON: Timer2 On bit

1-Timer2 is on
0-Timer2 is off

T2CKPS1:T2CKPS0: Timer2 Clock Prescale Select bits

00 = Prescaler is 1
01 = Prescaler is 4
1x = Prescaler is 16

2. TMR2 & PR2 Register

  • TMR2 – The register in which the “initial” count value is written.
  • PR2 – The register in which the final or the maximum count value is written.

3. PIR1 Register

This register contains the Timer2 overflow flag(TMR2IF).

Delay Calculation for 1 second

PIC16F877A Timer Delay Formula

Here, My fclk = 11.0592MHz (You can put your board’s fclk)

Prescaler = 1 (It is based on T2CKPS1:T2CKPS0 bits in T2CON)

Postscaler = 16 (It is based on TOUTPS3:TOUTPS0 bits in T2CON)

TMR2 = 0. (My TMR2’s value will be 0)

PR2 = 255 (My PR2’s value will be 255)

Desire Delay (Tout = 1 second) So Fout = 1  (Tout = 1/Fout)

Apply these values to the above formula.

Count = 11059200 / (4*1*(256-0)*16*1)

Count = 675.

Timer2 Code

In this code, the LED is connected to Port B. Those LEDs are blinking every 1 second.

#include<pic.h>
#include<htc.h>

void t2delay();

void main()
{
	TRISB=0;
	T2CON=0b01111000;			//postscale=16,prescale=1,timer off
	while(1)
	{
		PORTB=255;
		t2delay();
		PORTB=0;
		t2delay();
	}
}

void t2delay()
{
	unsigned int i;
	T2CON|=(1<<2);				//timer2 on
	for(i=0;i<675;i++)
	{
		while(!TMR2IF);	
		TMR2IF=0;
	}
}

In our next tutorial, we will see how to use the USART in PIC16F877A.

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
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials - STM32Zephyr RTOS Tutorials - ESP32
AUTOSAR TutorialsUDS Protocol Tutorials
Product ReviewsSTM32 MikroC Bootloader Tutorial
VHDL Tutorials
Subscribe
Notify of
guest

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

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Table of Contents