View previous topic :: View next topic |
Author |
Message |
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
PIC12F629 Timer issue |
Posted: Sat Jul 22, 2006 8:32 pm |
|
|
Using 10MHz osc. I'm trying to run both timer0 and timer1 to generate
two frequencies 1100Hz and 1500Hz
To toggle the pin each half cycle in the ISR:
Timer0Value = 256 - (0.00033333/(8/(10000000/4)))
Timer1Value = 65536 - (0.000454545/(1/(10000000/4)))
The output is not what I expected, allowing for the timer resolution these values are too far off.
Measured output 1063Hz not 1100Hz
Measured output 1430Hz not 1500Hz
The resonator is running a little high, the measured frequency 10,050,250 Hz. Yet the outputs are far too low !
Did I do something wrong or is there something strange with the 12F629 ?
Thanks.
Hans W
#FUSES HS, NOMCLR, NOPROTECT, NOWDT, NOBROWNOUT, PUT
#use delay(clock=10000000)
#use fast_io(a)
int Timer0Value;
long Timer1Value;
#int_TIMER0
TIMER0_isr()
{
output_toggle(PIN_A1);
set_Timer0 ( Timer0Value );
}
#int_TIMER1
TIMER1_isr()
{
output_toggle(PIN_A2);
set_Timer1 ( Timer1Value );
}
void main()
{
Timer0Value = 152;
Timer1Value = 64400L;
set_tris_A(0b00101000);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_8);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
set_Timer0 ( Timer0Value );
set_Timer1 ( Timer1Value );
while(1){;} // do nothing
} |
|
|
epideath
Joined: 07 Jun 2006 Posts: 47
|
|
Posted: Sat Jul 22, 2006 10:44 pm |
|
|
Looks like you got some numbers crossed
Timer0Value = 256 - (0.00033333/(8/(10000000/4)))
Timer1Value = 65536 - (0.000454545/(1/(10000000/4)))
should be
Timer0Value = 256 - (0.000454545/(8/(10000000/4)))
Timer1Value = 65536 - (0.00033333/(1/(10000000/4)))
Note the .0004545454 and the .000333333333
If you take the half scycle speed for 1100 hz so 2200. 1/2200 = .000454545
If you take the half scycle speed for 1500 hz so 3000. 1/3000 = .0003333333
So the numbers should be
Timer0Value = 141
Timer1Value = 64703L
Hope this helps |
|
|
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
Timer1 will control the 1100Hz |
Posted: Sun Jul 23, 2006 5:04 am |
|
|
epideath wrote: | Looks like you got some numbers crossed
Timer0Value = 256 - (0.00033333/(8/(10000000/4)))
Timer1Value = 65536 - (0.000454545/(1/(10000000/4)))
|
No it's correct as is.
In the end I want the ability to fine tune Timer1 which controls the 1100Hz.
Timer0 controls the 1500Hz which is not as critical.
I can allow it to be off frequency, but the 1100Hz needs to be as good as possible.
The final "tweaked" value for Timer1 turns out to be 64435
Thanks. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Sun Jul 23, 2006 10:09 am |
|
|
The problem is the interrupt latency. In your interrupt, you are setting timer1 to a value. When the interrupt occurs, timer1's value is 0. By the time the interrupt is processed, many instructions (and time) has gone by. By setting the timer with your "roll over value" you lose this time in your calculations and thus the difference in what you see. From the numbers, it appears to be off by about 35 instructions which is about how many instructions it takes to process the interrupts. A better approach would be to add the "roll over value" to the current timer1 value so that you don't lose its count. |
|
|
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
Not sure |
Posted: Mon Jul 24, 2006 3:39 pm |
|
|
Mark wrote: | A better approach would be to add the "roll over value" to the current timer1 value so that you don't lose its count. |
Thanks forthe reply.
Not sure I understand this!
Do you mean the Set_Timer1(value) is taking too long ?
How would adding a value be any quicker ?
Can you please show me in code how the ISR can be any simpler than it is now !
As it is the ISR (not accounting for CCS interrupt stuff) is about as tight as assembly could do it.
#int_TIMER1
TIMER1_isr()
{
.................... output_toggle(PIN_A2);
0047: MOVLW 04
0048: XORWF 05,F
.................... set_Timer1(Timer1Value); // reload 16 bit value
0049: MOVF 2F,W
004A: MOVWF 0F
004B: MOVF 2E,W
004C: MOVWF 0E
.................... } |
|
|
Guest
|
Re: Not sure |
Posted: Mon Jul 24, 2006 8:20 pm |
|
|
[quote="Hans Wedemeyer"] Mark wrote: | A better approach would be to add the "roll over value" to the current timer1 value so that you don't lose its count. |
Quote: | Thanks forthe reply.
Not sure I understand this!
Do you mean the Set_Timer1(value) is taking too long ?
How would adding a value be any quicker ? |
I think you should subtract from your original timer value the time spent in the interrupt routine + time in CCS interrupt stuff. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Mon Jul 24, 2006 8:33 pm |
|
|
By the time the interrupt is processed, timer1 already has a count. Run the code in the simulator and put a break point just before the set timer function. Take a look at the value of timer1. I think it will become clear then. It is not that the isr code is fat, it is the saving of all the registers that happens behind the scene. Since timer1 is not 0, when you set timer1 to a value, you lose the time hince the adjusted value. If you want to avoid this, you cannot lose this time so if you add the timer1 value to the computed value this will correct it. Actually, adding the value will produce a slightly larger isr routine but will keep in time better. |
|
|
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
Re: Not sure |
Posted: Mon Jul 24, 2006 8:44 pm |
|
|
[quote="Anonymous"] Hans Wedemeyer wrote: | Mark wrote: | A better approach would be to add the "roll over value" to the current timer1 value so that you don't lose its count. |
Quote: | Thanks forthe reply.
Not sure I understand this!
Do you mean the Set_Timer1(value) is taking too long ?
How would adding a value be any quicker ? |
I think you should subtract from your original timer value the time spent in the interrupt routine + time in CCS interrupt stuff. |
Well that's what I did when I adjusted the values. see earlier post.
Thanks. |
|
|
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
OK and here is another solution |
Posted: Mon Jul 24, 2006 8:56 pm |
|
|
Mark wrote: | By the time the interrupt is processed, timer1 already has a count. Run the code in the simulator and put a break point just before the set timer function. Take a look at the value of timer1. I think it will become clear then. It is not that the isr code is fat, it is the saving of all the registers that happens behind the scene. Since timer1 is not 0, when you set timer1 to a value, you lose the time hince the adjusted value. If you want to avoid this, you cannot lose this time so if you add the timer1 value to the computed value this will correct it. Actually, adding the value will produce a slightly larger isr routine but will keep in time better. |
Adding means I also have to determine the value to be added, and then add the value. That brings up other issues. There will be a further delay to take into account forthe code that adds the value ! This could get messy.
Although I wanted maximum resolution for adjusting Timer1 hence the div. 1 in Timer1 setup.
If I set Timer1 prescaler to Div 8 then Timer1 will increment 8 times slower then it does now, but the ISR will still be processed at full speed. That should bring the error down.
Another solution would be to do my own Interrupt handler and minimize the overhead, CCS interrupt handling is not as lean as perhaps it wcould be.
Final solution is to live with it and use a tweeked value to compensate for the error.
Thanks for the help. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Mon Jul 24, 2006 9:08 pm |
|
|
If you do it like
Code: |
#include <12F629>
#FUSES HS, NOMCLR, NOPROTECT, NOWDT, NOBROWNOUT, PUT
#use delay(clock=10000000)
#use fast_io(a)
int Timer0Value;
long Timer1Value;
long Timer1Reg;
#locate Timer1Reg = 0x0E
#int_TIMER0
TIMER0_isr()
{
output_toggle(PIN_A1);
set_Timer0 ( Timer0Value );
}
#int_TIMER1
TIMER1_isr()
{
output_toggle(PIN_A2);
Timer1Reg += Timer1Value;
}
void main()
{
Timer0Value = 152;
Timer1Value = 64400L;
set_tris_A(0b00101000);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_8);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
set_Timer0 ( Timer0Value );
set_Timer1 ( Timer1Value );
while(1){;} // do nothing
}
|
then it is just a bit more code
Code: | .................... output_toggle(PIN_A2);
0047: MOVLW 04
0048: XORWF 05,F
.................... Timer1Reg += Timer1Value;
0049: MOVF 2F,W
004A: ADDWF 0E,F
004B: BTFSC 03.0
004C: INCF 0F,F
004D: MOVF 30,W
004E: ADDWF 0F,F
.................... }
|
|
|
|
Guest
|
Re: Not sure |
Posted: Tue Jul 25, 2006 12:41 am |
|
|
Quote: | Well that's what I did when I adjusted the values. see earlier post.
Thanks. |
What about extra time when both interrupts happen at the same time, or one isr is running and the other is pending ?
I made a quick and dirty calculation and this might happen every fifteenth interrupt of the 1100Hz routine. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Jul 25, 2006 6:36 am |
|
|
Use the #priority to have the Timer1 int go first. This will take care of some of the cases. There will be other cases where you will be in the timer0 interrupt when the timer1 int fires. This will cause a slight error in the signal. In the way I suggested, the frequency will remain the same but the duty cycle will change slightly when the timer0 int fires just before the timer1 int does. To improve upon this even further, you can embed a goto at the end of the timer0 routine to jump back to the beginning of the interrupt flag test in the CCS interrupt handler. If timer1 int has occurred, then it will get executed. Note that the registers have already been saved so you save this amount of time. You could also write your own int handler or use the global handler and do your own bit testing. |
|
|
Hans Wedemeyer
Joined: 15 Sep 2003 Posts: 226
|
|
Posted: Tue Jul 25, 2006 4:15 pm |
|
|
Mark wrote: | If you do it like
#int_TIMER1
TIMER1_isr()
{
output_toggle(PIN_A2);
Timer1Reg += Timer1Value;
}
|
OK now I see it thanks{
I'll try it, and let you know.
Good point about the two timers interrupts happening at the same time, I'll take care of that.
hansw |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jul 25, 2006 4:22 pm |
|
|
Quote: | you can embed a goto at the end of the timer0 routine to jump back to the beginning of the interrupt flag test in the CCS interrupt handler. |
That seems very risky. Why not do a test of the interrupt flag for
Timer1 while inside the Timer0 isr ? Then if it's set, clear it, and
execute the Timer1 isr code at that time. (Put a copy of the Timer1
code inside the Timer0 isr). Do a similar thing for the other Timer isr.
This is much more clean method. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue Jul 25, 2006 9:17 pm |
|
|
PCM programmer wrote: | Quote: | you can embed a goto at the end of the timer0 routine to jump back to the beginning of the interrupt flag test in the CCS interrupt handler. |
That seems very risky. Why not do a test of the interrupt flag for
Timer1 while inside the Timer0 isr ? Then if it's set, clear it, and
execute the Timer1 isr code at that time. (Put a copy of the Timer1
code inside the Timer0 isr). Do a similar thing for the other Timer isr.
This is much more clean method. |
With all that trouble, he'd be better off just using the int_global and testing the flags. The best approach if his isr's are as simple as what is posted is to just write his own int handler. |
|
|
|