Connect with us

16F628 Interrupt problem - help!

Discussion in 'Electronic Design' started by Mark Jones, Jan 8, 2005.

  1. Mark Jones

    Mark Jones Guest

    -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    Rob wrote:
    | I've got what I think is an interrupt problem with a simple optical
    encoder
    | / PIC 16F628 circuit.
    |
    | The PIC loops around and sends the encoder count values out when a
    counter
    | in the main loop expires. The encoder is read and the count updated
    when the
    | encoder input generates a falling edge interrupt on RBO. It all
    works fine,
    | however every now and again it will crash and cease sending out the
    count
    | data. This only seems to happen if the encoder is changing position.
    I think
    | the problem may be do do with my context saving.
    |
    | The encoder is a quadrature optical unit, which is powered up
    intermittently
    | at around 2kHz. Some glue logic grabs the status and presents it to
    the PIC.
    | A falling edge on RBO means a step has occurred, with the direction
    | indicated by the second input on RB3. All of this works fine.
    |
    |
    | Clock
    | |
    | Power switch
    | |
    | Encoder --> Glue Logic --> PIC - RB0 (Step) / RB3 (Dir)
    |
    |
    |
    | I've pasted the relevant code sections below - can anyone see where I am
    | going wrong?
    |
    | Many thanks.
    | rob
    |
    |
    ;##########################################################################
    | ORG 04H ;INTERRUPT VECTOR
    | ;SAVE CONTEXT BEFORE SERVICING INTERRUPT
    | MOVWF W_TEMP ;COPY W TO W_TEMP REGISTER
    | SWAPF STATUS,W ;COPY STATUS REG INTO W
    | CLRF STATUS ;BANK ZERO
    | MOVWF STATUS_TEMP ;SAVE STATUS IN STATUS_TEMP
    |
    | MOVF PCLATH,W ;COPY PCLATH INTO W
    | MOVWF PCLATH_TEMP ;SAVE PCLATH IN PCLATH_TEMP
    | CLRF PCLATH ;CLR PCLATH - GOTO PAGE 0, ONLY REQ IF USING PAGES 1,2
    AND
    | /OR 3
    | MOVF FSR,W ;COPY FSR TO W
    | MOVWF FSR_TEMP ;COPY FSR FROM W TO FSR_TEMP
    | ;CONTEXT SAVED
    |
    | ;IDENTIFY INTERRUPT SOURCE
    | ;BTFSC INTCON,INTF ;IS INT DUE TO A RB0 TRANSITION?
    | BCF INTCON,INTE ;DISABLE RBO INTERRUPT
    | CALL ISR_ENC ;YES - SO SERVICE ENCODER STEP INTERRUPT
    |
    | ;RE-ENABLE PORTB INTERRUPT AFTER SERVICING...........
    | BCF INTCON,INTF
    | BSF INTCON,INTE
    | ;NO MORE INTERRUPTS
    |
    | ISR_DONE ;RESTORE CONTEXT PREVIOUSLY SAVED
    | MOVF PCLATH_TEMP,W ;RESTORE PCLATH
    | MOVWF PCLATH
    | SWAPF STATUS_TEMP,W ;RESTORE STATUS
    | MOVWF STATUS ;RESTORES TO ORIGINAL BANK
    | SWAPF W_TEMP,F ;SWAP W_TEMP
    | SWAPF W_TEMP,W ;W_TEMP INTO W
    | MOVF FSR_TEMP,W ;RESTORE FSR
    | MOVWF FSR
    | RETFIE ;RETURN FROM INTERRUPT - ALSO RESETS GIE BIT CLEARED BY
    INTERRUPT
    |
    ;##########################################################################
    |
    | ;MAIN LOOP
    |
    |
    | CALL INIT_INTERRUPTS
    | HERE
    | INCFSZ TX_CNT1,1 ;loop until ctr overflows and then send out
    count data
    | GOTO HERE
    | INCFSZ TX_CNT2,1
    | GOTO HERE
    | CALL CNT2SER ;CONVERTS HEX COUNT TO ASCII & SENDS OUT SERIAL
    | GOTO HERE
    |
    |
    |
    ;##########################################################################
    | ISR_ENC ;SERVICE ENCODER STEP INTERRUPT - ENCODER CHECK ROUTINE
    |
    ;##########################################################################
    | BTFSS DIR ;IS DIR HIGH?
    | GOTO CCW ;DIR=0 SO STEP IS CCW
    | GOTO CW ;DIR=1 SO STEP IS CW
    |
    |
    | CW ;INCREMENT 24 BIT COUNTER
    | ;/TEST
    | LED2_PULSE ;PULSES LED2
    | LED1_ON
    | ;/END TEST
    | INCFSZ CTR0,1
    | RETURN
    | INCF CTR1,1
    | RETURN
    |
    | CCW ;DECREMENT 24 BIT COUNTER
    | ;/TEST
    | LED2_PULSE ;PULSES LED2
    | LED1_OFF
    | ;/END TEST
    |
    | DECF CTR0,1
    | INCFSZ CTR0,0 ;TEST FOR FF 'UNDERFLOW'
    | RETURN ;NO UNDERFLOW
    | DECF CTR1,1
    | RETURN ;NO UNDERFLOW
    |
    |
    ;##########################################################################
    | INIT_INTERRUPTS
    |
    ;##########################################################################
    | GOBANK1 ;CONFIG RB0 EDGE ETC
    | BCF OPTION_REG,INTEDG ;CLEARED FOR FALLING EDGE INTERRUPT
    | GOBANK0
    | BCF INTCON,INTF ;ENSURE EXT INT FLAG IS CLEAR
    | BSF INTCON,INTE ;ENABLE RB0 INT
    | CLRF PIR1 ;CLEAR INT FLAGS
    | BSF INTCON,GIE ;ENABLE GIE <7>,
    | RETURN
    |
    ;##########################################################################
    |
    |

    ~ Your LED2_PULSE and LED1_OFF, what do they do?

    ~ The optical encoder, is it measuring something fast? Precise? Or is
    it just being used as a knob? (100ms sample rate is much easier to
    deal with than 2kHz.)

    ~ The general rule of thumb regarding ISR's & PICs is A) 95% of the
    time they are not needed, B) they should be very short, and C) all
    port activity has to be double-buffered. (Think, what happens if an
    interrupt occurs while a port is being modified?)

    ~ That said, if the encoder is suppling a very fast interrupt, what
    happens if another interrupt occurs before the line
    ~ BCF INTCON,INTE ;DISABLE RBO INTERRUPT
    ~ ?

    - -- The answer is fractals. http://www.lilavois.com/nick/fractals/.
    Chaos theory. http://www.softology.com.au/voc.htm. Infinite
    complexity. Everything that we see around us is somehow related to an
    infnitely complex, unbalanced dynamic. But what could that be?
    ~ Mark Jones / Helios Studios
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.0 (MingW32)
    Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

    iD8DBQFB34RJUoq45ERxHvoRAvgKAJ42I9VHGwHJRixBgPiCbjE+B9TERwCfc2Wr
    fWQ28XAUMRvoPfzP8utT3qk=
    =YUFk
    -----END PGP SIGNATURE-----
     
  2. The biggest problem I see is that you clobber W when restoring the FSR
    value before returning from the interrupt. You absolutely have to
    save/restore context carefully or strange things happen. W must be the
    last thing restored.

    To save context, I use this:
    movwf W_TEMP ;Save everything
    swapf STATUS, W
    movwf STATUS_TEMP
    movfw FSR
    movwf FSR_TEMP
    movfw PCLATH
    movwf PCLATH_TEMP

    These variables must be located in ram that is mirrored accross the
    banks, this can be accomplished like this:

    ;
    ; ISR Register Save Areas
    ;
    cblock 0x70
    W_TEMP
    STATUS_TEMP
    FSR_TEMP
    PCLATH_TEMP
    endc

    Then, to restore context, use this code in exactly this order:
    ;------------------------------------------------------
    IntExit
    movfw PCLATH_TEMP
    movwf PCLATH
    movfw FSR_TEMP
    movwf FSR
    swapf STATUS_TEMP, W
    movwf STATUS
    swapf W_TEMP, F
    swapf W_TEMP, W
    retfie
     
  3. He was referring to keeping a "shadow" ram location that represents the
    value of an ouput port. You then set and reset individual bits within
    the shadow register and move the whole shadow register to the output
    port, instead of flipping bits directly on the output port. This avoids
    potential Read/Modify/Write issues having to do with the way the PIC
    ports are designed. These issues are generally associated with highly
    loaded output pins and/or fast clock speeds.

    When you flip an output bit on a PIC, the whole port is read, a single
    bit is modified, and the whole port is re-written. With heavily loaded
    pins, or in the case of not allowing enough settling time after changing
    a pins state, the pin state can be misread. It will consequently be
    re-written with the incorrect value resulting in the appearance of
    output pins mysteriously changing themselves when other pins on the same
    port are changed.
    Your interpretation is correct, Mark appears to be somewhat unfamiliar
    with PICs. In fact your clearing and setting of INTE in the ISR is not
    really necessary, since no other interrupts will be serviced until GIE
    is set again (by retfie).

    Be aware that the PIC's stack is quite small. Your ISR is using two of
    the eight locations available.
     
  4. nospam

    nospam Guest

    On restore, you restore STATUS selecting the pre-interrupt bank
    then restore W and FSR which you saved to bank zero. If you take an
    interrupt while other than bank 0 is selected the interrupt will corrupt W
    and FSR.
     
  5. CBarn24050

    CBarn24050 Guest

    errupts", " reenable interrupt after servicing", the counter direction routine.
    Your 24 counter appears to be 16 bits. Your problem is most likely to be in the
    convertion routines which you havent shown, I suspect that they are table read
    routines. These allways need some care.
     
  6. nospam

    nospam Guest

    My mistake, think W is OK, FSR is still a problem.
     
  7. Mark Jones

    Mark Jones Guest

    No, it's just that I avoid ISR's because of all the inherent trouble
    with them. It's been so long, I'd forgotten GIE was automatically
    cleared. It's just as easy most of the time to check for things in the
    main program loop anyways.

    Also check out JAL, it's a friendly pascal-like language for PICs
    (which will do assembler also if you like.)
    http://groups.yahoo.com/group/jallist/ - binary from
    sourceforge.net/projects/jal/

    Also sorry about the strange message formatting... was tinkering with
    PGP stuff.

    -- "The one truth of the universe is: infinite recursion. That is all."
     
  8. Rob

    Rob Guest

    I've got what I think is an interrupt problem with a simple optical encoder
    / PIC 16F628 circuit.

    The PIC loops around and sends the encoder count values out when a counter
    in the main loop expires. The encoder is read and the count updated when the
    encoder input generates a falling edge interrupt on RBO. It all works fine,
    however every now and again it will crash and cease sending out the count
    data. This only seems to happen if the encoder is changing position. I think
    the problem may be do do with my context saving.

    The encoder is a quadrature optical unit, which is powered up intermittently
    at around 2kHz. Some glue logic grabs the status and presents it to the PIC.
    A falling edge on RBO means a step has occurred, with the direction
    indicated by the second input on RB3. All of this works fine.


    Clock
    |
    Power switch
    |
    Encoder --> Glue Logic --> PIC - RB0 (Step) / RB3 (Dir)



    I've pasted the relevant code sections below - can anyone see where I am
    going wrong?

    Many thanks.
    rob

    ;##########################################################################
    ORG 04H ;INTERRUPT VECTOR
    ;SAVE CONTEXT BEFORE SERVICING INTERRUPT
    MOVWF W_TEMP ;COPY W TO W_TEMP REGISTER
    SWAPF STATUS,W ;COPY STATUS REG INTO W
    CLRF STATUS ;BANK ZERO
    MOVWF STATUS_TEMP ;SAVE STATUS IN STATUS_TEMP

    MOVF PCLATH,W ;COPY PCLATH INTO W
    MOVWF PCLATH_TEMP ;SAVE PCLATH IN PCLATH_TEMP
    CLRF PCLATH ;CLR PCLATH - GOTO PAGE 0, ONLY REQ IF USING PAGES 1,2 AND
    /OR 3
    MOVF FSR,W ;COPY FSR TO W
    MOVWF FSR_TEMP ;COPY FSR FROM W TO FSR_TEMP
    ;CONTEXT SAVED

    ;IDENTIFY INTERRUPT SOURCE
    ;BTFSC INTCON,INTF ;IS INT DUE TO A RB0 TRANSITION?
    BCF INTCON,INTE ;DISABLE RBO INTERRUPT
    CALL ISR_ENC ;YES - SO SERVICE ENCODER STEP INTERRUPT

    ;RE-ENABLE PORTB INTERRUPT AFTER SERVICING...........
    BCF INTCON,INTF
    BSF INTCON,INTE
    ;NO MORE INTERRUPTS

    ISR_DONE ;RESTORE CONTEXT PREVIOUSLY SAVED
    MOVF PCLATH_TEMP,W ;RESTORE PCLATH
    MOVWF PCLATH
    SWAPF STATUS_TEMP,W ;RESTORE STATUS
    MOVWF STATUS ;RESTORES TO ORIGINAL BANK
    SWAPF W_TEMP,F ;SWAP W_TEMP
    SWAPF W_TEMP,W ;W_TEMP INTO W
    MOVF FSR_TEMP,W ;RESTORE FSR
    MOVWF FSR
    RETFIE ;RETURN FROM INTERRUPT - ALSO RESETS GIE BIT CLEARED BY INTERRUPT
    ;##########################################################################

    ;MAIN LOOP


    CALL INIT_INTERRUPTS
    HERE
    INCFSZ TX_CNT1,1 ;loop until ctr overflows and then send out count data
    GOTO HERE
    INCFSZ TX_CNT2,1
    GOTO HERE
    CALL CNT2SER ;CONVERTS HEX COUNT TO ASCII & SENDS OUT SERIAL
    GOTO HERE


    ;##########################################################################
    ISR_ENC ;SERVICE ENCODER STEP INTERRUPT - ENCODER CHECK ROUTINE
    ;##########################################################################
    BTFSS DIR ;IS DIR HIGH?
    GOTO CCW ;DIR=0 SO STEP IS CCW
    GOTO CW ;DIR=1 SO STEP IS CW


    CW ;INCREMENT 24 BIT COUNTER
    ;/TEST
    LED2_PULSE ;PULSES LED2
    LED1_ON
    ;/END TEST
    INCFSZ CTR0,1
    RETURN
    INCF CTR1,1
    RETURN

    CCW ;DECREMENT 24 BIT COUNTER
    ;/TEST
    LED2_PULSE ;PULSES LED2
    LED1_OFF
    ;/END TEST

    DECF CTR0,1
    INCFSZ CTR0,0 ;TEST FOR FF 'UNDERFLOW'
    RETURN ;NO UNDERFLOW
    DECF CTR1,1
    RETURN ;NO UNDERFLOW

    ;##########################################################################
    INIT_INTERRUPTS
    ;##########################################################################
    GOBANK1 ;CONFIG RB0 EDGE ETC
    BCF OPTION_REG,INTEDG ;CLEARED FOR FALLING EDGE INTERRUPT
    GOBANK0
    BCF INTCON,INTF ;ENSURE EXT INT FLAG IS CLEAR
    BSF INTCON,INTE ;ENABLE RB0 INT
    CLRF PIR1 ;CLEAR INT FLAGS
    BSF INTCON,GIE ;ENABLE GIE <7>,
    RETURN
    ;##########################################################################
     
  9. What inherent trouble with PIC ISR's are you referring to, or are you
    talking about ISR's in general? I happen to love using interrupt
    handlers, they are quite beneficial to have IMO. ;-) I like to use
    them for serial i/o, button press sensing, or virtually anything that
    involves some kind of unpredictable input. They are also good for
    performing repetitive tasks on a precise interval (like switch
    debouncing, or even polling ;-) The whole key is in saving and
    restoring context properly for the main level and setting up the correct
    context for the ISR. Also, it's good to keep in mind when writing your
    code that an interrupt can (and eventually will ;-) occur between any
    two instructions of your program.

    You should try to stop avoiding them. Once you get the hang of it, you
    will begin to see more and more applications for them vs. polling at
    mainlevel. I tend to use allot of circular queues for my projects,
    usually stuffing data in them in an ISR and then pulling it out at
    liesure during main level processing.
    Polling is ok I suppose, unless you need absolute precision. And
    polling introduces it's own set of problems anyway, which is best all
    depends upon the specific application. Interrupts are also nice in that
    they let you seperate your program flow into asynchronous pieces (in a
    sorta threadish way). The 18F pics have two priority levels, letting
    you receive a high priority interrupt while a lower one is already
    running. :)))
    Thanks anyway, but I'm already aware of JAL. It's pretty neat, but it's
    just not my thing. I've used high level languages on micros before, but
    I tend to stick with assembler for my PIC projects. Although I am
    thinking about trying one of the free BASIC compilers to see how I like
    it. I will say though that the most trouble I've had out of using
    interrupts is when using them from a high level language. But even
    then, I only really needed to figure out the rules for the particular
    compiler and then things worked as expected.
     
  10. Rob

    Rob Guest

    Snipped original post content


    Thanks for the reply Mark.

    The LEDs are just being turned on/off for test purposes. The encoder runs
    very slowly - it is to be used to measure a slowly changing mechanical
    position, << 500 Hz encoder pulse rate (500 slot encoder wheel). The
    application is battery powered hence the need for the extra glue logic to
    power up and sample the encoder periodically.

    Re point C, can you please elaborate on "double buffering" port
    read/writes - I don't understand what you mean by this.


    I was of the understanding that as soon as an interrupt occurs the GIE bit
    was cleared - preventing futher interrupts being registered, so the problem
    of an interrupt occuring before the line "BCF INTCON,INTE" would not be an
    issue - am I incorrect on this?

    regards
    rob
     
  11. Rob

    Rob Guest

    snipped all.........

    Thanks for your advice Mark, Anthony & others.
    Anthony's context save/restore method seems to have fixed the problem. I
    added the changes and have beat up on it for a day without seeing any
    misbehavior.

    regards
    rob
     
Ask a Question
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Electronics Point Logo
Continue to site
Quote of the day

-