micropython-mcp23017 icon indicating copy to clipboard operation
micropython-mcp23017 copied to clipboard

finding the mcp pin which caused the interrupt

Open Guru-Solidz opened this issue 2 years ago • 2 comments
trafficstars

Hi, I am trying to use the wonderful library that you have provided.

I have connected 1 MCP23017 with esp32, and configured all 16 pins as input. my requirement is as follows:

I need to need to know which pin of the mcp23017 triggered the input.

My code is as below: ###################################

from machine import Pin, SoftI2C
import mcp23017, time, gc
i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=400000, timeout=255)
mcp = mcp23017.MCP23017(i2c, 0x20)

# method interface
mcp.pin(0, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(1, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(2, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(3, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(4, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(5, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(6, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(7, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(8, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(9, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(10, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(11, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(12, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(13, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(14, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.pin(15, mode=1, pullup=0, polarity=0, interrupt_enable=1)

mcp.config(interrupt_polarity=0, interrupt_mirror=1)

# mcp.interrupt_triggered_gpio(port=0)
# mcp.interrupt_captured_gpio(port=0)

# mcp.interrupt_triggered_gpio(port=1)
# mcp.interrupt_captured_gpio(port=1)

p5 = Pin(5, Pin.IN, Pin.PULL_DOWN)
p6 = Pin(6, Pin.IN, Pin.PULL_DOWN)

def int1(pin):

    if Pin(5).value() == 1:
        print ('Int A Triggered')
    elif Pin(5).value() == 0:
        print ('Int A Released')
    gc.collect()
    # print(mcp.porta.interrupt_flag)
    # print(mcp.porta.interrupt_captured)


def int2(pin):

    if Pin(6).value() == 1:
        print ('Int B Triggered')
    elif Pin(6).value() == 0:
        print ('Int B Released')
    gc.collect()

p5.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=int1)
p6.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=int2)

#################################################

If I uncomment the "print(mcp.porta.interrupt_flag)" i get error as follows: Traceback (most recent call last): File "mcptest.py", line 44, in int1 File "mcp23017.py", line 269, in interrupt_triggered_gpio File "mcp23017.py", line 135, in interrupt_flag File "mcp23017.py", line 73, in _read OSError: [Errno 19] ENODEV

Could you kindly help me out in letting me know how to get the pin which generated the interrupt ?

Thanks

Guru-Solidz avatar Apr 13 '23 09:04 Guru-Solidz

Hi,

OSError: [Errno 19] ENODEV means the device was not found on the I2C bus. Perform an i2c.scan() and make sure 0x20 or 32 is present.

The two properties you're after are interrupt_flag and interrupt_captured. When interrupts are enabled on a MCP pin and its set to input, each time it changes state it sets interrupt_flag to the pin that triggered the interrupt (only 1) and the current state of all GPIO pins in interrupt_captured.

Not sure what is connected to p5/p6. Assuming they are connected to INTA and INTB. You've set interrupt_mirror=1, so it will trigger an interrupt when either port detects a change (internally linked). This means you only need the one ESP32 pin reading INTA. If you want them to fire independently, set interrupt_mirror=0 and use two ESP32 pins and two interrupts.

# config mcp using method interface
for i in range(16):
    mcp.pin(i, mode=1, pullup=0, polarity=0, interrupt_enable=1)
mcp.config(interrupt_polarity=0, interrupt_mirror=1)

# or the same using the property interface
mcp.mode = 0xffff
mcp.pullup = 0x0000
mcp.input_polarity = 0x0000
mcp.interrupt_enable = 0xffff
mcp.io_config = 0x40

def int1(pin):
    if pin.value() == 1:
        print('Int Triggered')
    elif pin.value() == 0:
        print('Int Released')
    # this will give you which pin on PortA triggered the int (8-bit value):
    # print(mcp.porta.interrupt_flag)
    # this will give you PortB
    # print(mcp.portb.interrupt_flag)
    # since you are linking INTA and INTB, use the 16-bit value (both ports a and b):
    print(mcp.interrupt_flag)
    # each time you read this property, it is cleared. It is also cleared if you read the GPIO values.

p5 = Pin(5, Pin.IN, Pin.PULL_DOWN)
p5.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=int1)

You probably don't need garbage collection inside the interrupt handler. The interrupt handler has a pin argument, so you don't need to call Pin(5) to get a reference inside.

The mcp.interrupt_triggered_gpio(1) method is the same as calling mcp.portb.interrupt_flag.

The mcp.interrupt_captured_gpio(0) method is the same as calling mcp.porta.interrupt_captured.

If you want to use Pin.PULL_UP on p5, set interrupt_polarity=1 on the mcp.

The mcp clears interrupts when the flag or gpio are read. You probably don't need the pin.irq() to react on both rising and falling edges. Rising is enough as the int is cleared as soon as it is read.

Use mcp.interrupt_compare_default to make the mcp fire an int when the GPIO a) changes state compared to its previous state, or b) compared to the value in mcp.default_value. If you don't have a specific use case for this, use the default mcp.interrupt_compare_default = 0x0000

mcauser avatar Apr 13 '23 10:04 mcauser

Hi Mike, Thanks for the fast reply.

---> OSError: [Errno 19] ENODEV means the device was not found on the I2C bus. Perform an i2c.scan() and make sure 0x20 or 32 is present. [image: image.png]

As shown above, mcp23017 is detected on the i2c.scan() function.

----> Not sure what is connected to p5/p6. Assuming they are connected to INTA and INTB p5 and p6 are connected to INTA and INTB respectively.

As shown by you, the code now looks as follows:

from machine import Pin, SoftI2C import mcp23017 i2c = SoftI2C(scl=Pin(5), sda=Pin(4), freq=400000, timeout=255) mcp = mcp23017.MCP23017(i2c, 0x20)

for i in range(16): mcp.pin(i, mode=1, pullup=0, polarity=0, interrupt_enable=1, interrupt_compare_default=0)

mcp.config(interrupt_polarity=0, interrupt_mirror=1)

def int1(pin): if pin.value() == 1: print('Int Triggered') elif pin.value() == 0: print('Int Released') print(mcp.interrupt_flag)

p5 = Pin(5, Pin.IN, Pin.PULL_DOWN) p5.irq(trigger=Pin.IRQ_RISING, handler=int1)

When I run the application, I am not receiving any interrupt / print at all.

However, when I run i2c.scan() function again, I continuously receive Int Triggered 1 as shown below: [image: image.png]

Request you to kindly guide me on where I am making a mistake / error.

Note: when I use the same MCP23017 with Raspberry pi, with Adafruit I2C and MCP23017 library, it gives me the pin and value of the mcp whenever it triggers. _____________________________________________________________ Thanks *Guruprasad NK | +91 98718 43210 Founder & CEO | *Solidz.IO

On Thu, Apr 13, 2023 at 3:32 PM Mike Causer @.***> wrote:

Hi,

OSError: [Errno 19] ENODEV means the device was not found on the I2C bus. Perform an i2c.scan() and make sure 0x20 or 32 is present.

The two properties you're after are interrupt_flag and interrupt_captured. When interrupts are enabled on a MCP pin and its set to input, each time it changes state it sets interrupt_flag to the pin that triggered the interrupt (only 1) and the current state of all GPIO pins in interrupt_captured.

Not sure what is connected to p5/p6. Assuming they are connected to INTA and INTB. You've set interrupt_mirror=1, so it will trigger an interrupt when either port detects a change (internally linked). This means you only need the one ESP32 pin reading INTA. If you want them to fire independently, set interrupt_mirror=0 and use two ESP32 pins and two interrupts.

config mcp using method interfacefor i in range(16):

mcp.pin(i, mode=1, pullup=0, polarity=0, interrupt_enable=1)mcp.config(interrupt_polarity=0, interrupt_mirror=1)

or the same using the property interfacemcp.mode = 0xffffmcp.pullup = 0x0000mcp.input_polarity = 0x0000mcp.interrupt_enable = 0xffffmcp.io_config = 0x40

def int1(pin): if pin.value() == 1: print('Int Triggered') elif pin.value() == 0: print('Int Released') # this will give you which pin on PortA triggered the int (8-bit value): # print(mcp.porta.interrupt_flag) # this will give you PortB # print(mcp.portb.interrupt_flag) # since you are linking INTA and INTB, use the 16-bit value (both ports a and b): print(mcp.interrupt_flag) # each time you read this property, it is cleared. It is also cleared if you read the GPIO values. p5 = Pin(5, Pin.IN, Pin.PULL_DOWN)p5.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=int1)

You probably don't need garbage collection inside the interrupt handler. The interrupt handler has a pin argument, so you don't need to call Pin(5) to get a reference inside.

The mcp.interrupt_triggered_gpio(1) method is the same as calling mcp.portb.interrupt_flag.

The mcp.interrupt_captured_gpio(0) method is the same as calling mcp.porta.interrupt_captured.

If you want to use Pin.PULL_UP on p5, set interrupt_polarity=1 on the mcp.

The mcp clears interrupts when the flag or gpio are read. You probably don't need the pin.irq() to react on both rising and falling edges. Rising is enough as the int is cleared as soon as it is read.

Use mcp.interrupt_compare_default to make the mcp fire an int when the GPIO a) changes state compared to its previous state, or b) compared to the value in mcp.default_value. If you don't have a specific use case for this, use the default mcp.interrupt_compare_default = 0x0000

— Reply to this email directly, view it on GitHub https://github.com/mcauser/micropython-mcp23017/issues/15#issuecomment-1506693521, or unsubscribe https://github.com/notifications/unsubscribe-auth/A3VWIEIYBICLPHGNU6UALB3XA7FLPANCNFSM6AAAAAAW4YYTWQ . You are receiving this because you authored the thread.Message ID: @.***>

Guru-Solidz avatar Apr 13 '23 13:04 Guru-Solidz