View previous topic :: View next topic |
Author |
Message |
Blob
Joined: 02 Jan 2006 Posts: 75 Location: Neeroeteren, Limburg, Belgium
|
Interrupt / seconds limit? on 18f242 |
Posted: Tue Apr 29, 2008 7:56 am |
|
|
Hello,
I am using a pic 18f242 and a 20MHz Xtal.
I have to measure the rotation speed of a wheel with 18 thooths on it.
Each tooth gives an interrupt.
This seems to go very well till 11600 RPM
example 10 000 rpm:
166.666 r/sec
18 pulses/rotation
3000 pulses/sec
1 pulse every 0.33 ms.
I wrote a finite state machine
so my interrupt code only does an increment of a variable position.
Code: |
position++;
if (position == 18)
position =0;
|
I'm sure there is a limit to the interrupts i can capture each second,
but how to calculate it, or where can i find it?
Thanks in advance![/code] |
|
|
Matro Guest
|
|
Posted: Tue Apr 29, 2008 8:02 am |
|
|
2 solutions :
- The limit comes from the hardware (for example your sensor is out of range or the signal is over and eventual filter bandwidth)
- The limit comes from your code, but here you need to look carefully at your code. The length of the ISR is something, but there is other limiting things. For instance some built-in functions that disable the interrupts.
But we can't give a value or a calculation method with the information that you gave.
Matro. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: Interrupt / seconds limit? on 18f242 |
Posted: Tue Apr 29, 2008 8:21 am |
|
|
For complete control over a time-critical interrupt, use #INT_GLOBAL and write you entire ISR using in-line assembly. That way you can count instruction cycles and know exactly how much time your interrupt code takes.
Robert Scott
Real-Time Specialties |
|
|
Blob
Joined: 02 Jan 2006 Posts: 75 Location: Neeroeteren, Limburg, Belgium
|
|
Posted: Tue Apr 29, 2008 8:47 am |
|
|
Hello, some more information,
the sensor is a magnetic pickup. giving a sinus wave.
this is digitalised with some diodes and a flipflop.
I measured this pulse with the scope, it stays ok also at high rpm.
at certain points in a rotation i have to perform some actions depending on the RPM.
there is one tooth different from the others. so i determine my 0 position there.
for example i have to flash a light at 25° at 5000 RPM
Code: |
#int_ext
void EXT_ISR()
{
if (start) //start cycle //determines where my 0 position
//this code is performed only on the first rotation
{
setup_timer_1(T1_DISABLED);
tNow = get_timer1();
set_timer1(0);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_2);
t6 = t5;
t5 = t4;
t4 = t3;
t3 = t2;
t2 = t1;
t1 = tNow;
if(t4 < t5-t5/5 && t4 < t6-t6/5 && t3 > t1-t1/5 && t3 > t2-t2/5)
{
pos = 2;
start = false;
setup_timer_1(T1_DISABLED);
set_timer1(0);
digi = false;
first = true;
}
}
else
{
pos++;
if(pos == 19) //reset position
pos = 1;
bl_interupt=true;
}
}
|
in my eeprom i store some values, on which rpm and what angle my light must flash
Code: |
void main(void)
{
// Set port directions, set PIC to safe analog mode and disable valve
set_tris_a(63);
set_tris_b(5);
set_tris_c(129);
output_low(Valve_A); // zet power jet af
output_low(Valve_B); // zet power jet af
output_low(DigitalOutput); // zet ignition op 0
disable_interrupts(int_EXT);
curve = 0;
step = 0;
t1 = 0;
t2 = 0;
t3 = 0;
t4 = 0;
t5 = 0;
t6 = 0;
digi = 0;
start = 1;
limit = 0;
pos = 0;
overflow = 0;
first = true;
setup_timer_1(T1_INTERNAL | T1_DIV_BY_2);
setup_timer_3(T3_INTERNAL | T1_DIV_BY_8);
ext_int_edge(L_TO_H);
enable_interrupts(global);
enable_interrupts(int_rda);
bl_interupt=false;
disable_interrupts(int_rda);
enable_interrupts(int_timer1);
enable_interrupts(int_timer3);
setup_timer_3(T3_DISABLED);
if(input(DigitalInput))
{
enable_interrupts(int_EXT);
while(true)
{
if(bl_interupt)
{
bl_interupt=false;
if (pos == 18) { if(!digi)
flash();
}
else if (pos == 15)
{
timed_flash();
}
}
}
}
} |
in the timed_flash function, i read the rpm, and calculate when the light should flash. i use 2 timers for this, one for determing the rpm and one that will activate the light flash.
Code: |
#separate
void timed_flash()
{
setup_timer_1(T1_DISABLED);
tNow = get_timer1();
set_timer1(0);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
tNow_copy = tNow;
tNow = tNow / 16;
adr = (int16)(46875 / tNow) - 11;
adr = (adr > 309 ? 0 : adr);
if ((adr >= start_digi || tNow_copy < 10000) && overflow == 0 && !first)
{
digi = true;
if ( tNow_copy > 2360 )
wait = read_program_eeprom(2*(start_tabel+adr));
else
wait = tNow_copy/128*end_angle;
wait = 65535 - wait;
ign = true;
setup_timer_3(T3_DISABLED);
set_timer3(wait);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
}
else
{
setup_timer_3(T3_DISABLED);
digi = false;
}
overflow = 0;
first = false;
}
|
In my isr, i only set my position in that way it is not depending on the timing of the other functions.
The light flashes ok till 11600 rpm.
The problem is not the light system, this works up to 25000 rpm in a one tooth application.
if i let the ligt flash at a fix degree, f.e. 25°
then it looks like the wheel is not turning, because the light will flash every rotation at the same point.
if rpm becomes higher then 11600 the wheel seems to turn very unstable (in the light, in reality it runs ok)
if i then drop the rpm, i becomes stable again, but at an other position...
Thanks in advance |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Apr 29, 2008 9:43 am |
|
|
Executing interrupts takes more time than most beginners are aware of. To check if you are here running into the processor's speed limit I did a simple calculation.
- On a PIC18 the interrupt handler has to store all registers and restore them on exit, this takes about 75 instruction cycles.
- Your interrupt routine is nice small, about 10 instruction cycles.
- Your processor runs at 20MHz
Max. number of interrupts per second: (20MHz / 4) / (75 +10) = 58,823 From this it seems very unlikely that the timer1 interrupt is limited by processor speed.
What is the interrupt for timer3 doing?
Maybe this is conflicting with the time measurement in the Timer1 isr? |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Tue Apr 29, 2008 2:01 pm |
|
|
ckielstra wrote: | ..On a PIC18 the interrupt handler has to store all registers and restore them on exit, this takes about 75 instruction cycles.
- Your interrupt routine is nice small, about 10 instruction cycles.
|
Do you mean his EXT_ISR()? I don't think so.:
Code: |
#int_ext
void EXT_ISR()
{
if (start) //start cycle //determines where my 0 position
//this code is performed only on the first rotation
{
setup_timer_1(T1_DISABLED);
tNow = get_timer1();
set_timer1(0);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_2);
t6 = t5;
t5 = t4;
t4 = t3;
t3 = t2;
t2 = t1;
t1 = tNow;
if(t4 < t5-t5/5 && t4 < t6-t6/5 && t3 > t1-t1/5 && t3 > t2-t2/5)
{
pos = 2;
start = false;
setup_timer_1(T1_DISABLED);
set_timer1(0);
digi = false;
first = true;
}
}
else
{
pos++;
if(pos == 19) //reset position
pos = 1;
bl_interupt=true;
}
}
|
I know the comment says that the big block is performed only on the first cycle, but if you look at what it does, it seems to rely on a FIFO of previous readings. So that block must be executed more than once. And if you count that block, then you are probably are more like 200 instruction times, not 10. Unless perhaps the first revolution takes place at reduced speed?
If that missing tooth code needs to execute faster, then I would rewrite it to avoid all those divisions. Or perhaps the entire missing tooth detection algorithm can be re-evaluated to come up with something simpler.
Robert Scott
Real-Time Specialties |
|
|
Blob
Joined: 02 Jan 2006 Posts: 75 Location: Neeroeteren, Limburg, Belgium
|
|
Posted: Wed Apr 30, 2008 1:27 am |
|
|
Hello,
the "start" cycle is indeed only executed at startup of the system.
This is in low rpm.
timer_3 interrupt is just for flashing the light:
Code: | #INT_TIMER3
void timer3_handler()
{
setup_timer_3(T3_DISABLED);
if (ign)
{
output_high(DigitalOutput);
ign = false;
set_timer3(65500);
setup_timer_3(T3_INTERNAL | T3_DIV_BY_8);
}
else
{
output_low(DigitalOutput);
}
}
|
nothing exotic here i think... |
|
|
SuperDave
Joined: 22 May 2008 Posts: 63 Location: Madison, TN
|
Interrupt / seconds limit? on 18f242 |
Posted: Thu May 22, 2008 8:35 am |
|
|
Assuming you have not yet jumped off a bridge here's an angle I haven't seen discussed.
From your code:
Quote: |
enable_interrupts(int_timer1);
enable_interrupts(int_timer3);
|
I suspect that the problem is that one interrupt is being processed when another occurs and is thus ignored. Two timing sensitive interrupts will automatically clash at some point (eg. the product of their times) especially since any interrupt other than int_global uses the default handler which as noted elsewhere has a minimum process time of about 15uS (75 instruction cycles).
I was trying to create a 13uS interrupt and a 100uS interrupt, boy was that nuts. Now I do a 13uS using Int_global (so I don't have the 15uS minimum) and that code does its thing then increments a counter which is interrogated by a loop. When the counter reaches 7 (91uS), I do my 100uS thing, fix it for the 9% error and subtract 7 from the counter just in case I got there a little late. Not perfect but no clash. |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Thu May 22, 2008 9:54 am |
|
|
Could you write a test version of the code that polls instead of using interrupts? With no interrupt overhead it would be faster and would help you understand the limits. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
Blob
Joined: 02 Jan 2006 Posts: 75 Location: Neeroeteren, Limburg, Belgium
|
|
Posted: Fri May 23, 2008 12:38 am |
|
|
Don't worry about the timer_1 and timer_3 collision.
timer_1 will be a slow (div_by_8) timer and is not used for interrupt, only for time measurement. in some cases it will have the chance to interrupt but those are fail safe:
timer_3 will be fast (div_by_2) and will generate an interrupt.
the timers start almost at the same time with their count
so interrupt 3 will always be way ahead interrupt 1
thanks anyway |
|
|
Blob
Joined: 02 Jan 2006 Posts: 75 Location: Neeroeteren, Limburg, Belgium
|
|
Posted: Fri May 23, 2008 1:37 am |
|
|
by the way, the problem is solved.
I noticed the problem did not occur when i made over time.
so when all machinery here was down.
I assume there was some interference via the electrical net here.
adding some more ground connections to all components of the application fixed the problem when the machinery was back online
best regards
blob |
|
|
|