This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 885879dc429de7969aafe0d8817816b56dc7baa9 Author: Kerogit <[email protected]> AuthorDate: Wed Oct 21 01:33:19 2026 +0200 drivers/leds/ws2812: provide byte order transformation According to the datasheet, WS2812 programmable LEDs are supposed to receive green component byte first, followed by red and blue. However, there are WS2812 variants on the marker which behave differently and expect red first, then green. This patch adds configuration option that allows the user (board code author) to request that the upper half reorders bytes received from userspace before passing them to the lower half for processing. Signed-off-by: Kerogit <[email protected]> --- Documentation/os/drivers/character/leds/ws2812.rst | 55 +++++++ drivers/leds/Kconfig | 20 +++ drivers/leds/ws2812.c | 164 ++++++++++++++++++++- include/nuttx/leds/ws2812.h | 48 ++++++ 4 files changed, 285 insertions(+), 2 deletions(-) diff --git a/Documentation/os/drivers/character/leds/ws2812.rst b/Documentation/os/drivers/character/leds/ws2812.rst index 4f9f8a4486f..806d84dcd13 100644 --- a/Documentation/os/drivers/character/leds/ws2812.rst +++ b/Documentation/os/drivers/character/leds/ws2812.rst @@ -30,3 +30,58 @@ Files supporting ws2812 can be found in the following locations: in ``arch/<architecture>/src/<hardware>`` directory for the specific processor ``<architecture>`` and for the specific ``<chip>`` ws2812 peripheral devices. + +Configuration +============= + +Upper half configuration resides in :menuselection:`Device Drivers --> LED support --> WS2812 LED driver` . +Some of the configuration options are described here - mostly for the purpose of helping +with implementation of lower half drivers and board code. + +WS2812 driver variant +--------------------- + +Allows user to choose between old-style SPI driver and driver split into upper and +lower-half. The old style SPI driver could only drive the LEDs using a SPI interface, +the new style can use any hardware supported by the lower half (including SPI, +despite the naming.) + +When implementing lower half driver for new architecture, make the appropriate +Kconfig option depend on :menuselection:`WS2812_NON_SPI_DRIVER` + +The old-style driver and its associated options are not described in this document. + +Number of ws2812s connected +--------------------------- + +These LEDs are designed to be chained and this configuration option allows the user +to specify how many LEDs are in the chain. + +Do note though that this is only a suggestion for other code - for example for default +board configuration. The value set here may be ignored by the lower half and it is +not used by the upper half either. The upper half reads the LED count from ``nleds`` +element of ``struct ws2812_dev_s`` + +ws2812s have white LED +---------------------- + +Some variants of these LEDs have a white component byte. Lower half can disable support +for such devices if this configuration option is not set. + +Add byte order transformation functions +--------------------------------------- + +According to datasheets, these LEDs expect RGB data in green-red-blue order on the wire. +Data provided by the user space are formatted as 32bit pixels in RGB order +(ie. ``0x00RRGGBB``.) The lower half driver therefore needs to reorder bytes received +from the user space through the upper half to transmit them in the order the LED expects. + +However, there are variants of these LEDs available on the market which expect the RGB +data in red-green-blue order. This configuration option adds byte order transformation +functions (currently one) to the upper half driver. If requested by ``struct ws2812_dev_s`` +member ``byte_order_transformation``, chosen transformation function is called on data +received from user space before passing them to the lower half when writing. For reads, +reverse transformation is applied. + +Do note that choosing - and making use - of this option requires a buffer allocated +by lower half for every driver instance that needs this feature. diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index f84dc200724..30134d75a3c 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -217,6 +217,26 @@ config WS2812_HAS_WHITE enables support for such chips. Note that the driver does not support chains with a mix of RGB and RGBW pixels. +config WS2812_COMPONENT_TRANSFORM + bool "Add byte order transformation functions" + default n + depends on WS2812 + ---help--- + Some WS2812 variants expect pixel data in different on-wire + byte order than the usual GRB. Enable this option to compile + transformation functions that will optionally (based on device + configuration) swap bytes in read/write buffers to compensate. + +if WS2812_COMPONENT_TRANSFORM + +config WS2812_COMPONENT_TRANSFORM_RGB + bool "Transformation for RGB LED" + default n + ---help--- + Add transformation function for RGB byte order LED. + +endif # WS2812_COMPONENT_TRANSFORM + config WS2812_FREQUENCY int "WS2812 Bit Frequency (Hz)" default 800000 diff --git a/drivers/leds/ws2812.c b/drivers/leds/ws2812.c index a87ce76f867..1493dd8e970 100644 --- a/drivers/leds/ws2812.c +++ b/drivers/leds/ws2812.c @@ -233,6 +233,54 @@ static const uint8_t ws2812_gamma[256] = #ifdef CONFIG_WS2812_NON_SPI_DRIVER +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM_RGB + +/**************************************************************************** + * Name: ws2812_byte_order_transform_rgb + * + * Description: + * Transform provided buffer of pixel data (presumably coming + * from userspace) to reorder bytes for LED that expects color + * component bytes in RGB order. + * + * Considering it only swaps red and green component, it is used + * for reverse transformation as well. + * + * Input Parameters: + * src - source buffer + * dst - destination buffer + * cnt - number of PIXELS to transform + * + * Returned Value: void + * + ****************************************************************************/ + +static void ws2812_byte_order_transform_rgb(FAR const char *src, + FAR char *dst, + size_t cnt) +{ + /* Note - userspace is providing the data as an array of uint32_t, + * this array is pushed through a write() interface as a uint8_t + * buffer. We need to keep that in mind because endianness may + * force us to do different things here. + * + * Not in this case though, we are only swapping the middle two bytes + * and therefore don't care which is which. + */ + + for (; cnt; cnt--) + { + *(dst) = *(src); + *(dst + 1) = *(src + 2); + *(dst + 2) = *(src + 1); + *(dst + 3) = *(src + 3); + dst += 4; + src += 4; + } +} + +# endif + /**************************************************************************** * Name: ws2812_open * @@ -319,7 +367,63 @@ ssize_t ws2812_write(FAR struct file *filep, return -EINVAL; } - res = priv->write(filep, data, len); +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM + + /* Byte order transformation enabled but is it in use for this device? */ + + if (priv->byte_order_transformation == WS2812_COMPONENT_TRANSFORM_NONE) + { + /* It is not. The ifdef block is terminated here, taking the same + * action regardless of if byte order transformation is enabled + * or not. + * + * NOTE - with transformation set to NONE, neither byte_order_buffer + * nor byte_order_lock are required to be initialized. Neither + * may be used with this setting. + */ + +# endif /* CONFIG_WS2812_COMPONENT_TRANSFORM */ + + res = priv->write(filep, data, len); + +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM + /* Back to the ifdef block and continue the if statement */ + } + else + { + /* Byte order transformation enabled, which one is in use? + * Note - must lock, single buffer per driver instance. + */ + + nxmutex_lock(&priv->byte_order_lock); + + switch (priv->byte_order_transformation) + { +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM_RGB + case WS2812_COMPONENT_TRANSFORM_RGB: + { + ws2812_byte_order_transform_rgb( + data, + priv->byte_order_buffer, + len >> 2 + ); + res = priv->write(filep, priv->byte_order_buffer, len); + } + break; +# endif /* CONFIG_WS2812_COMPONENT_TRANSFORM_RGB */ + default: + /* Should not ever happen with enums but the compiler tends + * to complain about unhandled values. + */ + + res = priv->write(filep, data, len); + break; + } + + nxmutex_unlock(&priv->byte_order_lock); + } + +# endif /* CONFIG_WS2812_COMPONENT_TRANSFORM */ return res; } @@ -358,7 +462,63 @@ ssize_t ws2812_read(FAR struct file *filep, return -EINVAL; } - res = priv->read(filep, data, len); +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM + + /* Byte order transformation enabled but is it in use for this device? */ + + if (priv->byte_order_transformation == WS2812_COMPONENT_TRANSFORM_NONE) + { + /* It is not. The ifdef block is terminated here, taking the same + * action regardless of if byte order transformation is enabled + * or not. + * + * NOTE - with transformation set to NONE, neither byte_order_buffer + * nor byte_order_lock are required to be initialized. Neither + * may be used with this setting. + */ + +# endif /* CONFIG_WS2812_COMPONENT_TRANSFORM */ + + res = priv->read(filep, data, len); + +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM + /* Back to the ifdef block and continue the if statement */ + } + else + { + /* Byte order transformation enabled, which one is in use? + * Note - must lock, single buffer per driver instance. + */ + + nxmutex_lock(&priv->byte_order_lock); + + switch (priv->byte_order_transformation) + { +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM_RGB + case WS2812_COMPONENT_TRANSFORM_RGB: + { + res = priv->read(filep, priv->byte_order_buffer, len); + ws2812_byte_order_transform_rgb( + priv->byte_order_buffer, + data, + len >> 2 + ); + } + break; +# endif /* CONFIG_WS2812_COMPONENT_TRANSFORM_RGB */ + default: + /* Should not ever happen with enums but the compiler tends + * to complain about unhandled values. + */ + + res = priv->read(filep, data, len); + break; + } + + nxmutex_unlock(&priv->byte_order_lock); + } + +# endif /* CONFIG_WS2812_COMPONENT_TRANSFORM */ return res; } diff --git a/include/nuttx/leds/ws2812.h b/include/nuttx/leds/ws2812.h index bbee3407acb..52149c79fd1 100644 --- a/include/nuttx/leds/ws2812.h +++ b/include/nuttx/leds/ws2812.h @@ -71,6 +71,30 @@ extern "C" #ifdef CONFIG_WS2812_NON_SPI_DRIVER +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM + +enum ws2812_component_transform_e +{ + /* Supposedly, the LED expects color bytes in GRB order. However, + * variant that expect RGB order exists as well. Upper-half can + * transform the data from userspace (coming in RGB order) + * so the lower-half (which expects GRB) does the correct thing. + * + * NONE transformation is always available and default + * if the configuration struct is allocated with zalloc. + */ + + WS2812_COMPONENT_TRANSFORM_NONE = 0 + +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM_RGB + + , WS2812_COMPONENT_TRANSFORM_RGB /* LED expects bytes in RGB order */ + +# endif +}; + +# endif /* CONFIG_WS2812_COMPONENT_TRANSFORM */ + struct ws2812_dev_s { /* -------------------------------------------------------- @@ -100,6 +124,30 @@ struct ws2812_dev_s uint16_t nleds; /* Count of WS2812s in the chain */ bool has_white; /* Set true if chain has LED with * white component */ + +# ifdef CONFIG_WS2812_COMPONENT_TRANSFORM + + /* This is the byte order transformation the LED needs */ + + enum ws2812_component_transform_e byte_order_transformation; + + /* This is a buffer used for the byte order transformation. + * Must be allocated by the caller of ws2812_register and must have + * enough room for WS2812_RW_PIXEL_SIZE * nleds bytes. + * + */ + + FAR char *byte_order_buffer; + + /* Using single buffer per driver instance. Must prevent concurrent + * access but without using the "lock" mutex above. Lower half + * drivers are using that one. Must be initialized by caller + * of ws2812_register as well. + */ + + mutex_t byte_order_lock; + +# endif }; #endif
