Continue to Site

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.

  • 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.

Interrupt On Change 'problem'

Status
Not open for further replies.

Nigel Goodwin

Super Moderator
Most Helpful Member
I've been struggling with this for a while now, trying to use the PIC I/O pins set to schmitt for hardware debouncing - my original version uses an external schmitt trigger 40106 with the usual capacitor and two resistors on each of the required inputs, with the 40106 feeding the PIC inputs, and using IOC positive edge to generate an interrupt when the buttons (or input signal) are pressed. This works fine, and gives no problems.

But recent PIC's have schmitt inputs (selectable via a register), so I thought dump the 40106, use a newer PIC, and save an IC - I've been trying both the 16F18857 and 18F27K40. However, both chips give the same problem, intermittent multiple presses, which I presumed was down to switch bounce. Obviously without the inverting action of the 40106 I'm now using IOC negative edge, and so trying to find out why I added an extra line of code to toggle an I/O pin in the ISR, so I could try and spot the bouncing on the scope. I'd done this originally when I first started playing with hardware debouncing, and it allowed me to get nice 'clean' shots of the bounce.

However, there was no sign of contact bounce, but while I was repeatedly trying it (with the scope set to single sweep) I noticed a double count on the project display - on checking the scope I noticed the ISR had been triggered twice, once on the falling edge (as expected) but also on the rising edge (which it wasn't set to do).

Here's the scope display, the yellow display is the switch input pin, there's a 1uF to ground, and it's fed from two 22k in series, the blue trace is the output from the ISR toggle. Notice the nice schmitt trigger effect, with a suitable amount of hysteresis between the switching points. I also tried switching the inputs back to TTL, where the hysteresis all but disappears, so it's definately changing the input type as required.

NewFile1.png


So has anyone got any idea why the ISR should occasionally trigger on the rising edge?, when it's not set to do so - it probably only happens 10% of the time or so. There's three different inputs used, and it's the same on all of them.

For a 'work around' I've taken to checking if the I/O pin is LOW in the ISR, and exiting if it's not - but the older version with the 40106 works perfectly, using a 16F1847

Here's the code from the ISR for one of the buttons, it simply selects between two different modes using the Mode variable.

C:
if (IOCBF4)                        // Mode button was just pressed
    {
        IOCBF4 = 0;                 // must clear the flag in software
        TestPulse = ~TestPulse;     // toggle test I/O pin
        if (ModeKey == 0)           // check button still pressed
        {
            mode++;                        // toggle through modes (0-2)
            if (mode==2)
            {
                mode=0;
            }
            modechange = 1;                // mode changed
            display = 1;                // update display   
        }
    }
 
I had this same argument with the Int pin.. I was getting false triggering on a pic16f1825.. But a pullup resistor seemed to fix it... Sometimes I need to make sure the pins either side of the offender need stabilizing.. I think they have an input issue looming..
 
Just taking a stab, but does this issue raise any flags for you?

https://ww1.microchip.com/downloads/en/DeviceDoc/39564c.pdf Page 75...
"Note: A mismatch condition will continue to set this bit. Reading PORTB will end the mismatch condition and allow the bit to be cleared"

but

 
I'm not sure, it's all a bit confusing - but I'll study it further :D

I have not tested this 'bug' out at all. I do have a curiosity 16f18875 board and I should probably put it on the todo list.

That being said, if I understand what is stated in the cited thread, it is not that you were getting spurious interrupts...

So has anyone got any idea why the ISR should occasionally trigger on the rising edge?, when it's not set to do so - it probably only happens 10% of the time or so. There's three different inputs used, and it's the same on all of them.

but rather, that the flag was not staying cleared...sometimes. If the explanation is correct(and I am understanding), then replacing your isr with this:

C:
if (IOCBF4)  // Mode button was just pressed
{
  dummy = PORTB;
  IOCBF4 = 0;   // must clear the flag in software
  dummy = PORTB;
  TestPulse = ~TestPulse;   // toggle test I/O pin
  mode++;   // toggle through modes (0-2)
  if (mode == 2)
  {
    mode = 0;
  }
  modechange = 1;  // mode changed
  display = 1; // update display
}

(should fix the proble). I would be interested to hear whether that works or however you resolve it beyond your current work-around.
 
Last edited:
I have not tested this 'bug' out at all. I do have a curiosity 16f18875 board and I should probably put it on the todo list.

That being said, if I understand what is stated in the cited thread, it is not that you were getting spurious interrupts...

but rather, that the flag was not staying cleared...sometimes. If the explanation is correct(and I am understanding), then replacing your isr with this:

I would be interested to hear whether that works or however you resolve it beyond your current work-around.

I'll give it a try tomorrow (the code is at work) - however, I presume I'm getting spurious interrupts, as they seem to be pretty well exactly where the positive going edge should be, which isn't set to generate an interrupt.
 
I have not tested this 'bug' out at all. I do have a curiosity 16f18875 board and I should probably put it on the todo list.

That being said, if I understand what is stated in the cited thread, it is not that you were getting spurious interrupts...



but rather, that the flag was not staying cleared...sometimes. If the explanation is correct(and I am understanding), then replacing your isr with this:

C:
if (IOCBF4)  // Mode button was just pressed
{
  dummy = PORTB;
  IOCBF4 = 0;   // must clear the flag in software
  dummy = PORTB;
  TestPulse = ~TestPulse;   // toggle test I/O pin
  mode++;   // toggle through modes (0-2)
  if (mode == 2)
  {
    mode = 0;
  }
  modechange = 1;  // mode changed
  display = 1; // update display
}

(should fix the proble). I would be interested to hear whether that works or however you resolve it beyond your current work-around.

OK, i gave it a try, it doesn't help at all :grumpy:
 
OK, a little bit of 'slightly' related information.

Another reason for moving to the 16F18857 (besides it saving an extra chip) is for the SMT (Signal Measurement Timer) hardware - as it has 24 bit range. Currently I'm using TMR1 (16 bit) and incrementing an extra variable every time it rolls over (it's all a bit 'messy') - essentially I'm measuring the width of pulses, which can vary over quite a wide range. Also, if there are no pulses for a certain time, then it decides all input has ceased and resets the reading to zero.

