View previous topic :: View next topic |
Author |
Message |
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
Timer 0 interrupt doesn't work correct. |
Posted: Mon Nov 26, 2012 1:41 pm |
|
|
In this code I'm trying to make phase shifting of incoming signal. Here is the code:
Code: |
#include <16f684.h>
#fuses NOMCLR,INTRC
#use delay(clock = 8M)
#use rs232 (baud=9600, xmit=PIN_C0,rcv=PIN_C2)
#define outp pin_c3
#PRIORITY INT_TIMER0,INT_RA,INT_TIMER1
unsigned int16 tval,per;
int1 sync_done=0;
void sync();
#INT_TIMER0
tmr0()
{
output_low(outp);
disable_interrupts(INT_TIMER0);
}
#INT_TIMER1
tmr1()
{
output_high(outp);
set_timer0(0);
enable_interrupts(INT_TIMER0);
}
#INT_RA
void I_RA1()
{
output_low(outp);
if(sync_done)
{
if(input(pin_a1)); //read port A
tval-=20;
set_timer1(65535-tval); //makes TMR1 to overflow after tval seconds
}
else
{
if(input(pin_a1)); //read port A
tval=get_timer1();
set_timer1(0);
}
}
void main ()
{
SETUP_TIMER_0(T0_INTERNAL|T0_DIV_8); //overflows every 1ms
SETUP_TIMER_1(T1_INTERNAL|T1_DIV_BY_2); //overflows every 65.5ms
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
SET_TIMER1(0);
sync();
while(true)
{
}
}
void sync()
{
printf("Syncronizinc in process...");
delay_ms(1000);
sync_done=1;
tval=tval-50;
per=tval;
printf(" %lu",tval);
}
|
My question is, Why timer 0 doesn't wait 1ms (256x4us) before activate interruption? It goes into an interruption immediately after INT_TIMER1 function being expired. |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1934 Location: Norman, OK
|
|
Posted: Mon Nov 26, 2012 1:57 pm |
|
|
Disabling the interrupt does not stop the timer. It continues to run and over flow. _________________ Google and Forum Search are some of your best tools!!!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Nov 26, 2012 3:29 pm |
|
|
Also, setting the timer to a value, does not clear the interrupt flag. So to make the timer trigger in a known count, you need:
Code: |
set_timer0(val);
clear_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER0);
|
Best Wishes |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Mon Nov 26, 2012 9:26 pm |
|
|
And don't enable the interrupt inside the interrupt.
That's a useless instruction -- if the interrupt wasn't enabled, the code inside the interrupt wouldn't be executing.
The CCS interrupt handler takes care of clearing interrupts for you in general. You typically do not need clear/enable inside the ISR.
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Nov 27, 2012 12:29 am |
|
|
He is enabling Timer0, inside Timer1. This is a perfectly reasonable way of giving one delay after another.
Best Wishes |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Tue Nov 27, 2012 2:59 am |
|
|
Ttelmah wrote: | He is enabling Timer0, inside Timer1. This is a perfectly reasonable way of giving one delay after another.
|
Sorry - my bad.
That's legit indeed.
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Tue Nov 27, 2012 3:27 am |
|
|
Ttelmah, where exactly in my code I need to clear_interrupts(int_timer0)? |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Nov 27, 2012 3:27 am |
|
|
Quote: | My question is, Why timer 0 doesn't wait 1ms (256x4us) before activate interruption? It goes into an interruption immediately after INT_TIMER1 function being expired. |
You're doing this at the start of main.
Code: | SETUP_TIMER_0(T0_INTERNAL|T0_DIV_8); //overflows every 1ms
SETUP_TIMER_1(T1_INTERNAL|T1_DIV_BY_2); //overflows every 65.5ms |
By the time you get into the timer1 ISR timer0 will have overflowed several times, setting the interrupt flag in the process.
When you do this in the timer1 ISR
Code: |
set_timer0(0);
enable_interrupts(INT_TIMER0);
|
You don't have to wait for timer0 to overflow, the interrupt flag has already been set. Enabling the interrupt has the effect of enabling the interrupt routine only, it does not control the timers ability to set the interrupt flag. This is why you need to follow Ttelmah's instructions. It might also be an idea to either stop timer0 or disable timer0 interrupts in the timer0 ISR, to prevent it from repeatedly firing.
Mike
Your Int_RA code is superfluous to this thread. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Nov 27, 2012 3:31 am |
|
|
Quote: | Ttelmah, where exactly in my code I need to clear_interrupts(int_timer0)? |
You should not need to clear interrupts. It's supposed to be automatic, except maybe on initialisation.
Mike |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Tue Nov 27, 2012 3:37 am |
|
|
Ttelmah, where exactly in my code I need to clear_interrupts(int_timer0)?
Why I need to do this? Hasn't it done automatically by interrupt handler, as batman says? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Nov 27, 2012 5:03 am |
|
|
The interrupt is cleared at the end of the interrupt handler automatically, for the interrupt that called that handler. _But not for any other interrupt_.
You are attempting to start the timer0 interrupt, from inside the timer1 routine. When you arrive at timer1, the timer0 interrupt flag will already be set, since it occurs more frequently than timer1, and you have the interrupt disabled (so the handler won't have been called, and the interrupt won't have been automatically cleared).
So, when you use:
Code: |
#INT_TIMER1
void tmr1(void) {
output_high(outp);
set_timer0(0);
enable_interrupts(INT_TIMER0);
}
//At this point, as soon as you exit the routine. the timer0 interrupt will _immediately_ be called, since the flag is already set.....
|
To have timer0 trigger the defined time _after_ this point, you need to clear the flag. So:
Code: |
#INT_TIMER1
void tmr1(void) {
output_high(outp);
set_timer0(0);
clear_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER0);
}
|
You need to get your head round the fact the once the timer is started, it runs continuously. Whenever it wraps from 0xFFFF to 0, the interrupt flag is set. This happens whether the interrupt is enabled or not. Then if the interrupt is enabled, and the global interrupt is enabled, the handler will be called. If the flag is already set when you enable the interrupt, the handler will be called ASAP, without waiting for the next 'wrap.
Since the timer is running with the interrupt disabled, the automatic clearing in the handler won't happen, so you have to clear the interrupt yourself.
Yes, you should only need to clear interrupts manual during 'initialisation', but this is the initialisation of the timer0 interrupt......
Best Wishes |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Nov 27, 2012 8:46 am |
|
|
An alternative is to enable timer0 in the timer1 ISR and disable it in the timer0 ISR.
No need for enabling/disabling interrupt, clearing interrupt, or setting timer0.
It's fewer instructions and will produce slightly different timings which may (or may not) be more accurate.
I suspect the two delays in getting into each of the two ISRs will largely cancel each other.
Mike
EDIT I'm not saying one way is better than the other, just a different approach. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Nov 27, 2012 10:05 am |
|
|
Yes.
You'd need to set timer0, to zero in the main before starting (the timer registers are _undefined_ on start-up) but once this is done, then just start and stop the actual timer as required.
Has the advantage of fractionally reducing power consumption.
Best Wishes |
|
|
rikotech8
Joined: 10 Dec 2011 Posts: 376 Location: Sofiq,Bulgariq
|
|
Posted: Tue Nov 27, 2012 12:50 pm |
|
|
Guys YOU ARE GREAT! I changed my code to this:
Code: |
#INT_TIMER0
void tmr0()
{
output_low(outp);
}
#INT_TIMER1
void tmr1()
{
output_high(outp);
enable_interrupts(INT_TIMER0);
clear_interrupt(INT_TIMER0);
set_timer0(0);
} |
and what I want happened.
The information I got from your posts is very valuable for me.
Now Slow Start for my AC induction monophase motor is done.
Thank you again! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Nov 27, 2012 1:30 pm |
|
|
There is one tiny problem that may happen occasionally.
Because you set the timer after clearing the interrupt, once in 65536 times, the timer can wrap on the instruction between the interrupt being cleared, and the timer being reset. This will cause the original problem to come back...
The order of the instructions given in the original post avoids this....
Best Wishes |
|
|
|