Using MCP23XX gpio expander interrupts with nRF5340

Hello,

I am having some trouble using Zephyr I2C microchip,mcp230xx gpio expander interrupts with nRF5340 DK. I am using nRF Connect v2.3.0, and it appears that the devicetree parameter int-gpios is not yet merged into the current nRF Connect version. I am able to read the the pins using polling, but would like to get the interrupts working.

I have attempted copying over the following files to my workspace

  • microchip,mcp230xx.yaml
  • microchip,mcp23xxx.yaml
  • gpio_mcp230xx.c
  • gpio_mcp23xxx.c
  • gpio_mcp230xx.h
  • Kconfig.mcp23xxx

and updated

  • gpio/Kconfig
  • gpio/Kconfig.mcp23xxx
  • gpio/CMakeLists.txt

however, I still do not understand how to implement the interrupt in my main.c code.

I have read over this previous case, https://devzone.nordicsemi.com/f/nordic-q-a/94886/gpio-p1-xx-interrupt-configuration, but it appears that it uses gpio_interrupt, which is implemented differently than the "int-gpios" used within the dts file for mcp230xx.

I have included my code in the attachments.

Best regards,

Victor

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/i2c.h>

#define I2C1_NODE DT_NODELABEL(mcp23017_20)
static const struct i2c_dt_spec gpioExpander = I2C_DT_SPEC_GET(I2C1_NODE);

#define INT_NODE DT_PHANDLE(DT_PATH(soc, peripheral_40000000, i2c_9000, mcp23017_20), interrupt_gpios)

/* STEP 9 - Increase the sleep time from 100ms to 10 minutes  */
#define SLEEP_TIME_MS   10*60*1000

/* SW0_NODE is the devicetree node identifier for the node with alias "sw0" */
#define SW0_NODE	DT_ALIAS(gpioext20p0) 
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(SW0_NODE, gpios);

/* LED0_NODE is the devicetree node identifier for the node with alias "led0". */
#define LED0_NODE	DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);


/* STEP 4 - Define the callback function */
void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    gpio_pin_toggle_dt(&led);
}
/* STEP 5 - Define a variable of type static struct gpio_callback */
static struct gpio_callback button_cb_data;

void main(void)
{
	int ret;
	
	if(!device_is_ready(gpioExpander.bus)) {
		printk("I2C bus %s is not ready!\n\r", gpioExpander.bus->name);
		return;
	}

	if (!device_is_ready(led.port)) {
		return;
	}

	if (!device_is_ready(button.port)) {
		return;
	}

	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
	if (ret < 0) {
		return;
	}

	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
	if (ret < 0) {
		return;
	}
	/* STEP 3 - Configure the interrupt on the button's pin */
	ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE );

	/* STEP 6 - Initialize the static struct gpio_callback variable   */
    gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin)); 	
	
	/* STEP 7 - Add the callback function by calling gpio_add_callback()   */
	 gpio_add_callback(button.port, &button_cb_data);
	 
	while (1) {
		/* STEP 8 - Remove the polling code */

        k_msleep(SLEEP_TIME_MS);
	}
}

