View previous topic :: View next topic |
Author |
Message |
TomC
Joined: 25 May 2004 Posts: 5 Location: NH
|
CCP Compare issue |
Posted: Tue May 25, 2004 11:51 am |
|
|
I am trying to use CCP2 to generate an interrupt every 125 ms (based on a 32KHz clock on Timer 1). To do this I incrementing the compare register with a fixed value (4016 -> 125 ms of 32 KHz pulses) every time the interrupt occurs.
This seems to work well MOST of the time, but every once in a while the interrupt is skipped. Then after 2.63 seconds it interrupts and everything is back to normal (until the error re-occurs). I assume what is happening is that int is delayed while timer1 goes around again and it catches the next time through. I am using the PIC18F8720 demo board and running a 32 KHz clock signal into timer1.
NOTE: When I moved this code to CPP5 (so that I could monitor the output compare pin toggling) the HW pin toggles but the software does not get called. I.E. the interrupt appears to be happening (since the HW toggle occurs) but the sw code to toggle the other pins and update the compare register does not occur. HELP!
Anyone have any ideas? I don't have anything else happening at this point. I have RS232 connected but not used, and no other interrupts that I am aware of.
#int_ccp2
void compare2_handler()
{
static unsigned int timer_count = 0;
static short toggle = 0;
TEST4(ON); //toggle LED to show int happens fast
if(toggle){
TEST1(ON); // togle an LED to show progress
toggle = false;
} else {
TEST1(OFF);
toggle = true;
}
CCP_2 += TIMER_TICKS_PER_INTERRUPT; //increment ccp2 register
TEST4(OFF);
}
void init_dstimer()
{
bit_clear(pir2, CCP2);
setup_ccp2(CCP_COMPARE_INT);
CCP_2 = TIMER_TICKS_PER_INTERRUPT + get_timer1();
enable_interrupts(INT_CCP2);
} |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue May 25, 2004 3:27 pm |
|
|
Your code looks OK, so I can't tell you for sure why you are sometimes missing an interrupt. One possible reason might be that the time spent in your interrupt routine is almost equal to a period time of your 32kHz clock.
As a rule of thumb: A standard interrupt handler has 40 instructions overhead on saving the context and the same number for restoring the context. Let's say your routine takes 20 instructions, then you have a total of 100 instructions per interrupt.
100 instructions * 32 kHz = 3,2M instructions
4 cycles/instruction makes a minimum clock speed of 12,8MHz
What clock frequency are you running at? |
|
|
TomC
Joined: 25 May 2004 Posts: 5 Location: NH
|
|
Posted: Wed May 26, 2004 6:11 am |
|
|
I am running at 20 MHz. So the interrupt routine takes a very minor fraction of the period. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed May 26, 2004 6:58 am |
|
|
Quote: | So the interrupt routine takes a very minor fraction of the period. |
Sorry, but I had to smile reading this. It is the same mistake I made a loooong time ago.
Running at 20Mhz you execute 5 million instructions/second.
32kHz period time leaves you 156 instructions / period.
As described above a short, single, not optimized, interrupt routine takes about 100 instructions.
I wouldn't call 64% a 'very minor fraction'. |
|
|
TomC
Joined: 25 May 2004 Posts: 5 Location: NH
|
|
Posted: Wed May 26, 2004 7:20 am |
|
|
Well after typing a lot of stuff carefully explaining to you that you don't know what you are talking about, it finally occured to me that I didn't know what you were talking about.
My interrupt code takes about 2 us to complete plus the overhead of the interrupt handler, which I am not sure of.
Can you explain why this matters? I don't see how it should matter what fraction of the 32 KHz clock I use up while servicing the interrupt. Once the interrupt is serviced the compare register now points to a match in the future and the int bit isn't cleared until then.
??? |
|
|
Ttelmah Guest
|
|
Posted: Wed May 26, 2004 10:44 am |
|
|
TomC wrote: | Well after typing a lot of stuff carefully explaining to you that you don't know what you are talking about, it finally occured to me that I didn't know what you were talking about.
My interrupt code takes about 2 us to complete plus the overhead of the interrupt handler, which I am not sure of.
Can you explain why this matters? I don't see how it should matter what fraction of the 32 KHz clock I use up while servicing the interrupt. Once the interrupt is serviced the compare register now points to a match in the future and the int bit isn't cleared until then.
??? |
It runs like this:
The processor executes a 'call' to the main interrupt handler.
This then saves all the main registers (typically about five scratch registers, the table pointers, the FSR registers etc. - think in terms of about 20 registers).
The interrupt flag registers are then checked to find which interrupt occured.
Once the interrupt is found, the code then checks if this interrupt is enabled.
If these are both true, the code then jumps to the handler routine. typically perhaps 35 instruction times to this point.
The routine then executes.
The routine then jumps back, and the handler now clears the interrupt flag, then restores all the registers, and returns.
The overhead in this regard, is _worse_ on the latter chips. On the 16 family, you are looking at perhaps a total of 30 to 50 instruction times depending on how many interrupts are enabled. On the 18 family, there are a lot more registers to save, and this rises to as much as 120 instruction times in total, in some cases....
If you set the hardware CCP, to reset the counter when it gets a match, you do not need to 'fiddle' with the count (you will get problems here, when the count advances between reading, and writing the registers). I suspect this is what is causing your glitch.
You can also speed the interrupt handling massively, if you make sure that the routine does not use a lot of the registers. In your case, none of the table pointers are used, so you can use a int_global with something like:
Code: |
#int_global
void inthdlr(void) {
INTFLAG=0
LED^=1;
#asm
RETFIE 1
#ENDASM
}
|
This simply does no test for what interrupt has occured, and sets/resets the bit 'LED' on alternate calls (you can modify to do what you want). This relies on not changing any registers, except the status register, BSR, and W, with all the latter being automatically restored using RETFIE 1. Even this takes nearly 2uSec. Obviously, INTFLAG, STATUS etc., have to be defined to suit the chip, and LED will need to be defined as well (something like #bit LED=PORTC.0 - read up on using the bit defintions).
Best Wishes |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed May 26, 2004 10:50 am |
|
|
Quote: | My interrupt code takes about 2 us to complete plus the overhead
of the interrupt handler, which I am not sure of. |
Here is an old post from Stefan Daystrom, in which he goes
through the code in the CCS handler and explains it line by line.
http://www.ccsinfo.com/wwwboard/messages/470.html |
|
|
TomC
Joined: 25 May 2004 Posts: 5 Location: NH
|
|
Posted: Wed May 26, 2004 11:12 am |
|
|
I understand what the compiler is doing when the interrupt occurs. What I don't understand is the relevance of the 32KHz period to what I am doing.
I am not resetting timer1 or adjusting it in any way. I am letting timer1 free run forever. What I am doing is adjusting the compare value of CCP5 to be ahead of timer1. So each time the interrupt occurs I move CCP5 ahead by 4016 (125 ms.) and clear the interrupt. Since CCP5 is static I shouldn't have any problems updating it while the timer is running (should I). Could it be that when the low value of CCP5 gets written that once in a while it matches before I can write the high value too?
By adding 4016 to the CCP5 register I am assuming that the rollover will occur naturally. I.E. that when CCP5 is close to 0xffff and I add a number to it, it will roll over to something less than 4016. This seems to work in that the int occurs correctly many many times and then fails asynchronously.
What I hope to do is have two or more CCP interrupts available to use in this manner (if I can figure out why it isn't working). That way I can have a 125 ms interrupt to keep track of system time variables and another CCP interrupt to use for another timing function that would give me 15 ms pulses. |
|
|
TomC
Joined: 25 May 2004 Posts: 5 Location: NH
|
|
Posted: Wed May 26, 2004 1:53 pm |
|
|
I think that I have found my own answer. I have the timer set up for external asynchronous input and the data sheet says that compare is not guaranteed with the asynchronous counter.
Doh. How many times did I read over that little tid bit of knowledge?
After changing to a synchronous clock it works seemlessly. Now to figure out how that fits in with my desire to use clock switching and run from the 32 KHz clock sometimes...
|
|
|
|