interrupt on change issue

65 views
Skip to first unread message

vsurducan

unread,
Sep 30, 2021, 11:51:16 AM9/30/21
to jal...@googlegroups.com

According to the PIC12F1572 datasheet, rising edge IOC can be detected by enabling edge detection on input pins (IOCAPx or IOCANx)
and reading the IOC flag (IOCAFx) or the INTCON_IOCIF, then clearing the flag for the next IOC detection.
Seems it does not work.  Any ideas?  Thx.

include 12lf1572                     -- target PICmicro
--
-- This program uses the internal oscillator at 4 MHz.
pragma target clock    32_000_000       -- oscillator frequency
--
pragma target OSC      INTOSC_NOCLKOUT           -- internal oscillator
pragma target PLLEN    DISABLED                  -- PLL off
pragma target CLKOUTEN DISABLED                  -- no clock output
pragma target WDT      DISABLED                  -- watchdog
pragma target BROWNOUT ENABLED                  -- brownout reset
pragma target LVP      DISABLED                  -- no low voltage programming
pragma target MCLR     INTERNAL                  -- internal reset
--
--
OSCCON_SCS = 0                      -- select primary oscillator
OSCCON_IRCF = 0b1110                -- 8 MHz
OSCCON_SPLLEN = ENABLED             -- 4x PLL: 8 -> 32 MHz
--
enable_digital_io()                 -- make all pins digital I/O

include delay                      

alias pin_RDY_IN is pin_A0
alias pin_RDY_IN_direction is pin_A0_direction
pin_RDY_IN_direction = input


alias pin_RDY_OUT is pin_A5
alias pin_RDY_OUT_direction is pin_A5_direction
pin_RDY_OUT_direction = output
pin_RDY_OUT = low

OPTION_REG_WPUEN = 0
WPUA_WPUA0 = 0 ; pull-up disabled on RA0


-- main program
-- ----------------------------------------------------------------------------
INTCON_GIE = high   ; not necessary without interrupt routine
INTCON_IOCIE = high ; enable interrupt on change, not necessary without interrupt routine
IOCAP_IOCAP0 = high ; enable positive edge interrupt on change on RDY_IN


forever loop

; if INTCON_IOCIF  then
  if IOCAF_IOCAF0  then  ; a positive edge interrupt on change on RDY_IN occured
    pin_RDY_OUT = high
    delay_1mS (20)
    pin_RDY_OUT = low
    IOCAF_IOCAF0 = low    ; clear IOCAF0 flag
;    INTCON_IOCIF = low
 else ; no interrupt occured
    pin_RDY_OUT = low
 end if
 
end loop




Rob CJ

unread,
Sep 30, 2021, 12:38:35 PM9/30/21
to jal...@googlegroups.com
Hi Vasile,

I have used it in the past but sometimes it did not detect interrupt and once you miss one interrupt it will never be detected. Maybe because I accessed the port in the wrong way.

The datasheet states that you should AND the data with the port in order not to miss an interrupt. As said, if you miss an interrupt by not accessing the port in the right way it will never generate an interrupt anymore.

Hope this helps.

Kind regards,

Rob



Van: jal...@googlegroups.com <jal...@googlegroups.com> namens vsurducan <vsur...@gmail.com>
Verzonden: donderdag 30 september 2021 17:51
Aan: jal...@googlegroups.com <jal...@googlegroups.com>
Onderwerp: [jallib] interrupt on change issue
 
--
You received this message because you are subscribed to the Google Groups "jallib" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jallib+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jallib/CAM%2Bj4qvv6k0rGBYftBOhsT7HR4wJe8GrM8_vEGUPiTk3MVa%2B8Q%40mail.gmail.com.

Rob Hamerling

unread,
Oct 1, 2021, 4:56:07 AM10/1/21
to jal...@googlegroups.com

I think the cause of your problem is that you set INTCON_GIE = high, but have no ISR (Interrupt Service Routine).
An interrupt (hardware jump to the interrupt vector at address 0x0004) becomes practically a restart of the program.
Possible solutions: INTCON_GIE = low, or use an ISR to handle the IOC interrupt (but no delay in an ISR!)

Regards, Rob.
--
You received this message because you are subscribed to the Google Groups "jallib" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jallib+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jallib/CAM%2Bj4qvv6k0rGBYftBOhsT7HR4wJe8GrM8_vEGUPiTk3MVa%2B8Q%40mail.gmail.com.

--
Rob Hamerling, Vianen, NL

vsurducan

