# Interrupt On Change 'problem'

Status
Not open for further replies.

#### Nigel Goodwin

##### Super Moderator
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.

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
}
}

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
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..

#### DrG

##### Active Member
Just taking a stab, but does this issue raise any flags for you?

"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

#### Nigel Goodwin

##### Super Moderator
Just taking a stab, but does this issue raise any flags for you?

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

#### DrG

##### Active Member
I'm not sure, it's all a bit confusing - but I'll study it further

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:

#### Nigel Goodwin

##### Super Moderator
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.

#### Nigel Goodwin

##### Super Moderator
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

#### Nigel Goodwin

##### Super Moderator
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.

#### DrG

##### Active Member
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.

Man, this list is getting mighty long

#### Nigel Goodwin

##### Super Moderator
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.

Man, this list is getting mighty long

It's all getting far too complicated!

#### Dr_Doggy

##### Well-Known Member
i wonder if it could be related to noise on the line and maybe extra smoothing would help?

#### rjenkinsgb

##### Well-Known Member
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.

#### gophert

##### Well-Known Member
It's all getting far too complicated!

It all seems so straightforward! [/sarcasm]

#### Nigel Goodwin

##### Super Moderator
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

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

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

#### gophert

##### Well-Known Member
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

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

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

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.

#### Nigel Goodwin

##### Super Moderator
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.

#### Mike - K8LH

##### Well-Known Member
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
;    }                          //
;  }                            //
;

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:

#### Nigel Goodwin

##### Super Moderator
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?.

#### Mike - K8LH

##### Well-Known Member
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.

Replies
4
Views
1K
Replies
7
Views
5K
Replies
5
Views
2K
Replies
2
Views
2K
Replies
8
Views
6K