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

Reply Children
No Data
Related