View previous topic :: View next topic |
Author |
Message |
SeeCwriter
Joined: 18 Nov 2013 Posts: 160
|
Interrupt Latency |
Posted: Fri Mar 06, 2015 5:34 pm |
|
|
I'm using a 18F8722 with v5.042 IDE.
I have the pic configured for PWM, half-bridge, at 100kHz, 50% duty cycle, using timer 2. Works great. I'm assuming this free runs without involving any interrupts or cpu interaction.
The pic is also trying to respond to external interrupt #2 every 10usec. The ISR executes in 3.3usec max. The first interrupt is responded to in 2.8usec. The second interrupt is responded to in 6.74usec. The third interrupt is responded to in 9.18usec. Add in the 3.3usec processing time, and the pic has just missed the next interrupt.
There are no other interrupts enabled. But timer0 is running providing ticks for the wizard generated scheduler.
According to the pic data sheet, the external interrupt latency is 3 or 4 instruction cycles. At 40MHz, that should be 400nsec.
What am I missing?
Code: |
#use delay(clock=40000000,crystal=10000000,restart_wdt)
void main()
setup_timer_2(T2_DIV_BY_1,99,1); // Used by CCP1 for PWM mode.
setup_ccp1(CCP_PWM|CCP_PWM_HALF_BRIDGE, 2);
set_pwm1_duty((int16)198); // 50% duty cycle.
ext_int_edge( 2, L_TO_H );
enable_interrupts(GLOBAL);
TICK_TYPE CurrentTick, PreviousTick;
CurrentTick = PreviousTick = get_ticks();
while(TRUE)
{
CurrentTick = get_ticks();
if ( GetTickDifference(CurrentTick, PreviousTick) >= oneSec )
{
PreviousTick = CurrentTick;
heartbeat();
GetData(); // Int2 is enabled in this function.
}
CheckStatus();
}
}
|
|
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9221 Location: Greensville,Ontario
|
|
Posted: Fri Mar 06, 2015 5:45 pm |
|
|
What you're missing is the time it takes for the PIC to execute the CCS created interrupt 'handler'. The time you're referring to is the PICs hardware responding to the interrupt. To that you have to add the time it takes for the PIC program to save some registers,decide which interrupt flag is set, maybe the 'priority' of the interrupts,clearing the flags, etc..
If you print out the listing of your program you'll see there's a LOT going on.
Now you can 'trim' the 'latency' time , if you create your own 'handler' and some guys do to get max performance BUT you better KNOW what you're doing.
'Old school' guys always write their own handlers as they KNOW the hardware and software inside out. With that knowledge you could cut the 'latency' time by a factor of 5 to 10 over the 'generic' one CCS supplies.
Just remember the CCS one does WORK.
Jay |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Mar 06, 2015 6:08 pm |
|
|
Quote: |
But timer0 is running providing ticks for the wizard generated scheduler. |
Post your #use timer() line. |
|
|
SeeCwriter
Joined: 18 Nov 2013 Posts: 160
|
|
Posted: Fri Mar 06, 2015 6:18 pm |
|
|
Code: |
#use TIMER(TIMER=0,TICK=204us,BITS=32,ISR)
|
That blows one of my assumptions. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19499
|
|
Posted: Sat Mar 07, 2015 1:34 am |
|
|
As a comment, if the interrupt is only providing 'ticks' for your internal handler, then don't use an interrupt at all. Just read one of the timers.....
Feed your external signal as the clock input to a 16bit timer, and you can read the value from it at any point, and it'll just merrily count the signal. |
|
|
SeeCwriter
Joined: 18 Nov 2013 Posts: 160
|
|
Posted: Sat Mar 07, 2015 12:17 pm |
|
|
I may have to scrap the whole ticks thing and do something much simpler.
But the 100kHz interrupt is a little more involved than counting pulses. The 100kHz clock is a free-running clock used to clock out data from an A/D converter. It's constantly running, but for 16-clocks the ADC is not enabled so the serial data into the PIC is high. For the next 16-clocks the ADC is enabled and clocks out 16-bits of data. And since the ADC is a 12-bit ADC, the upper 4-bits are always low when the ADC is enabled.
Every few seconds the PIC needs to sync up with the ADC by looking for 16-clocks (interrupts) with the data input high. When that condition is met, the next 16-clocks (interrupts) are considered valid data and are shifted in.
But since I'm not going to replace the CCS interrupt handling scheme with my own, I may have to re-write the program to not use interrupts and instead spin on the clock and data ports. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19499
|
|
Posted: Sat Mar 07, 2015 12:21 pm |
|
|
The alternative is to poll the interrupt.
This is much faster than using the interrupt hardware.
You test the interrupt flag, and when it goes true, do your task, and clear the flag. |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1634 Location: Perth, Australia
|
|
Posted: Sat Mar 07, 2015 9:59 pm |
|
|
SeeCwriter wrote: |
But since I'm not going to replace the CCS interrupt handling scheme with my own, I may have to re-write the program to not use interrupts and instead spin on the clock and data ports. |
It is easier that you think. The PIC18F supports (in hardware) high priority and standard priority interrupts. You can fool the CCS compiler to think you are only using standard priority interrupts. For your critical interrupt handler place it at the HP interrupt vector , code it in assembler using ASM statements within the standard C construct. Do not use any local variables. Add code to save registers on entry for only the registers your code will modify and restore these at the end prior to adding a return from interrupt at the end of this handler. Add instructions in the mainline to enable high priority interrupts.
This will give you a low latency high priority interrupt handler which will interrupt the low priority handler. The high priority handler will be both lower and consistent latency and still enable you to use the standard CCS interrupt handler for all other interrupt sources. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19499
|
|
Posted: Sun Mar 08, 2015 2:04 am |
|
|
You don't have to fool the compiler like this at all....
Just enable the priorities (#device HIGH_INTS=TRUE).
Declare all your normal interrupts.
Then declare just one interrupt as 'FAST', (not HIGH).
This creates a single high priority interrupt, using the RETFIE abilities (so automatically saving the minimum registers only). Write your interrupt code, and look in the assembler at what registers are used other than the basic W etc.. As Asmallri says, then just add the code to save and restore these.
The 'fast' interrupt is treated like it is a global interrupt handler for the high priority interrupts, so does half the work for you.
However caveats.
Remember that the INT interrupt will always be 'high' if priorities are enabled. Study the data sheet on this. |
|
|
SeeCwriter
Joined: 18 Nov 2013 Posts: 160
|
|
Posted: Mon Mar 09, 2015 11:12 am |
|
|
I tried changing the tick timer from interrupt driven to polled and polling doesn't seem to work. I never get inside the if statement.
Code: |
//#use TIMER(TIMER=0,TICK=204us,BITS=32,ISR)
#use TIMER(TIMER=0,TICK=204us,BITS=32,NOISR)
// Initiialize Tick values.
CurrentTick = PreviousTick1s = get_ticks();
while(TRUE)
{
CurrentTick = get_ticks();
if( GetTickDifference(CurrentTick, PreviousTick1s) >= oneSec )
{
PreviousTick1s = CurrentTick;
// do stuff
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19499
|
|
Posted: Mon Mar 09, 2015 1:17 pm |
|
|
Forget the CCS tick function.
Problem is you don't know how it really sets things up. It'll often use combinations that are less than optimal.
Just poll the _interrupt_.
If you don't enable the interrupt you were using, but setup the timer yourself, then it'll still trigger as before.
So test the interrupt flag (if (interrupt_active(THE_INT)), and when this goes true, just clear it, and do your job.
Obviously 'THE_INT' is whatever interrupt you were using. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
|