unread,
Oct 1, 2021, 7:11:47 AM10/1/21
to jal...@googlegroups.com
Hi Rob and Rob, :)
I've tried with or without ISR, with AND IOCAP reset or normal IOCAP reset. This IOC is erratic and has a lot of latency. The RDY_IN signal is 20ms square 50% duty cycle, the RDY_OUT signal has a latency most of the time...but sometimes has a 20mS pulse as it should. The code which somehow "works" is below, however the need of clearing the IOCAF0 in the main loop is weird. Without it IOC is not working. If the RDY_IN (pin_A0) signal disappears, the RDY_OUT (pin_A5) remains high.
INTCON_IOCIF is read only, it's content is changed by IOCAF. To clear INTCON_IOCIF the whole IOCAF register has to be cleared.

Perhaps I missed something...

------------------------------------------
pin_A0_direction = input
pin_A5_direction = output

OPTION_REG_WPUEN = 1 ; pull-up disabled on portA
;WPUA_WPUA0 = low ; pull-up disabled on RA0
INLVLA_INLVLA0 = high ; schmitt trigger on RA0 enabled

-- main program
-- ----------------------------------------------------------------------------
INTCON_GIE = high   ; enable global interrupts
INTCON_PEIE = high  ; enable peripheral interrupts

INTCON_IOCIE = high ; enable interrupt on change,
 IOCAP_IOCAP0 = high ; enable positive edge interrupt on change on RDY_IN
IOCAF = 0; clear IOCAF flags

procedure IOC () is
 pragma interrupt


   if INTCON_IOCIF & IOCAF_IOCAF0 then
      asm bsf pin_A5
 ;     IOCAF = 0  ; it's never cleared here
;        assembler ; AND clear IOCAF
;       movlw 0xff
;       bank xorwf IOCAF, w
;       bank andwf IOCAF, f
;        end assembler
   else
      asm bcf pin_A5
   end if
;        assembler ; AND clear IOCAF
;       movlw 0xff
;       bank xorwf IOCAF, w
;       bank andwf IOCAF, f
;        end assembler
   IOCAF = 0

end procedure
   
forever loop
      IOCAF = 0   ; clear INTCON_IOCIF
end loop

Rob Hamerling

unread,
Oct 1, 2021, 10:40:56 AM10/1/21
to jal...@googlegroups.com

Hi Vasili,


On 01/10/2021 13.11, vsurducan wrote:
Hi Rob and Rob, :)
I've tried with or without ISR, with AND IOCAP reset or normal IOCAP reset. This IOC is erratic and has a lot of latency. The RDY_IN signal is 20ms square 50% duty cycle, the RDY_OUT signal has a latency most of the time...but sometimes has a 20mS pulse as it should. The code which somehow "works" is below, however the need of clearing the IOCAF0 in the main loop is weird. Without it IOC is not working. If the RDY_IN (pin_A0) signal disappears, the RDY_OUT (pin_A5) remains high.
INTCON_IOCIF is read only, it's content is changed by IOCAF. To clear INTCON_IOCIF the whole IOCAF register has to be cleared.

Perhaps I missed something...

It could be as well be me who is missing something....

If the only purpose of your program is to have pin_RDY-OUT following pin_RDY_IN then forget IOC and simply use:

    forever loop
     pin_RDY_OUT = pin_RDY_IN
  end loop

But I suppose you showed us only a part of your program/functionality.

BTW You don't need assembler to clear the interrupt flag of pin_RDY_IN. I used successfully:   
   IOCAF = IOCAF & 0xFE  
(check the assembler output of the compiler).


Oliver Seitz

unread,
Oct 1, 2021, 11:30:49 AM10/1/21
to jal...@googlegroups.com
Hi Rob,

that was a thought I had, but I didn't check what the compiler does if there's no interrupt service routine. Would it be smart to always waste five program words just to be sure not to accidentally restart the program? I.e., always put a RETFIE at position 0x0004 when there's no pragma interrupt?

Greets,
Kiste


Am Freitag, 1. Oktober 2021, 10:56:08 MESZ hat Rob Hamerling <robham...@gmail.com> Folgendes geschrieben:
https://groups.google.com/d/msgid/jallib/dd13bf5c-9a69-5157-afa9-fa27d38ece98%40gmail.com
.

Rob Hamerling

unread,
Oct 1, 2021, 1:46:50 PM10/1/21
to jal...@googlegroups.com

Hello Kiste,


