Problems configuring Device Tree and GPIO for PCA9538 GPIO expander

I'm developing with an NRF52832, using NCS v2.6.1/Zephyr 3.5.99

My custom hardware has three NXP PCA9538 GPIO expanders on it, to allow me to control various address and enable pins on some other devices, plus a few status LEDs.

I seem to be having issues getting my setup to correctly talk to and configure the GPIO outputs on any of the PCA9538's.

This is how I have them set up in my .dts file for my custom board:

&i2c0 {
    status = "okay";
    compatible = "nordic,nrf-twim";
    pinctrl-0 = <&i2c0_ct_default>;
    pinctrl-1 = <&i2c0_ct_sleep>;
    pinctrl-names = "default", "sleep";

    pca9538_70: pca9538@70 {
        compatible = "nxp,pca95xx";
        reg = <0x70>;
        gpio-controller;
        #gpio-cells = <2>;
        ngpios = <8>;
	    status = "okay";

		addrenpins {
			compatible = "gpio-leds";

			enai: gpio_out_enai {
				gpios = <&pca9538_70 0 GPIO_ACTIVE_LOW>;
				label = "Enable Input A Device";
			};

			enao: gpio_out_enao {
				gpios = <&pca9538_70 1 GPIO_ACTIVE_LOW>;
				label = "Enable Output A Device";
			};

			enbi: gpio_out_enbi {
				gpios = <&pca9538_70 2 GPIO_ACTIVE_LOW>;
				label = "Enable Input B Device";
			};

			enbo: gpio_out_enbo {
				gpios = <&pca9538_70 3 GPIO_ACTIVE_LOW>;
				label = "Enable Output B Device";
			};

			enci: gpio_out_enci {
				gpios = <&pca9538_70 4 GPIO_ACTIVE_LOW>;
				label = "Enable Input C Device";
			};

			enco: gpio_out_enco {
				gpios = <&pca9538_70 5 GPIO_ACTIVE_LOW>;
				label = "Enable Output C Device";
			};

			endi: gpio_out_endi {
				gpios = <&pca9538_70 6 GPIO_ACTIVE_LOW>;
				label = "Enable Input D Device";
			};

			endo: gpio_out_endo {
				gpios = <&pca9538_70 7 GPIO_ACTIVE_LOW>;
				label = "Enable Output D Device";
			};

		};
    };

   pca9538_72: pca9538@72 {
        compatible = "nxp,pca95xx";
        reg = <0x72>;
        gpio-controller;
        #gpio-cells = <2>;
        ngpios = <8>;
	    status = "okay";

		ledpins {
			compatible = "gpio-leds";

			testactive: gpio_out_testactive {
				gpios = <&pca9538_72 0 GPIO_ACTIVE_LOW>;
				label = "Test is active";
			};

			testpass: gpio_out_testpass {
				gpios = <&pca9538_72 1 GPIO_ACTIVE_LOW>;
				label = "Test has passed";
			};

			testfail: gpio_out_testfail {
				gpios = <&pca9538_72 2 GPIO_ACTIVE_LOW>;
				label = "Test has failed";
			};
		};
    };

   pca9538_73: pca9538@73 {
        compatible = "nxp,pca95xx";
        reg = <0x73>;
        gpio-controller;
        #gpio-cells = <2>;
        ngpios = <8>;
	    status = "okay";
    };
};

Note that <&i2c0_ct_default> and <&i2c0_ct_sleep>; are set up in my .dtsi file as follows:

i2c0_ct_default: i2c0_default {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 26)>,
				<NRF_PSEL(TWIM_SCL, 0, 27)>;
		};
	};

	i2c0_ct_sleep: i2c0_sleep {
		group1 {
			psels = <NRF_PSEL(TWIM_SDA, 0, 26)>,
				<NRF_PSEL(TWIM_SCL, 0, 27)>;
			low-power-enable;
		};
	};

I then have a buch of alias in my overlay file referencing all the gpio:

aliases {
	testactive = &testactive;
	testpass = &testpass;
	testfail = &testfail;
    enai = &enai;
    enbi = &enbi;
    enci = &enci;
    endi = &endi;
    enao = &enao;
    enbo = &enbo;
    enco = &enco;
    endo = &endo;
    };

Then, in my firmware, when I set up the gpio that I want to use on my expanders, I do this:

