• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Getting out of an interrupt service Routine

Thread starter #1
Hello Forum members,

I am implementing a slow starter circuit for AC inductive loads. For this, I am using a Zero Crossing Detection scheme with a Pic 16f887. The zero crossing is detected as an external interrupt on RB0 and voltage across the load is reduced by firing a TRIAC just after the zero crossing. My problem is that I need to bypass the TRIAC after counting 100 zero crossings but it's not happening with my code. I need you to assist me in correcting the code

C++:
int cnt;   //  counter variable to store zero crossing count
  unsigned char FlagReg;
  sbit ZC at FlagReg.B0;


     void interrupt(){
     if (INTCON.INTF){          //INTF flag raised, so external interrupt occured
        ZC = 1;
        cnt++;
        INTCON.INTF = 0;
     }
     }

  void fire() {
              PORTD.B0 = 1; //Send a 1ms pulse
              delay_us(500);
              PORTD.B0 = 0;
              }

 void main() {
     TRISA = 0;
     PORTA = 0;
     PORTB = 0;
     TRISB = 0x01;              //RB0 input for interrupt
     PORTD = 0;
     TRISD = 0;                 //PORTD all output
     ANSEL = 0;                 // make ports digital (lower order)
     ANSELH = 0;                //  make ports digital (higher order)
     OPTION_REG.INTEDG = 0;      //interrupt on falling edge
     INTCON.INTF = 0;           //clear interrupt flag
     INTCON.INTE = 1;           //enable external interrupt
     INTCON.GIE = 1;            //enable global interrupt
     cnt =0;





            do {
            if (ZC){ //zero crossing occurred
            delay_ms(1);
            fire();
            ZC = 0;
            }
            } while(cnt<=100);
            PORTD = 0b00000010;
          }
 

Pommie

Well-Known Member
Most Helpful Member
#2
Something like,
Code:
    for(i=0;i<100;i++){
        while(!ZC);          //wait for interrupt
        delay_ms(1);         //short delay
        fire();              //turn on triac
        ZC=0;                //clear flag
    }
    while(1);                //wait forever
You could instead of using interrupts just use INTF as the flag.

Mike.
 
Last edited:
Thread starter #4
Something like,
Code:
    for(i=0;i<100;i++){
        while(!ZC);          //wait for interrupt
        delay_ms(1);         //short delay
        fire();              //turn on triac
        ZC=0;                //clear flag
    }
    while(1);                //wait forever
You could instead of using interrupts just use INTF as the flag.

Mike.
With the above code, does it mean I will still be using interrupts?

Regards
 
Thread starter #5
I did change my code to
C:
int cnt;   //  counter variable to store zero crossing count
  unsigned char FlagReg;
  sbit ZC at FlagReg.B0;


     void interrupt(){
     if (INTCON.INTF){          //INTF flag raised, so external interrupt occured
        ZC = 1;
        INTCON.INTF = 0;
     }
     }

  void fire() {
              PORTD.B0 = 1; //Send a 1ms pulse
              delay_us(500);
              PORTD.B0 = 0;
              }

 void main() {
     TRISA = 0;
     PORTA = 0;
     PORTB = 0;
     TRISB = 0x01;              //RB0 input for interrupt
     PORTD = 0;
     TRISD = 0;                 //PORTD all output
     ANSEL = 0;                 // make ports digital (lower order)
     ANSELH = 0;                //  make ports digital (higher order)
     OPTION_REG.INTEDG = 0;      //interrupt on falling edge
     INTCON.INTF = 0;           //clear interrupt flag
     INTCON.INTE = 1;           //enable external interrupt
     INTCON.GIE = 1;            //enable global interrupt
     cnt =0;





     for(cnt=0;cnt<100;cnt++){
        while(!ZC);          //wait for interrupt
        delay_ms(1);         //short delay
        fire();              //turn on triac
        ZC=0;                //clear flag
    }
    PORTD = 0b00000010;
    while(1);                //wait forever
          }
and when I power up the circuit which I am going to attach here, I am now getting 15V AC across the load
Schematic_Soft-Starter-Pic16F887.png Schematic_Soft-Starter-Pic16F887.png
 
#11
If this is a modern PIC16 you can't rely on the ESD diodes by pulling the pin below zero. This is due to the way pins are reassignable with analog functions. Instead use a PIC16 with a ZCD peripheral. A bonus is these pics come with a linked timer (used for frequency calculation etc) which will do the 100 count and fire an interrupt for you. Both these are more accurate timing wise than using the CPU.

Here is the new version of your chip.

https://www.microchip.com/wwwproducts/en/PIC16F18877

Edit: using opto, ok ignore the above.
 