On 01/10/2021 17.30, 'Oliver Seitz' via jallib wrote:
                   I didn't check what the compiler does if there's no interrupt service routine. Would it be smart to always waste five program words just to be sure not to accidentally restart the program? I.e., always put a RETFIE at position 0x0004 when there's no pragma interrupt?
Yeah, I've thought about that too, but that would mask design errors. Not a good idea in my opinion.

Regards, Rob.

vsurducan

unread,
Oct 2, 2021, 2:38:10 AM10/2/21
to jal...@googlegroups.com
Hi Rob, Kiste,
I'm not sure on the actual compiler, but one of old pragma interrupt versions allows you to save, restore all registers and retfie manually at your need.
I still believe it was a great option.

--
You received this message because you are subscribed to the Google Groups "jallib" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jallib+un...@googlegroups.com.

Oliver Seitz

unread,
Oct 2, 2021, 3:17:56 AM10/2/21
to jal...@googlegroups.com
Hi!

That's something else, I think it's called "fast interrupt" or the like. It's not such an easy thing, as it restricts a project to have only *one* ISR (=Interrupt Service Routine), which, e.g. with the serial_hw_int_cts library is not possible as that library uses interrupts transparently to the user. So, activating that switch can be dangerous, depending on what libraries you use.

If there is one or more ISR procedures in a program, INTCON_GIE can be activated safely. What we were talking about is a single "fake ISR" which the compiler could always generate if there is no real ISR, just in case the program accidentally enables INTCON_GIE.

There are two philosophies:

1. Make program errors have an effect to make them visible and easier correctable

2. Try to hide program errors and increase reliability, doing your best to just keep running

IMHO I dont' really have a preference. But watchdogs and especially PPS lock features are clear approaches to Nr. 2.

Greets,
Kiste

 

Am Samstag, 2. Oktober 2021, 08:38:12 MESZ hat vsurducan <vsur...@gmail.com> Folgendes geschrieben:
To view this discussion on the web visit https://groups.google.com/d/msgid/jallib/CAM%2Bj4qt0LpyrKTn_f1r-Ze%3DAk%3Dfam3ika_4b4wqRAmCt924_mQ%40mail.gmail.com.

Rob Hamerling

unread,
Oct 2, 2021, 6:02:56 AM10/2/21
to jallib

Hi Vasili,

On 01/10/2021 13.11, vsurducan wrote:
Hi Rob and Rob, :)
I've tried with or without ISR, with AND IOCAP reset or normal IOCAP reset. This IOC is erratic and has a lot of latency. The RDY_IN signal is 20ms square 50% duty cycle, the RDY_OUT signal has a latency most of the time...but sometimes has a 20mS pulse as it should.

I'm using IOC for quadrature encoders on the wheels of an RC vehicle for navigation. With higher speeds the interrupt frequency is significantly higher then once per 20ms. Nevertheless this seems to work quite well (but latency is not a real issue).

Below my test with a 12F1572 and modified version of your program on a little board with LEDs on pins A0, A1 and A2.
The IOC of RDY_IN is handled by an interrupt routine. Both rising and falling edge interrupts are activated and RDY_OUT (in my setup on pin_A2)  is controlled in the ISR as well. The input signal for RDY_IN is generated in the forever loop on pin_A1. When connecting pin_A0 with pin_A1  RDY_OUT is following RDY_IN neatly (visual observation shows all 3 LEDs blinking synchronously).

Hopefully this is of some help to solve your issue.

Regards, Rob.


=================================
alias pin_RDY_IN is pin_A0
alias pin_RDY_IN_direction is pin_A0_direction
pin_RDY_IN_direction = input
OPTION_REG_WPUEN = 0
WPUA_WPUA0 = 0 ; pull-up disabled on RA0

alias pin_RDY_OUT is pin_A2
alias pin_RDY_OUT_direction is pin_A2_direction

pin_RDY_OUT_direction = output
pin_RDY_OUT = low

alias pin_TRIGGER is pin_A1
pin_A1_direction = OUTPUT
pin_TRIGGER = low

-- main program

INTCON_GIE = high
INTCON_IOCIE = high     -- enable IOC interrupts
IOCAP_IOCAP0 = high     -- enable positive edge interrupts RDY_IN
IOCAN_IOCAN0 = high     -- enable negative edge interrupts RDY_IN

