PIC16F73 - Timer0 Interrupt

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
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





#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;
}

}
}
 

Papabravo

Joined Feb 24, 2006
21,158
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?
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
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
 

Papabravo

Joined Feb 24, 2006
21,158
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.
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
"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?
 

JohnInTX

Joined Jun 26, 2012
4,787
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.
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
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
 

ErnieM

Joined Apr 24, 2011
8,377
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.
 

ErnieM

Joined Apr 24, 2011
8,377
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.
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
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)
{

}
}
 

JohnInTX

Joined Jun 26, 2012
4,787
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?
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..

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.
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.
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
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 =]
 

JohnInTX

Joined Jun 26, 2012
4,787
Now, what do you mean by "reload in the interrupt routine"?
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.

C:
#define TMR0COUNTS 10
// in the interrupt routine:
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.
 

dannyf

Joined Sep 13, 2015
2,197
"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.
 

dannyf

Joined Sep 13, 2015
2,197
Code:
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.
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
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?
 

dannyf

Joined Sep 13, 2015
2,197
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:
  my_counter+=TMR_PERIOD;  //increment counter
  if (my_counter >= COUNTER_PERIOD) {  //overflow happened
    my_counter-=COUNTER_PERIOD; //reset counter
    //do something here
  }
Say that your timer0 runs at 1Mhz, 2:1 prescaler, and you want to trigger every 10 seconds, use the following defines:
Code:
#define TMR_PERIOD  (256*2) //timer period
#define COUNTER_PERIOD (1000000) //counter period
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.
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
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?
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
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:
#define TMR_PERIOD  (256*2) //timer period
#define COUNTER_PERIOD (1000000) //counter period

my_counter+=TMR_PERIOD;  //increment counter
  if (my_counter >= COUNTER_PERIOD) {  //overflow happened
    my_counter-=COUNTER_PERIOD; //reset counter
    //do something here
  }
 
Top