#14
With the circuit as shown, you are only triggering on one polarity zero crossing rather than both, so only possibly switching the triac on alternate half cycles.

Can you use "interrupt on change" with the PIC you are using? I think that can be configured to interrupt on both low to high and high to low transitions.
(I only use the DSPIC series now, I've lost track of the 16 & 18 ones..)

I'd use one of the PIC pins to drive a LED and copy the state of the zero crossing detect input to that each time the loop sees "ZC" true.
It gives you a definite indication of things happening.


Other thoughts:
If the load is inductive, you _must_ have a snubber circuit across the triac. That's not only for suppression, but provides current to allow the triac to latch before the current though the actual load increases.
See page 8 of the MOC3020 datasheet.
https://www.mouser.com/ds/2/239/MOC302-1175440.pdf

If you are testing with a resistive load or lamp, that won't be a problem or affect anything at present.


Also, just use the normally open contact on the relay to directly bridge the triac - there is no need to break that connection and doing so means the relay contacts are switching under high inductive load, while just shorting a triac after the starter has ramped up to full will be a very "clean" operation with no contact burn.
 

Pommie

Well-Known Member
Most Helpful Member
#15
With the circuit as shown, you are only triggering on one polarity zero crossing rather than both, so only possibly switching the triac on alternate half cycles.
The OP appears to have a transformer followed by a bridge rectifier and is detecting falling edges. This will detect all zero crossings. However, as currently drawn the schematic lacks smoothing capacitors and when theses are added they will prevent the LED from ever switching off. A blocking diode or two bridges will fix this.

Mike.
 
Thread starter #16
Also, just use the normally open contact on the relay to directly bridge the triac - there is no need to break that connection and doing so means the relay contacts are switching under high inductive load, while just shorting a triac after the starter has ramped up to full will be a very "clean" operation with no contact burn.
Great advice, hadn't thought of that
 
Thread starter #17
The OP appears to have a transformer followed by a bridge rectifier and is detecting falling edges. This will detect all zero crossings. However, as currently drawn the schematic lacks smoothing capacitors and when these are added they will prevent the LED from ever switching off. A blocking diode or two bridges will fix this.
The circuit may be misleading a bit but for powering the circuit I have a separate power supply. I will have to upload the corrected version of the circuit.

Thank you
 
Thread starter #18
The OP appears to have a transformer followed by a bridge rectifier and is detecting falling edges. This will detect all zero crossings. However, as currently drawn the schematic lacks smoothing capacitors and when these are added they will prevent the LED from ever switching off. A blocking diode or two bridges will fix this.

Mike.
Afterthought:

I will add another bridge for the power supply, I think it's smarter
 
#19
transformer followed by a bridge rectifier
Yes, I did see that when I first looked at the schematic - then got distracted on other things; just ignore me on that point..:facepalm:


To use a single bridge with a zero crossing detect circuit, you can use a single diode in series with the positive DC output.

That goes to the PSU smoothing cap and the detector runs directly from the bridge output, with a load resistor to ensure the voltage does drop off rapidly.

Or, for high precision and symmetry, I've often used two optos (or rather one dual opto) with the LEDs in parallel, opposite polarity & a single limiting resistor, running from the AC side of the bridge & feeding two PIC inputs.
(Though I don't using the inputs directly for output timing, I run a software & timer based phase lock loop for synchronising, to avoid problems with power system noise & false triggering in industrial environments. Nothing is enabled until that's been in lock for eg. ten cycles).
 
#20
Some PIC assembly fragments of code to do PLL based mains sync and 255-level output phase control.
I wrote this for a 16C73 about 20 years ago.. Most later stuff is in C rather than assembly.
[Using a 20MHz clock; timing based on that].

All main product and customer-specific code removed..



Put the control value in "outphase".

You need a few other memory locations like "clockref", "phase", "mainsref", "stat_temp" etc.

The timing reference input looks to have been a direct pickup from AC via a resistor to a port A pin (hz).
It uses each transition either way and compares against the last input state at each interrupt.
You need to change the mains00 / main10 parts if you use a different type of sync input, eg. trigger on the negative [high to low] transition only and bypass the routine [exitint] (but still set the image flag high) on low to high, with the full-wave opto setup.

mainsref,7 is the bit that the normal program loop monitors to count cycles & do low speed timing, read it and reset when seen in that.
(Also used by the PLL phase comparison, so be careful if you change that).


PORTB,s_oe is the triac trigger ouput.
PORTB,mref is a spare pin to monitor the program zero detection.

[Edit - typos & clarification].

Some initialisation stuff..

Code:
;
; PAGE 0 Register setup ***
;
                bcf     STATUS,RP0      ; Select page 0
                movlw   000             ; Set port B to
                movwf   PORTB           ; $00 = all o/p off.
                movlw   D'001'          ; Init. timer
                movwf   TMR0            ; to avoid w/d
;
; Clear workspace
;
                movlw   021             ; Clear RAM
                movwf   FSR             ; Set indirect ptr
                movlw   05E             ; Byte Count
                movwf   iomode          ; Save Count
;
begin2          clrf    INDF            ; Clear ram via indirect
                incf    FSR,f           ; next addr
                decfsz  iomode,f        ; Loop till done
                goto    begin2          ;
                clrf    iomode            ;
;
; All ram clear.
;
; Set o/p on phase
; (Higher value = shorter firing pulse)
;
                movlw    d'160'            ; Triac firing pulses turned off
                movwf    outphase        ; at ??/256 from end of each half cycle.
;
; Set master Timer value
;
                movlw    D'050'            ; 256 - 50 = 200 Cycles
                movwf    clockref        ;
                movwf    TMR0            ; Reset Timer
                bcf        INTCON,2        ; Reset int flag.
;
; Enable Interrupts.
;
                bsf     INTCON,T0IE     ; Enable timer int
                bsf     INTCON,GIE      ; & global int.

The interrupt routine that does all the work:

Code:
;
;
; Interrupt driven routines
;
; Executed at interval of (200nS * 200) = approx. 40uS
;
intrpt          movwf   w_temp          ; Save W
                swapf   STATUS,w        ; Save context
                bcf     STATUS,5        ; Bank 0
                movwf   stat_temp       ;
                movf    PCLATH,w        ; Get latch
                movwf   pcl_temp        ; save it
;
;Timer counts UP, Int at rollover 255->0
;
                movf    clockref,w      ; Get timer value
                movwf   TMR0            ; reload timer
                bcf     INTCON,2        ; reset ctr int flg
;
; Shift Phase counter
;
                decf    phase,f            ; Next step
;
; Do Phase Control outputs
;
outangle        movf    phase,w            ; Get Phase
                subwf    outphase,w        ; Compare to o/p value
                btfsc    STATUS,C        ; Negative result?
                goto    out_off            ; No, turn OFF
out_on            bcf        PORTB,s_oe        ; Yes, turn ON
                goto    endphase        ;
out_off            bsf        PORTB,s_oe        ; Set outputs OFF
;
; Do mains Zero-Crossing detection        ;
;
;endphase        movf    phase,w            ; Look at phase
;                subwf    D'64'            ; 3/4 way through?
;                btfsc    STATUS,Z        ;
;                bsf        mainsref,7        ; Show zero crossing occurred for async. program.
;
endphase        btfsc    mainsref,0        ; Look at present value
                goto    mains10            ;
;
; Last was 0, has it changed?
;
mains00            btfss    PORTA,hz        ; Still 0?
                goto    exitint            ; Yes.
                bsf        mainsref,0        ; Update flag,
;                bsf        PORTB,mref        ;
                goto    mainsx            ;
;
; Last was 1, has it changed?
;
mains10            btfsc    PORTA,hz        ; Still 1?
                goto    exitint            ; yes.
                bcf        mainsref,0        ;
;                bcf        PORTB,mref        ;
;
; Zero crossing occurred.
;
; Update phase values from output values.
;
; Look at Phase counter (counts down from 255 - 0).
; Adjust master clock to get as near 255 counts as possible per sequence.
;
;
; Do clock timing.
;
mainsx            movf    phase,w            ; Look at count
                btfsc    STATUS,Z        ; Is it Zero?
                goto    mainsnul        ; Yes, no change.
                btfsc    phase,7            ; Look at phase counter
                goto    mainsdec        ;
;
; Phase has not overflowed, shorten timer interval.
;
mainsinc        incf    clockref,f        ; Clock will run faster.
                goto    mainsnul        ;
;
; Phase has overflowed, lengthen timer interval.
;
mainsdec        decf    clockref,f        ; Clock will run slower.
;
mainsnul        movlw    0FE                ; Start just below 255, so 100% on possible.
                movwf    phase            ; Restart phase counter
                bcf        clockref,7        ; Ensure clock can't get too fast...
;
                bsf        mainsref,7        ; Show zero crossing occurred for async. program.
;
;
; Restore context & return
;
exitint         movf    pcl_temp,w      ; Restore saved regs.
                movwf   PCLATH          ;
                swapf   stat_temp,w        ;
                movwf   STATUS             ;
                swapf   w_temp,f        ;
                swapf   w_temp,w        ;
                retfie                  ; Back to prog.
;
; End of base program.
;
 
Last edited:

Latest threads

EE World Online Articles

Loading

 
Top