procedure IOC() is
    pragma interrupt
    if INTCON_IOCIF then                -- IOC interrupt
        if IOCAF_IOCAF0 then            -- edge interrupt on RDY_IN
            if pin_RDY_IN then          -- was a positive edge
                pin_RDY_OUT = high
            else                        -- negative edge

                pin_RDY_OUT = low
            end if
            IOCAF = IOCAF & 0xFE        -- clear IOCAF0
        end if
        INTCON_IOCIF = 0                -- reset IOC interrupt flag
    end if
end procedure

forever loop
    pin_TRIGGER = high
    delay_1ms(20)
    pin_TRIGGER = low
    delay_1ms(20)
end loop

vsurducan

unread,
Oct 3, 2021, 4:32:58 AM10/3/21
to jal...@googlegroups.com
Hi Rob, thanks for your feedback.
Your program somehow works, however  in my level of understanding it is not based on pure IOC.
Once the IOCAF_IOCAF0 is set ( a IOC occured) you are testing again the level of RDY_IN which should not happen... IOC is the transition of IOCAF0 or INTCON_IOCIF to high  (and I'm assuming only that should be tested since the transition is defined by IOCAP/IOCAN).


On the other hand, if you set only the detection of the rising edge ( IOCAP_IOCAP0 = high,  IOCAN_IOCAN0 = low),  RDY_OUT stayed indefinitely on high even on RDY_IN a positive edge occured, which seems wrong.

According to the datasheet The "IOCIF Flag bit is read-only and cleared when all the Interrupt-On-Change flags in the IOCxF registers
have been cleared by software", so that line can be commented, it will not be cleared. In fact this seems to be the real problem with the IOC module.
I can send you a scope image for positive edge detection to see the lost IOCs on positive edges if that helps.
best wishes,



     -- enable negative edge interrupts RDY_IN

procedure IOC() is
    pragma interrupt
    if INTCON_IOCIF then                -- IOC interrupt
        if IOCAF_IOCAF0 then            -- edge interrupt on RDY_IN
            if pin_RDY_IN then          -- was a positive edge
                pin_RDY_OUT = high
            else                        -- negative edge
                pin_RDY_OUT = low
            end if
            IOCAF = IOCAF & 0xFE        -- clear IOCAF0
        end if
        INTCON_IOCIF = 0                -- reset IOC interrupt flag
    end if
end procedure

forever loop
    pin_TRIGGER = high
    delay_1ms(20)
    pin_TRIGGER = low
    delay_1ms(20)
end loop



--
Rob Hamerling, Vianen, NL

--
You received this message because you are subscribed to the Google Groups "jallib" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jallib+un...@googlegroups.com.

Rob CJ

unread,
Oct 3, 2021, 5:21:29 AM10/3/21
to jal...@googlegroups.com
Hi Vasile,

I had a closer look at your program (and at the one of Rob). I saw your 20 ms delay (which Rob got rid of) before you clear the interrupt flag.

If I am right the Interrupt On Edge will no longer work if you did not process it before the next change on the pin. So your 20ms delay may be the cause of your problem. You should first clear the IOCAF0 as the first step. 

Stil the 20ms delay may give you problems so that may be the reason that the program of Rob does not have the problem.

Kind regards,

The other Rob



Verzonden: zondag 3 oktober 2021 10:32
Aan: jal...@googlegroups.com <jal...@googlegroups.com>
Onderwerp: Re: [jallib] interrupt on change issue
 

Rob Hamerling

unread,
Oct 3, 2021, 8:15:06 AM10/3/21
to jallib

Hi Vasili,



On 03/10/2021 10.32, vsurducan wrote:
Your program somehow works, however  in my level of understanding it is not based on pure IOC.
Once the IOCAF_IOCAF0 is set ( a IOC occured) you are testing again the level of RDY_IN which should not happen... IOC is the transition of IOCAF0 or INTCON_IOCIF to high  (and I'm assuming only that should be tested since the transition is defined by IOCAP/IOCAN).


In my provisional solution RDY_OUT is following RDY_IN. Therefore I enabled both rising and falling edge interrupts.   Since there are no individual interrupt status bits for falling and rising edges the only way to determine which type of edge interrupt occured (which is needed to decide whether to set or reset RDY_OUT) I see no other way than looking at the current RDY_IN level.

Alternative: When you wish/need RDY_OUT be high during a specific time (20 ms) after each rising edge on RDY_IN then you might set a flag in the ISR, use a delay or timer and reset the flag in the forever loop.


According to the datasheet The "IOCIF Flag bit is read-only and cleared when all the Interrupt-On-Change flags in the IOCxF registers
have been cleared by software", so that line can be commented, it will not be cleared. In fact this seems to be the real problem with the IOC module.

You are right, I overlooked that the IOCIF bit is read-only.


I can send you a scope image for positive edge detection to see the lost IOCs on positive edges if that helps.

Well, not necessary, I believe you! (I have a scope on my wish-list for a long time!).
I'm just trying to help you with your problem. I'm not aware that I have a problem with IOC, and I'm using an 18F chip!

Regards, Rob.

Rob Hamerling

unread,
Oct 3, 2021, 8:45:12 AM10/3/21
to jal...@googlegroups.com

Hi Vasili,

Follow up of my previous message ...


On 03/10/2021 10.32, vsurducan wrote:

On the other hand, if you set only the detection of the rising edge ( IOCAP_IOCAP0 = high,  IOCAN_IOCAN0 = low),  RDY_OUT stayed indefinitely on high even on RDY_IN a positive edge occured, which seems wrong.

With only rising edge interrupts this is a possibility:


procedure IOC() is
    pragma interrupt
    if INTCON_IOCIF & IOCAF_IOCAF0 then -- edge interrupt on RDY_IN

        IOCAF = IOCAF & 0xFE            -- clear IOCAF0
        pin_RDY_OUT = high              -- RDY_OUT high

    end if
end procedure

forever loop
    if pin_RDY_OUT then
        delay_1ms(20)

        pin_RDY_OUT = low
    end if
end loop

vsurducan

unread,
Oct 4, 2021, 1:01:12 AM10/4/21
to jal...@googlegroups.com
Cj_Rob, thanks, you caught the issue!
See the answer to HRob. :)

vsurducan

unread,
Oct 4, 2021, 1:19:05 AM10/4/21
to jal...@googlegroups.com
Rob, I like your way of tricking the problem. :)
The final effect looks better, now indeed it detects the positive edge of RDY_IN. Assuming the RDY_In is 20ms and RDY_OUT delay in the main loop is say 1ms that can be correctly seen.
The IOC is almost the same of mine (which started as jal and ended as assembler)
But if you need to precise control the RDY_OUT time, that seems impossible in a long main loop which is also large jitter generator ( using the USB_serial for instance) .
Imagine that RDY_IN varies between say 100us to 100ms and RDY_OUT must stay high a precise time until user reset it. The RDY_OUT control has to be moved in the ISR, but then no delay will be allowed to catch the rising edge...an interrupt  timer delay must be implemented.
thanks for your help!

