[riot-notifications] [RIOT-OS/RIOT] periph/gpio: enable API usage for expanders (#9690)

Gunar Schorcht notifications at github.com
Wed Sep 25 10:40:39 CEST 2019

@kaspar030 After spending some time in recent weeks changing a lot of code to try your suggested approach, I am at a point where I would be very grateful for your help :sunglasses: Maybe you have an idea how to solve the problem or get around it.

What we have are CPU specific or default GPIO definitions that usually look like:
#define GPIO_CPU_PIN(x, y)  (x << 4 | y)
#define GPIO_CPU_UNDEF      ((gpio_cpu_t)(UINT_MAX))

enum {
    PORT_A = 0,
    PORT_B = 1,

typedef unsigned gpio_cpu_t;
In a first step, I renamed all `gpio_*` and `GPIO_*` definitions to `gpio_cpu_*` and `GPIO_CPU_*`, respectively.  To keep the existing code with a lot of binary operations working, CPU peripheral drivers in `cpu/*/periph` and peripheral configurations in `bords/*/periph_conf*.h` use only `gpio_cpu_*` and `GPIO_CPU_*`. From my point of view, it makes sense that CPU peripheral drivers and peripheral configurations use only CPU GPIOs.

According to your proposed approach and the work of @ZetaR60 ZetaR60, I then defined the GPIO extension API as follows:
#define GPIO_EXT_PORTS      (0x80)           /**< default definition, can be overriden by CPU */
#define GPIO_EXT_PIN(x, y)  ((gpio_t){ .dev = &gpio_ext[x - GPIO_EXT_PORTS], .pin.ext_pin = y })

#define GPIO_UNDEF          { .dev = 0, .pin.cpu_pin = GPIO_CPU_UNDEF }
#define GPIO_PIN(x, y)      ((x < GPIO_EXT_PORTS) ? (gpio_t){ .dev=0, .pin.cpu_pin=GPIO_CPU_PIN(x, y) } \
                                                  : GPIO_EXT_PIN(x, y))

enum {

typedef uint8_t gpio_ext_t;

typedef struct gpio_ext_driver {
    /** extension driver functions */
} gpio_ext_driver_t;

typedef struct gpio_ext_dev {
    const gpio_ext_driver_t *driver;    /**< pointer to extension device driver */
    void *dev;                          /**< pointer to extension device descriptor */
} gpio_ext_dev_t;

typedef struct {
    const gpio_ext_dev_t *dev;  /**< extension device, 0 in case of CPU pin */
    union {
        gpio_cpu_t cpu_pin;     /**< pin number in case of CPU GPIO pin */
        gpio_ext_t ext_pin;     /**< pin number in case of GPIO exension device */
    } pin;
} gpio_t;
where `GPIO_EXT_PORTS` is any port number greater than the highest CPU port number and the application defines the extender configuration, for example:
static const gpio_ext_dev_t gpio_ext[] =
        .driver = &extend_gpio_driver1,
        .dev = (void *)0xbeef,
        .driver = &extend_gpio_driver2,
        .dev = (void *)0xf00,
The goal was to use GPIOs of CPU and extenders in same way in board configurations, e.g., for LED pins:
#define LED0_PIN = GPIO_PIN(PORT_A, 1);
#define LED1_PIN = GPIO_PIN(PORT_EXT_B, 2);
or driver configurations.

The problem is the `GPIO_PIN` macro. While it works perfectly for constant `gpio_t` variables like
static const gpio_t pin1 = GPIO_PIN(PORT_A, 1);
static const gpio_t pin2 = GPIO_PIN(PORT_EXT_A, 2);
it doesn't work for constant structures with `gpio_t` members like
typedef struct {
    gpio_t pin1;
    gpio_t pin2;
} foo_config_t;

static const foo_config_t cfg = {
    .pin1 = GPIO_PIN(PORT_B, 3),
    .pin2 = GPIO_PIN(PORT_EXT_B, 4)
because the compiler complains that the initializer isn't a constant. However, we have a lot of driver configuration structures with `gpio_t` members.

Changing the code in all `cpu/*/periph/*.c` to use a structure for `gpio_t` with `port` and `pin` fields instead of scalar types to have only one `GPIO_PIN` macro, is probably unrealistic and to error-prone.

Do you have any idea?

You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.riot-os.org/pipermail/notifications/attachments/20190925/b71b1911/attachment.htm>

More information about the notifications mailing list