So the SMT looks ideal, clock it with 1uS pulses from the clock oscillator divided by four, and we get a nice range from 1uS to 16+ seconds, with 1us resolution - so I've been playing with it a bit.

Initially I was using it in Window Measure mode (measuring from falling edge to next falling edge), and feeding it roughly 1Hz it mostly read about 1000000 (as expected), but dropped down to 500000 or so periodically, presumably the same issue is affecting the SMT input?, and my previous 'work rounds' don't seem to help.

Anyway, next I tried High and Low Measure mode (independent values for mark and space) - and that seems to work fine, it displays 500000 or so for each of the high and low pulses, and altering the mark/space ratio makes the two values vary accordingly. Obviously I can simply add the two values together to get the total if I need the time for a full cycle.

Once I'd got that working I added another SMT interrupt, this one for when it rolls over, in order to zero the reading as before - this is also easy, as you can set the value it rolls over at to any 24 bit value - I set it to 15000000, so if there's no pulses for 15 seconds, it resets the reading.

So far it's looking pretty good, and the SMT loks a useful (if complicated) device.
 
Glad you tested it out even if it didn't make a difference. I suppose, the resulting C code could have used a MOVFF instruction , but no matter at this point. That link that I originally cited was from 2007. I wonder if it has changed. I see nothing like that in the errata for the 18857 https://ww1.microchip.com/downloads/en/DeviceDoc/PIC16(L)F18857-18877-Errata-DS80000702E.pdf I'd still like to know what is happening (or not happening). This goes on the very long list.

I have never used the SMT, but as a result of this thread I was reading a little bit about it https://ww1.microchip.com/downloads/en/AppNotes/90003129A.pdf including an auto-calibration app note https://ww1.microchip.com/downloads/en/AppNotes/00002030A.pdf

Man, this list is getting mighty long :)
 
Glad you tested it out even if it didn't make a difference. I suppose, the resulting C code could have used a MOVFF instruction , but no matter at this point. That link that I originally cited was from 2007. I wonder if it has changed. I see nothing like that in the errata for the 18857 https://ww1.microchip.com/downloads/en/DeviceDoc/PIC16(L)F18857-18877-Errata-DS80000702E.pdf I'd still like to know what is happening (or not happening). This goes on the very long list.

