PIC16F73 - Timer0 Interrupt

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

  1. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    Hi Guys,

    Need some more help on interrupts, im trying to use the timer0 stack overflow as an interrupt. I've got the interruprt working without any errors. However i want to make 'PORTA=0xFF' during the interrupt function, but this doesnt seem to work and PORTA does not update.

    I've attached my code below.

    Thanks,
    Ash [​IMG]




    #include <pic16f73.h>
    #include <htc.h>
    #define _XTAL_FREQ 4000000 //Set frequency to 4Mhz, refer to resonator specs
    __CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & CP_ON & BOREN_OFF );

    void interrupt Timer0_ISR(void)
    {
    TMR0IF=0; //Disable TMR0 Overflow Flag Bit
    PORTA=0XFF;
    GIE=1; //Enable Global Interrupt
    }

    void main(void)
    {

    TRISA=0b00000000;
    TRISB=0b00000001; //Initialise I/O ports
    TRISC=0b00000000; //Initialise I/O ports
    PORTB=0b10000000; //Initialise I/O ports

    OPTION_REG=0b01000000; //Enable Rising Edge Trigger
    TMR0IE=1; //Enable TMR0 Overflow Interruprt
    GIE=1; //Enable Global Interrupt

    while(1)
    {

    if (TMR0<252)
    {
    TMR0=252;
    }

    }
    }
     
  2. Papabravo

    Expert

    Feb 24, 2006
    10,145
    1,791
    Setting GIE=1 inside the interrupt routine is a bad thing to do and unnecessary besides. What is the clock source for Timer0? What is default setup for Timer0?
     
  3. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    So how do I re-enable the global interrupt, so I can return to the interrupt routine again?

    I suppose clock source would be the external XTAL? I'm still getting my head around this, so bear with me.


    Ash
     
  4. Papabravo

    Expert

    Feb 24, 2006
    10,145
    1,791
    Read the description of the assembly language instruction RETFIE, which is used to exit from an interrupt subroutine. It is slightly, but importantly, different from the ordinary RETURN instruction. With microprocessors you should never "suppose", you MUST "confirm" and "verify". What I am asking you to do is confirm that Timer0 is actually a timer and not an event counter. Timers work off of a clock source, while counters work off of an input signal. That is they count rising or falling edges of an "input" signal.
     
  5. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    "The “return from interrupt” instruction, RETFIE, exits the interrupt routine, as well as sets the GIE bit, which re-enables interrupts."

    Thanks, found this in the data sheet. Any idea why PORTA isnt being set?
     
  6. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    You need to set ADCON1 to 07h to enable the digital outputs.
    You should reset TMR0 in the interrupt routine, not in main.
    Your settings, and the reload in main, have TMR0 incrementing to the FF->00 rollover in 4us. That's pretty fast.
    OPTION_REG has the timer configured as a timer for internal clock, 1us tik.


    EDIT: ADCON1 setting
    Consider using TMR2/PR2 for a uniform system interrupt. TMR0 is not as good for that since you have to reload it each time and reloading it clears the prescaler, causing some jitter in timing.
     
  7. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    Ok, I've enable digital outputs.

    I haven't reset TMR0 in the main, so i assumed it reset in the interrupt routine? I set TMR0 to 252 in order to simulate faster.

    Shall I set the OPTION_REG to external clock?


    The more answers I get the more questions I have.. Getting a little confusing
     
  8. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,392
    1,605
    Interrupts in either assembly or any higher level language get disabled (by hardware) when you enter the ISR.

    When you exit the ISR the retfi asm statement is used which will re enable them.

    It is slightly more complicated if your processor has multiple levels of interrupts but works the same way albeit that and lower priority levels all get suspended such that only a higher level ISR can interrupt a lower level.
     
  9. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,392
    1,605
    If you want to catch the rollover of the timer just let it run and do not touch it to reset it in any way. You are adding an unknown delay if you do such.

    The timer will happily roll over and start counting from zero all by itself.
     
  10. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    Okay so the interrupt enters ISR nicely, and PORTA is being set now. So there doesn't seem to be a problem.

    I'm not sure how to implement the RETFIE command using the correct syntax, because its an Assembly command. However the GIE bit is enabled automatically when I exit the ISR routine.




    #include <pic16f73.h>
    #include <htc.h>
    #define _XTAL_FREQ 4000000 //Set frequency to 4Mhz, refer to resonator specs
    __CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & CP_ON & BOREN_OFF );




    void interrupt Timer0_ISR(void)
    {
    PORTA=0XFF;
    //TMR0IF=0; //Disable TMR0 Overflow Flag Bit

    }

    void main(void)
    {
    ADCON1 = 0b00000111;
    TRISA=0b00000000;
    TRISB=0b00000001; //Initialise I/O ports
    TRISC=0b00000000; //Initialise I/O ports
    PORTA=0b00000000;
    PORTB=0b10000000; //Initialise I/O ports


    OPTION_REG=0b01000000; //Enable Rising Edge Trigger
    TMR0IE=1; //Enable TMR0 Overflow Interruprt
    GIE=1; //Enable Global Interrupt


    while(1)
    {

    }
    }
     
  11. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    If you are simulating, that's fine, but do the reload in the interrupt routine and consider TMR2. If you set it to external clock you'll need an external clock to drive it. Usually, you use internal..

    RETFIE is only for assembly, C does it for you automatically when you specify a routine as 'interrupt'.

    You might want to put some dummy thing in main - toggle an output bit or something - to run between interrupts.
     
  12. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    Okay so its all setup nicely, I have a 4MHz resonator setup externally thats why I'm using the external clock.

    I'll use PORTC to setup a dummy command or something.

    Now, what do you mean by "reload in the interrupt routine"?



    I appreciate all your help btw =]
     
  13. JohnInTX

    Moderator

    Jun 26, 2012
    2,347
    1,029
    Like you were doing in the original main, if you want some period other than 256 TMR0 counts, you have to reload it each time to 256-(the number of counts you want) i.e.

    Code (C):
    1. #define TMR0COUNTS 10
    2. // in the interrupt routine:
    3. TMR0 += 256-TMR0COUNTS;  // add the setting to account for any accumulated counts while getting to the interrupt.
    Note that writing to TMR0 clears the prescaler (losing some clock counts there) so there will be some error. That's why TMR2/PR2 is a better choice, once you set it up, it runs at the selected period without any further intervention.

    Have fun.
     
  14. dannyf

    Well-Known Member

    Sep 13, 2015
    1,819
    362
    "PORTA=0XFF;"

    you will just set the pins. Any easir way is to flip the pins with
    "PORTA^=0xff;"

    tmr0 is not auto reloading so you have toload it up with an offset, like this,

    "TMR0 +=-TMR0_OFFSET;"

    for example, if you want a timer period of 100 ticks, you can define the offset to 100.

    that kind of an approach works for short period. For long periods,different approaches are needed.
     
  15. dannyf

    Well-Known Member

    Sep 13, 2015
    1,819
    362
    "you have to reload it each time to 256-(the number of counts you want) i.e."

    not reason to perform that subtraction.
     
  16. dannyf

    Well-Known Member

    Sep 13, 2015
    1,819
    362
    Code (Text):
    1. TMR0 +=-TMR0_OFFSET;
    Full disclosure: this approach has a small residual error term to it -> due to the calculation being inherently non-atomic.

    This error term can be corrected, but the correction depends on a variety of factors, like compiler settings, etc.
     
  17. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    The timer seems to reset when I enter the interrupt? Is this not what auto reloading is?

    Whats the best way to create a delay of say 10 seconds?
     
  18. dannyf

    Well-Known Member

    Sep 13, 2015
    1,819
    362
    It is hard to say what you may or may not consider as "the best".

    For long delays like that, I tend to increment a counter and then test for overflow, all in the isr:

    Code (Text):
    1.  
    2.   my_counter+=TMR_PERIOD;  //increment counter
    3.   if (my_counter >= COUNTER_PERIOD) {  //overflow happened
    4.     my_counter-=COUNTER_PERIOD; //reset counter
    5.     //do something here
    6.   }
    7.  
    Say that your timer0 runs at 1Mhz, 2:1 prescaler, and you want to trigger every 10 seconds, use the following defines:
    Code (Text):
    1.  
    2. #define TMR_PERIOD  (256*2) //timer period
    3. #define COUNTER_PERIOD (1000000) //counter period
    4.  
    This approach's accuracy is determined only by the timing components's accuracy (crystal, external oscillator or internal oscillator) over the long timer. However, it does have short-term jitter - which can be alleviated further but not eliminated.
     
  19. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    I will need to create a 10 second delay and another delay of about 60 seconds.

    Is it possible to have two different delays in the same interrupt?
     
  20. Sadlercomfort

    Thread Starter New Member

    Sep 14, 2015
    11
    0
    Can someone show me a working example of the timer interrupt this exampel? I understand how TMR0 increments after each line of code, then when it overflows goes into the interrupt. But I dont understand how this example works at all
    Code (Text):
    1.  
    2. #define TMR_PERIOD  (256*2) //timer period
    3. #define COUNTER_PERIOD (1000000) //counter period
    4.  
    5. my_counter+=TMR_PERIOD;  //increment counter
    6.   if (my_counter >= COUNTER_PERIOD) {  //overflow happened
    7.     my_counter-=COUNTER_PERIOD; //reset counter
    8.     //do something here
    9.   }
     
Loading...