Initial commit.
Some checks failed
Sync remain PRs to Jira / Sync PRs to Jira (push) Has been cancelled

This commit is contained in:
2025-04-30 16:33:57 +03:00
commit 34cf3ec285
193 changed files with 25742 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
if(CONFIG_WS2812_LED_ENABLE)
set(srcs "ws2812_led.c" "led_strip_rmt_ws2812.c")
else()
set(srcs "ws2812_led.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
PRIV_REQUIRES "driver")

View File

@@ -0,0 +1,19 @@
menu "WS2812 RGB LED"
config WS2812_LED_ENABLE
bool "Enable RGB LED"
default n if IDF_TARGET_ESP32 || !SOC_RMT_SUPPORTED
default y
help
Disable the WS2812 RGB LED.
config WS2812_LED_GPIO
int "WS2812 LED GPIO"
default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32H2
default 48 if IDF_TARGET_ESP32S3
default 18
depends on WS2812_LED_ENABLE
help
Set the WS2812 RGB LED GPIO.
endmenu

View File

@@ -0,0 +1,5 @@
COMPONENT_ADD_INCLUDEDIRS := .
COMPONENT_SRCDIRS := .
ifndef CONFIG_WS2812_LED_ENABLE
COMPONENT_OBJEXCLUDE += led_strip_rmt_ws2812.o
endif

View File