I have never used the SMT, but as a result of this thread I was reading a little bit about it https://ww1.microchip.com/downloads/en/AppNotes/90003129A.pdf including an auto-calibration app note https://ww1.microchip.com/downloads/en/AppNotes/00002030A.pdf

Man, this list is getting mighty long :)

It's all getting far too complicated! :D
 
i wonder if it could be related to noise on the line and maybe extra smoothing would help?
 
Just as a test, what happens with an external schmitt trigger, so the PIC pin never sees a slowly changing signal?

If that eliminates the false triggers it's presumably that their schmitt inputs have a problem or due to the design just cannot accept very slow rate of change signals.
 
It all seems so straightforward! [/sarcasm]

To be fair, it's all going well at the moment - I've been doing some further work this morning (went in on a Saturday!!) - and the SMT is doing just what I wanted :D

I was also trying to find a way to generate fairly slow frequency outputs, by default it outputs 1Hz (roughly), and can be set to a small range of other values (10Hz, 0.1Hz and 0.05Hz). I currently do this using TMR4 generating 50mS interrupts, and counting then to generate the required output frequencies). But I thought NCO?, PWM? (I tried with PWM originally, you can't it that slow with a4MHz clock), use some nice hardware that takes no processor cycles - NCO will go down to 0.5Hz, but requires a slower clock to go and lower.

Unfortunately the NCO has a fairly limited range of clock options, but you can clock it from the CLC - so I'm wondering if I can get a decent lower clock speed from there?. To be honest, I'm rather confused by what the CLC can do, and how you go about configuring it :D

But to be fair, TMR4 is doing the job perfectly, it just seems a shame having lot's of nice unused hardware :D
 
To be fair, it's all going well at the moment - I've been doing some further work this morning (went in on a Saturday!!) - and the SMT is doing just what I wanted :D

I was also trying to find a way to generate fairly slow frequency outputs, by default it outputs 1Hz (roughly), and can be set to a small range of other values (10Hz, 0.1Hz and 0.05Hz). I currently do this using TMR4 generating 50mS interrupts, and counting then to generate the required output frequencies). But I thought NCO?, PWM? (I tried with PWM originally, you can't it that slow with a4MHz clock), use some nice hardware that takes no processor cycles - NCO will go down to 0.5Hz, but requires a slower clock to go and lower.

Unfortunately the NCO has a fairly limited range of clock options, but you can clock it from the CLC - so I'm wondering if I can get a decent lower clock speed from there?. To be honest, I'm rather confused by what the CLC can do, and how you go about configuring it :D

But to be fair, TMR4 is doing the job perfectly, it just seems a shame having lot's of nice unused hardware :D


I recently needed a very 50Hz PWM for a Futaba servo. I had to clock back the pic clock to 125kHz. It worked great in my case, not for every case.
 
I recently needed a very 50Hz PWM for a Futaba servo. I had to clock back the pic clock to 125kHz. It worked great in my case, not for every case.

People are always wanting to use the PWM modules for servos, and (as you found out) it's a pretty poor system for servo use.

I'm surprised MicroChip have never brought out a module specifically designed for servo use?, as it's a pretty common requirement.
 
People are always wanting to use the PWM modules for servos, and (as you found out) it's a pretty poor system for servo use.

I'm surprised MicroChip have never brought out a module specifically designed for servo use?, as it's a pretty common requirement.
The PWM module is actually quite handy for generating jitter-free high resolution (low overhead) 50-Hz servo pulses. I'd be happy to show you how if you're interested... Chips with the PWM 'steering' feature can provide a relatively simple 4-channel Servo solution.

Cheerful regards, Mike, K8LH

Code:
;******************************************************************
;  interrupt vector                                               *
;******************************************************************
        org     0x0004
;
;  /* single servo example (16F1823, 16-MHz clock) */
;
;  void interrupt()             // 250-uS (1000 cycle) interrupts
;  { static int width = 0;      // width, typically 500..2500
;    static char frame = 1;     // # of 250-us PWM frames, 1..80
;    TMR2IF = 0;                // clear TMR2 interrupt flag
;    if(width >= 250)           // if width >= 250 usecs
;      CCPR1L = 250;            // use full 100% duty cycle
;    else                       // otherwise
;      CCPR1L = width;          // use balance of 'width'
;    width -= CCPR1L;           // update 'width' variable
;    if(--frame == 0)           // if end of 20-ms servo period
;    { frame = 80;              // reset for a new 20-ms period
;      width = servo;           // refresh 'width', 500..2500
;    }                          //
;  }                            //
;
        radix   dec

        banksel PIR1            ; bank 0                          |00
        bcf     PIR1,TMR2IF     ; clear TMR2 interrupt flag       |00
        movlw   low(250)        ; if(width >= 250)                |00
        subwf   width+0,W       ;  "                              |00
        movlw   high(250)       ;  "                              |00
        subwfb  width+1,W       ;  "                              |00
        movf    width+0,W       ;                                 |00
        skpnc                   ; borrow? yes, skip, else         |00
        movlw   250             ;                                 |00
        banksel CCPR1L          ; bank 5                          |05
        movwf   CCPR1L          ; duty cycle for next frame       |05
        banksel width           ; bank 0                          |00
        subwf   width+0,F       ; width -= ccpr1l;                |00
        movlw   0               ;  "                              |00
        subwfb  width+1,F       ;  "                              |00
        decfsz  frame,F         ; end-of-period? yes, skip, else  |00
        retfie                  ;                                 |00

        movlw   80              ; eighty 250 us pwm frames        |00
        movwf   frame           ; reset 20-msec frame counter     |00
        movf    Servo+0,W       ; refresh 'width' work variable   |00
        movwf   width+0         ;  "                              |00
        movf    Servo+1,W       ;  "                              |00
        movwf   width+1         ;  "                              |00
        retfie                  ;                                 |00
 
Last edited:
The PWM module is actually quite handy for generating jitter-free high resolution (low overhead) 50-Hz servo pulses. I'd be happy to show you how if you're interested... Chips with the PWM 'steering' feature can provide a relatively simple 4-channel Servo solution.

Cheerful regards, Mike, K8LH

I'd be interested to see it Mike, I thought the PWM modules wouldn't go low enough?.
 
I'd be interested to see it Mike, I thought the PWM modules wouldn't go low enough?.
You need to break up the 20-ms Servo period into smaller PWM 'frames' and use an interrupt 'helper' to load the PWM value for each upcoming 'frame'... I added an example excerpt to my previous post. The interrupt 'helper' for a 4-channel Servo solution using "pwm steering" is slightly more involved;
Code:
;  16F1823
;
;  servo[0] -> RC5 (pin 5)
;  servo[1] -> RC4 (pin 6)
;  servo[2] -> RC3 (pin 7)
;  servo[3] -> RC2 (pin 8)
;
; /******************************************************************
;  *  4-Chan "PWM steering" Servo Driver        Mike McLaren, K8LH  *
;  ******************************************************************/
;
;  void interrupt()             // 250-uS (1000 cycle) interrupts
;  { static char index = 0;     //
;    static char frame = 1;     //
;    static char steer = 0;     //
;    static int width = 0;      //
; /*                                                                *
;  *  set the duty cycle and steer the next 250-us PWM 'frame'      *
;  *                                                                */
;    TMR2IF = 0;                // clear TMR2 interrupt flag
;    if(width >= 250)           // if width >= 250-us
;      CCPR1L = 250;            // use full 100% duty cycle
;    else                       // otherwise
;      CCPR1L = width;          // use balance of width or 0%
;    width -= CCPR1L;           // adjust balance
;
;    PSTR1CON = steer | 1<<STR1SYNC;
;
;    if(--frame == 0)           // if end of 5-ms chan interval
;    { frame = 20;              // reset 5-ms frame counter and
;      if(!(steer >>= 1))       // if last channel (20-msecs)
;        steer = 8;             // reset for channel 0
;      width = servo[index];    // new servo 'width'
;      index++; index &= 3;     // next channel index, 0..3
;    }                          //
;  }                            //
;
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top