--
You received this message because you are subscribed to the Google Groups "jallib" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jallib+un...@googlegroups.com.

Oliver Seitz

unread,
Oct 4, 2021, 1:50:21 AM10/4/21
to jal...@googlegroups.com
Hi!

Good to hear it's working now.

About what you're describing, it depends on what "precisely" means. You can choose a newer chip like from the Q43 series and use the peripherals. There's a CWG which has a shutdown feature that switches of the output when a configurable interrupt fires - with no software latency at all.

Greets,
Kiste




Am Montag, 4. Oktober 2021, 07:19:07 MESZ hat vsurducan <vsur...@gmail.com> Folgendes geschrieben:
To view this discussion on the web visit https://groups.google.com/d/msgid/jallib/CAM%2Bj4qsXs%2BJXfaaN10tYtfF6R-jO7OeyMTyqfXN8SzV-bSgGoQ%40mail.gmail.com.

Rob Hamerling

unread,
Oct 4, 2021, 4:24:47 AM10/4/21
to jallib


Hi Vasili,


On 04/10/2021 07.19, vsurducan wrote:

But if you need to precise control the RDY_OUT time, that seems impossible in a long main loop which is also large jitter generator ( using the USB_serial for instance) .
Imagine that RDY_IN varies between say 100us to 100ms and RDY_OUT must stay high a precise time until user reset it. The RDY_OUT control has to be moved in the ISR, but then no delay will be allowed to catch the rising edge...an interrupt  timer delay must be implemented.

Yes, for this situation a one-shot timer seems the appropriate solution: set RDY_OUT in the IOC ISR and start the one-shot timer, reset RDY_OUT in the timer ISR.

BTW Microchip has a document TB3122 which may be of help.

Success, Rob.

vsurducan

unread,
Oct 4, 2021, 4:40:29 AM10/4/21
to jal...@googlegroups.com
Karl, I'm synchronizing 64 microcontrollers...I'm already stuck on hardware
I could do it better than that, but perhaps next time only....:(
thx!

Reply all
Reply to author
Forward
0 new messages