@@ -0,0 +1,126 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* @brief LED Strip Type
*
*/
typedef struct led_strip_s led_strip_t;
/**
* @brief LED Strip Device Type
*
*/
typedef void *led_strip_dev_t;
/**
* @brief Declare of LED Strip Type
*
*/
struct led_strip_s {
/**
* @brief Set RGB for a specific pixel
*
* @param strip: LED strip
* @param index: index of pixel to set
* @param red: red part of color
* @param green: green part of color
* @param blue: blue part of color
*
* @return
* - ESP_OK: Set RGB for a specific pixel successfully
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
*/
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
/**
* @brief Refresh memory colors to LEDs
*
* @param strip: LED strip
* @param timeout_ms: timeout value for refreshing task
*
* @return
* - ESP_OK: Refresh successfully
* - ESP_ERR_TIMEOUT: Refresh failed because of timeout
* - ESP_FAIL: Refresh failed because some other error occurred
*
* @note:
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
*/
esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms);
/**
* @brief Clear LED strip (turn off all LEDs)
*
* @param strip: LED strip
* @param timeout_ms: timeout value for clearing task
*
* @return
* - ESP_OK: Clear LEDs successfully
* - ESP_ERR_TIMEOUT: Clear LEDs failed because of timeout
* - ESP_FAIL: Clear LEDs failed because some other error occurred
*/
esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms);
/**
* @brief Free LED strip resources
*
* @param strip: LED strip
*
* @return
* - ESP_OK: Free resources successfully
* - ESP_FAIL: Free resources failed because error occurred
*/
esp_err_t (*del)(led_strip_t *strip);
};
/**
* @brief LED Strip Configuration Type
*
*/
typedef struct {
uint32_t max_leds; /*!< Maximum LEDs in a single strip */
led_strip_dev_t dev; /*!< LED strip device (e.g. RMT channel, PWM channel, etc) */
} led_strip_config_t;
/**
* @brief Default configuration for LED strip
*
*/
#define LED_STRIP_DEFAULT_CONFIG(number, dev_hdl) \
{ \
.max_leds = number, \
.dev = dev_hdl, \
}
/**
* @brief Install a new ws2812 driver (based on RMT peripheral)
*
* @param config: LED strip configuration
* @return
* LED strip instance or NULL
*/
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,171 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include "esp_log.h"
#include "esp_attr.h"
#include "led_strip.h"
#include "driver/rmt.h"
static const char *TAG = "ws2812";
#define STRIP_CHECK(a, str, goto_tag, ret_value, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ret = ret_value; \
goto goto_tag; \
} \
} while (0)
#define WS2812_T0H_NS (350)
#define WS2812_T0L_NS (1000)
#define WS2812_T1H_NS (1000)
#define WS2812_T1L_NS (350)
#define WS2812_RESET_US (280)
static uint32_t ws2812_t0h_ticks = 0;
static uint32_t ws2812_t1h_ticks = 0;
static uint32_t ws2812_t0l_ticks = 0;
static uint32_t ws2812_t1l_ticks = 0;
typedef struct {
led_strip_t parent;
rmt_channel_t rmt_channel;
uint32_t strip_len;
uint8_t buffer[0];
} ws2812_t;
/**
* @brief Conver RGB data to RMT format.
*
* @note For WS2812, R,G,B each contains 256 different choices (i.e. uint8_t)
*
* @param[in] src: source data, to converted to RMT format
* @param[in] dest: place where to store the convert result
* @param[in] src_size: size of source data
* @param[in] wanted_num: number of RMT items that want to get
* @param[out] translated_size: number of source data that got converted
* @param[out] item_num: number of RMT items which are converted from source data
*/
static void IRAM_ATTR ws2812_rmt_adapter(const void *src, rmt_item32_t *dest, size_t src_size,
size_t wanted_num, size_t *translated_size, size_t *item_num)
{
if (src == NULL || dest == NULL) {
*translated_size = 0;
*item_num = 0;
return;
}
const rmt_item32_t bit0 = {{{ ws2812_t0h_ticks, 1, ws2812_t0l_ticks, 0 }}}; //Logical 0
const rmt_item32_t bit1 = {{{ ws2812_t1h_ticks, 1, ws2812_t1l_ticks, 0 }}}; //Logical 1
size_t size = 0;
size_t num = 0;
uint8_t *psrc = (uint8_t *)src;
rmt_item32_t *pdest = dest;
while (size < src_size && num < wanted_num) {
for (int i = 0; i < 8; i++) {
// MSB first
if (*psrc & (1 << (7 - i))) {
pdest->val = bit1.val;
} else {
pdest->val = bit0.val;
}
num++;
pdest++;
}
size++;
psrc++;
}
*translated_size = size;
*item_num = num;
}
static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
{
esp_err_t ret = ESP_OK;
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
STRIP_CHECK(index < ws2812->strip_len, "index out of the maximum number of leds", err, ESP_ERR_INVALID_ARG);
uint32_t start = index * 3;
// In thr order of GRB
ws2812->buffer[start + 0] = green & 0xFF;
ws2812->buffer[start + 1] = red & 0xFF;
ws2812->buffer[start + 2] = blue & 0xFF;
return ESP_OK;
err:
return ret;
}
static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
STRIP_CHECK(rmt_write_sample(ws2812->rmt_channel, ws2812->buffer, ws2812->strip_len * 3, true) == ESP_OK,
"transmit RMT samples failed", err, ESP_FAIL);
return rmt_wait_tx_done(ws2812->rmt_channel, pdMS_TO_TICKS(timeout_ms));
err:
return ret;
}
static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms)
{
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
// Write zero to turn off all leds
memset(ws2812->buffer, 0, ws2812->strip_len * 3);
return ws2812_refresh(strip, timeout_ms);
}
static esp_err_t ws2812_del(led_strip_t *strip)
{
ws2812_t *ws2812 = __containerof(strip, ws2812_t, parent);
free(ws2812);
return ESP_OK;
}
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config)
{
led_strip_t *ret = NULL;
STRIP_CHECK(config, "configuration can't be null", err, NULL);
// 24 bits per led
uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3;
ws2812_t *ws2812 = calloc(1, ws2812_size);
STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);
uint32_t counter_clk_hz = 0;
STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK,
"get rmt counter clock failed", err, NULL);
// ns -> ticks
float ratio = (float)counter_clk_hz / 1e9;
ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);
// set ws2812 to rmt adapter
rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter);
ws2812->rmt_channel = (rmt_channel_t)config->dev;
ws2812->strip_len = config->max_leds;
ws2812->parent.set_pixel = ws2812_set_pixel;
ws2812->parent.refresh = ws2812_refresh;
ws2812->parent.clear = ws2812_clear;
ws2812->parent.del = ws2812_del;
return &ws2812->parent;
err:
return ret;
}

View File

