Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] TMR0 weird behavior in pic16f877a

Status
Not open for further replies.

mamech

Full Member level 3
Joined
Nov 9, 2010
Messages
176
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,296
Activity points
3,135
hello

I am trying to make timer application using timer0. I made RB0 pin to toggle every 1 second using tmr0 interrupt, and I made in main function another delay for 1 second to toggle portc .
I expected that they will be synchronized together, but I got strange behaviour in simulator.

I found that in beginning , nearly there is no difference, but as the time goes, I notice that there is a time gap between RB0 and portc, and this time gap increases with time.

I used oshon and proteus, and I found that this happen in both.


in another code, I tried to use tmr0 alone on oshon with time period 1ms. and I found that the interrupt of timer in the beginning comes at 1.030 ms (the 0.03 error is not my issue here). I expected that next interrupts will happen at 2.06 ms , 3.09 ms , 4.12 ms ,etc.... , but this was not what I found on simulator. I found actual values less than expected (second interrupt happened at 2.042, not at 2.06). I found that the initial delay of 1.03 ms decreases with time. and I do not know why....

Code:
// Interrupt Handler
void interrupt()
{

  if (INTCON.TMR0IF ==1)        // timer 0 interrupt flag
  {


    PORTB.B0 = ~PORTB.B0;      // Toggle PORTB bit0 LED
    
    TMR0 = 131;                 // reset the timer with preset value

    INTCON.TMR0IF = 0;         // clear the flag
  }

}

void main()
{

  // setup portb to show the interrupts by blinking LEDs
  TRISB = 0x00;
  TRISC = 0x00;
  PORTB = 0;
  PORTC = 0 ;
OPTION_REG.T0CS = 0;  // bit 5  TMR0 is used as timer
OPTION_REG.T0SE = 0;  // bit 4 TMR0 changes with rising edge
OPTION_REG.PSA = 0;   // bit 3  Prescaler is assigned to tmr0
OPTION_REG.PS2 = 0;   // bits 2-0  PS2:PS0: Prescaler Rate Select bits the
OPTION_REG.PS1 = 1;    // rate of prescaler here is 1:256
OPTION_REG.PS0 = 0;
TMR0 = 131;             // preset for timer0 register


// Configuring Interrupt Control Registers INTCON
  INTCON = 0;           // clear the interrpt control register
  INTCON.TMR0IE = 1;        // bit5 TMR0 Overflow Interrupt Enable bit...1 = Enables the TMR0 interrupt
  INTCON.GIE = 1;           // bit7 global interrupt enable
  INTCON.TMR0IF = 0;        // bit2 clear timer 0 interrupt flag



  while(1)  //endless loop
  {
  PORTC = ~ PORTC;
  delay_ms(10);
  
  }
}


what is the reason of this weird behaviour??
 

Hi,

What clock source for PIC?
What clock frequency for PIC?
Is given code for your first or second try?

I can't find why both RB0 clock and Portc clock should synchronize.

Portc clock should be about 50Hz. It is not very precise, because it uses busy waits.

Klaus
 

It works fine. I have used Timer1 and tested in Proteus. My Proteus file is Proteus 8.2 SP2 format.
 

Attachments

  • Timer.rar
    30 KB · Views: 53

They will not synchronize because the 'delay_ms(10)' is a software generated delay, a loop to waste time. The loop will be broken each time the ISR code is executed so each TMR0 interrupt will slightly lengthen the delay. To make it exact you would have to stop TMR0 as you entered the software delay and restart it afterwards.

The actual TMR0 delay is impossible to tell without knowing your clock frequency.

Brian.
 
  • Like
Reactions: Vbase

    Vbase

    Points: 2
    Helpful Answer Positive Rating
Hi,

What clock source for PIC?
HS oscillator (note: everything i simulated, I did not connect real crystal osc to the micro in reality yet.)

What clock frequency for PIC?

4 Mhz

Is given code for your first or second try?
sorry, it is supposed to be the second try, but I forgot to comment the line containing delay of portc. but this makes no difference, the main problem is that interrupt of timer does not come in fixed time intervals.

I can't find why both RB0 clock and Portc clock should synchronize.

Portc clock should be about 50Hz. It is not very precise, because it uses busy waits.

this is not my main problem, my main problem is that the interrupt interval done by tmr0 becomes less as time increases, and this what cause the above mentioned problem

- - - Updated - - -

They will not synchronize because the 'delay_ms(10)' is a software generated delay, a loop to waste time. The loop will be broken each time the ISR code is executed so each TMR0 interrupt will slightly lengthen the delay. To make it exact you would have to stop TMR0 as you entered the software delay and restart it afterwards.

The actual TMR0 delay is impossible to tell without knowing your clock frequency.

Brian.


my main problem is that tmr0 does not give interrupts on fixed time intervals. I was just comparing its action to software delay, but the main issue is the weird performance of tmr0

I work with clock 4 Mhz
 

As you are using mikroC Compiler use mikroe Timer Calculator tool to generate Timer interrupt code.
 

You actually have the prescaler set to 1:8, not 1:256 as commented in the program. By my calculation, the interrupts should be every 1.096mS so your 10mS software delay probably gets interrupted 9 times every time it executes. I would expect the 10mS delay to vary slightly but the interrupts should accurate.

If TMR0 really is speeding up by itself it can only be an electrical problem or the value being loaded into it is getting higher.

Try this, no promises but worth a try:
Code:
// Interrupt Handler
void interrupt()
{

  if (INTCON.TMR0IF == 1)        // timer 0 interrupt flag
  {
    INTCON.TMR0IF = 0;         // clear the flag

    TMR0 = 131;                 // reset the timer with preset value

    PORTB.B0 = ~PORTB.B0;      // Toggle PORTB bit0 LED
  }
}

All I have done is rearranged the lines so the flag is cleared and the timer reloaded sooner.
If the 50Hz has to be accurate, generate it by ISR, not by using software delays. You might also see RMW problems on both ports.

Brian.
 

As you are using mikroC Compiler use mikroe Timer Calculator tool to generate Timer interrupt code.

can you please how to do this? where is this calculator tool?
 


I used it, but still same problem ... first interrupt came at 1035 us (1.035 ms), and the next interrupt came at 2049 us (2.049ms) , and it is supposed to be 2.07 ms (error here is 2.07- 2.049 = 0.021 ms ), and the next came at 3061 us (3.061 ms and error here is 3.105 - 3.061 = 0.044 ms ), and so on....

I really does not understand. the timers are supposed to be accurate, then why does this happen...


this is the code that is based on mikroc timer calculator
Code:
             //Timer0
//Prescaler 1:4; TMR0 Preload = 6; Actual Interrupt Time : 1 ms

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

void Interrupt(){
  if (TMR0IF_bit){
    TMR0IF_bit	 = 0;
    TMR0		 = 6;
    PORTB.B0 = ~PORTB.B0;
  }
}



void main()
{

  // setup portb to show the interrupts by blinking LEDs
  TRISB = 0x00;
  TRISC = 0x00;
  PORTB = 0;
  PORTC = 0 ;


   InitTimer0();

  while(1)  //endless loop
  {

  }
}

- - - Updated - - -

You actually have the prescaler set to 1:8, not 1:256 as commented in the program. By my calculation, the interrupts should be every 1.096mS so your 10mS software delay probably gets interrupted 9 times every time it executes. I would expect the 10mS delay to vary slightly but the interrupts should accurate.

If TMR0 really is speeding up by itself it can only be an electrical problem or the value being loaded into it is getting higher.

Try this, no promises but worth a try:
Code:
// Interrupt Handler
void interrupt()
{

  if (INTCON.TMR0IF == 1)        // timer 0 interrupt flag
  {
    INTCON.TMR0IF = 0;         // clear the flag

    TMR0 = 131;                 // reset the timer with preset value

    PORTB.B0 = ~PORTB.B0;      // Toggle PORTB bit0 LED
  }
}

All I have done is rearranged the lines so the flag is cleared and the timer reloaded sooner.
If the 50Hz has to be accurate, generate it by ISR, not by using software delays. You might also see RMW problems on both ports.

Brian.


yes, sorry there where typing mistake, yes the prescaler is 1:8. I tried your code, and it gave me nearly same results of the previous code. still the gap is getting bigger with the time, and timer interrupt does not come on after same time interval.

it can be electrical problem if there is hardware, but all of this is based on simulators. both oshon and proteus did the same weird behaviour for this simple code.
 

The small increase in the timer ISR timing is expected. This is because you are not taking into account the fact that the timer continues to 'tick' after it triggers the interrupt.
Timer 0 starts at the value you set in TMR0 and increments on each 'tick' until the timer register overflows. That sets the IF bit which will trigger the ISR.
However the timer continues to receive 'tick's while all of this is going on. To be accurate, you need to subtract the value of TMR0 from whatever value you want to reset it to - something like:
Code:
TMR0 = 131 - TMR0;
That will take into account the extra ticks.
By the way, the code before the 'timer reset' WILL impact on the accuracy of the timer (taken care of by the above suggestion) but the code after that (within the ISR) does not matter - the timer has been reset .
Susan
 
  • Like
Reactions: mamech

    mamech

    Points: 2
    Helpful Answer Positive Rating
Hi,

still the gap is getting bigger with the time,
Don´t use the first timestamp as absolute value. This has a liitle deviation to the others because of the initialisation routine at startup.
Pleas use the difference between timestamps only.

The small increase in the timer ISR timing is expected.
I´m not familiar with PICs. But isn´t there a timer/counter mode that creates PWM/pulses/interrupts withoutthe needfor the software to set the tmr0 value or limits every time a new?
Something like "preset after zero".
Especially for PWM generation there shoud be such a mode.
--> try to generate 50Hz PWM - completely without isr software.

Klaus
 
  • Like
Reactions: mamech

    mamech

    Points: 2
    Helpful Answer Positive Rating
Klaus, no the PIC PWM hardware uses two of the timer modules, one to set the PWM rate and the other to set the output duration, their difference being the high and low periods of the PWM.

Susan's system should work, it is very similar to the "zero error" timing routines from Roman Black (https://www.romanblack.com/one_sec.htm) but you should also be able to do it by taking into account the two cycle loss when you load TMR0.

If you need accurate timing of 1mS and 20mS (50Hz) it would be better to use a 'divide by 10' software routine, triggered by 1mS interrupts then toggle a variable before writing it to the port. As I mentioned earlier, you may see "Read Modify Write" problems in a real construction because you are performing math directly on the port contents, it will not show up in a simulation because it will not take into account the effects of capacitive charges held on the pins.

Brian.
 

I thought that first interrupt should be like next interrupts, but now I understand that the first one comes a little different than next interrupts.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top