GPIO Latch not behaving consistently

Hi,

I'm trying to get latch functionality working on two GPIO pins, but cannot seem get the latch register to behave consistently.

I have my GPIOs configured using a custom DTS binding and overlay, something like this:

mbuttons { 
    compatible = "any,spdt-switch";
    rmb: switch_0 { 
        ncgpios = <&gpio1 13 0>;
        nogpios = <&gpio1 12 0>;
    };
};

In my main code, the flow is roughly as follows:

#define RMB_NODE DT_NODELABEL(rmb)

// in main
// edit: I tried both low and high sense settings, but expect low to work as I need it to here
nrf_gpio_cfg_sense_input(NRF_DT_GPIOS_TO_PSEL(RMB_NODE, nogpios), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
nrf_gpio_cfg_sense_input(NRF_DT_GPIOS_TO_PSEL(RMB_NODE, ncgpios), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);

uint32_t p_mask;

while(1) {
    // I've also tried this one, with p_masks etc
    // pmask = nrf_gpio_latches_read_and_clear(0, 2, p_masks);
    
    // read latch values
    p_mask = NRF_P1->LATCH;
    
    // and clear them 
    NRF_P1->LATCH = p_mask;
    
    if (p_mask != 0){
        LOG_INF("Event: %d", p_mask);
    }
}

I get the logging statement very rarely when actuating my switch. I have the switch hooked up to a scope, which is showing behavior as I'd expect for an SPDT switch (common is GND, default NC low & NO high, on click switching to NC high and NO low, where high is ~3.4v and low is ~200mv). 

A few questions:

1. Is there anything obviously wrong here?

2. Is there a recommended workflow for working with NRF-specific functionality in NCS/Zephyr? I'm using functions from nrf_gpio.h, e.g. nrf_gpio_cfg_sense_input, etc. I couldn't find any documentation on detect/latch configuration in the context of Zephyr/NCS, just GPIOTE (which I don't necessarily need/want to use yet).

  • There is nothing obviously wrong that points out to me.

    Can you check if nrf_gpio_cfg_sense_input has actually configured the GPIO config register to enable the sense configuration on those pins? The reason I am asking to check is to see if the NRF_DT_GPIOS_TO_PSEL is being decoded correctly from your device tree config.

  • This log statement

    LOG_INF("Pin 12: %d", NRF_P1->PIN_CNF[12]);
    LOG_INF("Pin 13: %d", NRF_P1->PIN_CNF[13]);

    Yields:

    I: Pin 12: 131084
    I: Pin 13: 196620

    131084 = 0b100000000000001100
    196620 = 0b110000000000001100
    Pin 13 seems to be configured correctly for sense and pin 12 isn't (!). I'm not sure why. Forcing the config via
    NRF_P1->PIN_CNF[12] |= NRF_GPIO_PIN_SENSE_LOW << GPIO_PIN_CNF_SENSE_Pos;
    NRF_P1->PIN_CNF[13] |= NRF_GPIO_PIN_SENSE_LOW << GPIO_PIN_CNF_SENSE_Pos;
    Makes the P1.12 value match P1.13's PIN_CNF, but doesn't seem to yield a different behavior.
    I think that connect and all the other parameters for input seem to be set correctly. It's very possible I'm missing something here.
  • That is strange, 

    Can you add 

    NRF_POWER->SYSTEMOFF = 1.

    before nrf_gpio_latches_read_and_clear. Just to check if the chip power state affects the detection of the sense pins? Something like below.

    while(1) {

    NRF_POWER->SYSTEMOFF = 1.
    // I've also tried this one, with p_masks etc
    // pmask = nrf_gpio_latches_read_and_clear(0, 2, p_masks);

  • When I add this, it seems to run through my main every single time I actuate the switch, without fail.

  • I'm sorry Susheel. I started moving code to a minimally reproducible example, and it looks like some other sensor reads are affecting how this latch reading code behaves.

    while (1) {
    	// NRF_POWER->SYSTEMOFF = 1;
    	// LOG_INF("Polling sensor...");
    	struct sensor_value pos_x, pos_y;
    	sensor_sample_fetch(dev);
    	sensor_channel_get(dev, SENSOR_CHAN_POS_DX, &pos_x);
    	sensor_channel_get(dev, SENSOR_CHAN_POS_DY, &pos_y);
    
    	if (old_pos_x.val1 != pos_x.val1 || old_pos_y.val1 != pos_y.val1) {
    		LOG_INF("x: %d, y: %d", pos_x.val1, pos_y.val1);
    	}
    
    	// nrf_gpio_latches_read(0, 2, p_masks);
    
    
    	NRF_P1->LATCH = NRF_P1->LATCH;
    	(void)NRF_TIMER1->EVENTS_COMPARE[0];
    	pmask = NRF_P1->LATCH;
    
    	// uint32_t pmask = p_masks[1];
    
    	// if ((pmask & (1U << 13U)) || (pmask & (1U << 12U))){
    	if (pmask != 0){
    		LOG_INF("Event: %d", pmask);
    		pmask_old = pmask;
    	}
    
    
    	old_pos_x = pos_x;
    	old_pos_y = pos_y;
    
    	//k_sleep(K_MSEC(10));
    }

    I'm using the PMW3360 driver (https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/drivers/pmw3360.html) and am not immediately sure why this causes the latch to not behave as expected. I haven't had a chance to go through the code for the driver yet.

    Here's my full device tree:

    &pinctrl {
        spi1_default_alt: spi1_default_alt {
            group1 {
                psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
                        <NRF_PSEL(SPIM_MISO, 0, 26)>,
                        <NRF_PSEL(SPIM_MOSI, 0, 27)>;
            };
        };
    
        spi1_sleep_alt: spi1_sleep_alt {
            group1 {
                psels = <NRF_PSEL(SPIM_SCK, 0, 2)>,
                        <NRF_PSEL(SPIM_MISO, 0, 26)>,
                        <NRF_PSEL(SPIM_MOSI, 0, 27)>;
                // low-power-enable;
            };
        };
    };
    
    &spi1 {
        compatible = "nordic,nrf-spim";
        status = "okay";
        pinctrl-0 = <&spi1_default_alt>;
        pinctrl-1 = <&spi1_sleep_alt>;
        pinctrl-names = "default", "sleep";
        cs-gpios = <&gpio1 14 0>;
    
        pmw3360@0 {
                compatible = "pixart,pmw3360";
                reg = <0>;
                irq-gpios = <&gpio1 15 0>;
                spi-max-frequency = <2000000>;
                label = "PMW3360";
        };
    };
    
    / {
        mbuttons { 
            compatible = "any,spdt-switch";
            rmb: switch_0 { 
                ncgpios = <&gpio1 12 0>;
                nogpios = <&gpio1 13 0>;
            };
        };
    };


    Is it likely to be the case that, since I'm using GPIOs on port 1 for IRQ (sensor motion) & CS for the sensor, the driver is (spuriously?) handling the latch behavior for the entire port? 

    If I want to use the latch functionality in the way that I'm attempting to now, should I keep those pins I want to operate on a latch with on a totally separate port, e.g. motion sensor and other peripherals on port 0, GPIO inputs with latch on port 1? Or should there be a way I can do this on the same port while not messing with the functionality?

Related