static const struct gpio_dt_spec enable_a_in = GPIO_DT_SPEC_GET(DT_ALIAS(enai), gpios);
static const struct gpio_dt_spec enable_b_in = GPIO_DT_SPEC_GET(DT_ALIAS(enbi), gpios);
static const struct gpio_dt_spec enable_c_in = GPIO_DT_SPEC_GET(DT_ALIAS(enci), gpios);
static const struct gpio_dt_spec enable_d_in = GPIO_DT_SPEC_GET(DT_ALIAS(endi), gpios);

static const struct gpio_dt_spec enable_a_out = GPIO_DT_SPEC_GET(DT_ALIAS(enao), gpios);
static const struct gpio_dt_spec enable_b_out = GPIO_DT_SPEC_GET(DT_ALIAS(enbo), gpios);
static const struct gpio_dt_spec enable_c_out = GPIO_DT_SPEC_GET(DT_ALIAS(enco), gpios);
static const struct gpio_dt_spec enable_d_out = GPIO_DT_SPEC_GET(DT_ALIAS(endo), gpios);

static const struct gpio_dt_spec test_active_led = GPIO_DT_SPEC_GET(DT_ALIAS(testactive), gpios);
static const struct gpio_dt_spec test_pass_led = GPIO_DT_SPEC_GET(DT_ALIAS(testpass), gpios);
static const struct gpio_dt_spec test_fail_led = GPIO_DT_SPEC_GET(DT_ALIAS(testfail), gpios);

And then I attempt to configure them as outputs, and set them to a starting value like this:

    CHECK_ERR(gpio_pin_configure_dt(&enable_a_in, GPIO_OUTPUT));
    CHECK_ERR(gpio_pin_configure_dt(&enable_b_in, GPIO_OUTPUT));
    CHECK_ERR(gpio_pin_configure_dt(&enable_c_in, GPIO_OUTPUT));
    CHECK_ERR(gpio_pin_configure_dt(&enable_d_in, GPIO_OUTPUT));
    CHECK_ERR(gpio_pin_configure_dt(&enable_a_out, GPIO_OUTPUT));
    CHECK_ERR(gpio_pin_configure_dt(&enable_b_out, GPIO_OUTPUT));
    CHECK_ERR(gpio_pin_configure_dt(&enable_c_out, GPIO_OUTPUT));
    CHECK_ERR(gpio_pin_configure_dt(&enable_d_out, GPIO_OUTPUT));

    CHECK_ERR(gpio_pin_set_dt(&enable_a_in, GPIO_INACTIVE));
    CHECK_ERR(gpio_pin_set_dt(&enable_b_in, GPIO_INACTIVE));
    CHECK_ERR(gpio_pin_set_dt(&enable_c_in, GPIO_INACTIVE));
    CHECK_ERR(gpio_pin_set_dt(&enable_d_in, GPIO_INACTIVE));
    CHECK_ERR(gpio_pin_set_dt(&enable_a_out, GPIO_INACTIVE));
    CHECK_ERR(gpio_pin_set_dt(&enable_b_out, GPIO_INACTIVE));
    CHECK_ERR(gpio_pin_set_dt(&enable_c_out, GPIO_INACTIVE));
    CHECK_ERR(gpio_pin_set_dt(&enable_d_out, GPIO_INACTIVE));

Note that CHECK_ERR() is a custom #define that just checks the return value and sends out some LOG info to the console if its < 0.

I seem to be having issues at the point that I call something like 

  CHECK_ERR(gpio_pin_configure_dt(&enable_a_in, GPIO_OUTPUT));
