This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Reading GPIO with NRFX

In Nordic's Zephyr fork (sdk-zephyr) there's this GPIO driver function:

drivers/gpio/gpio_nrfx.c:202

static int gpio_nrfx_port_get_raw(const struct device *port, uint32_t *value)
{
    NRF_GPIO_Type *reg = get_port_cfg(port)->port;

    *value = nrf_gpio_port_in_read(reg);

    return 0;
}

I don't think this is the entire story of querying GPIOs, though. This appears to only query the *input* GPIOs via IN, and does not read the current state of any *output* GPIOs via OUT/OUTSET/OUTCLR. The HAL drivers actually have an additional output read API for this, nrf_gpio_port_out_read().

Looking at the registers on my nRF9160, I see:

(gdb) print/x *(NRF_GPIO_Type *)0x50842500
$8 = {RESERVED = 0x0, OUT = 0x28014002, OUTSET = 0x28014002, OUTCLR = 0x28014002, IN = 0x30000003, DIR = 0x2801401e, DIRSET = 0x2801401e, DIRCLR = 0x2801401e,
  LATCH = 0x0, DETECTMODE = 0x0, DETECTMODE_SEC = 0x0, RESERVED1 = {0x0 <repeats 54 times>, 0x1, 0x1, 0x1, 0x0 <repeats 24 times>, 0x1, 0x0, 0x0, 0x0, 0x1,
    0x0 <repeats 21 times>, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0}, PIN_CNF = {0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3,
    0x0, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x3, 0x0, 0x3, 0x2, 0x2}}

I can then turn on an LED on our board using pin 2:

(gdb) set ((NRF_GPIO_Type *)0x50842500)->OUT=0x28014006

The LED does indeed turn on, IN hasn't changed, and OUT/OUTSET/OUTCLR show the new pin state:

(gdb) print/x *(NRF_GPIO_Type *)0x50842500
$16 = {RESERVED = 0x0, OUT = 0x28014006, OUTSET = 0x28014006, OUTCLR = 0x28014006, IN = 0x30000003, DIR = 0x2801401e, DIRSET = 0x2801401e, DIRCLR = 0x2801401e,
  LATCH = 0x0, DETECTMODE = 0x0, DETECTMODE_SEC = 0x0, RESERVED1 = {0x0 <repeats 54 times>, 0x1, 0x1, 0x1, 0x1, 0x0 <repeats 23 times>, 0x1, 0x0, 0x0, 0x0, 0x1,
    0x0 <repeats 21 times>, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0}, PIN_CNF = {0x0, 0x3, 0x3, 0x3, 0x3, 0x4, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3,
    0x0, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x3, 0x0, 0x3, 0x2, 0x2}}

So clearly IN doesn't reflect the state of output pins. Since this is the only API that the Zephyr GPIO driver uses to get a current output pin state, doesn't this *have* to also take into account the current configurations of pins and their state in OUT/OUTSET/OUTCLR? Interestingly, your previous driver actually did this correctly:

static int gpio_nrfx_read(struct device *port, int access_op,
              u32_t pin, u32_t *value)
{
    NRF_GPIO_Type *reg = get_port_cfg(port)->port;
    struct gpio_nrfx_data *data = get_port_data(port);

    u32_t dir = nrf_gpio_port_dir_read(reg);
    u32_t port_in = nrf_gpio_port_in_read(reg) & ~dir;
    u32_t port_out = nrf_gpio_port_out_read(reg) & dir;
    u32_t port_val = (port_in | port_out) ^ data->inverted;

    if (access_op == GPIO_ACCESS_BY_PORT) {
        *value = port_val;
    } else {
        *value = (port_val & BIT(pin)) ? 1 : 0;
    }

    return 0;
}

but that API was removed in:

commit 1f9beb193f354d52573c43f390d880e29de7d935
Author: Peter Bigot <[email protected]>
Date:   Thu Jan 30 09:06:37 2020 -0600

    gpio: remove legacy read/write API functions

    The last external reference to these was removed when the pin
    write/read functions were deprecated.  Remove the syscall support, API
    function table entries, and implementation from all drivers.

    Signed-off-by: Peter Bigot <[email protected]>

in favor of the "raw" API in:

commit ff90b2c1d638748bf9a445848c920eb2b68a5349
Author: Piotr Mienkowski <[email protected]>
Date:   Tue Aug 6 00:56:05 2019 +0200

    drivers: gpio_nrfx: update to use new GPIO API

    Update driver code and board files to use new GPIO configuration flags
    such as GPIO_ACTIVE_LOW. Also add implementation of new port_* driver
    API as well as gpio_pin_interrupt_configure function.

    Tested on nrf52840_pca10056 board.

    Signed-off-by: Piotr Mienkowski <[email protected]>
    Signed-off-by: Peter Bigot <[email protected]>

Parents
  • I actually might be wrong about the responsibility of the Zephyr driver interface after all:

    /**
     * @brief Get logical level of all input pins in a port.
     ...

    Is there just no longer any way to query the state of an output pin using Zephyr's drivers anymore? We used to have APIs for that in v2.1, but v2.4 doesn't appear to have any method for querying an output pin any longer, only input pins.

Reply
  • I actually might be wrong about the responsibility of the Zephyr driver interface after all:

    /**
     * @brief Get logical level of all input pins in a port.
     ...

    Is there just no longer any way to query the state of an output pin using Zephyr's drivers anymore? We used to have APIs for that in v2.1, but v2.4 doesn't appear to have any method for querying an output pin any longer, only input pins.

Children
  • I asked internally and got some answers:

    "That's the way the GPIO API is defined in Zephyr. The functions gpio_port_get*() and gpio_pin_get*() return the state of input pins (previously, when gpio_pin_read() was available, it was described as reading "the data value of a single pin" or "the input state of a pin"). I don't remember now why it was decided so when the API was reworked, but I think it could be because for some devices returning the values written to outputs might be not so straightforward as it is for nRF chips and actually the module that uses the GPIO API can store the values that is sets to outputs, if that's needed, so the driver .

    It is possible (not for all devices, but for nRF chips it is) to configure a pin as both input and output (to use GPIO_INPUT | GPIO_OUTPUT in flags). Then those getting functions will return the actual state of the output pin, what normally should be what was set for that pin (unless something else is somehow able to force a different state of this pin)."

    Best regards,

    Simon

Related