diff --git a/boards/st/stm32wb5mm_dk/doc/stm32wb5mm_dk.rst b/boards/st/stm32wb5mm_dk/doc/stm32wb5mm_dk.rst index a149ea79a3679..51bde0110c55d 100644 --- a/boards/st/stm32wb5mm_dk/doc/stm32wb5mm_dk.rst +++ b/boards/st/stm32wb5mm_dk/doc/stm32wb5mm_dk.rst @@ -170,6 +170,14 @@ Serial Port STM32WB5MM-DK board has 2 (LP)U(S)ARTs. The Zephyr console output is assigned to USART1. Default settings are ``115200 8N1``. +LEDs +---- +STM32WB5MM-DK has two types of LEDs, The resources coming from STM32WB5MMG are +shared between the RGB and IR LEDs. It is not possible to use them +simultaneously. The selection is done by JP4 and JP5 jumpers. +To use the RGB LED, JP5 must be ON and JP4 OFF. In this configuration, +GPIO_SELECT2 (PH1) is the chip select for this RGB device on SPI1. + Programming and Debugging ************************* diff --git a/boards/st/stm32wb5mm_dk/stm32wb5mm_dk.dts b/boards/st/stm32wb5mm_dk/stm32wb5mm_dk.dts index 4f4fb99cea013..6e48823ae242e 100644 --- a/boards/st/stm32wb5mm_dk/stm32wb5mm_dk.dts +++ b/boards/st/stm32wb5mm_dk/stm32wb5mm_dk.dts @@ -8,6 +8,7 @@ #include #include #include +#include / { model = "STMicroelectronics STM32WB5MM Discovery Development Kit"; @@ -22,11 +23,23 @@ zephyr,flash = &flash0; zephyr,code-partition = &slot0_partition; }; + + rgb_led_strip: rgb_strip { + compatible = "ti,tlc59731"; + gpios = <&gpioa 7 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + chain-length = <1>; + color-mapping = ; + status = "disabled"; + }; + aliases { watchdog0 = &iwdg; die-temp0 = &die_temp; volt-sensor0 = &vref; volt-sensor1 = &vbat; + led-strip = &rgb_led_strip; }; }; @@ -136,6 +149,15 @@ zephyr_udc0: &usb { }; }; +&gpioh { + rgb_cs: rgb_cs { + gpios = <1 GPIO_ACTIVE_HIGH>; + gpio-hog; + output-high; + status = "disabled"; + }; +}; + &vref { status = "okay"; }; diff --git a/drivers/led_strip/CMakeLists.txt b/drivers/led_strip/CMakeLists.txt index aa4e95722a716..b293782e620fb 100644 --- a/drivers/led_strip/CMakeLists.txt +++ b/drivers/led_strip/CMakeLists.txt @@ -9,3 +9,4 @@ zephyr_library_sources_ifdef(CONFIG_WS2812_STRIP_SPI ws2812_spi.c) 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) diff --git a/drivers/led_strip/Kconfig b/drivers/led_strip/Kconfig index 269657dd0b8a0..3be80de47298c 100644 --- a/drivers/led_strip/Kconfig +++ b/drivers/led_strip/Kconfig @@ -35,4 +35,6 @@ source "drivers/led_strip/Kconfig.apa102" source "drivers/led_strip/Kconfig.tlc5971" +source "drivers/led_strip/Kconfig.tlc59731" + endif # LED_STRIP diff --git a/drivers/led_strip/Kconfig.tlc59731 b/drivers/led_strip/Kconfig.tlc59731 new file mode 100644 index 0000000000000..3857c08dc5f0d --- /dev/null +++ b/drivers/led_strip/Kconfig.tlc59731 @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Javad Rahimipetroudi +# SPDX-License-Identifier: Apache-2.0 + +config TLC59731_STRIP + bool "TLC59731 LED controller" + default y + depends on DT_HAS_TI_TLC59731_ENABLED + select GPIO + help + Enable driver for the Texas Instruments TLC59731 EasySet LED + controllers. diff --git a/drivers/led_strip/tlc59731.c b/drivers/led_strip/tlc59731.c new file mode 100644 index 0000000000000..c8501156e8143 --- /dev/null +++ b/drivers/led_strip/tlc59731.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2024 Javad Rahimipetroudi + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_tlc59731 + +/** + * @file + * @brief LED driver for the TLC59731 LED driver. + * + * TLC59731 is a 3-Channel, 8-Bit, PWM LED Driver + * With Single-Wire Interface (EasySet) + * + * The EasySet protocol is based on short pulses and the time between + * them. At least one pulse must be sent every T_CYCLE, which can be + * between 1.67us and 50us. We want to go as fast as possible, but + * delays under 1us don't work very well, so we settle on 5us for the + * cycle time. + * A pulse must be high for at least 14ns. In practice, turning a GPIO on + * and immediately off again already takes longer than that, so no delay + * is needed there. + * A zero is represented by no additional pulses within a cycle. + * A one is represented by an additional pulse between 275ns and 2.5us + * (half a cycle) after the first one. We need at least some delay to get to + * 275ns, but because of the limited granularity of k_busy_wait we use a + * full 1us. After the pulse, we wait an additional T_CYCLE_1 to complete + * the cycle. This time can be slightly shorter because the second pulse + * already closes the cycle. + * Finally we need to keep the line low for T_H0 to complete the address + * for a single chip, and T_H1 to complete the write for all chips. + */ + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(tlc59731, CONFIG_LED_STRIP_LOG_LEVEL); + +/* Pulse timing */ +#define TLC59731_DELAY 0x01 /* us */ +#define TLC59731_T_CYCLE_0 0x04 /* us */ +#define TLC59731_T_CYCLE_1 0x01 /* us */ +#define TLC59731_T_H0 (4 * TLC59731_T_CYCLE_0) +#define TLC59731_T_H1 (8 * TLC59731_T_CYCLE_0) +/* Threshould levels */ +#define TLC59731_HIGH 0x01 +#define TLC59731_LOW 0x00 + +/* Write command */ +#define TLC59731_WR 0x3A + +struct tlc59731_cfg { + struct gpio_dt_spec sdi_gpio; + size_t length; +}; + +static inline int rgb_pulse(const struct gpio_dt_spec *led_dev) +{ + int fret = 0; + + fret = gpio_pin_set_dt(led_dev, TLC59731_HIGH); + if (fret != 0) { + return fret; + } + + fret = gpio_pin_set_dt(led_dev, TLC59731_LOW); + if (fret != 0) { + return fret; + } + + return fret; +} + +static int rgb_write_bit(const struct gpio_dt_spec *led_dev, uint8_t data) +{ + rgb_pulse(led_dev); + + k_busy_wait(TLC59731_DELAY); + + if (data) { + rgb_pulse(led_dev); + k_busy_wait(TLC59731_T_CYCLE_1); + } else { + k_busy_wait(TLC59731_T_CYCLE_0); + } + + return 0; +} + +static int rgb_write_data(const struct gpio_dt_spec *led_dev, uint8_t data) +{ + int8_t idx = 7; + + while (idx >= 0) { + rgb_write_bit(led_dev, data & BIT((idx--))); + } + + return 0; +} + +static int tlc59731_led_set_color(const struct device *dev, struct led_rgb *pixel) +{ + + const struct tlc59731_cfg *tlc_conf = dev->config; + const struct gpio_dt_spec *led_gpio = &tlc_conf->sdi_gpio; + + rgb_write_data(led_gpio, TLC59731_WR); + rgb_write_data(led_gpio, pixel->r); + rgb_write_data(led_gpio, pixel->g); + rgb_write_data(led_gpio, pixel->b); + + return 0; +} + +static int tlc59731_gpio_update_rgb(const struct device *dev, struct led_rgb *pixels, + size_t num_pixels) +{ + size_t i; + int err = 0; + + for (i = 0; i < num_pixels; i++) { + err = tlc59731_led_set_color(dev, &pixels[i]); + if (err) { + break; + } + } + + return err; +} + +static size_t tlc59731_length(const struct device *dev) +{ + const struct tlc59731_cfg *config = dev->config; + + return config->length; +} + +static const struct led_strip_driver_api tlc59731_gpio_api = { + .update_rgb = tlc59731_gpio_update_rgb, + .length = tlc59731_length, +}; + +static int tlc59731_gpio_init(const struct device *dev) +{ + const struct tlc59731_cfg *tlc_conf = dev->config; + const struct gpio_dt_spec *led = &tlc_conf->sdi_gpio; + int err = 0; + + if (!device_is_ready(led->port)) { + LOG_ERR("%s: no LEDs found (DT child nodes missing)", dev->name); + err = -ENODEV; + goto scape; + } + + err = gpio_pin_configure_dt(led, GPIO_OUTPUT_ACTIVE); + if (err < 0) { + LOG_ERR("%s: Unable to setup SDI port", dev->name); + err = -EIO; + goto scape; + } + + err = gpio_pin_set_dt(led, TLC59731_LOW); + if (err < 0) { + LOG_ERR("%s: Unable to set the SDI-GPIO)", dev->name); + err = -EIO; + goto scape; + } + + gpio_pin_set_dt(led, TLC59731_HIGH); + gpio_pin_set_dt(led, TLC59731_LOW); + + k_busy_wait((TLC59731_DELAY + TLC59731_T_CYCLE_0)); +scape: + return err; +} + +#define TLC59731_DEVICE(i) \ + static struct tlc59731_cfg tlc59731_cfg_##i = { \ + .sdi_gpio = GPIO_DT_SPEC_INST_GET(i, gpios), \ + .length = DT_INST_PROP(i, chain_length), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(i, tlc59731_gpio_init, NULL, NULL, &tlc59731_cfg_##i, POST_KERNEL, \ + CONFIG_LED_STRIP_INIT_PRIORITY, &tlc59731_gpio_api); +DT_INST_FOREACH_STATUS_OKAY(TLC59731_DEVICE) diff --git a/dts/bindings/led_strip/ti,tlc59731.yaml b/dts/bindings/led_strip/ti,tlc59731.yaml new file mode 100644 index 0000000000000..596bee98d165d --- /dev/null +++ b/dts/bindings/led_strip/ti,tlc59731.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Javad Rahimipetroudi +# SPDX-License-Identifier: Apache-2.0 + +compatible: "ti,tlc59731" +description: TLC59731 RGB LED Controller +include: [led-strip.yaml] + +properties: + gpios: + required: true + type: phandle-array + description: | + GPIO to send command data to the controller.