Driving the lines

We want to add the information about the 8 lines to the device tree. Let's invent a new node type to do this, we'll call it "rgb_matrix".

Add a dts/bindings/rgb_matrix.yaml file:

description: RGB matrix

compatible: "rgb_matrix"

properties:
  rows-gpios:
    type: phandle-array
    required: true

Any node identified as compatible with "rgb_matrix" in the device tree will need to have a row-gpios property in which we will store the lines information.

Let's add such a node in our board overlay boards/disco_l475_iot1.overlay which now becomes:

/ {
  dm163: dm163 {
    compatible = "siti,dm163";
    selbk-gpios = <&gpioc 5 0>;
    lat-gpios = <&gpioc 4 GPIO_ACTIVE_LOW>;
    rst-gpios = <&gpioc 3 GPIO_ACTIVE_LOW>;
    gck-gpios = <&gpiob 1 0>;
    sin-gpios = <&gpioa 4 0>;
  };

  rgb_matrix: rgb_matrix {
    compatible = "rgb_matrix";
    rows-gpios = <&gpiob 2 0>, <&gpioa 15 0>, <&gpioa 2 0>, <&gpioa 7 0>,
                 <&gpioa 6 0>, <&gpioa 5 0>, <&gpiob 0 0>, <&gpioa 3 0>;
  };
};

Using GPIO_DT_SPEC_GET_BY_IDX, we can retrieve one item from the phandle-array by its index. Here is the new content of main.c:

#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/led.h>
#include <zephyr/kernel.h>

#define DM163_NODE DT_NODELABEL(dm163)
static const struct device *dm163_dev = DEVICE_DT_GET(DM163_NODE);

#define RGB_MATRIX_NODE DT_NODELABEL(rgb_matrix)

BUILD_ASSERT(DT_PROP_LEN(RGB_MATRIX_NODE, rows_gpios) == 8);

static const struct gpio_dt_spec rows[] = {
    GPIO_DT_SPEC_GET_BY_IDX(RGB_MATRIX_NODE, rows_gpios, 0),
    GPIO_DT_SPEC_GET_BY_IDX(RGB_MATRIX_NODE, rows_gpios, 1),
    GPIO_DT_SPEC_GET_BY_IDX(RGB_MATRIX_NODE, rows_gpios, 2),
    GPIO_DT_SPEC_GET_BY_IDX(RGB_MATRIX_NODE, rows_gpios, 3),
    GPIO_DT_SPEC_GET_BY_IDX(RGB_MATRIX_NODE, rows_gpios, 4),
    GPIO_DT_SPEC_GET_BY_IDX(RGB_MATRIX_NODE, rows_gpios, 5),
    GPIO_DT_SPEC_GET_BY_IDX(RGB_MATRIX_NODE, rows_gpios, 6),
    GPIO_DT_SPEC_GET_BY_IDX(RGB_MATRIX_NODE, rows_gpios, 7),
};

int main() {
  if (!device_is_ready(dm163_dev)) {
    return -ENODEV;
  }
  for (int row = 0; row < 8; row++)
    gpio_pin_configure_dt(&rows[row], GPIO_OUTPUT_INACTIVE);
  // Set brightness to 5% for all leds so that we don't become blind
  for (int i = 0; i < 8; i++)
    led_set_brightness(dm163_dev, i, 5);
  // Animate the leds on every row and every column
  for (;;) {
    for (int row = 0; row < 8; row++) {
      gpio_pin_set_dt(&rows[row], 1);
      for (int col = 0; col < 8; col++) {
        led_on(dm163_dev, col);
        k_sleep(K_MSEC(30));
        led_off(dm163_dev, col);
      }
      gpio_pin_set_dt(&rows[row], 0);
    }
  }
}

Nice, isn't it? Notice the BUILD_ASSERT() macro which checks at compile time that the rows-gpios property contains exactly eight items.

Why have we created a new node type instead of adding a property to the "siti,dm163" node? Because this has nothing to do with the DM163 which can perfectly be used to drive a single line of led.