Interrupts latency

Status
Not open for further replies.

atferrari

Well-Known Member
Most Helpful Member
Micro 18F452

I have implemented a clock, with the module CCP1 (compare mode) against TMR1 (counter mode).

To get a fixed duty cycle in RC2 pin, upon matching TMR1, the ISR loads alternatively two values in the CCPR1H:L registers.

Above ISR is high priority. Another one, running in low priority have no problems to coexist.

Regarding the latency, in the manual, I read:

" For external interrupt events, such as the INT pins or the PORTB input change interrupt, the interrupt latency will be three to four instruction cycles."

"The exact latency is the same for one or two-cycle instructions."

Having run all this through MPSIM I found a variation in the time elapsed between successives entries to the ISR, with the adequate allowance for the actual value loaded in the CCPR1 registers which seems to be of 1 or 2 Tcy.

My questions:

a) The second paragraph, is it actualy including the rest of the interrupts as well? Found it quite unclear.

b) Is it any way to compensate the differences in time elapsed between succesive entries to the ISR?

Help appreciated.
 
Use the special event trigger on CCP1 and latency no longer matters. The entry may be a couple of cycles late but as you don't have to touch timer1 then it doesn't matter.

Mike
 
I do have to touch TMR1

Hola Pommie,

I failed in telling that, every time one of the two values is loaded to the CCPR1 registers, TMR1 is to be started (from zero) again. All inside the ISR.
 
There is no need to zero timer1. The special event trigger zeroes timer1 when it matches ccpr1 and generates an interupt. Simply change the value in ccpr1 in your isr and leave timer1 alone.

Mike.
 
But, if I recall properly I discarded that option because there is not automatic toggling of the RC2 pin which materializes the clock I need. Am I right?

In one hour, at the office I will check the manual.
 
You simply toggle it manually and write to ccp1.

You have to either write timer 1 or toggle RC3. Writing timer1 causes timing errors.

Mike.
 
But then my clock (in RC2) would be subject to latency again. And that's what I want to escape from.

Isn't it?
 
Because it affects the precision of the generated clock through pin RC2 (as it is currently implemented).

If I could make it totally independent clock would be related only to the micro's oscillator stability only.
 
Sorry, I still don't see the problem?, what are you trying to do? - and what do you mean by precision in this case?.
 
Just use the special event trigger. Your interrupt will vary by 1 or 2 cycles but your clock will keep perfect time.

Mike.
 
Sorry Pommie, but how could it be perfect as you say, if the toggling is now inside the ISR which is subject to that difference?

I started to consider if there is a way to overcome those extra Tcy?

Hope you understand my point.

Nigel,

While I could not say ppm, I would like to have it free of latency to depend only of the quality of the micro's clock.

I need it for a signal generator intended for testing something else.
 
I assumed you meant a clock as in a timepiece. If you want to generate a rock steady signal then something like this in your ISR should fix it,
Code:
	movfw	TMR1L
	andlw	3
	addwf	PCL,F
	goto	Was0
	goto	Was1
	goto	Was2
	goto	Was3
	
Was0	nop
Was1	nop
Was2	nop
Was3	nop
; now toggle the port bit

You may have to swap the various routines around. Probably best run it through the simulator.

Mike.
 
I would suggest using CCP module "compare" mode which can toggle the CCPx pin with zero jitter. I will leave it for you to research the CCP module "compare" function, timing, etc., and provide just a simple C18 example (below).

Code:
/*****************************************************************
 *  K8LH CCP "compare" HiRez Servo Algorithm Interrupt Driver     *
 *****************************************************************/
#pragma interrupt isr_hi

void isr_hi ()
{ if (PIR1bits.CCP1IF == 1)             // if CCP1 interrupt
  { if (CCP1CONbits.CCP1M0 == 0)        // if CCP1 is 'hi'
    { CCPR1 += Pulse;                   // setup Pulse "on time"
      CCP1CONbits.CCP1M0 = 1;           // go 'lo' next interrupt
    }
    else                                // if CCP1 is 'lo'
    { CCPR1 += (20000-Pulse);           // setup Pulse "off time"
      CCP1CONbits.CCP1M0 = 0;           // go 'hi' next interrupt
    }
    PIR1bits.CCP1IF = 0;                // clr CCP1 interrupt flag
  }
}
 
Gracias Mike, but I am not C conversant at all. I find it quite hard to follow.Could you just tell in plain English?

From yesterday I started to consider something like the solution Pommie has shown. I have to run through it to be sure I know how it works.

I used something similar in a codethat use no interrupts and had to maintain all loops with the same length. Everytime, PC landed somewhere in the middle of a string of NOPs.

I will revert with the outcome.

Oh, you are helpful guys. Really you are!
 
Please study the effect of changing the CCP1M0 bit in CCP1CON. This particular example is generating a Servo ouput with a 20.0-msec period so it's probably a little more complex than what you require but I hope it gives you a good starting point. You really don't want to zero out Timer 1 each interrupt but update the CCPR1 "compare" registers instead.

