Timers are very fundamental for microcontrollers and we frequently use these timers for different purposes. In this chapter, we’ll learn how Timer0 works and the way we can use it for different purposes. This is very fundamental, so sit with concentration for Understanding Timer0 interrupt of PIC microcontrollers. Let’s start.

Disclaimer:

Advertisements

If you miss the previous chapter you can read it here:

Understanding Timer0

What is a Timer?

A timer is something that can count time. We can use a timer for either acting after a certain time or periodically or whatever we want to do with respect to time. So in Embedded systems, timers are very important for different counting and other purposes.

Timers in PIC microcontrollers are versatile and can be used in various applications, such as controlling motors, generating precise delays, measuring pulse widths, and implementing real-time control algorithms. The specific timer features and configurations may vary depending on the PIC microcontroller model you are working with, so it’s important to refer to the datasheet and reference manual for your specific microcontroller to understand its timer capabilities and how to use them effectively.

Interrupts:

Interrupts means doing something out of regular tasks. When we use an MCU, we mostly do lots of repeated tasks inside a While(1) or similar infinity loop and the MCU does those tasks inside of an infinity loop forever.
This is the regular task of the MCU. When interrupts are used, MCU goes outside this loop for a while performs all the tasks inside that interrupt loop, and then comes back to regular tasks again continuing from where it left off.

Understanding Timer0

A microcontroller can have multiple sources of interrupts. Before Understanding Timer0, we need to know the Timer Interrupt first. Let’s know more about it.

Timer Interrupt:

A timer interrupt in a microcontroller is a mechanism that allows the microcontroller to execute a specific set of instructions at predetermined intervals. Microcontrollers often include one or more timers, which are hardware modules designed to count time or events.

Here’s how it generally works:

  1. Timer Configuration: You can configure a timer with a specific time duration or to count a certain number of events.
  2. Counting: The timer continuously counts up or down based on the configured parameters. This counting can be based on clock cycles or external events.
  3. Interrupt Generation: When the timer reaches a predefined value or when a specific event occurs, it triggers an interrupt.
  4. Interrupt Service Routine (ISR): In response to the timer interrupt, the microcontroller interrupts its current execution and jumps to a predefined Interrupt Service Routine. This routine contains the set of instructions that you want to execute when the timer reaches its specified value.

Each timer has two major scales. Prescaller and postscaller.

Understanding Timer0

Prescaller:

A prescaler is a divider that reduces the frequency of the input clock to the timer. It allows you to control how often the timer updates or triggers an interrupt. By dividing the clock frequency before it reaches the timer, the prescaler can effectively change the rate at which the timer counts.

Imagine you are walking and after each 100 seconds, you are blinking your eyes for once but not stopping walking. That means, the time counting is happening inside you and once the time reaches its target point, an interrupt is being called.

Now, imagine the same thing differently, you are walking and for every 10s, you are counting a variable in your mind. Once that variable reaches 100, you are blinking your eyes. Or, you are counting that variable by +1 in each 10s.

So what is happening here? in the 2nd case, your variable will reach 100 slowly but in your 1st case, it will reach the target faster.

This is called the pre-scale. Pre-scale determines how fast or slow the timer will run and after reaching the target point it will call an interrupt.

Postscaller:

Similar to a prescaler, a postscaler performs clock division, but it does so after timer increments. Simply put, after the timer interrupt completes several actions, there is an output activity. The number of actions counted before this activity occurs is determined by the divisional factor known as the postscaler. Note that some microcontrollers may have no postscaller but just a prescaller. Which is completely normal.

When combining these two scalers, you get a timer interrupt. In any microcontroller, there can be multiple timers, such as Timer0, Timer1, etc. Furthermore, each timer can operate at different resolutions, such as 8-bit or 16-bit. The higher the bit count, the more fractional the resolution becomes.

Now, let’s have a practical timer to learn. Here we are using PIC microcontrollers. So one of the most common timers is Timer0.

Understanding Timer0/TMR0:

Practically the most used timer is the Timer0 (TMR0) in the PIC microcontroller series. Almost all microcontrollers have this Timer0 feature. Depending on MCU, this may be 8bit or higher. But for common MCUs, it’s 8-bit. So we will learn this here.

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

  • 8-bit timer/counter;
  • 8-bit prescaler (shared with Watchdog timer);
  • Programmable internal or external clock source;
  • Interrupt on overflow; and
  • Programmable external clock edge selection.
Understanding Timer0

Each timer has a control register for it to configure it. Like this, Timer0 is controlled by the OPTION_REG register. Once you work with a microcontroller, open its datasheet and look for OPTION_REG you’ll get it there explaining everything. This is the most important part of Understanding Timer0.

OPTION_REG Register

pic-microcontrollers-examples-in-assembly-language-chapter-04-fig4-2

