From 3f29b300ef77da07b7cf3e1a136843e82d6ee90e Mon Sep 17 00:00:00 2001 From: Jelle De Vleeschouwer Date: Mon, 23 Dec 2024 12:36:41 +0100 Subject: [PATCH] caf: modules: leds: fix support GPIO-based LED devicetree definitions When CONFIG_CAF_LEDS_GPIO was chosen over CONFIG_CAF_LEDS_PWM, an assertion fails that checks whether a gpio-leds compatible node has either 1 or 3 child nodes to define multi-color LEDs. However, most Nordic development boards have 4 leds, which the CAF leds module does not support and hence breaks these builds. This commit adds support for up to a combination of 4 leds in what could be analogous to an RGBW LED. The changes are made as such that as little application code as possible is affected by the change. If user code wants to take full advantage of the full channel color support it will have to redefine the OFF, ON, ON_GO_OFF, BLINK, BLINK2, BREATH, and CLOCK effects since LED_NOCOLOR is only defined for 3 channels. Signed-off-by: Jelle De Vleeschouwer --- include/caf/led_effect.h | 56 +++++++++++++++++++++++++++++++++++---- subsys/caf/modules/leds.c | 15 ++++++----- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/include/caf/led_effect.h b/include/caf/led_effect.h index c2b5a65b3027..1651839f4cb2 100644 --- a/include/caf/led_effect.h +++ b/include/caf/led_effect.h @@ -20,13 +20,16 @@ extern "C" { #endif -#define _CAF_LED_COLOR_CHANNEL_COUNT 3 +#define _CAF_LED_COLOR_CHANNEL_COUNT 4 /** @brief Color of LED. */ struct led_color { /** Values for color channels. */ uint8_t c[_CAF_LED_COLOR_CHANNEL_COUNT]; + + /** Number of channels in color */ + size_t channel_count; }; /** @brief Single step of a LED effect. @@ -82,7 +85,7 @@ struct led_effect { */ #define LED_COLOR_ARG_PASS(...) __VA_ARGS__ -/** Create LED color initializer for LED turned on. +/** Create LED color initializer for LED with RGB channels turned on. * * @note As arguments, pass the brightness levels for every color channel. * @@ -97,14 +100,57 @@ struct led_effect { .c = { \ COLOR_BRIGHTNESS_TO_PCT(_r), \ COLOR_BRIGHTNESS_TO_PCT(_g), \ - COLOR_BRIGHTNESS_TO_PCT(_b) \ - } \ + COLOR_BRIGHTNESS_TO_PCT(_b), \ + 0 \ + }, \ + .channel_count = 3 \ +} + +/** Create LED color initializer for LED with RGBW channels turned on. + * + * @note As arguments, pass the brightness levels for every color channel. + * + * @note The macro returns the structure initializer that once expanded + * contains commas not placed in brackets. + * This means that when passed as an argument, this argument + * cannot be passed simply to another macro. + * Use @ref LED_COLOR_ARG_PASS macro for the preprocessor + * to treat it as a single argument again. + */ +#define LED_COLOR_RGBW(_r, _g, _b, _w) { \ + .c = { \ + COLOR_BRIGHTNESS_TO_PCT(_r), \ + COLOR_BRIGHTNESS_TO_PCT(_g), \ + COLOR_BRIGHTNESS_TO_PCT(_b), \ + COLOR_BRIGHTNESS_TO_PCT(_w), \ + }, \ + .channel_count = 4 \ +} + +/** Create LED color initializer for single LED color turned on. + * + * @note As arguments, pass the brightness level for the LED. + * + * @note The macro returns the structure initializer that once expanded + * contains commas not placed in brackets. + * This means that when passed as an argument, this argument + * cannot be passed simply to another macro. + * Use @ref LED_COLOR_ARG_PASS macro for the preprocessor + * to treat it as a single argument again. + */ +#define LED_COLOR_SINGLE(_c) { \ + .c = { \ + COLOR_BRIGHTNESS_TO_PCT(_c), \ + 0, 0, 0 \ + }, \ + .channel_count = 1 \ } /** Create LED color initializer for LED turned off. */ #define LED_NOCOLOR() { \ - .c = {0, 0, 0} \ + .c = {0, 0, 0, 0}, \ + .channel_count = 3 \ } /** Create LED turned on effect initializer. diff --git a/subsys/caf/modules/leds.c b/subsys/caf/modules/leds.c index dd0a52ea0f57..9226fde1fd3e 100644 --- a/subsys/caf/modules/leds.c +++ b/subsys/caf/modules/leds.c @@ -65,10 +65,10 @@ static int set_color_one_channel(struct led *led, struct led_color *color) /* For a single color LED convert color to brightness. */ unsigned int brightness = 0; - for (size_t i = 0; i < ARRAY_SIZE(color->c); i++) { + for (size_t i = 0; i < color->channel_count; i++) { brightness += color->c[i]; } - brightness /= ARRAY_SIZE(color->c); + brightness /= color->channel_count; return led_set_brightness(led->dev, 0, brightness); } @@ -77,7 +77,7 @@ static int set_color_all_channels(struct led *led, struct led_color *color) { int err = 0; - for (size_t i = 0; (i < ARRAY_SIZE(color->c)) && !err; i++) { + for (size_t i = 0; (i < led->color_count) && !err; i++) { err = led_set_brightness(led->dev, i, color->c[i]); } @@ -88,7 +88,7 @@ static void set_color(struct led *led, struct led_color *color) { int err; - if (led->color_count == ARRAY_SIZE(color->c)) { + if (led->color_count == color->channel_count) { err = set_color_all_channels(led, color); } else { err = set_color_one_channel(led, color); @@ -117,7 +117,10 @@ static void work_handler(struct k_work *work) __ASSERT_NO_MSG(effect_step->substep_count > 0); int substeps_left = effect_step->substep_count - led->effect_substep; - for (size_t i = 0; i < ARRAY_SIZE(led->color.c); i++) { + __ASSERT_NO_MSG(effect_step->color.channel_count <= ARRAY_SIZE(effect_step->color.c)); + + led->color.channel_count = effect_step->color.channel_count; + for (size_t i = 0; i < effect_step->color.channel_count; i++) { int diff = (effect_step->color.c[i] - led->color.c[i]) / substeps_left; led->color.c[i] += diff; @@ -184,7 +187,7 @@ static int leds_init(void) for (size_t i = 0; (i < ARRAY_SIZE(leds)) && !err; i++) { struct led *led = &leds[i]; - __ASSERT_NO_MSG((led->color_count == 1) || (led->color_count == 3)); + __ASSERT_NO_MSG((led->color_count <= ARRAY_SIZE(led->color.c))); if (!device_is_ready(led->dev)) { LOG_ERR("Device %s is not ready", led->dev->name);