mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-17 10:55:06 +00:00
171 lines
7.1 KiB
C
171 lines
7.1 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/cdefs.h>
|
|
#include "esp_log.h"
|
|
#include "esp_check.h"
|
|
#include "driver/rmt_tx.h"
|
|
#include "drivers/esp32_led_strip.h"
|
|
#include "esp32_led_strip_rmt_encoder.h"
|
|
#include "esp32_led_strip_rmt.h"
|
|
|
|
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
|
|
#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
|
|
// the memory size of each RMT channel, in words (4 bytes)
|
|
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
|
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
|
|
#else
|
|
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
|
|
#endif
|
|
|
|
static const char *TAG = "led_strip_rmt";
|
|
|
|
typedef struct led_strip_t {
|
|
rmt_channel_handle_t rmt_chan;
|
|
rmt_encoder_handle_t strip_encoder;
|
|
uint32_t strip_len;
|
|
uint8_t bytes_per_pixel;
|
|
led_color_component_format_t component_fmt;
|
|
uint8_t pixel_buf[];
|
|
} led_strip_rmt_obj;
|
|
|
|
esp_err_t led_strip_rmt_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
|
{
|
|
led_strip_rmt_obj *rmt_strip = strip;
|
|
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
|
|
|
struct format_layout format = rmt_strip->component_fmt.format;
|
|
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
|
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
|
uint8_t pos_bytes = format.bytes_per_color;
|
|
|
|
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
|
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
|
pixel_buf[start + format.r_pos * pos_bytes + i] = (red >> color_shift) & 0xFF;
|
|
pixel_buf[start + format.g_pos * pos_bytes + i] = (green >> color_shift) & 0xFF;
|
|
pixel_buf[start + format.b_pos * pos_bytes + i] = (blue >> color_shift) & 0xFF;
|
|
if (format.num_components > 3) {
|
|
pixel_buf[start + format.w_pos * pos_bytes + i] = 0;
|
|
}
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t led_strip_rmt_refresh(led_strip_handle_t strip)
|
|
{
|
|
led_strip_rmt_obj *rmt_strip = strip;
|
|
rmt_transmit_config_t tx_conf = {
|
|
.loop_count = 0,
|
|
};
|
|
|
|
ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed");
|
|
ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf,
|
|
rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed");
|
|
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed");
|
|
ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t led_strip_rmt_clear(led_strip_handle_t strip)
|
|
{
|
|
led_strip_rmt_obj *rmt_strip = strip;
|
|
// Write zero to turn off all leds
|
|
memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
|
return led_strip_rmt_refresh(strip);
|
|
}
|
|
|
|
esp_err_t led_strip_rmt_del(led_strip_handle_t strip)
|
|
{
|
|
led_strip_rmt_obj *rmt_strip = strip;
|
|
ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed");
|
|
ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed");
|
|
free(rmt_strip);
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
|
|
{
|
|
led_strip_rmt_obj *rmt_strip = NULL;
|
|
esp_err_t ret = ESP_OK;
|
|
ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
|
led_color_component_format_t component_fmt = led_config->color_component_format;
|
|
// If R/G/B order is not specified, set default GRB order as fallback
|
|
if (component_fmt.format_id == 0) {
|
|
component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
|
|
}
|
|
if (led_config->led_model == LED_MODEL_WS2816) {
|
|
component_fmt.format.bytes_per_color = 2;
|
|
}
|
|
if (component_fmt.format.bytes_per_color == 0) {
|
|
component_fmt.format.bytes_per_color = 1;
|
|
}
|
|
// check the validation of the color component format
|
|
uint8_t mask = 0;
|
|
if (component_fmt.format.num_components == 3) {
|
|
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
|
|
// Check for invalid values
|
|
ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
|
} else if (component_fmt.format.num_components == 4) {
|
|
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos);
|
|
// Check for invalid values
|
|
ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
|
} else {
|
|
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
|
|
}
|
|
uint8_t bytes_per_pixel = component_fmt.format.num_components;
|
|
if (component_fmt.format.bytes_per_color > 1) {
|
|
bytes_per_pixel *= component_fmt.format.bytes_per_color;
|
|
}
|
|
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
|
|
ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip");
|
|
uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION;
|
|
|
|
// for backward compatibility, if the user does not set the clk_src, use the default value
|
|
rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT;
|
|
if (rmt_config->clk_src) {
|
|
clk_src = rmt_config->clk_src;
|
|
}
|
|
size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
|
// override the default value if the user sets it
|
|
if (rmt_config->mem_block_symbols) {
|
|
mem_block_symbols = rmt_config->mem_block_symbols;
|
|
}
|
|
rmt_tx_channel_config_t rmt_chan_config = {
|
|
.clk_src = clk_src,
|
|
.gpio_num = led_config->strip_gpio_num,
|
|
.mem_block_symbols = mem_block_symbols,
|
|
.resolution_hz = resolution,
|
|
.trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE,
|
|
.flags.with_dma = rmt_config->flags.with_dma,
|
|
.flags.invert_out = led_config->flags.invert_out,
|
|
};
|
|
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed");
|
|
|
|
led_strip_encoder_config_t strip_encoder_conf = {
|
|
.resolution = resolution,
|
|
.led_model = led_config->led_model
|
|
};
|
|
ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed");
|
|
|
|
rmt_strip->component_fmt = component_fmt;
|
|
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
|
rmt_strip->strip_len = led_config->max_leds;
|
|
*ret_strip = rmt_strip;
|
|
return ESP_OK;
|
|
err:
|
|
if (rmt_strip) {
|
|
if (rmt_strip->rmt_chan) {
|
|
rmt_del_channel(rmt_strip->rmt_chan);
|
|
}
|
|
if (rmt_strip->strip_encoder) {
|
|
rmt_del_encoder(rmt_strip->strip_encoder);
|
|
}
|
|
free(rmt_strip);
|
|
}
|
|
return ret;
|
|
}
|