Fig. 4-2 OPTION_REG Register

  • RBPU – PORTB Pull-up enable bit
    • 1 – PORTB pull-up resistors are disabled; and
    • 0 – PORTB pins can be connected to pull-up resistors.
  • INTEDG – Interrupt Edge Select bit
    • 1 – Interrupt on the rising edge of INT pin (0-1); and
    • 0 – Interrupt on the falling edge of the INT pin (1-0).
  • T0CS – TMR0 Clock Select bit
    • 1 – Pulses are brought to TMR0 timer/counter input through the RA4 pin; and
    • 0 – Internal cycle clock (Fosc/4).
  • T0SE – TMR0 Source Edge Select bit
    • 1 – Increment on high-to-low transition on TMR0 pin; and
    • 0 – Increment on low-to-high transition on TMR0 pin.
  • PSA – Prescaler Assignment bit
    • 1 – Prescaler is assigned to the WDT; and
    • 0 – Prescaler is assigned to the TMR0 timer/counter.
  • PS2, PS1, PS0 – Prescaler Rate Select bit
    • The prescaler rate is adjusted by combining these bits As seen in Table, the same combination of bits gives different prescaler rates for the timer/counter and watch-dog timer respectively.
PS2PS1PS0TMR0WDT
0001:21:1
0011:41:2
0101:81:4
0111:161:8
1001:321:16
1011:641:32
1101:1281:64
1111:2561:128
Prescaller settings table

Let’s open one datasheet, for an example here is the PIC16F76 datasheet says:

On the page 46, you’ll find the OPTION_REG Register. There you can read what we discussed earlier about the register settings.

Just reading and reading datasheets or any documents makes you bored to learn something. That is why, read it once you need to know it. Not always read many things and do nothing. Let’s be practical which is much more interesting.

Practical uses of Timer0:

You already have the datasheet and you know timer0. Now, let’s make a practical project then we can explain what is happening.

/* 
 Program for Testing Timer0 Interrupt
 Program written by_ Engr. Mithun K. Das; mithun060@gmail.com
 MCU:PIC16F76; X-Tal: 8MHz. Compiler: mikroC pro for PIC v7.6.0
 Date:14-01-2023
*/

//Timer0
//Prescaler 1:128; TMR0 Preload = 100; Actual Interrupt Time : 9.984 ms

//Place/Copy this part in declaration section
void InitTimer0()
{
  OPTION_REG	 = 0x86;
  TMR0		 = 100;
  INTCON	 = 0xA0;
}

void void Interrupt() iv 0x0004 ics ICS_AUTO
{
  if (TMR0IF_bit){
    TMR0IF_bit	 = 0;
    TMR0	 = 100;
    //Enter your code here
    RB0_bit=~RB0_bit;
  }
}



void main() 
{
  TRISB = 0x00;
  ADCON1 = 0x07;//turn off ADC
  ADCON0 = 0x00;//turn off ADC
  PORTB = 0x00;
  InitTimer0();
  while(1)
  {
     RB1_bit=~RB1_bit;
     Delay_ms(1000);
  }
}

//end

In this code, I’m using Timer0 as our timer interrupt. For this, I used a free tool from mikorE, Timer Calculator. Only a minor editing is required to use the generated code. First, this is what the Timer Calculator generates:

Understanding Timer0

You need to add the “iv 0x0004 ics ICS_AUTO” with the Interrupt() or it won’t get the Interrupt Vector and can’t run the ISR.

Anyway, in this code I’m doing a very simple task. Inside the timer interrupt, the ISR is called in every 10mS(9.984 ms in actuality). And pin RB0 is toggled in this ISR. And in the while loop, RB1 is toggled by 1000mS. The main purpose of this RB1 pin is to show you if we do something including a normal Delay loop inside while(1) loop, the ISR still can work in the background.

And inside ISR, whatever we can do is independent of its normal tasks.

The output of this code is:

Now you see the result. The RB0 is toggling in every 9.984 ms and the RB1 is toggling in every 1S. Now we can do faster timing inside Timer0 ISR if we need to. But note that in that case, if the ISR is really fast, the common task timing will be slower as the MCU is busy inside the ISR most of the time. But let’s try a bit with a fast interrupt.

Code Explanation:

In the time settings:

void InitTimer0()
{
  OPTION_REG	 = 0x86;
  TMR0		 = 100;
  INTCON	 = 0xA0;
}

OPTION_REG = 0x86 is configuring the Timer0 for 1:128 [TMR0 Rate] &

INTCON = 0xA0 is configuring GIE(Global Interrupt Enable)=1 & TMR0IE(Timer0 Enable) at the same time it is clearing the Timer Flag for TMR0.

Understanding Timer0

Timer ISR:

Inside the Interrupt code:

void void Interrupt() iv 0x0004 ics ICS_AUTO
{
  if (TMR0IF_bit){
    TMR0IF_bit	 = 0;
    TMR0	 = 100;
    //Enter your code here
    RB0_bit=~RB0_bit;
  }
}

Is the main timer ISR code. In this case, it is simply toggling RB0 once it gets the TMR0IF flag high. But before doing anything, it’s better to set the flag to 0 to prevent bouncing. Then the value of the TMR0 variable is called. These two lines are very important to clear the flag and set the TMR0 value. Otherwise, it will not work properly. So Understanding Timer0 was not so difficult, isn’t it?