Regards, Mike

Code:
;******************************************************************
;
; static unsigned int Pulse = 1500;       // range 600..2400 (usecs)
;
; void isr_hi ()
; { if (PIR1bits.CCP1IF == 1)             // if CCP1 interrupt
;   { if (CCP1CONbits.CCP1M0 == 0)        // CCP1 is 'hi' so
;     { CCPR1 += Pulse;                   // setup Pulse "on time"
;       CCP1CONbits.CCP1M0 = 1;           // make pin 'lo' on next
;     }                                   // compare match interrupt
;     else                                // CCP1 is 'lo' so
;     { CCPR1 += (20000-Pulse);           // setup Pulse "off time"
;       CCP1CONbits.CCP1M0 = 0;           // make pin 'hi' on next
;     }                                   // compare match interrupt
;     PIR1bits.CCP1IF = 0;                // clear CCP1 int flag
;   }
; }
;
ISR
        movwf   W_ISR           ; save W-reg                      |B?
        swapf   STATUS,W        ; doesn't change STATUS bits      |B?
        movwf   S_ISR           ; save STATUS reg                 |B?
        clrf    STATUS          ; force bank 0                    |B0
        movf    FSR,W           ;                                 |B0
        movwf   F_ISR           ; save FSR                        |B0
;
        btfss   PIR1,CCP1IF     ; CCP module interrupt?           |B0
        goto    ISR_Xit         ; no, branch, else                |B0
;
        btfsc   CCP1CON,CCP1M0  ; is Pulse hi or lo?              |B0
        goto    Pulse_Lo        ; lo, branch, else                |B0
;
;  pulse is hi so setup CCP1 to go lo on Pulse "on-time" match
;
Pulse_Hi
        movf    PulseLo,W       ;                                 |B0
        addwf   CCPR1L,f        ; CCPR1L += (Pulse)%256           |B0
        movf    PulseHi,W       ;                                 |B0
        skpnc                   ;                                 |B0
        incf    PulseHi,W       ;                                 |B0
        addwf   CCPR1H,f        ; CCPR1H += (Pulse)/256           |B0
        bsf     CCP1CON,CCP1M0  ; setup CCP1 to go lo next match  |B0
        goto    ISR_Xit         ;                                 |B0
;
;  pulse is lo so setup CCP1 to go hi on pulse "off-time" match
;
Pulse_Lo
        movf    PulseLo,W       ;                                 |B0
        sublw   low  d'20000'   ;                                 |B0
        skpc                    ; borrow? no, skip, else          |B0
        decf    CCPR1H,f        ;                                 |B0
        addwf   CCPR1L,f        ; CCPR1L += (20000-Pulse)%256     |B0
        skpnc                   ;                                 |B0
        incf    CCPR1H,f        ;                                 |B0
        movf    PulseHi,W       ;                                 |B0
        sublw   high d'20000'   ;                                 |B0
        addwf   CCPR1H,f        ; CCPR1H += (20000-Pulse)/256     |B0
        bcf     CCP1CON,CCP1M0  ; setup CCP1 to go hi next match  |B0
;
;  restore main program context
;
ISR_Xit
        bcf     PIR1,CCP1IF     ; clear CCP1 interrupt flag       |B0
        movf    F_ISR,W         ;                                 |B0
        movwf   FSR             ; restore FSR                     |B0
        swapf   S_ISR,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   W_ISR,f         ; don't screw up STATUS           |B?
        swapf   W_ISR,W         ; restore W-reg                   |B?
        retfie                  ; return from interrupt           |B?
 
Last edited:
Mike,

I like your method of adding the value to the compare register and therefore not needing to reset timer1. I remember this from a while back and think I mentioned that you could get problems with the order you do things. If PulseLo is a smallish value you could get a match in the 4 instructions that follow the write to CCPR1L. If you move the change to CCP1M0 to after the change to CCPR1 then it won't matter. This would also apply to the C version.

Mike.
 
Mike,

I remember that discussion and I changed the C18 program as you can see in my first post but it seems I never changed this particular ASM file. Argh! Sorry!

So, I updated the old ASM file and the previous ASM post with the updated listing.

Regards, Mike
 
Last edited:
I was away of the bench for two days. While there, one question came to my mind: is latency, for internal interrupts, a fixed one? The manual says nothing about. If so, how many cycles is its value?

Or can it vary?

Is the latency measured, (always for internal interrupts) in "instruction cycles" no in "clock cycles", isn't it?

If it is fix, my concern makes no sense. If it varies along the running of the code, then I am lost!

More in need of help than before.

Pommie?
 
As I understand it, the latency for an internal interrupt (not pin change) is fixed at 2 cycles but then has an additional cycle if the instruction it is executing is a 2 cycle instruction such as goto, call btf etc.

The other cause of latency is if the previous ISR has not finished when the next interrupt comes along. Not much you can do about that except shorten your ISR or get a faster chip.

You should try to understand Mike's code above. It does exactly what you need. If you're having trouble understanding what it is doing then I can explain it.

Mike.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…