From 1d1dc09ca3a12b443cb69f85424f98ceba8c8e40 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Sun, 6 Apr 2025 16:29:19 +0100 Subject: [PATCH] leds: add arduino,modulino-smartleds Add an led_strip driver for the modulino smartleds module. This is a pluggable I2C board with 8 addressable RGB LEDs The I2C protocol is implemented on an microcontroller on the modulino board itself, the firmware for that is open source and can be updated using an Arduino sketch: Link: https://github.com/arduino/node_modulino_firmware Link: https://github.com/arduino-libraries/Modulino Signed-off-by: Fabio Baltieri --- drivers/led_strip/CMakeLists.txt | 1 + drivers/led_strip/Kconfig | 2 + drivers/led_strip/Kconfig.modulino | 10 ++ drivers/led_strip/modulino_smartleds.c | 119 ++++++++++++++++++ .../led_strip/arduino,modulino-smartleds.yaml | 12 ++ 5 files changed, 144 insertions(+) create mode 100644 drivers/led_strip/Kconfig.modulino create mode 100644 drivers/led_strip/modulino_smartleds.c create mode 100644 dts/bindings/led_strip/arduino,modulino-smartleds.yaml diff --git a/drivers/led_strip/CMakeLists.txt b/drivers/led_strip/CMakeLists.txt index b293782e620..9d7bf6ffcbe 100644 --- a/drivers/led_strip/CMakeLists.txt +++ b/drivers/led_strip/CMakeLists.txt @@ -10,3 +10,4 @@ zephyr_library_sources_ifdef(CONFIG_WS2812_STRIP_I2S ws2812_i2s.c) zephyr_library_sources_ifdef(CONFIG_WS2812_STRIP_RPI_PICO_PIO ws2812_rpi_pico_pio.c) zephyr_library_sources_ifdef(CONFIG_TLC5971_STRIP tlc5971.c) zephyr_library_sources_ifdef(CONFIG_TLC59731_STRIP tlc59731.c) +zephyr_library_sources_ifdef(CONFIG_MODULINO_SMARTLEDS modulino_smartleds.c) diff --git a/drivers/led_strip/Kconfig b/drivers/led_strip/Kconfig index 3be80de4729..2e0c54b1d0b 100644 --- a/drivers/led_strip/Kconfig +++ b/drivers/led_strip/Kconfig @@ -37,4 +37,6 @@ source "drivers/led_strip/Kconfig.tlc5971" source "drivers/led_strip/Kconfig.tlc59731" +source "drivers/led_strip/Kconfig.modulino" + endif # LED_STRIP diff --git a/drivers/led_strip/Kconfig.modulino b/drivers/led_strip/Kconfig.modulino new file mode 100644 index 00000000000..e60f2ac2493 --- /dev/null +++ b/drivers/led_strip/Kconfig.modulino @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Google, LLC +# SPDX-License-Identifier: Apache-2.0 + +config MODULINO_SMARTLEDS + bool "Arduino Modulino smart LEDs" + default y + depends on DT_HAS_ARDUINO_MODULINO_SMARTLEDS_ENABLED + select I2C + help + Enable driver Arduino Modulino smart LEDs. diff --git a/drivers/led_strip/modulino_smartleds.c b/drivers/led_strip/modulino_smartleds.c new file mode 100644 index 00000000000..220a88799e5 --- /dev/null +++ b/drivers/led_strip/modulino_smartleds.c @@ -0,0 +1,119 @@ +/* + * Copyright 2025 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT arduino_modulino_smartleds + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(modulino_smartleds, CONFIG_LED_STRIP_LOG_LEVEL); + +#define MODULINO_SMARTLEDS_NUM_LEDS 8 + +/* This is a strip of LC8822 driven by the microcontroller on the Modulino + * board, the start frame is sent automatically, the rest uses the LC8822 + * protocol: + * - 4x "1" marker bits + * - 5x brightness bits + * - 3x bytes for B, G, R + */ + +#define MODULINO_SMARTLEDS_MARKER (0xe0 << 24) +#define MODULINO_SMARTLEDS_FULL_BRIGHTNESS (0x1f << 24) + +struct modulino_smartleds_config { + struct i2c_dt_spec bus; +}; + +struct modulino_smartleds_data { + uint32_t buf[MODULINO_SMARTLEDS_NUM_LEDS]; +}; + +static int modulino_smartleds_update_rgb(const struct device *dev, + struct led_rgb *pixels, + size_t count) +{ + const struct modulino_smartleds_config *cfg = dev->config; + struct modulino_smartleds_data *data = dev->data; + int ret; + + if (count > MODULINO_SMARTLEDS_NUM_LEDS) { + return -EINVAL; + } + + for (uint8_t i = 0; i < count; i++) { + data->buf[i] = sys_cpu_to_be32( + MODULINO_SMARTLEDS_MARKER | + MODULINO_SMARTLEDS_FULL_BRIGHTNESS | + (pixels[i].b << 16) | + (pixels[i].g << 8) | + pixels[i].r); + } + + ret = i2c_write_dt(&cfg->bus, (uint8_t *)data->buf, sizeof(data->buf)); + if (ret < 0) { + LOG_ERR("i2c write error: %d", ret); + return ret; + } + + return 0; +} + +static size_t modulino_smartleds_length(const struct device *dev) +{ + return MODULINO_SMARTLEDS_NUM_LEDS; +} + +static int modulino_smartleds_init(const struct device *dev) +{ + const struct modulino_smartleds_config *cfg = dev->config; + struct modulino_smartleds_data *data = dev->data; + int ret; + + if (!i2c_is_ready_dt(&cfg->bus)) { + LOG_ERR("Bus device is not ready"); + return -ENODEV; + } + + for (uint8_t i = 0; i < ARRAY_SIZE(data->buf); i++) { + data->buf[i] = sys_cpu_to_be32(MODULINO_SMARTLEDS_MARKER); + } + + /* Reset to all LEDs off */ + ret = i2c_write_dt(&cfg->bus, (uint8_t *)data->buf, sizeof(data->buf)); + if (ret < 0) { + LOG_ERR("i2c write error: %d", ret); + return ret; + } + + return 0; +} + +static DEVICE_API(led_strip, modulino_smartleds_api) = { + .update_rgb = modulino_smartleds_update_rgb, + .length = modulino_smartleds_length, +}; + +#define MODULINO_SMARTLEDS_INIT(inst) \ + static const struct modulino_smartleds_config \ + modulino_smartleds_cfg_##inst = { \ + .bus = I2C_DT_SPEC_INST_GET(inst), \ + }; \ + \ + static struct modulino_smartleds_data modulino_smartleds_data_##inst; \ + \ + DEVICE_DT_INST_DEFINE(inst, modulino_smartleds_init, NULL, \ + &modulino_smartleds_data_##inst, \ + &modulino_smartleds_cfg_##inst, \ + POST_KERNEL, CONFIG_LED_STRIP_INIT_PRIORITY, \ + &modulino_smartleds_api); + + +DT_INST_FOREACH_STATUS_OKAY(MODULINO_SMARTLEDS_INIT) diff --git a/dts/bindings/led_strip/arduino,modulino-smartleds.yaml b/dts/bindings/led_strip/arduino,modulino-smartleds.yaml new file mode 100644 index 00000000000..884f461775e --- /dev/null +++ b/dts/bindings/led_strip/arduino,modulino-smartleds.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Google, LLC +# SPDX-License-Identifier: Apache-2.0 + +description: Arduino Modulino smart LEDs + +compatible: "arduino,modulino-smartleds" + +include: [led-strip.yaml, i2c-device.yaml] + +properties: + reg: + required: true