The rest of the code is very common so I think no need to explain that again.

Now, with the following changes in the previous code:

void InitTimer0()//10uS
{
  OPTION_REG	 = 0x88;
  TMR0		 = 236;
  INTCON	 = 0xA0;
}

void void Interrupt() iv 0x0004 ics ICS_AUTO
{
  if (TMR0IF_bit){
    TMR0IF_bit	 = 0;
    TMR0	 = 236;
    //Enter your code here
    RB0_bit=~RB0_bit;
  }
}

We can make it fast ISR. This gives us a result like this:

Now you know that you can use Timer interrupts for different purposes. Well, you need to know about the settings now then. Because of course, you can use developed tools but if you do not know the basics, then you can’t find the reality.

Calculations:

Before Understanding the Timer0 calculation we need to open the datasheet first. In the datasheet, you found OPTION_Reg, right? Now, the MCU clock frequency is the basic scale of its operation. The Crystal value we use in the circuit is divided by 4 in the first point.

So the,

Fmcu = Crystal/4;

in this case, we used 8MHz crystal. So the Fmcu = 8/4=2MHz or the time in each step is 5×10-7 S.

Now if we set the prescale at 0, it will directly use this timing for timer steps. And counting TMR0 from 236 to 256(up to counting 0 after 255), it will need 20 steps.

So, it will take 5×10-7 x 20 = 10uS.

So that is the math behind the timer. You can do it manually or using any kind of tool, it’s up to you. But you know the thing in real, that’s important.

Now, you can utilize this timing for any purpose like creating time calculations, doing something from time to time, and so on.

Example2:

Understanding Timer0, we can utilize this for our practical purposes. For example, let’s reduce the task frequency inside ISR to do something practically usable such as an inverter gate operation with feedback.

/* 
 Program for Testing Timer0 Interrupt
 Program written by_ Engr. Mithun K. Das; mithun060@gmail.com
 MCU:PIC16F76; X-Tal: 8MHz. Compiler: mikroC pro for PIC v7.6.0
 Date:14-01-2023
*/

//Timer0
//Prescaler 1:128; TMR0 Preload = 100; Actual Interrupt Time : 9.984 ms

//Place/Copy this part in declaration section
void InitTimer0()
{
  OPTION_REG	 = 0x87;
  TMR0		 = 252;
  INTCON	 = 0xA0;
}

unsigned int cnt=0;
int fb=0;

void void Interrupt() iv 0x0004 ics ICS_AUTO
{
  if (TMR0IF_bit){
    TMR0IF_bit	 = 0;
    TMR0	 = 252;
    
    cnt++;
    if(cnt>0 && cnt<(20-fb))
    {
      RB0_bit = 1;
      RB1_bit = 0;
    }
    else if(cnt>=(20-fb) && cnt<22)
    {
      RB0_bit = 0;
      RB1_bit = 0;
    }
    
    if(cnt>=22 && cnt<(40-fb))
    {
      RB0_bit = 0;
      RB1_bit = 1;
    }
    else if(cnt>=(40-fb) && cnt<44)
    {
      RB0_bit = 0;
      RB1_bit = 0;
    }
    else if(cnt>=(44-fb))cnt=0;
    
  }
}



void main() 
{
  TRISB = 0x00;
  TRISA = 0xFF;
  ADCON1 = 0x00;
  ADCON0 = 0x01;//ADC channel 0;
  PORTB = 0x00;
  InitTimer0();
  while(1)
  {
     fb = ADC_Read(0)/10;
  }
}

I know you guys are very interested in Inverters. here is a very basic code that you can use for learning. Again saying, for learning. Then you can make your code once you understand it.

Output of this:

Tuning the variable resistor, the feedback operation can be done. Using the same concept with minor editing, you can use these signals in modified sine wave inverter circuits too.

Conclusion:

In this article, we have learned about interrupts and Timer interrupts, and Understanding Timer0 goes well. Also, we have seen some practical codes and results. So this was something practical that you can use for your work. But you need to learn the concept first. I hope this article will help you understand the Timer Interrupt of PIC microcontrollers. If you want to read previous articles, please check these:

In the next article, we’ll learn something more useful. So don’t forget to check again.

For Professional Designs or Help:

Loading


MKDas

Mithun K. Das; B. Sc. in EEE from KUET; Head of R&D @ M's Lab Engineering Solution. "This is my personal blog. I post articles on different subjects related to electronics in the easiest way so that everything becomes easy for all, especially for beginners. If you have any questions, feel free to ask through the contact us page." Thanks.

2 Comments

Rafiu Ajape · 19/01/2024 at 1:38 am

Wonderful sir. Thank you sir for the new tutorial. I have been visiting your blog, almost every week for the past 8 months. I just think about your blog today and I found new development. Thank you sir

    MKDas · 22/01/2024 at 12:04 pm

    Welcome, and sorry for late posting due to extreme workload.

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *