PIC16F73 - Timer0 Interrupt

Discussion in 'Embedded Systems and Microcontrollers' started by Sadlercomfort, Sep 14, 2015.

  1. Papabravo

    Expert

    Feb 24, 2006
    12,075
    2,610
    From your example it should be obvious that COUNTER_PERIOD is not an integer multiple of TMR_PERIOD. After adding TMR_PERIOD to my_counter and testing the result for >= to COUNNTER_PERIOD some number of times you will find the comparison to be true. By subtracting COUNTER_PERIOD from my_counter the result will be between zero and TMR_PERIOD. This new initial value will correct for the previous counting of too many TMR_PERIOD's.

    100,000 / 512 = 195 Remainder 160
    So on the first cycle you will count 196 TMR_PERIOD's
    512 * 196 = 100,352
    On the second cycle you will count 195 TMR_PERIOD's
    352 + 195*512 = 100,192
    On the third cycle you will count 195 TMR_PERIOD's
    192 + 195*512 = 100,032
    On the fourth cycle you will count 196 TMR_PERIOD's
    32 + 196*512 =100,384
    and so on...

    Does that help?
     
  2. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    421
    Just go through an example and you will figure it out - it is very simple.
     
  3. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    421
    Yes.
     
  4. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    Thanks Guys I'm understanding this better now. I'm using 4Mhz so if my TMR0 overflows after 256 and I count 16 overflows I can create a delay of 1 second. Using this I can create different delays.

    I'm on mobile atm so can't show you my code but it runs great

    I created a counter
    Code (Text):
    1. volatile char counter = 0;
    And set the timer at the beginning of the program, clearing the timer and counter at the end of the interrupt.

    Code (Text):
    1. TMR0=12;
     
  5. jayanthd

    Active Member

    Jul 4, 2015
    863
    72
    Code to blink PORTA LEDs once a second.

    Code (C):
    1.  
    2. #define _XTAL_FREQ 4000000
    3.  
    4. #include <pic16f73.h>
    5. #include <htc.h>
    6.  
    7. __CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & CP_ON & BOREN_OFF);
    8.  
    9. char counter = 0;
    10.  
    11. //Timer0
    12. //Prescaler 1:256; TMR0 Preload = 61; Actual Interrupt Time : 49.92 ms
    13.  //Place/Copy this part in declaration section
    14. void InitTimer0() {
    15.   OPTION_REG = 0x87;
    16.   TMR0 = 61;
    17.   INTCON = 0xA0;
    18. }
    19.  
    20. void interrupt isr(void) {
    21.     if(TMR0IF) {
    22.         TMR0IF = 0;
    23.         TMR0 = 61;
    24.        //Enter your code here
    25.         if(++counter == 20) {
    26.             PORTA = ~PORTA;
    27.         }  
    28.     }
    29. }
    30.  
    31. void main(void) {
    32.  
    33.     ADCON1 = 0x07;
    34.  
    35.     TRISA = 0x00;
    36.     TRISB = 0x00;
    37.     TRISC = 0x00;  
    38.  
    39.     PORTA = 0x00;
    40.     PORTB = 0x80;  
    41.  
    42.     InitTimer0();
    43.  
    44.     while(1) {
    45.    
    46.     }
    47. }
    48.  
     
  6. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    421
    Code (Text):
    1.         TMR0 =61;
    That basically negated the reason to go down this path.

    You are taking the manual reloading approach -> not that it wouldn't work.
     
  7. jayanthd

    Active Member

    Jul 4, 2015
    863
    72
    Sorry, I didn't understand what you are telling. English is not my native language. Please tell in simple words. I have tested this code in hardware and it works fine.
     
  8. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    Think I've got it sorted =) if I get any more trouble shall I make a new post or stick to this one?
     
  9. jayanthd

    Active Member

    Jul 4, 2015
    863
    72
    Fixed code

    Code (C):
    1.  
    2.  
    3. void interrupt isr(void) {
    4.    if(TMR0IF) {
    5.         TMR0IF = 0;
    6.         TMR0 = 61;
    7.        //Enter your code here
    8.        if(++counter == 20) {
    9.             PORTA = ~PORTA;
    10.             counter = 0;
    11.        }
    12.    }
    13. }
    14.  
     
  10. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    421
    Code (Text):
    1. TMR0 =61;
    The issue was discussed earlier in the thread.

    Essentially when TMR0 rolls over to 0, an interrupt is trigger. While the mcu services that interrupt (ie., "interrupt latency"), TMR0 continues to advance. Interrupt latency varies based on a lot of factors but generally anywhere from 10 - 20+ ticks could be passed before the first "user instructions" in the ISR are executed.

    So by the time you get to "TMR0 =61;", TMR0 has contained a value (10 - 20+, depending on how the code is structured). And that value is destroyed by your assignment statement. This will cause the interrupt intervals to be longer than expected: in your case, 61 = -195. ie, it should have been 195 ticks long.

    That's why you see that in the two proposals posted earlier, rather than "TMR0 = 61;", "TMR0 += 61;" is used, to ***more*** accurately reproduced a 195 tick interval.

    Now, even that approach has issues but it would have greatly reduced the errors introduced by a simple assignment statement.

    You can clearly see this effect by simulating the code and stepping through the ISR while watch TMR0's value changes.
     
  11. dannyf

    Well-Known Member

    Sep 13, 2015
    2,196
    421
    Here is a simulation that shows the issue.

    I reconfigured the tmr0 to run at 1:1 prescaler - one of the posters earlier correctly pointed out that writing to TMR0 will clear the prescaler which is not viewable. So to make the problem more visible, I have reconfigured the timer0 to run at 1:1 so TMR0 will serve as a way to measure the latency.

    As you can see, before the first user instruction is executed, TMR0 contains a value of 16 -> under XC free mode.

    Using "TMR0 = 61;"
    TMR0 value before the first isr instruction: 16 @ XC free, 12 @ XC pro;
    TMR0 value before the assignment statement: 21 @ XC free, 15 @ XC pro;
    TMR0 value after the assignment statement: 61 @ XC free, 61 @ XC pro;
    Total interrupt interval: 220 @ XC free, 214 @ XC pro.
    vs. Expected interrupt interval: 195 -> timing error of 27 / 19;

    Using "TMR0+= 61;"
    TMR0 value before the first isr instruction: 16 @ XC free, 12 @ XC pro;
    TMR0 value before the assignment statement: 21 @ XC free, 15 @ XC pro;
    TMR0 value after the assignment statement: 85 @ XC free, 77 @ XC pro.
    Total interrupt interval: 198 @ XC free, 198 @ XC pro.
    vs. Expected interrupt interval: 195 -> timing error of 3 / 3.

    The story here really is that if you don't have to write to TMR0, try not to write to TMR0. If you have to write to TMR0, try to preserve its existing value.
     
  12. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,978
    1,841
    Often I put a timer to work to create an accurate 1ms period ISR. It is exactly as accurate as the clock on PIC.

    Rather brainlessly simple if you just use timer 2 for this task.
     
    JohnInTX likes this.
Loading...