@@ -0,0 +1,159 @@
/* WS2812 RGB LED helper functions
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/* It is recommended to copy this code in your example so that you can modify as
* per your application's needs.
*/
#include <stdint.h>
#include <esp_err.h>
#include <esp_log.h>
static const char *TAG = "ws2812_led";
#ifdef CONFIG_WS2812_LED_ENABLE
#include <driver/rmt.h>
#include "led_strip.h"
#define RMT_TX_CHANNEL RMT_CHANNEL_0
static led_strip_t *g_strip;
/**
* @brief Simple helper function, converting HSV color space to RGB color space
*
* Wiki: https://en.wikipedia.org/wiki/HSL_and_HSV
*
*/
static void ws2812_led_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b)
{
h %= 360; // h -> [0,360]
uint32_t rgb_max = v * 2.55f;
uint32_t rgb_min = rgb_max * (100 - s) / 100.0f;
uint32_t i = h / 60;
uint32_t diff = h % 60;
// RGB adjustment amount by hue
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
switch (i) {
case 0:
*r = rgb_max;
*g = rgb_min + rgb_adj;
*b = rgb_min;
break;
case 1:
*r = rgb_max - rgb_adj;
*g = rgb_max;
*b = rgb_min;
break;
case 2:
*r = rgb_min;
*g = rgb_max;
*b = rgb_min + rgb_adj;
break;
case 3:
*r = rgb_min;
*g = rgb_max - rgb_adj;
*b = rgb_max;
break;
case 4:
*r = rgb_min + rgb_adj;
*g = rgb_min;
*b = rgb_max;
break;
default:
*r = rgb_max;
*g = rgb_min;
*b = rgb_max - rgb_adj;
break;
}
}
esp_err_t ws2812_led_set_rgb(uint32_t red, uint32_t green, uint32_t blue)
{
if (!g_strip) {
return ESP_ERR_INVALID_STATE;
}
g_strip->set_pixel(g_strip, 0, red, green, blue);
g_strip->refresh(g_strip, 100);
return ESP_OK;
}
esp_err_t ws2812_led_set_hsv(uint32_t hue, uint32_t saturation, uint32_t value)
{
if (!g_strip) {
return ESP_ERR_INVALID_STATE;
}
uint32_t red = 0;
uint32_t green = 0;
uint32_t blue = 0;
ws2812_led_hsv2rgb(hue, saturation, value, &red, &green, &blue);
return ws2812_led_set_rgb(red, green, blue);
}
esp_err_t ws2812_led_clear(void)
{
if (!g_strip) {
return ESP_ERR_INVALID_STATE;
}
g_strip->clear(g_strip, 100);
return ESP_OK;
}
esp_err_t ws2812_led_init(void)
{
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(CONFIG_WS2812_LED_GPIO, RMT_TX_CHANNEL);
// set counter clock to 40MHz
config.clk_div = 2;
if (rmt_config(&config) != ESP_OK) {
ESP_LOGE(TAG, "RMT Config failed.");
return ESP_FAIL;
}
if (rmt_driver_install(config.channel, 0, 0) != ESP_OK) {
ESP_LOGE(TAG, "RMT Driver install failed.");
return ESP_FAIL;
}
// install ws2812 driver
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(1, (led_strip_dev_t)config.channel);
g_strip = led_strip_new_rmt_ws2812(&strip_config);
if (!g_strip) {
ESP_LOGE(TAG, "Install WS2812 driver failed.");
return ESP_FAIL;
}
return ESP_OK;
}
#else /* !CONFIG_WS2812_LED_ENABLE */
esp_err_t ws2812_led_set_rgb(uint32_t red, uint32_t green, uint32_t blue)
{
/* Empty function, since WS2812 RGB LED has been disabled. */
return ESP_OK;
}
esp_err_t ws2812_led_set_hsv(uint32_t hue, uint32_t saturation, uint32_t value)
{
/* Empty function, since WS2812 RGB LED has been disabled. */
return ESP_OK;
}
esp_err_t ws2812_led_clear(void)
{
/* Empty function, since WS2812 RGB LED has been disabled. */
return ESP_OK;
}
esp_err_t ws2812_led_init(void)
{
ESP_LOGW(TAG, "WS2812 LED is disabled");
return ESP_OK;
}
#endif /* !CONFIG_WS2812_LED_ENABLE */

View File

@@ -0,0 +1,44 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include <esp_err.h>
/** Initialize the WS2812 RGB LED
*
* @return ESP_OK on success.
* @return error in case of failure.
*/
esp_err_t ws2812_led_init(void);
/** Set RGB value for the WS2812 LED
*
* @param[in] red Intensity of Red color (0-100)
* @param[in] green Intensity of Green color (0-100)
* @param[in] blue Intensity of Green color (0-100)
*
* @return ESP_OK on success.
* @return error in case of failure.
*/
esp_err_t ws2812_led_set_rgb(uint32_t red, uint32_t green, uint32_t blue);
/** Set HSV value for the WS2812 LED
*
* @param[in] hue Value of hue in arc degrees (0-360)
* @param[in] saturation Saturation in percentage (0-100)
* @param[in] value Value (also called Intensity) in percentage (0-100)
*
* @return ESP_OK on success.
* @return error in case of failure.
*/
esp_err_t ws2812_led_set_hsv(uint32_t hue, uint32_t saturation, uint32_t value);
/** Clear (turn off) the WS2812 LED
* @return ESP_OK on success.
* @return error in case of failure.
*/
esp_err_t ws2812_led_clear(void);