Edge connectors and pin numbers


Frank Duignan
 

I'm working with the BBC Microbit V2 and can use the peripherals using the NRF port pin numbers etc.  These port pins are brought to an edge connector which numbers them completely differently.  There seems to be some support for this in the bbc_microbit_v2 dts file in the following form:
  edge_connector: connector {
compatible = "microbit,edge-connector";
#gpio-cells = <2>;
gpio-map-mask = <0xffffffff 0xffffffc0>;
gpio-map-pass-thru = <0 0x3f>;
gpio-map = <0 0 &gpio0 2 0>, /* P0 */
   <1 0 &gpio0 3 0>, /* P1 */
   <2 0 &gpio0 4 0>, /* P2 */
   <3 0 &gpio0 31 0>, /* P3 */
   <4 0 &gpio0 28 0>, /* P4 */
   <5 0 &gpio0 14 0>, /* P5 */
   <6 0 &gpio1 5 0>, /* P6 */
   <7 0 &gpio0 11 0>, /* P7 */
   <8 0 &gpio0 10 0>, /* P8 */
   <9 0 &gpio0 9 0>, /* P9 */
   <10 0 &gpio0 30 0>, /* P10 */
   <11 0 &gpio0 23 0>, /* P11 */
   <12 0 &gpio0 12 0>, /* P12 */
   <13 0 &gpio0 17 0>, /* P13 */
   <14 0 &gpio0 1 0>, /* P14 */
   <15 0 &gpio0 13 0>, /* P15 */
   <16 0 &gpio1 2 0>, /* P16 */
   <19 0 &gpio0 26 0>, /* P19 */
   <20 0 &gpio1 0 0>; /* P20 */
};
};

My question is: how do I get a binding to one/all of these i/o pins.  I've tried many many combinations of DT_ macros.  The following returns a non null value for "edge" but I can't figure out how to access the pins on that edge connector using the connector pin  numbering scheme.
struct device *edge; edge=DEVICE_DT_GET(DT_NODELABEL(edge_connector));
It's not a show stopper as I can revert to nordic pin numbering but it would be nice if the numbers in the C code corresponded to the label on the board.


Bolivar, Marti
 

Hi,

"Frank Duignan via lists.zephyrproject.org"
<frank.duignan=gmail.com@lists.zephyrproject.org> writes:

I'm working with the BBC Microbit V2 and can use the peripherals using the NRF port pin numbers etc.  These port pins are brought to an edge connector which numbers them completely differently.  There seems to be some support for this in the bbc_microbit_v2 dts file in the following form:
edge_connector: connector {
compatible = "microbit,edge-connector";
#gpio-cells = <2>;
gpio-map-mask = <0xffffffff 0xffffffc0>;
gpio-map-pass-thru = <0 0x3f>;
gpio-map = <0 0 &gpio0 2 0>, /* P0 */
<1 0 &gpio0 3 0>, /* P1 */
<2 0 &gpio0 4 0>, /* P2 */
<3 0 &gpio0 31 0>, /* P3 */
<4 0 &gpio0 28 0>, /* P4 */
<5 0 &gpio0 14 0>, /* P5 */
<6 0 &gpio1 5 0>, /* P6 */
<7 0 &gpio0 11 0>, /* P7 */
<8 0 &gpio0 10 0>, /* P8 */
<9 0 &gpio0 9 0>, /* P9 */
<10 0 &gpio0 30 0>, /* P10 */
<11 0 &gpio0 23 0>, /* P11 */
<12 0 &gpio0 12 0>, /* P12 */
<13 0 &gpio0 17 0>, /* P13 */
<14 0 &gpio0 1 0>, /* P14 */
<15 0 &gpio0 13 0>, /* P15 */
<16 0 &gpio1 2 0>, /* P16 */
<19 0 &gpio0 26 0>, /* P19 */
<20 0 &gpio1 0 0>; /* P20 */
};
};
Yes, this is a GPIO nexus node. For details on these, see "2.5 Nexus
Nodes and Specifier Mapping" in the DT spec v0.3:

https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.3

Nexus nodes are weird and can be confusing to deal with at first.

I've been meaning to improve the documentation for these for a while,
but hopefully the following reply will help, with extra details for the
sake of the list archives.

My question is: how do I get a binding to one/all of these i/o pins.  I've tried many many combinations of DT_ macros.  The following returns a non null value for "edge" but I can't figure out how to access the pins on that edge connector using the connector pin  numbering scheme.
struct device *edge; edge=DEVICE_DT_GET(DT_NODELABEL(edge_connector));
Right, this doesn't work because there is no driver which allocates
struct devices for the "microbit,edge-connector" compatible.

I'm going to shamelessly keep plugging my recent talk which explains how
all this works in more detail:

https://www.youtube.com/watch?v=sWaxQyIgEBY

Since it's not a GPIO controller node, but a nexus node, there isn't a
single GPIO device in C which corresponds to it. All you can really do
with it is specify GPIOs in other properties, which will map to the
underlying GPIO controller/pin/flags/etc according to the gpio-map and
other properties.

I said it was confusing at first, right?

It's not a show stopper as I can revert to nordic pin numbering but it
would be nice if the numbers in the C code corresponded to the label
on the board.
Here is a simple example of how you can use this from C with
/zephyr,user and struct gpio_dt_spec, with error handling omitted for
brevity. (See the DT bindings guide for more on /zephyr,user.)

app.overlay:

/ {
zephyr,user {
/* P1, active high */
foo-gpios = <&edge_connector 1 GPIO_ACTIVE_HIGH>;
};
};

main.c:

#include <zephyr.h>
#include <sys/printk.h>
#include <devicetree.h>
#include <drivers/gpio.h>

static struct gpio_dt_spec foo =
GPIO_DT_SPEC_GET(DT_PATH(zephyr_user), foo_gpios);

void main(void)
{
printk("Using device %s pin %d\n",
foo.port->name, foo.pin);

gpio_pin_configure_dt(&foo, GPIO_OUTPUT);
while (1) {
gpio_pin_set_dt(&foo, 0);
k_msleep(10);
gpio_pin_set_dt(&foo, 1);
k_msleep(10);
}
}

This should print "Using device GPIO_0 pin 3", and toggle that GPIO
forever. I don't have your hardware, so I can't test, but something
similar works on nRF52-DK with the arduino_header nexus node.

Notice how a full specification of a pin requires GPIO flags in addition
to the controller/pin number. See include/dt-bindings/gpio/gpio.h for
other flags you can use besides GPIO_ACTIVE_HIGH.

This is another case of the devicetree "vanishing" like I described in
my talk -- the edge_connector node itself has no representation as a
device, but you can use it at compile time to find the "real" devices
(foo.port in this case) that are broken out onto the connector.

If you want to get fancy with multiple pins, you can do something like
this:

foo-gpios = <&edge_connector 0 GPIO_ACTIVE_HIGH>, /* P0 */
<&edge_connector 1 GPIO_ACTIVE_LOW>, /* P1, opposite polarity */
...;

and then:

#define GET_PIN_N(node_id, prop, n) GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, n),

static struct gpio_dt_spec foos[] = {
DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), foo_gpios, GET_PIN_N)
};

for (int i = 0; i < ARRAY_SIZE(foos); i++) {
printk("device %s pin %d\n", foos[i].port->name, foos[i].pin);
gpio_pin_configure_dt(&foos[i], GPIO_OUTPUT);
}

etc.

HTH,
Marti