for any of the GPIO on my PCA9538, and I can't work out what the issue is.  I'm getting error -5.
I've got these settings in my proj.conf file:
CONFIG_GPIO=y
CONFIG_I2C=y
CONFIG_GPIO_PCA95XX=y
I've not used a GPIO expander IC before, but have certainly done plenty of stuff with the I2C on the nRF52832 on custom boards without issue.  I don't believe its an I2C issue, but that I am doing something wrong with the way I have my device tree set up, or the way I am trying to access the GPIO.
Can anyone help me fault find what I'm doing wrong?
Cheers,
Mike
Parents
  • Hello,

    The old thingy52 (nrf52832) have an io expander.

    If you try to compile samples/basic/blinky for the thingy52/nRF52832 then you should be able to compare I think.

    Kenneth

  • Hi Kenneth,

    OK, I seem to have made some progress.  First issue was hardware related (schematic symbol had all the wrong pin outs, so my hardware never had a hope of working!).  Have fixed that up, and have a PCA9538 in isolation, hooked up to the I2C on an nRF52DK, to remove as many variables as possible.

    I have a simple program that tries to toggle an output on the PCA9538 (see attached).  But I'm seeing this logging output (I've set the log level on the gpio_pca95xx.c file to DBG so I can see everything that's going on)

    Starting PCA9538 Demo sample
    [00:00:00.386,383] <dbg> gpio_pca95xx: write_port_reg: PCA95XX[0x70]: Write: REG[0x2] = 0xFF
    [00:00:00.386,535] <dbg> gpio_pca95xx: write_port_reg: PCA95XX[0x70]: Write: REG[0x6] = 0xFE
    [00:00:00.386,657] <err> gpio_pca95xx: PCA95XX[0x70]: error writing to register 0x6 (-5)
    [00:00:00.386,657] <err> gpio_pca95xx: PCA95XX[0x70]: error setting pin direction (-5)
    [00:00:00.386,688] <dbg> main: main: Error -5: failed to configure gpio_out pin
    [00:00:00.386,718] <dbg> gpio_pca95xx: write_port_regs: PCA95XX[0x70]: Write: REG[0x2] = 0xFE, REG[0x3] = 0xFF
    Toggled gpio_out pin
    Toggled led pin
    [00:00:01.386,993] <dbg> gpio_pca95xx: write_port_regs: PCA95XX[0x70]: Write: REG[0x2] = 0xFF, REG[0x3] = 0xFF
    Toggled gpio_out pin
    Toggled led pin
    [00:00:02.387,298] <dbg> gpio_pca95xx: write_port_regs: PCA95XX[0x70]: Write: REG[0x2] = 0xFE, REG[0x3] = 0xFF
    Toggled gpio_out pin
    Toggled led pin

    So, it seems to be having an issue writing to the pin direction register (0x06).

    In gpio_pca95xx.c, it has all the register addresses set up as follows:

    /* Register definitions */
    #define REG_INPUT_PORT0			0x00
    #define REG_INPUT_PORT1			0x01
    #define REG_OUTPUT_PORT0		0x02
    #define REG_OUTPUT_PORT1		0x03
    #define REG_POL_INV_PORT0		0x04
    #define REG_POL_INV_PORT1		0x05
    #define REG_CONF_PORT0			0x06
    #define REG_CONF_PORT1			0x07
    #define REG_OUT_DRV_STRENGTH_PORT0_L	0x40
    #define REG_OUT_DRV_STRENGTH_PORT0_H	0x41
    #define REG_OUT_DRV_STRENGTH_PORT1_L	0x42
    #define REG_OUT_DRV_STRENGTH_PORT1_H	0x43
    #define REG_INPUT_LATCH_PORT0		0x44
    #define REG_INPUT_LATCH_PORT1		0x45
    #define REG_PUD_EN_PORT0		0x46
    #define REG_PUD_EN_PORT1		0x47
    #define REG_PUD_SEL_PORT0		0x48
    #define REG_PUD_SEL_PORT1		0x49
    #define REG_INT_MASK_PORT0		0x4A
    #define REG_INT_MASK_PORT1		0x4B
    #define REG_INT_STATUS_PORT0		0x4C
    #define REG_INT_STATUS_PORT1		0x4D
    #define REG_OUTPUT_PORT_CONF		0x4F

    But if I look at the datasheet, those don't align.  It seems I actually need to be using gpio_pca953x.c, as this is how that defines the register addresses, and this aligns with the datasheet for the PCA9538

    /* PCA953X Register addresses */
    #define PCA953X_INPUT_PORT		0x00
    #define PCA953X_OUTPUT_PORT		0x01
    #define PCA953X_CONFIGURATION		0x03

    To do that, I need to set my child node compatible value to "ti,tca9538", rather than the more intuitive "nxp,pca95xx" (I'm actually using an NXP PCA9538 IC)

    If I then enable DBG logging, and run my firmware, this is what I see:

    [00:00:00.383,331] <inf> pca953x: pca9538@70 init ok
    *** PCA9538 Demo v3.5.99-ncs1 ***
    Starting PCA9538 Demo sample
    [00:00:00.383,422] <dbg> main: main: Error -134: failed to configure gpio_out pin
    [00:00:00.383,605] <dbg> pca953x: gpio_pca953x_port_write: write ff msk 00000000 val 00000000 => fe: 0
    Toggled gpio_out pin
    Toggled led pin

    So, looks like its failing one of the checks in gpio_pca953x_config() and returning -ENOTSUP.

    Not really sure where to go from here, as to me it looks like there is an issue with the drivers.

    Cheers,

    Mike

  • Hi,

    I am sorry to say , but we are not able to help debugging this issue due to the vacation period with reduced staff. Hopefully looking at examples and trial and fail approach you are able to solve it.

    Kenneth

Reply Children
No Data
Related