/ {
    gpio_external {
        compatible = "gpio-keys";
        mcp23017_20p0: mcp23017_20p0 {
            gpios = <&mcp23017_20 0 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p1: mcp23017_20p1 {
            gpios = <&mcp23017_20 1 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p2: mcp23017_20p2 {
            gpios = <&mcp23017_20 2 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p3: mcp23017_20p3 {
            gpios = <&mcp23017_20 3 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p4: mcp23017_20p4 {
            gpios = <&mcp23017_20 4 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p5: mcp23017_20p5 {
            gpios = <&mcp23017_20 5 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p6: mcp23017_20p6 {
            gpios = <&mcp23017_20 6 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p7: mcp23017_20p7 {
            gpios = <&mcp23017_20 7 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p8: mcp23017_20p8 {
            gpios = <&mcp23017_20 8 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p9: mcp23017_20p9 {
            gpios = <&mcp23017_20 9 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p10: mcp23017_20p10 {
            gpios = <&mcp23017_20 10 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p11: mcp23017_20p11 {
            gpios = <&mcp23017_20 11 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p12: mcp23017_20p12 {
            gpios = <&mcp23017_20 12 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p13: mcp23017_20p13 {
            gpios = <&mcp23017_20 13 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p14: mcp23017_20p14 {
            gpios = <&mcp23017_20 14 GPIO_ACTIVE_HIGH>;
        };
        mcp23017_20p15: mcp23017_20p15 {
            gpios = <&mcp23017_20 15 GPIO_ACTIVE_HIGH>;
        };
    };

    aliases {
        gpioext20p0 = &mcp23017_20p0;
        gpioext20p1 = &mcp23017_20p1;
        gpioext20p2 = &mcp23017_20p2;
        gpioext20p3 = &mcp23017_20p3;
        gpioext20p4 = &mcp23017_20p4;
        gpioext20p5 = &mcp23017_20p5;
        gpioext20p6 = &mcp23017_20p6;
        gpioext20p7 = &mcp23017_20p7;
        gpioext20p8 = &mcp23017_20p8;
        gpioext20p9 = &mcp23017_20p9;
        gpioext20p10 = &mcp23017_20p10;
        gpioext20p11 = &mcp23017_20p11;
        gpioext20p12 = &mcp23017_20p12;
        gpioext20p13 = &mcp23017_20p13;
        gpioext20p14 = &mcp23017_20p14;
        gpioext20p15 = &mcp23017_20p15;
    };
};

&i2c1 {
    mcp23017_20: mcp23017@20 {
        compatible = "microchip,mcp230xx";
        reg = < 0x20 >;
        gpio-controller;
        ngpios = < 16 >;
        #gpio-cells = < 2 >;
        int-gpios = <&gpio0 4 (GPIO_PULL_UP | GPIO_ACTIVE_HIGH)>;
    };
};

CONFIG_GPIO=y
CONFIG_I2C=y
CONFIG_GPIO_MCP230XX=y

  • I have since followed the manual installation instructions https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/getting_started/installing.html to get the main branch of nRF Connect SDK, containing the recent merges with mcp230xx interrupts. I updated my main.c as follows

    /*
     * Copyright (c) 2016 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     * Note: 
     * Tested on nRF Connect SDK Version : 2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/gpio.h>
    #include <zephyr/drivers/i2c.h>
    
    #define I2C1_NODE DT_NODELABEL(mcp23017_20)
    static const struct i2c_dt_spec gpioExpander = I2C_DT_SPEC_GET(I2C1_NODE);
    
    #define INT_NODE DT_PHANDLE(DT_NODELABEL(mcp23017_20), int_gpios)
    const struct device *int_dev = DEVICE_DT_GET(INT_NODE);
    static const struct gpio_dt_spec int_gen = GPIO_DT_SPEC_GET_BY_IDX(DT_NODELABEL(mcp23017_20), int_gpios, 0);
    static struct gpio_callback int_cb_data;
    void interrupt_service(const struct device *dev, struct gpio_callback *cb,
    		    uint32_t pins)
    {
    	printk("I2C interrupt triggered\n");
    }
    
    /* STEP 9 - Increase the sleep time from 100ms to 10 minutes  */
    #define SLEEP_TIME_MS   10*60*1000
    
    /* SW0_NODE is the devicetree node identifier for the node with alias "sw0" */
    // #define SW0_NODE	DT_ALIAS(gpioext20p0) 
    #define SW0_NODE	DT_ALIAS(sw0) 
    static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(SW0_NODE, gpios);
    
    /* LED0_NODE is the devicetree node identifier for the node with alias "led0". */
    #define LED0_NODE	DT_ALIAS(led0)
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    
    
    /* STEP 4 - Define the callback function */
    void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
    {
        gpio_pin_toggle_dt(&led);
    }
    /* STEP 5 - Define a variable of type static struct gpio_callback */
    static struct gpio_callback button_cb_data;
    
    void main(void)
    {
    	int ret;
    	
    	if(!device_is_ready(gpioExpander.bus)) {
    		printk("I2C bus %s is not ready!\n\r", gpioExpander.bus->name);
    		return;
    	}
    
    	if (!device_is_ready(led.port)) {
    		return;
    	}
    
    	if (!device_is_ready(button.port)) {
    		return;
    	}
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return;
    	}
    
    	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
    	if (ret < 0) {
    		return;
    	}
    	/* STEP 3 - Configure the interrupt on the button's pin */
    	ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE );
    
    	/* STEP 6 - Initialize the static struct gpio_callback variable   */
        gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin)); 	
    	
    	/* STEP 7 - Add the callback function by calling gpio_add_callback()   */
    	 gpio_add_callback(button.port, &button_cb_data);
    
    	///////////////NEW INTERRUPT///////////////////////////////////////////////////////////////
    	ret = gpio_pin_configure_dt(&int_gen, GPIO_INPUT);
    	//ret = gpio_pin_configure(int_dev, MY_DEV_IRQ, GPIO_INPUT);
    	if (ret != 0) {
    		printk("Error ");
    		return;
    	}
    	
    	ret = gpio_pin_interrupt_configure_dt(&int_gen, GPIO_INT_EDGE_TO_ACTIVE);
    	//ret = gpio_pin_interrupt_configure(int_dev, MY_DEV_IRQ, GPIO_INT_EDGE_RISING);
    	if (ret != 0) {
    		printk("Error ");
    		return;
    	}
    
    	gpio_init_callback(&int_cb_data, interrupt_service, BIT(int_gen.pin));
    	gpio_add_callback(int_gen.port, &int_cb_data);
    	printk("Set up button at %s pin %d\n", int_gen.port->name, int_gen.pin);
    
    	///////////////NEW INTERRUPT END///////////////////////////////////////////////////////////
    	 
    	while (1) {
    		/* STEP 8 - Remove the polling code */
    
            k_msleep(SLEEP_TIME_MS);
    	}
    }

    It builds and flashes, but the expander interrupt still does not work. Please help me get the i2c gpio expander working.

    Thanks,

    Victor

  • Hi,

    I'm not familiar with mcp230xx devices, but I'll take a look at the driver and api, and get back to you.

  • Hi, i working with mcp23sxx and it is working

    with 

    int-gpios = <&gpio0 4  GPIO_ACTIVE_LOW>; 

    Instead of GPIO_ACTIVE_HIGH.

    Best Regards
    Lucas

  • if you have no external Pull up Resistor you have to activate it 

    here 
    ret = gpio_pin_configure_dt(&button, GPIO_INPUT | GPIO_PULL_UP);
    if (ret < 0) {
    return;
    }

    or in the devicetree

    Best Regards
    Lucas

Related