Initial commit of Arduino libraries

This commit is contained in:
Sam
2025-05-23 10:47:41 +10:00
commit 5bfce5fc3e
2476 changed files with 1108481 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
#pragma once
// Arduino JSON may be included by the user, so we need to save the current state
// of the macros and restore them after including the library
#pragma push_macro("ARDUINO")
#pragma push_macro("ARDUINOJSON_ENABLE_STD_STREAM")
#pragma push_macro("ARDUINOJSON_ENABLE_STRING_VIEW")
#pragma push_macro("ARDUINOJSON_ENABLE_STD_STRING")
#pragma push_macro("ARDUINOJSON_ENABLE_ARDUINO_STRING")
#pragma push_macro("ARDUINOJSON_ENABLE_ARDUINO_STREAM")
#pragma push_macro("ARDUINOJSON_ENABLE_ARDUINO_PRINT")
#pragma push_macro("ARDUINOJSON_ENABLE_PROGMEM")
// Safely undefine FLArduinoJson macros if defined
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
#undef ARDUINOJSON_ENABLE_STD_STREAM
#endif
#define ARDUINOJSON_ENABLE_STD_STREAM 0
#ifdef ARDUINOJSON_ENABLE_STRING_VIEW
#undef ARDUINOJSON_ENABLE_STRING_VIEW
#endif
#define ARDUINOJSON_ENABLE_STRING_VIEW 0
#ifdef ARDUINOJSON_ENABLE_STD_STRING
#undef ARDUINOJSON_ENABLE_STD_STRING
#endif
#define ARDUINOJSON_ENABLE_STD_STRING 0
#ifdef ARDUINOJSON_ENABLE_ARDUINO_STRING
#undef ARDUINOJSON_ENABLE_ARDUINO_STRING
#endif
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0
#ifdef ARDUINOJSON_ENABLE_ARDUINO_STREAM
#undef ARDUINOJSON_ENABLE_ARDUINO_STREAM
#endif
#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0
#ifdef ARDUINOJSON_ENABLE_ARDUINO_PRINT
#undef ARDUINOJSON_ENABLE_ARDUINO_PRINT
#endif
#define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0
#ifdef ARDUINOJSON_ENABLE_PROGMEM
#undef ARDUINOJSON_ENABLE_PROGMEM
#endif
#define ARDUINOJSON_ENABLE_PROGMEM 0
#ifdef ARDUINO
#undef ARDUINO
#endif
#define FASTLED_JSON_GUARD
#include "json.hpp"
#undef FASTLED_JSON_GUARD
#pragma pop_macro("ARDUINOJSON_ENABLE_PROGMEM")
#pragma pop_macro("ARDUINOJSON_ENABLE_ARDUINO_PRINT")
#pragma pop_macro("ARDUINOJSON_ENABLE_ARDUINO_STREAM")
#pragma pop_macro("ARDUINOJSON_ENABLE_ARDUINO_STRING")
#pragma pop_macro("ARDUINOJSON_ENABLE_STD_STRING")
#pragma pop_macro("ARDUINOJSON_ENABLE_STRING_VIEW")
#pragma pop_macro("ARDUINOJSON_ENABLE_STD_STREAM")
#pragma pop_macro("ARDUINO")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
From espressif led_strip component repository.
Version: 3.0.0
This library has been lightly modified to support async operations for both spi and rmt strip drivers.
The following files were added by FastLED:
strip_spi.h
strip_spi.cpp
strip_rmt.h
strip_rmt.cpp

View File

@@ -0,0 +1,60 @@
#pragma once
#include "sdkconfig.h"
#include "platforms/esp/esp_version.h"
#if CONFIG_IDF_TARGET_ESP32C2
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
#define FASTLED_ESP32_HAS_RMT 0
#define FASTLED_ESP32_HAS_RMT5 0
#elif CONFIG_IDF_TARGET_ESP32C3
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
#define FASTLED_ESP32_HAS_RMT 1
#define FASTLED_ESP32_HAS_RMT5 1
#elif CONFIG_IDF_TARGET_ESP32C6
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
#define FASTLED_ESP32_HAS_RMT 1
#define FASTLED_ESP32_HAS_RMT5 1
#elif CONFIG_IDF_TARGET_ESP32S2
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
#define FASTLED_ESP32_HAS_RMT 1
#define FASTLED_ESP32_HAS_RMT5 1
#elif CONFIG_IDF_TARGET_ESP32S3
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
#define FASTLED_ESP32_HAS_RMT 1
#define FASTLED_ESP32_HAS_RMT5 1
#elif CONFIG_IDF_TARGET_ESP32H2
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
#define FASTLED_ESP32_HAS_RMT 1
#define FASTLED_ESP32_HAS_RMT5 1
#elif CONFIG_IDF_TARGET_ESP32P4
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 1
#define FASTLED_ESP32_HAS_RMT 1
#define FASTLED_ESP32_HAS_RMT5 1
#elif CONFIG_IDF_TARGET_ESP8266
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 0
#define FASTLED_ESP32_HAS_RMT 0
#define FASTLED_ESP32_HAS_RMT5 0
#elif CONFIG_IDF_TARGET_ESP32 || defined(ARDUINO_ESP32_DEV)
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 0
#define FASTLED_ESP32_HAS_RMT 1
#define FASTLED_ESP32_HAS_RMT5 1
#else
#warning "Unknown board, assuming support for clockless RMT5 and SPI chipsets. Please file an bug report with FastLED and tell them about your board type."
#endif
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#undef FASTLED_ESP32_HAS_RMT5
#undef FASTLED_ESP32_HAS_CLOCKLESS_SPI
#define FASTLED_ESP32_HAS_RMT5 0
#define FASTLED_ESP32_HAS_CLOCKLESS_SPI 0
#endif
// Note that FASTLED_RMT5 is a legacy name,
// so we keep it because "RMT" is specific to ESP32
#if FASTLED_ESP32_HAS_RMT5 && !defined(FASTLED_RMT5)
#define FASTLED_RMT5 1
#else
#define FASTLED_RMT5 0
#endif

View File

@@ -0,0 +1,112 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "led_strip_rmt.h"
#include "led_strip_spi.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @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 led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
/**
* @brief Set RGBW for a specific pixel
*
* @note Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
* @note Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
*
* @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
* @param white: separate white component
*
* @return
* - ESP_OK: Set RGBW color for a specific pixel successfully
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
*/
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
/**
* @brief Set HSV for a specific pixel
*
* @param strip: LED strip
* @param index: index of pixel to set
* @param hue: hue part of color (0 - 360)
* @param saturation: saturation part of color (0 - 255, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 127)
* @param value: value part of color (0 - 255, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 127)
*
* @return
* - ESP_OK: Set HSV color for a specific pixel successfully
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
*/
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value);
/**
* @brief Refresh memory colors to LEDs
*
* @param strip: LED strip
*
* @return
* - ESP_OK: Refresh successfully
* - 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 led_strip_refresh(led_strip_handle_t strip);
esp_err_t led_strip_refresh_async(led_strip_handle_t strip);
esp_err_t led_strip_refresh_wait_done(led_strip_handle_t strip);
/**
* @brief Clear LED strip (turn off all LEDs)
*
* @param strip: LED strip
*
* @return
* - ESP_OK: Clear LEDs successfully
* - ESP_FAIL: Clear LEDs failed because some other error occurred
*/
esp_err_t led_strip_clear(led_strip_handle_t strip);
/**
* @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 led_strip_del(led_strip_handle_t strip);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,118 @@
#ifdef ESP32
#include "enabled.h"
#if FASTLED_RMT5 || FASTLED_ESP32_HAS_CLOCKLESS_SPI
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_log.h"
#include "esp_check.h"
#include "led_strip.h"
#include "led_strip_interface.h"
static const char *TAG = "led_strip";
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
{
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return strip->set_pixel(strip, index, red, green, blue);
}
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value)
{
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
uint32_t red = 0;
uint32_t green = 0;
uint32_t blue = 0;
uint32_t rgb_max = value;
uint32_t rgb_min = rgb_max * (255 - saturation) / 255.0f;
uint32_t i = hue / 60;
uint32_t diff = hue % 60;
// RGB adjustment amount by hue
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
switch (i) {
case 0:
red = rgb_max;
green = rgb_min + rgb_adj;
blue = rgb_min;
break;
case 1:
red = rgb_max - rgb_adj;
green = rgb_max;
blue = rgb_min;
break;
case 2:
red = rgb_min;
green = rgb_max;
blue = rgb_min + rgb_adj;
break;
case 3:
red = rgb_min;
green = rgb_max - rgb_adj;
blue = rgb_max;
break;
case 4:
red = rgb_min + rgb_adj;
green = rgb_min;
blue = rgb_max;
break;
default:
red = rgb_max;
green = rgb_min;
blue = rgb_max - rgb_adj;
break;
}
return strip->set_pixel(strip, index, red, green, blue);
}
esp_err_t led_strip_refresh_wait_done(led_strip_handle_t strip)
{
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return strip->refresh_wait_done(strip);
}
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
{
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return strip->set_pixel_rgbw(strip, index, red, green, blue, white);
}
esp_err_t led_strip_refresh(led_strip_handle_t strip)
{
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_ERROR(strip->refresh_async(strip), TAG, "refresh failed");
ESP_RETURN_ON_ERROR(strip->refresh_wait_done(strip), TAG, "wait for done failed");
return ESP_OK;
}
esp_err_t led_strip_refresh_async(led_strip_handle_t strip)
{
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return strip->refresh_async(strip);
}
esp_err_t led_strip_clear(led_strip_handle_t strip)
{
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return strip->clear(strip);
}
esp_err_t led_strip_del(led_strip_handle_t strip)
{
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return strip->del(strip);
}
#endif // FASTLED_RMT5 || FASTLED_ESP_HAS_CLOCKLESS_SPI
#endif // ESP32

View File

@@ -0,0 +1,99 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct led_strip_t led_strip_t; /*!< Type of LED strip */
/**
* @brief LED strip interface definition
*/
struct led_strip_t {
/**
* @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 Set RGBW for a specific pixel. Similar to `set_pixel` but also set the white component
*
* @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
* @param white: separate white component
*
* @return
* - ESP_OK: Set RGBW color for a specific pixel successfully
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
*/
esp_err_t (*set_pixel_rgbw)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
/**
* @brief Refresh memory colors to LEDs
*
* @param strip: LED strip
* @param timeout_ms: timeout value for refreshing task
*
* @return
* - ESP_OK: Refresh successfully
* - 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);
esp_err_t (*refresh_async)(led_strip_t *strip);
esp_err_t (*refresh_wait_done)(led_strip_t *strip);
/**
* @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_FAIL: Clear LEDs failed because some other error occurred
*/
esp_err_t (*clear)(led_strip_t *strip);
/**
* @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);
};
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "led_strip_types.h"
#include "esp_idf_version.h"
#include "driver/rmt_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LED Strip RMT specific configuration
*/
typedef struct {
rmt_clock_source_t clk_src; /*!< RMT clock source */
uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */
size_t mem_block_symbols; /*!< How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size. */
/*!< Extra RMT specific driver flags */
struct led_strip_rmt_extra_config {
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
} flags; /*!< Extra driver flags */
uint8_t interrupt_priority; /*!< RMT interrupt priority, 0-3 are valid values */
} led_strip_rmt_config_t;
/**
* @brief Create LED strip based on RMT TX channel
*
* @param led_config LED strip configuration
* @param rmt_config RMT specific configuration
* @param ret_strip Returned LED strip handle
* @return
* - ESP_OK: create LED strip handle successfully
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
* - ESP_FAIL: create LED strip handle failed because some other error
*/
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);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,229 @@
#ifdef ESP32
#include "enabled.h"
#if FASTLED_RMT5
#include "fl/unused.h"
/*
* 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 "led_strip.h"
#include "led_strip_interface.h"
#include "led_strip_rmt_encoder.h"
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
#ifndef LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER
#if CONFIG_IDF_TARGET_ESP32C3
// Fixes https://github.com/FastLED/FastLED/issues/1846
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER 2
#else
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER 1
#endif
#endif
// the memory size of each RMT channel, in words (4 bytes)
#ifndef LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS (LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER * 64)
#else
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS (LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS_MULTIPLIER * 48)
#endif
#endif
static const char *TAG = "led_strip_rmt";
typedef struct {
led_strip_t base;
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;
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
{
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
led_color_component_format_t component_fmt = rmt_strip->component_fmt;
uint32_t start = index * rmt_strip->bytes_per_pixel;
uint8_t *pixel_buf = rmt_strip->pixel_buf;
pixel_buf[start + component_fmt.format.r_pos] = red & 0xFF;
pixel_buf[start + component_fmt.format.g_pos] = green & 0xFF;
pixel_buf[start + component_fmt.format.b_pos] = blue & 0xFF;
if (component_fmt.format.num_components > 3) {
pixel_buf[start + component_fmt.format.w_pos] = 0;
}
return ESP_OK;
}
static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
{
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
led_color_component_format_t component_fmt = rmt_strip->component_fmt;
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
ESP_RETURN_ON_FALSE(component_fmt.format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
uint32_t start = index * rmt_strip->bytes_per_pixel;
uint8_t *pixel_buf = rmt_strip->pixel_buf;
pixel_buf[start + component_fmt.format.r_pos] = red & 0xFF;
pixel_buf[start + component_fmt.format.g_pos] = green & 0xFF;
pixel_buf[start + component_fmt.format.b_pos] = blue & 0xFF;
pixel_buf[start + component_fmt.format.w_pos] = white & 0xFF;
return ESP_OK;
}
static esp_err_t led_strip_rmt_refresh_async(led_strip_t *strip)
{
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
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");
return ESP_OK;
}
static esp_err_t led_strip_rmt_wait_for_done(led_strip_t *strip)
{
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "wait for RMT done failed");
ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
return ESP_OK;
}
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
{
ESP_RETURN_ON_ERROR(led_strip_rmt_refresh_async(strip), TAG, "refresh async failed");
ESP_RETURN_ON_ERROR(led_strip_rmt_wait_for_done(strip), TAG, "wait for done failed");
return ESP_OK;
}
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
{
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
// 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);
}
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
{
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
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;
FASTLED_UNUSED(ret);
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;
}
// 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);
}
// TODO: we assume each color component is 8 bits, may need to support other configurations in the future, e.g. 10bits per color component?
uint8_t bytes_per_pixel = component_fmt.format.num_components;
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,
.timings = led_config->timings,
};
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;
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw;
rmt_strip->base.refresh = led_strip_rmt_refresh;
rmt_strip->base.refresh_async = led_strip_rmt_refresh_async;
rmt_strip->base.refresh_wait_done = led_strip_rmt_wait_for_done;
rmt_strip->base.clear = led_strip_rmt_clear;
rmt_strip->base.del = led_strip_rmt_del;
*ret_strip = &rmt_strip->base;
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;
}
#endif // FASTLED_RMT5
#endif // ESP32

View File

@@ -0,0 +1,195 @@
#ifdef ESP32
#include "enabled.h"
#if FASTLED_RMT5
#include "fl/unused.h"
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_check.h"
#include "led_strip_rmt_encoder.h"
static const char *TAG = "led_rmt_encoder";
typedef struct {
rmt_encoder_t base;
rmt_encoder_t *bytes_encoder;
rmt_encoder_t *copy_encoder;
int state;
rmt_symbol_word_t reset_code;
} rmt_led_strip_encoder_t;
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
rmt_encode_state_t session_state = 0;
rmt_encode_state_t state = 0;
size_t encoded_symbols = 0;
switch (led_encoder->state) {
case 0: // send RGB data
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
led_encoder->state = 1; // switch to next state when current encoding session finished
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space for encoding artifacts
}
// fall-through
case 1: // send reset code
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
sizeof(led_encoder->reset_code), &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
led_encoder->state = 0; // back to the initial encoding session
state |= RMT_ENCODING_COMPLETE;
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space for encoding artifacts
}
}
out:
*ret_state = state;
return encoded_symbols;
}
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_del_encoder(led_encoder->bytes_encoder);
rmt_del_encoder(led_encoder->copy_encoder);
free(led_encoder);
return ESP_OK;
}
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
{
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
rmt_encoder_reset(led_encoder->bytes_encoder);
rmt_encoder_reset(led_encoder->copy_encoder);
led_encoder->state = 0;
return ESP_OK;
}
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
{
esp_err_t ret = ESP_OK;
FASTLED_UNUSED(ret);
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model");
// Create a temporary config with the same base values
led_strip_encoder_config_t timing_config = *config;
const bool has_encoder_timings = config->timings.t0h || config->timings.t0l || config->timings.t1h || config->timings.t1l || config->timings.reset;
if (!has_encoder_timings) {
// Set the timing values based on LED model
if (config->led_model == LED_MODEL_SK6812) {
timing_config.timings = (led_strip_encoder_timings_t) {
.t0h = 300, // 0.3us = 300ns
.t0l = 900, // 0.9us = 900ns
.t1h = 600, // 0.6us = 600ns
.t1l = 600, // 0.6us = 600ns
.reset = 280 // 280us
};
} else if (config->led_model == LED_MODEL_WS2812) {
timing_config.timings = (led_strip_encoder_timings_t) {
.t0h = 300, // 0.3us = 300ns
.t0l = 900, // 0.9us = 900ns
.t1h = 900, // 0.9us = 900ns
.t1l = 300, // 0.3us = 300ns
.reset = 280 // 280us
};
} else if (config->led_model == LED_MODEL_WS2811) {
timing_config.timings = (led_strip_encoder_timings_t) {
.t0h = 500, // 0.5us = 500ns
.t0l = 2000, // 2.0us = 2000ns
.t1h = 1200, // 1.2us = 1200ns
.t1l = 1300, // 1.3us = 1300ns
.reset = 50 // 50us
};
} else {
return ESP_ERR_INVALID_ARG;
}
}
// Delegate to the timing-based encoder creation
return rmt_new_led_strip_encoder_with_timings(&timing_config, ret_encoder);
err:
return ESP_ERR_INVALID_ARG;
}
esp_err_t rmt_new_led_strip_encoder_with_timings(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder) {
esp_err_t ret = ESP_OK;
FASTLED_UNUSED(ret);
rmt_led_strip_encoder_t *led_encoder = NULL;
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
led_encoder->base.encode = rmt_encode_led_strip;
led_encoder->base.del = rmt_del_led_strip_encoder;
led_encoder->base.reset = rmt_led_strip_encoder_reset;
// Convert nanosecond timings to ticks using resolution
rmt_bytes_encoder_config_t bytes_encoder_config = {
.bit0 = {
.level0 = 1,
.duration0 = (float)config->timings.t0h * config->resolution / 1000000000, // Convert ns to ticks
.level1 = 0,
.duration1 = (float)config->timings.t0l * config->resolution / 1000000000,
},
.bit1 = {
.level0 = 1,
.duration0 = (float)config->timings.t1h * config->resolution / 1000000000,
.level1 = 0,
.duration1 = (float)config->timings.t1l * config->resolution / 1000000000,
},
.flags.msb_first = 1
};
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
rmt_copy_encoder_config_t copy_encoder_config = {};
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
// Convert reset time from microseconds to ticks
uint32_t reset_ticks = config->resolution / 1000000 * config->timings.reset / 2;
led_encoder->reset_code = (rmt_symbol_word_t) {
.level0 = 0,
.duration0 = reset_ticks,
.level1 = 0,
.duration1 = reset_ticks,
};
*ret_encoder = &led_encoder->base;
return ESP_OK;
err:
if (led_encoder) {
if (led_encoder->bytes_encoder) {
rmt_del_encoder(led_encoder->bytes_encoder);
}
if (led_encoder->copy_encoder) {
rmt_del_encoder(led_encoder->copy_encoder);
}
free(led_encoder);
}
return ret;
}
#endif // FASTLED_RMT5
#endif // ESP32

View File

@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "driver/rmt_encoder.h"
#include "led_strip_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type of led strip encoder configuration
*/
typedef struct {
uint32_t resolution; /*!< Encoder resolution, in Hz */
led_model_t led_model; /*!< LED model */
led_strip_encoder_timings_t timings; /*!< Encoder timings */
} led_strip_encoder_config_t;
/**
* @brief Create RMT encoder for encoding LED strip pixels into RMT symbols
*
* @param[in] config Encoder configuration
* @param[out] ret_encoder Returned encoder handle
* @return
* - ESP_ERR_INVALID_ARG for any invalid arguments
* - ESP_ERR_NO_MEM out of memory when creating led strip encoder
* - ESP_OK if creating encoder successfully
*/
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
/**
* @brief Create RMT encoder for encoding LED strip pixels into RMT symbols with custom timings
*
* @param[in] config Encoder configuration including custom timings
* @param[out] ret_encoder Returned encoder handle
* @return
* - ESP_ERR_INVALID_ARG for any invalid arguments
* - ESP_ERR_NO_MEM out of memory when creating led strip encoder
* - ESP_OK if creating encoder successfully
*/
esp_err_t rmt_new_led_strip_encoder_with_timings(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/spi_master.h"
#include "led_strip_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LED Strip SPI specific configuration
*/
typedef struct {
spi_clock_source_t clk_src; /*!< SPI clock source */
spi_host_device_t spi_bus; /*!< SPI bus ID. Which buses are available depends on the specific chip */
struct {
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
} flags; /*!< Extra driver flags */
} led_strip_spi_config_t;
/**
* @brief Create LED strip based on SPI MOSI channel
*
* @note Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
*
* @param led_config LED strip configuration
* @param spi_config SPI specific configuration
* @param ret_strip Returned LED strip handle
* @return
* - ESP_OK: create LED strip handle successfully
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
* - ESP_ERR_NOT_SUPPORTED: create LED strip handle failed because of unsupported configuration
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
* - ESP_FAIL: create LED strip handle failed because some other error
*/
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,261 @@
#ifdef ESP32
#include "enabled.h"
#if FASTLED_ESP32_HAS_CLOCKLESS_SPI
/*
* 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 "esp_rom_gpio.h"
#include "soc/spi_periph.h"
#include "led_strip.h"
#include "led_strip_interface.h"
#include "fl/unused.h"
#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
#define SPI_BYTES_PER_COLOR_BYTE 3
#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
static const char *TAG = "led_strip_spi";
typedef struct {
led_strip_t base;
spi_host_device_t spi_host;
spi_device_handle_t spi_device;
uint32_t strip_len;
uint8_t bytes_per_pixel;
led_color_component_format_t component_fmt;
uint8_t pixel_buf[];
} led_strip_spi_obj;
// please make sure to zero-initialize the buf before calling this function
static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
{
// Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
// So a color byte occupies 3 bytes of SPI.
*(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
*(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
*(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
*(buf + 1) |= BIT(0);
*(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
*(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
*(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
*(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
*(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
}
static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
{
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
// 3 pixels take 72bits(9bytes)
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
uint8_t *pixel_buf = spi_strip->pixel_buf;
led_color_component_format_t component_fmt = spi_strip->component_fmt;
memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
__led_strip_spi_bit(red, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.r_pos]);
__led_strip_spi_bit(green, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.g_pos]);
__led_strip_spi_bit(blue, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.b_pos]);
if (component_fmt.format.num_components > 3) {
__led_strip_spi_bit(0, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.w_pos]);
}
return ESP_OK;
}
static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
{
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
led_color_component_format_t component_fmt = spi_strip->component_fmt;
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
ESP_RETURN_ON_FALSE(component_fmt.format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
// LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
uint8_t *pixel_buf = spi_strip->pixel_buf;
memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
__led_strip_spi_bit(red, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.r_pos]);
__led_strip_spi_bit(green, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.g_pos]);
__led_strip_spi_bit(blue, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.b_pos]);
__led_strip_spi_bit(white, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * component_fmt.format.w_pos]);
return ESP_OK;
}
static esp_err_t spi_led_strip_refresh_async(led_strip_t *strip)
{
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
spi_transaction_t tx_conf;
memset(&tx_conf, 0, sizeof(tx_conf));
tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
tx_conf.tx_buffer = spi_strip->pixel_buf;
tx_conf.rx_buffer = NULL;
spi_device_queue_trans(spi_strip->spi_device, &tx_conf, portMAX_DELAY);
return ESP_OK;
}
static esp_err_t spi_led_strip_refresh_wait_done(led_strip_t *strip)
{
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
spi_transaction_t* tx_conf = 0;
spi_device_get_trans_result(spi_strip->spi_device, &tx_conf, portMAX_DELAY);
return ESP_OK;
}
static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
{
ESP_RETURN_ON_ERROR(spi_led_strip_refresh_async(strip), TAG, "refresh async failed");
ESP_RETURN_ON_ERROR(spi_led_strip_refresh_wait_done(strip), TAG, "wait for done failed");
return ESP_OK;
}
static esp_err_t led_strip_spi_clear(led_strip_t *strip)
{
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
//Write zero to turn off all leds
memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
uint8_t *buf = spi_strip->pixel_buf;
for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
__led_strip_spi_bit(0, buf);
buf += SPI_BYTES_PER_COLOR_BYTE;
}
return led_strip_spi_refresh(strip);
}
static esp_err_t led_strip_spi_del(led_strip_t *strip)
{
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
free(spi_strip);
return ESP_OK;
}
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip)
{
led_strip_spi_obj *spi_strip = NULL;
esp_err_t ret = ESP_OK;
FASTLED_UNUSED(ret);
ESP_GOTO_ON_FALSE(led_config && spi_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;
}
// 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);
}
// TODO: we assume each color component is 8 bits, may need to support other configurations in the future, e.g. 10bits per color component?
uint8_t bytes_per_pixel = component_fmt.format.num_components;
uint32_t mem_caps = MALLOC_CAP_DEFAULT;
if (spi_config->flags.with_dma) {
// DMA buffer must be placed in internal SRAM
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
}
spi_strip = heap_caps_calloc(1, sizeof(led_strip_spi_obj) + led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, mem_caps);
ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
spi_strip->spi_host = spi_config->spi_bus;
// for backward compatibility, if the user does not set the clk_src, use the default value
spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
if (spi_config->clk_src) {
clk_src = spi_config->clk_src;
}
spi_bus_config_t spi_bus_cfg = {
.mosi_io_num = led_config->strip_gpio_num,
//Only use MOSI to generate the signal, set -1 when other pins are not used.
.miso_io_num = -1,
.sclk_io_num = -1,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
};
ESP_GOTO_ON_ERROR(spi_bus_initialize(spi_strip->spi_host, &spi_bus_cfg, spi_config->flags.with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED), err, TAG, "create SPI bus failed");
if (led_config->flags.invert_out == true) {
esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
}
spi_device_interface_config_t spi_dev_cfg = {
.clock_source = clk_src,
.command_bits = 0,
.address_bits = 0,
.dummy_bits = 0,
.clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
.mode = 0,
//set -1 when CS is not used
.spics_io_num = -1,
.queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
};
ESP_GOTO_ON_ERROR(spi_bus_add_device(spi_strip->spi_host, &spi_dev_cfg, &spi_strip->spi_device), err, TAG, "Failed to add spi device");
//ensure the reset time is enough
esp_rom_delay_us(10);
int clock_resolution_khz = 0;
spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
// TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
// But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION
// clock_resolution between 2.2MHz to 2.8MHz is supported
ESP_GOTO_ON_FALSE((clock_resolution_khz < LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 + 300) && (clock_resolution_khz > LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 - 300), ESP_ERR_NOT_SUPPORTED, err,
TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
spi_strip->component_fmt = component_fmt;
spi_strip->bytes_per_pixel = bytes_per_pixel;
spi_strip->strip_len = led_config->max_leds;
spi_strip->base.set_pixel = led_strip_spi_set_pixel;
spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw;
spi_strip->base.refresh = led_strip_spi_refresh;
spi_strip->base.refresh_async = spi_led_strip_refresh_async;
spi_strip->base.refresh_wait_done = spi_led_strip_refresh_wait_done;
spi_strip->base.clear = led_strip_spi_clear;
spi_strip->base.del = led_strip_spi_del;
*ret_strip = &spi_strip->base;
return ESP_OK;
err:
if (spi_strip) {
if (spi_strip->spi_device) {
spi_bus_remove_device(spi_strip->spi_device);
}
if (spi_strip->spi_host) {
spi_bus_free(spi_strip->spi_host);
}
free(spi_strip);
}
return ret;
}
#endif // FASTLED_ESP_HAS_CLOCKLESS_SPI
#endif // ESP32

View File

@@ -0,0 +1,84 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type of LED strip handle
*/
typedef struct led_strip_t *led_strip_handle_t;
/**
* @brief LED strip model
* @note Different led model may have different timing parameters, so we need to distinguish them.
*/
typedef enum {
LED_MODEL_WS2812, /*!< LED strip model: WS2812 */
LED_MODEL_SK6812, /*!< LED strip model: SK6812 */
LED_MODEL_WS2811, /*!< LED strip model: WS2811 */
LED_MODEL_INVALID /*!< Invalid LED strip model */
} led_model_t;
/**
* @brief LED strip encoder timings.
* @note The timings are in nanoseconds. A zero filled structure will represent no timings given.
*/
typedef struct {
uint32_t t0h; /*!< High time for 0 bit, */
uint32_t t1h; /*!< High time for 1 bit */
uint32_t t0l; /*!< Low time for 0 bit */
uint32_t t1l; /*!< Low time for 1 bit */
uint32_t reset; /*!< Reset time, microseconds */
} led_strip_encoder_timings_t;
/**
* @brief LED color component format
* @note The format is used to specify the order of color components in each pixel, also the number of color components.
*/
typedef union {
struct format_layout {
uint32_t r_pos: 2; /*!< Position of the red channel in the color order: 0~3 */
uint32_t g_pos: 2; /*!< Position of the green channel in the color order: 0~3 */
uint32_t b_pos: 2; /*!< Position of the blue channel in the color order: 0~3 */
uint32_t w_pos: 2; /*!< Position of the white channel in the color order: 0~3 */
uint32_t reserved: 21; /*!< Reserved */
uint32_t num_components: 3; /*!< Number of color components per pixel: 3 or 4. If set to 0, it will fallback to 3 */
} format; /*!< Format layout */
uint32_t format_id; /*!< Format ID */
} led_color_component_format_t;
/// Helper macros to set the color component format
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .num_components = 3}}
#define LED_STRIP_COLOR_COMPONENT_FMT_GRBW (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .num_components = 4}}
#define LED_STRIP_COLOR_COMPONENT_FMT_RGB (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .num_components = 3}}
#define LED_STRIP_COLOR_COMPONENT_FMT_RGBW (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .num_components = 4}}
/**
* @brief LED Strip common configurations
* The common configurations are not specific to any backend peripheral.
*/
typedef struct {
int strip_gpio_num; /*!< GPIO number that used by LED strip */
uint32_t max_leds; /*!< Maximum number of LEDs that can be controlled in a single strip */
led_model_t led_model; /*!< Specifies the LED strip model (e.g., WS2812, SK6812) */
led_color_component_format_t color_component_format; /*!< Specifies the order of color components in each pixel.
Use helper macros like `LED_STRIP_COLOR_COMPONENT_FMT_GRB` to set the format */
/*!< LED strip extra driver flags */
struct led_strip_extra_flags {
uint32_t invert_out: 1; /*!< Invert output signal */
} flags; /*!< Extra driver flags */
led_strip_encoder_timings_t timings; /*!< Encoder timings */
} led_strip_config_t;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,6 @@
https://github.com/KurtMF/ObjectFLED
Version 1.10: https://github.com/KurtMF/ObjectFLED/releases/tag/v1.1.0
downloaded date: 2025-Jan. 6th
Light modifications have been made to this library.

View File

@@ -0,0 +1,206 @@
/* ObjectFLED - Teensy 4.x DMA to all pins for independent control of large and
multiple LED display objects
Copyright (c) 2024 Kurt Funderburg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
OctoWS2811 library code was well-studied and substantial portions of it used
to implement high-speed, non-blocking DMA for LED signal output in this library.
See ObjectFLED.cpp for a summary of changes made to the original OctoWS2811.
OctoWS2811 - High Performance WS2811 LED Display Library
Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC
http://www.pjrc.com/teensy/td_libs_OctoWS2811.html
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef __IMXRT1062__
#error "Sorry, ObjectFLED only works on Teensy 4.x boards."
#endif
#if TEENSYDUINO < 121
#error "Teensyduino version 1.21 or later is required to compile this library."
#endif
#ifndef ObjectFLED_h
#define ObjectFLED_h
#include <WProgram.h>
#include "DMAChannel.h"
//Experimentally found DSE=3, SPEED=0 gave best LED overclocking
//boot defaults DSE=6, SPEED=2.
#define OUTPUT_PAD_DSE 3 //Legal values 0-7
#define OUTPUT_PAD_SPEED 0 //Legal values 0-3
// Ordinary RGB data is converted to GPIO bitmasks on-the-fly using
// a transmit buffer sized for 2 DMA transfers. The larger this setting,
// the more interrupt latency OctoWS2811 can tolerate, but the transmit
// buffer grows in size. For good performance, the buffer should be kept
// smaller than the half the Cortex-M7 data cache.
//bitdata[B_P_D * 64] buffer holds data (10KB) for 80 LED bytes: 4DW * 8b = 32DW/LEDB = 96DW/LED
//framebuffer_index = B_P_D * 2 = pointer to next block for transfer (80 LEDB / bitdata buffer)
#define BYTES_PER_DMA 20 //= number of pairs of LEDB (40=80B) bitmasks in bitdata.
#define CORDER_RGB 0 //* WS2811, YF923
#define CORDER_RBG 1
#define CORDER_GRB 2 //* WS2811B, Most LED strips are wired this way
#define CORDER_GBR 3 //*
#define CORDER_BRG 4 //* Adafruit Product ID: 5984 As of November 5, 2024 - this strand has different 'internal' color ordering. It's now BRG not RGB,
#define CORDER_BGR 5 //* Adafruit Dotstar LEDs SK9822 uses this CO but they use inverted start/stop bits
#define CORDER_RGBW 6 //* Popular
#define CORDER_RBGW 7
#define CORDER_GRBW 8
#define CORDER_GBRW 9
#define CORDER_BRGW 10
#define CORDER_BGRW 11 // Adafruit Dotstar LEDs SK9822 uses this CO but they use inverted start/stop bits
#define CORDER_WRGB 12
#define CORDER_WRBG 13
#define CORDER_WGRB 14
#define CORDER_WGBR 15
#define CORDER_WBRG 16
#define CORDER_WBGR 17
#define CORDER_RWGB 18
#define CORDER_RWBG 19
#define CORDER_GWRB 20
#define CORDER_GWBR 21
#define CORDER_BWRG 22
#define CORDER_BWGR 23
#define CORDER_RGWB 24
#define CORDER_RBWG 25
#define CORDER_GRWB 26
#define CORDER_GBWR 27
#define CORDER_BRWG 28
#define CORDER_BGWR 29
namespace fl {
//Usage: ObjectFLED myCube ( Num_LEDs, *drawBuffer, LED_type, numPins, *pinList, serpentineNumber )
class ObjectFLED {
public:
// Usage: ObjectFLED myCube ( Num_LEDs, *drawBuffer, LED_type, numPins, *pinList, serpentineNumber )
// Example:
// byte pinList[NUM_CHANNELS] = {1, 8, 14, 17, 24, 29, 20, 0, 15, 16, 18, 19, 21, 22, 23, 25};
// byte pinListBlank[7] = {1, 8, 14, 17, 24, 29, 20};
// CRGB testCube[NUM_PLANES][NUM_ROWS][PIX_PER_ROW];
// CRGB blankLeds[7][8][8];
// ObjectFLED leds(PIX_PER_ROW * NUM_ROWS * NUM_PLANES, testCube, CORDER_RGB, NUM_CHANNELS, pinList);
// void setup() {
// leds.begin(1.6, 72); //1.6 ocervlock factor, 72uS LED latch delay
// leds.setBrightness(64);
// }
// void loop() {
// leds.show();
// delay(100);
// }
ObjectFLED(uint16_t numLEDs, void* drawBuf, uint8_t config, uint8_t numPins, const uint8_t* pinList, \
uint8_t serpentine = 0);
~ObjectFLED() {
// Wait for prior xmission to end, don't need to wait for latch time before deleting buffer
while (micros() - update_begin_micros < numbytes * 8 * TH_TL / OC_FACTOR / 1000 + 5);
delete frameBuffer;
}
//begin() - Use defalut LED timing: 1.0 OC Factor, 1250 nS CLK (=800 KHz), 300 nS T0H, 750 nS T1H, 300 uS LED Latch Delay.
void begin(void);
//begin(LED_Latch_Delay_uS) - sets the LED Latch Delay.
void begin(uint16_t);
//begin(LED_Overclock_Factor, LED_Latch_Delay_uS) - divides default 1250 nS LED CLK (=800 KHz),
// 300 nS T0H, 750 nS T1H; and optionally sets the LED Latch Delay.
void begin(double, uint16_t = 300);
//begin(LED_CLK_nS, LED_T0H_nS, LED_T1H_nS, LED_Latch_Delay_uS) - specifies full LED waveform timing.
void begin(uint16_t, uint16_t, uint16_t, uint16_t = 300);
void show(void);
void waitForDmaToFinish() {
while (!dma3.complete()) { // wait for dma to complete before reset/re-use
delayMicroseconds(10);
}
}
int busy(void);
//Brightness values 0-255
void setBrightness(uint8_t);
//Color Balance is 3-byte number in RGB order. Each byte is a brightness value for that color.
void setBalance(uint32_t);
uint8_t getBrightness() { return brightness; }
uint32_t getBalance() { return colorBalance; }
private:
static void isr(void);
void genFrameBuffer(uint32_t);
static uint8_t* frameBuffer; //isr()
static uint32_t numbytes; //isr()
static uint8_t numpins; //isr()
static uint8_t pin_bitnum[NUM_DIGITAL_PINS]; //isr()
static uint8_t pin_offset[NUM_DIGITAL_PINS]; //isr()
DMAMEM static uint32_t bitdata[BYTES_PER_DMA * 64] __attribute__((used, aligned(32))); //isr()
DMAMEM static uint32_t bitmask[4] __attribute__((used, aligned(32)));
static DMAChannel dma1, dma2, dma3;
static DMASetting dma2next;
uint32_t update_begin_micros = 0;
uint8_t brightness = 255;
uint32_t colorBalance = 0xFFFFFF;
uint32_t rLevel = 65025;
uint32_t gLevel = 65025;
uint32_t bLevel = 65025;
void* drawBuffer;
uint16_t stripLen;
uint8_t params;
uint8_t pinlist[NUM_DIGITAL_PINS];
uint16_t comp1load[3];
uint8_t serpNumber;
float OC_FACTOR = 1.0; //used to reduce period of LED output
uint16_t TH_TL = 1250; //nS- period of LED output
uint16_t T0H = 300; //nS- duration of T0H
uint16_t T1H = 750; //nS- duration of T1H
uint16_t LATCH_DELAY = 300; //uS time to hold output low for LED latch.
//for show context switch
uint32_t bitmaskLocal[4];
uint8_t numpinsLocal;
uint8_t* frameBufferLocal;
uint32_t numbytesLocal;
uint8_t pin_bitnumLocal[NUM_DIGITAL_PINS];
uint8_t pin_offsetLocal[NUM_DIGITAL_PINS];
}; // class ObjectFLED
//fadeToColorBy(RGB_array, LED_count, Color, FadeAmount)
//Fades an RGB array towards the background color by amount.
void fadeToColorBy(void*, uint16_t, uint32_t, uint8_t);
//drawSquare(RGB_Array, LED_Rows, LED_Cols, Y_Corner, X_Corner, square_Size)
//Draws square in a 2D RGB array with lower left corner at (Y_Corner, X_Corner).
//Safe to specify -Y, -X corner, safe to draw a box which partially fits on LED plane.
void drawSquare(void*, uint16_t, uint16_t, int, int, uint32_t, uint32_t);
} // namespace fl
#endif

View File

@@ -0,0 +1,656 @@
/* ObjectFLED - Teensy 4.x DMA to all pins for independent control of large and
multiple LED display objects
Copyright (c) 2024 Kurt Funderburg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
OctoWS2811 library code was well-studied and substantial portions of it used
to implement high-speed, non-blocking DMA for LED signal output in this library.
See below for a summary of changes made to the original OctoWS2811.
OctoWS2811 - High Performance WS2811 LED Display Library
Copyright (c) 2013 Paul Stoffregen, PJRC.COM, LLC
http://www.pjrc.com/teensy/td_libs_OctoWS2811.html
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/*
Teensy 4.0 pin - port assignments
GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 } //12 top, 4 bottom
GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32 } //8 top, 1 bottom
GPIO8List = { 28, 30, 31, 34, 35, 36, 37, 38, 39 } //0 top, 9 bottom
GOIO9List = { 2, 3, 4, 5, 29, 33 } //4 top, 2 bottom
Teensy 4.1 pin - port assignments
GPIO6List = { 0, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 38, 39, 40, 41 } //20 top, 0 bottom
GPIO7List = { 6, 7, 8, 9, 10, 11, 12, 13, 32, 34, 35, 36, 37 } //13 top, 0 bottom
GPIO8List = { 28, 30, 31, 42, 43, 44, 45, 46, 47 } //3 top, 6 bottom
GOIO9List = { 2, 3, 4, 5, 29, 33, 48, 49, 50, 51, 52, 53, 54 } //6 top, 7 bottom
* 4 pin groups, 4 timer groups, !4 dma channel groups: only 1 DMA group (4 ch) maps to GPIO (DMAMUX
* mapping) Also, only DMA ch0-3 have periodic mode for timer trigger (p77 manual). Separate objects
* cannot DMA at once.
*
* CHANGES:
* Moved some variables so class supports multiple instances with separate LED config params
* Implemented context switching so multiple instances can show() independently
* Re-parameterized TH_TL, T0H, T1H, OC_FACTOR; recalc time for latch at end of show()
* Added genFrameBuffer() to implement RGB order, brightness, color balance, and serpentine
* Added setBrightness(), setBalance()
* FrameBuffer no longer passed in, constructor now creates buffer; destructor added
* Added support for per-object setting of OC factor, TH+TL, T0H, T1H, and LATCH_DELAY in begin function
* Set DSE=3, SPEED=0, SRE=0 on output pins per experiment & PJRC forum guidance
* New default values for TH_TL, T0H, T1H, LATCH_DELAY to work with Audio lib and more LED types
* Added wait for prior xmission to complete in destructor
*/
#ifndef __IMXRT1062__
// Do nothing for other platforms.
#else
#include "ObjectFLED.h"
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b))
#endif
namespace fl {
volatile uint32_t framebuffer_index = 0; //isr()
uint8_t* ObjectFLED::frameBuffer; //isr()
uint32_t ObjectFLED::numbytes; //isr()
uint8_t ObjectFLED::numpins; //isr()
uint8_t ObjectFLED::pin_bitnum[NUM_DIGITAL_PINS]; //isr()
uint8_t ObjectFLED::pin_offset[NUM_DIGITAL_PINS]; //isr()
uint32_t ObjectFLED::bitdata[BYTES_PER_DMA * 64] __attribute__((used, aligned(32))); //isr()
uint32_t ObjectFLED::bitmask[4] __attribute__((used, aligned(32)));
DMASetting ObjectFLED::dma2next;
DMAChannel ObjectFLED::dma1;
DMAChannel ObjectFLED::dma2;
DMAChannel ObjectFLED::dma3;
volatile bool dma_first;
ObjectFLED::ObjectFLED(uint16_t numLEDs, void *drawBuf, uint8_t config, uint8_t numPins, \
const uint8_t *pinList, uint8_t serpentine) {
serpNumber = serpentine;
drawBuffer = drawBuf;
params = config;
if (numPins > NUM_DIGITAL_PINS) numPins = NUM_DIGITAL_PINS;
numpins = numPins; //static/isr
stripLen = numLEDs / numpins;
memcpy(pinlist, pinList, numpins);
if ((params & 0x3F) < 6) {
frameBuffer = new uint8_t[numLEDs * 3]; //static/isr
numbytes = stripLen * 3; // RGB formats //static/isr
}
else {
frameBuffer = new uint8_t[numLEDs * 4]; //static/isr
numbytes = stripLen * 4; // RGBW formats //static/isr
}
numpinsLocal = numPins;
frameBufferLocal = frameBuffer;
numbytesLocal = numbytes;
} // ObjectFLED constructor
extern "C" void xbar_connect(unsigned int input, unsigned int output); // in pwm.c
static volatile uint32_t *standard_gpio_addr(volatile uint32_t *fastgpio) {
return (volatile uint32_t *)((uint32_t)fastgpio - 0x01E48000);
}
void ObjectFLED::begin(uint16_t latchDelay) {
LATCH_DELAY = latchDelay;
begin();
}
void ObjectFLED::begin(double OCF, uint16_t latchDelay) {
OC_FACTOR = (float)OCF;
LATCH_DELAY = latchDelay;
begin();
}
void ObjectFLED::begin(uint16_t period, uint16_t t0h, uint16_t t1h, uint16_t latchDelay) {
TH_TL = period;
T0H = t0h;
T1H = t1h;
LATCH_DELAY = latchDelay;
begin();
}
// INPUT stripLen, frameBuffer, params, numPins, pinList
// GPIOR bits set for pins[i] -> bitmask, pin_bitnum[i], pin_offset[i]
// init timers, xbar to DMA, DMA bitdata -> GPIOR; clears frameBuffer (total LEDs * 3 bytes)
void ObjectFLED::begin(void) {
numpins = numpinsLocal; //needed to compute pin mask/offset & bitmask since static for isr
// Set each pin's bitmask bit, store offset & bit# for pin
memset(bitmask, 0, sizeof(bitmask));
for (uint32_t i=0; i < numpins; i++) {
uint8_t pin = pinlist[i];
if (pin >= NUM_DIGITAL_PINS) continue; // ignore illegal pins
uint8_t bit = digitalPinToBit(pin); // pin's bit index in word port DR
// which GPIO R controls this pin: 0-3 map to GPIO6-9 then map to DMA compat GPIO1-4
uint8_t offset = ((uint32_t)portOutputRegister(pin) - (uint32_t)&GPIO6_DR) >> 14;
if (offset > 3) continue; //ignore unknown pins
pin_bitnum[i] = bit; //static/isr
pin_offset[i] = offset; //static/isr
uint32_t mask = 1 << bit; //mask32 = bit set @position in GPIO DR
bitmask[offset] |= mask; //bitmask32[0..3] = collective pin bit masks for each GPIO DR
//bit7:6 SPEED; bit 5:3 DSE; bit0 SRE (default SPEED = 0b10; def. DSE = 0b110)
*portControlRegister(pin) &= ~0xF9; //clear SPEED, DSE, SRE
*portControlRegister(pin) |= ((OUTPUT_PAD_SPEED & 0x3) << 6) | \
((OUTPUT_PAD_DSE & 0x7) << 3); //DSE = 0b011 for LED overclock
//clear pin bit in IOMUX_GPR26 to map GPIO6-9 to GPIO1-4 for DMA
*(&IOMUXC_GPR_GPR26 + offset) &= ~mask;
*standard_gpio_addr(portModeRegister(pin)) |= mask; //GDIR? bit flag set output mode
}
//stash context for multi-show
memcpy(bitmaskLocal, bitmask, 16);
memcpy(pin_bitnumLocal, pin_bitnum, numpins);
memcpy(pin_offsetLocal, pin_offset, numpins);
arm_dcache_flush_delete(bitmask, sizeof(bitmask)); //can't DMA from cached memory
// Set up 3 timers to create waveform timing events
comp1load[0] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)TH_TL / OC_FACTOR );
comp1load[1] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T0H / OC_FACTOR );
comp1load[2] = (uint16_t)((float)F_BUS_ACTUAL / 1000000000.0 * (float)T1H / (1.0 + ((OC_FACTOR - 1.0)/3)) );
TMR4_ENBL &= ~7;
TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
TMR4_CSCTRL0 = TMR_CSCTRL_CL1(1) | TMR_CSCTRL_TCF1EN;
TMR4_CNTR0 = 0;
TMR4_LOAD0 = 0;
TMR4_COMP10 = comp1load[0];
TMR4_CMPLD10 = comp1load[0];
TMR4_CTRL0 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH | TMR_CTRL_OUTMODE(3);
TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
TMR4_CNTR1 = 0;
TMR4_LOAD1 = 0;
TMR4_COMP11 = comp1load[1]; // T0H
TMR4_CMPLD11 = comp1load[1];
TMR4_CTRL1 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
TMR4_CNTR2 = 0;
TMR4_LOAD2 = 0;
TMR4_COMP12 = comp1load[2]; // T1H
TMR4_CMPLD12 = comp1load[2];
TMR4_CTRL2 = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_COINIT | TMR_CTRL_OUTMODE(3);
// route the timer outputs through XBAR to edge trigger DMA request: only 4 mappings avail.
CCM_CCGR2 |= CCM_CCGR2_XBAR1(CCM_CCGR_ON);
xbar_connect(XBARA1_IN_QTIMER4_TIMER0, XBARA1_OUT_DMA_CH_MUX_REQ30);
xbar_connect(XBARA1_IN_QTIMER4_TIMER1, XBARA1_OUT_DMA_CH_MUX_REQ31);
xbar_connect(XBARA1_IN_QTIMER4_TIMER2, XBARA1_OUT_DMA_CH_MUX_REQ94);
XBARA1_CTRL0 = XBARA_CTRL_STS1 | XBARA_CTRL_EDGE1(3) | XBARA_CTRL_DEN1 |
XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
XBARA1_CTRL1 = XBARA_CTRL_STS0 | XBARA_CTRL_EDGE0(3) | XBARA_CTRL_DEN0;
// configure DMA channels
dma1.begin();
dma1.TCD->SADDR = bitmask; // source 4*32b GPIO pin mask
dma1.TCD->SOFF = 8; // bytes offset added to SADDR after each transfer
// SMOD(4) low bits of SADDR to update with adds of SOFF
// SSIZE(3) code for 64 bit transfer size DSIZE(2) code for 32 bit transfer size
dma1.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
dma1.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE | // Dest minor loop offsetting enable
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(16); // #bytes to tansfer, offsetting enabled
dma1.TCD->SLAST = 0; // add to SADDR after xfer
dma1.TCD->DADDR = &GPIO1_DR_SET;
dma1.TCD->DOFF = 16384; //&GPIO1_DR_SET + DOFF = next &GPIO2_DR_SET
dma1.TCD->CITER_ELINKNO = numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write
dma1.TCD->DLASTSGA = -65536; // add to DADDR after xfer
dma1.TCD->BITER_ELINKNO = numbytes * 8; // Beginning CITER (not decremented by transfer)
dma1.TCD->CSR = DMA_TCD_CSR_DREQ; // channel ERQ field cleared when minor loop completed
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_0); // only 4 XBAR1 triggers (DMA MUX mapping)
dma2next.TCD->SADDR = bitdata; //uint32_t bitdata[BYTES_PER_DMA*64]
dma2next.TCD->SOFF = 8;
dma2next.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_DSIZE(2);
dma2next.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
dma2next.TCD->SLAST = 0;
dma2next.TCD->DADDR = &GPIO1_DR_CLEAR;
dma2next.TCD->DOFF = 16384;
dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
dma2next.TCD->DLASTSGA = (int32_t)(dma2next.TCD);
dma2next.TCD->BITER_ELINKNO = BYTES_PER_DMA * 8;
dma2next.TCD->CSR = 0;
dma2.begin();
dma2 = dma2next; // copies TCD
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_1);
dma2.attachInterrupt(isr);
dma3.begin();
dma3.TCD->SADDR = bitmask;
dma3.TCD->SOFF = 8;
dma3.TCD->ATTR = DMA_TCD_ATTR_SSIZE(3) | DMA_TCD_ATTR_SMOD(4) | DMA_TCD_ATTR_DSIZE(2);
dma3.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-65536) |
DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
dma3.TCD->SLAST = 0;
dma3.TCD->DADDR = &GPIO1_DR_CLEAR;
dma3.TCD->DOFF = 16384;
dma3.TCD->CITER_ELINKNO = numbytes * 8;
dma3.TCD->DLASTSGA = -65536;
dma3.TCD->BITER_ELINKNO = numbytes * 8;
dma3.TCD->CSR = DMA_TCD_CSR_DREQ | DMA_TCD_CSR_DONE;
dma3.triggerAtHardwareEvent(DMAMUX_SOURCE_XBAR1_2);
} // begin()
//*dest = *bitdata + pin offset
//*pixels = pin's block in frameBuffer
//mask = pin's bit position in GPIOR
//set a pin's mask32 for each color bit=0 at every 4*words32 in bitdata+offset
void fillbits(uint32_t *dest, const uint8_t *pixels, int n, uint32_t mask) {
do {
uint8_t pix = *pixels++;
if (!(pix & 0x80)) *dest |= mask;
dest += 4;
if (!(pix & 0x40)) *dest |= mask;
dest += 4;
if (!(pix & 0x20)) *dest |= mask;
dest += 4;
if (!(pix & 0x10)) *dest |= mask;
dest += 4;
if (!(pix & 0x08)) *dest |= mask;
dest += 4;
if (!(pix & 0x04)) *dest |= mask;
dest += 4;
if (!(pix & 0x02)) *dest |= mask;
dest += 4;
if (!(pix & 0x01)) *dest |= mask;
dest += 4;
} while (--n > 0);
}
void ObjectFLED::genFrameBuffer(uint32_t serp) {
uint32_t j = 0;
int jChange = -3;
if (serp == 0) { // use faster loops if no serp
switch (params & 0x3F) {
case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \
*((uint8_t*)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025);
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += 3;
} //for(leds in drawbuffer)
break;
case CORDER_GBR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} //for(leds in drawbuffer)
break;
case CORDER_BGR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} //for(leds in drawbuffer)
break;
case CORDER_BRG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} //for(leds in drawbuffer)
break;
case CORDER_GRB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} //for(leds in drawbuffer)
break;
case CORDER_RGB:
default:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} //for(leds in drawbuffer)
} // switch()
} else { //serpentine
switch (params & 0x3F) {
case CORDER_RGBW: // R,G,B = R,G,B - MIN(R,G,B); W = MIN(R,G,B)
for (uint16_t i = 0; i < (numbytes * numpins); i += 4) {
uint8_t minRGB = MIN(*((uint8_t*)drawBuffer + j) * rLevel / 65025, \
* ((uint8_t*)drawBuffer + j + 1) * rLevel / 65025);
minRGB = MIN(minRGB, *((uint8_t*)drawBuffer + j + 2) * rLevel / 65025);
if (i % (serp * 4) == 0) {
if (jChange < 0) { j = i / 4 * 3; jChange = 3; }
else { j = (i / 4 + serp - 1) * 3; jChange = -3; }
}
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025 - minRGB;
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025 - minRGB;
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025 - minRGB;
*(frameBuffer + i + 3) = minRGB;
j += jChange;
} //for(leds in drawbuffer)
break;
case CORDER_GBR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) { j = i; jChange = 3; }
else { j = i + (serp - 1) * 3; jChange = -3; }
}
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} //for(leds in drawbuffer)
break;
case CORDER_BGR:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) { j = i; jChange = 3; }
else { j = i + (serp - 1) * 3; jChange = -3; }
}
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += 3;
} //for(leds in drawbuffer)
break;
case CORDER_BRG:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) { j = i; jChange = 3; }
else { j = i + (serp - 1) * 3; jChange = -3; }
}
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += jChange;
} //for(leds in drawbuffer)
break;
case CORDER_GRB:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) { j = i; jChange = 3; }
else { j = i + (serp - 1) * 3; jChange = -3; }
}
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += jChange;
} //for(leds in drawbuffer)
break;
case CORDER_RGB:
default:
for (uint16_t i = 0; i < (numbytes * numpins); i += 3) {
if (i % (serp * 3) == 0) {
if (jChange < 0) { j = i; jChange = 3; }
else { j = i + (serp - 1) * 3; jChange = -3; }
}
*(frameBuffer + i) = *((uint8_t*)drawBuffer + j) * rLevel / 65025;
*(frameBuffer + i + 1) = *((uint8_t*)drawBuffer + j + 1) * gLevel / 65025;
*(frameBuffer + i + 2) = *((uint8_t*)drawBuffer + j + 2) * bLevel / 65025;
j += jChange;
} //for(leds in drawbuffer)
} // switch()
} // else serpentine
} //genFrameBuffer()
// pre-show prior transfer wait, copies drawBuffer -> frameBuffer
// resets timers, clears pending DMA reqs
// fills bitdata[BYTES_PER_DMA * 64 * 4 bytes] from frameBuffer with 4-block bitmasks for 0's in led data
// 4 word32s for each bit in (led data)/pin = 16 * 8 = 96 bitdata bytes for each LED byte: 288 bytes / LED
// launches DMA with IRQ activation to reload bitdata from frameBuffer
void ObjectFLED::show(void) {
waitForDmaToFinish(); //wait for prior DMA to finish
//Restore context if needed
if (frameBuffer != frameBufferLocal) {
numpins = numpinsLocal;
frameBuffer = frameBufferLocal;
numbytes = numbytesLocal;
memcpy(bitmask, bitmaskLocal, 16);
memcpy(pin_bitnum, pin_bitnumLocal, numpins);
memcpy(pin_offset, pin_offsetLocal, numpins);
arm_dcache_flush_delete(bitmask, sizeof(bitmask)); //can't DMA from cached memory
// Restore 3 timers to create waveform timing events
TMR4_COMP10 = comp1load[0];
TMR4_CMPLD10 = comp1load[0];
TMR4_COMP11 = comp1load[1]; // T0H
TMR4_CMPLD11 = comp1load[1];
TMR4_COMP12 = comp1load[2]; // T1H
TMR4_CMPLD12 = comp1load[2];
//restore DMA loop control
dma1.TCD->CITER_ELINKNO = numbytes * 8; // CITER outer loop count (linking disabled) = # LED bits to write
dma1.TCD->BITER_ELINKNO = numbytes * 8; // Beginning CITER (not decremented by transfer)
dma3.TCD->CITER_ELINKNO = numbytes * 8;
dma3.TCD->BITER_ELINKNO = numbytes * 8;
} //done restoring context
genFrameBuffer(serpNumber);
// disable timers
uint16_t enable = TMR4_ENBL;
TMR4_ENBL = enable & ~7;
// force all timer outputs to logic low
TMR4_SCTRL0 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE | TMR_SCTRL_MSTR;
TMR4_SCTRL1 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
TMR4_SCTRL2 = TMR_SCTRL_OEN | TMR_SCTRL_FORCE;
// clear any prior pending DMA requests
XBARA1_CTRL0 |= XBARA_CTRL_STS1 | XBARA_CTRL_STS0;
XBARA1_CTRL1 |= XBARA_CTRL_STS0;
// fill the DMA transmit buffer
memset(bitdata, 0, sizeof(bitdata)); //BYTES_PER_DMA * 64 words32
uint32_t count = numbytes; //bytes per strip
if (count > BYTES_PER_DMA*2) count = BYTES_PER_DMA*2;
framebuffer_index = count; //ptr to framebuffer last byte output
//Sets each pin mask in bitdata32[BYTES_PER_DMA*64] for every 0 bit of pin's frameBuffer block bytes
for (uint32_t i=0; i < numpins; i++) { //for each pin
fillbits(bitdata + pin_offset[i], (uint8_t *)frameBuffer + i*numbytes,
count, 1<<pin_bitnum[i]);
}
arm_dcache_flush_delete(bitdata, count * 128); // don't need bitdata in cache for DMA
// set up DMA transfers
if (numbytes <= BYTES_PER_DMA*2) {
dma2.TCD->SADDR = bitdata;
dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
dma2.TCD->CITER_ELINKNO = count * 8;
dma2.TCD->CSR = DMA_TCD_CSR_DREQ;
} else {
dma2.TCD->SADDR = bitdata;
dma2.TCD->DADDR = &GPIO1_DR_CLEAR;
dma2.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
dma2.TCD->CSR = 0;
dma2.TCD->CSR = DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_ESG;
dma2next.TCD->SADDR = bitdata + BYTES_PER_DMA*32;
dma2next.TCD->CITER_ELINKNO = BYTES_PER_DMA * 8;
if (numbytes <= BYTES_PER_DMA*3) {
dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
} else {
dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
}
dma_first = true;
}
dma3.clearComplete();
dma1.enable();
dma2.enable();
dma3.enable();
// initialize timers
TMR4_CNTR0 = 0;
TMR4_CNTR1 = comp1load[0] + 1;
TMR4_CNTR2 = comp1load[0] + 1;
// wait for last LED reset to finish
while (micros() - update_begin_micros < numbytes * 8 * TH_TL / OC_FACTOR / 1000 + LATCH_DELAY);
// start everything running!
TMR4_ENBL = enable | 7;
update_begin_micros = micros();
} // show()
//INPUT: dma2, dma2next, bitdata, framebuffer_inedex, numpins, numbytes, pin_offset[], pin_bitnum[]
//Reads next block of framebuffer -> fillbits() -> bitdata
//Checks for last block to transfer, next to last, or not to update dma2next major loop
void ObjectFLED::isr(void)
{
// first ack the interrupt
dma2.clearInterrupt();
// fill (up to) half the transmit buffer with new fillbits(frameBuffer data)
//digitalWriteFast(12, HIGH);
uint32_t *dest;
if (dma_first) {
dma_first = false;
dest = bitdata;
} else {
dma_first = true;
dest = bitdata + BYTES_PER_DMA*32;
}
memset(dest, 0, sizeof(bitdata)/2);
uint32_t index = framebuffer_index;
uint32_t count = numbytes - framebuffer_index;
if (count > BYTES_PER_DMA) count = BYTES_PER_DMA;
framebuffer_index = index + count;
for (int i=0; i < numpins; i++) {
fillbits(dest + pin_offset[i], (uint8_t *)frameBuffer + index + i*numbytes,
count, 1<<pin_bitnum[i]);
}
arm_dcache_flush_delete(dest, count * 128);
//digitalWriteFast(12, LOW);
// queue it for the next DMA transfer
dma2next.TCD->SADDR = dest;
dma2next.TCD->CITER_ELINKNO = count * 8;
uint32_t remain = numbytes - (index + count);
if (remain == 0) {
dma2next.TCD->CSR = DMA_TCD_CSR_DREQ;
} else if (remain <= BYTES_PER_DMA) {
dma2next.TCD->CSR = DMA_TCD_CSR_ESG;
} else {
dma2next.TCD->CSR = DMA_TCD_CSR_ESG | DMA_TCD_CSR_INTMAJOR;
}
} // isr()
int ObjectFLED::busy(void)
{
if (micros() - update_begin_micros < numbytes * TH_TL / OC_FACTOR / 1000 * 8 + LATCH_DELAY) {
return 1;
}
return 0;
}
void ObjectFLED::setBrightness(uint8_t brightLevel) {
brightness = brightLevel;
rLevel = brightness * (colorBalance >> 16);
gLevel = brightness * ((colorBalance >> 8) & 0xFF);
bLevel = brightness * (colorBalance & 0xFF);
}
void ObjectFLED::setBalance(uint32_t balMask) {
colorBalance = balMask & 0xFFFFFF;
rLevel = brightness * (colorBalance >> 16);
gLevel = brightness * ((colorBalance >> 8) & 0xFF);
bLevel = brightness * (colorBalance & 0xFF);
}
//Fades CRGB array towards the background color by amount.
void fadeToColorBy(void* leds, uint16_t count, uint32_t color, uint8_t fadeAmt) {
for (uint32_t x = 0; x < count * 3; x += 3) {
//fade red
*((uint8_t*)leds + x) = (( *((uint8_t*)leds + x) * (1 + (255 - fadeAmt))) >> 8) + \
(( ((color >> 16) & 0xFF) * (1 + fadeAmt)) >> 8);
//fade green
*((uint8_t*)leds + x + 1) = (( *((uint8_t*)leds + x + 1) * (1 + (255 - fadeAmt))) >> 8) + \
(( ((color >> 8) & 0xFF) * (1 + fadeAmt)) >> 8);
//fade blue
*((uint8_t*)leds + x + 2) = (( *((uint8_t*)leds + x + 2) * (1 + (255 - fadeAmt))) >> 8) + \
(( (color & 0xFF) * (1 + fadeAmt)) >> 8);
}
} //fadeToColorBy()
// Safely draws box even if partially offscreen on 2D CRGB array
void drawSquare(void* leds, uint16_t planeY, uint16_t planeX, int yCorner, int xCorner, uint32_t size, uint32_t color) {
if (size != 0) { size--; }
else { return; }
for (int x = xCorner; x <= xCorner + (int)size; x++) {
// if validX { if validY+S {draw Y+S,X}; if validY {draw Y, X} }
if ((x >= 0) && (x < planeX)) { //valid X
if ((yCorner >= 0) && (yCorner < planeY)) {
*((uint8_t*)leds + (yCorner * planeX + x) * 3) = ((color >> 16) & 0xFF);
*((uint8_t*)leds + (yCorner * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF);
*((uint8_t*)leds + (yCorner * planeX + x) * 3 + 2) = (color & 0xFF);
}
if ((yCorner + size >= 0) && (yCorner + size < planeY)) {
*((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3) = ((color >> 16) & 0xFF);
*((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3 + 1) = ((color >> 8) & 0xFF);
*((uint8_t*)leds + ((yCorner + size) * planeX + x) * 3 + 2) = (color & 0xFF);
}
} //if valid x
} //for x
for (int y = yCorner; y <= yCorner + (int)size; y++) {
if ((y >= 0) && (y < planeY)) { //valid y
if ((xCorner >= 0) && (xCorner < planeX)) {
*((uint8_t*)leds + (xCorner + y * planeX) * 3) = ((color >> 16) & 0xFF);
*((uint8_t*)leds + (xCorner + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF);
*((uint8_t*)leds + (xCorner + y * planeX) * 3 + 2) = (color & 0xFF);
}
if ((xCorner + size >= 0) && (xCorner + size < planeX)) {
*((uint8_t*)leds + (xCorner + size + y * planeX) * 3) = ((color >> 16) & 0xFF);
*((uint8_t*)leds + (xCorner + size + y * planeX) * 3 + 1) = ((color >> 8) & 0xFF);
*((uint8_t*)leds + (xCorner + size + y * planeX) * 3 + 2) = (color & 0xFF);
}
} //if valid y
} //for y
} // drawSquare()
} // namespace fl
#endif // __IMXRT1062__

View File

@@ -0,0 +1,3 @@
Repo: https://github.com/hpwit/I2SClockLessLedDriveresp32s3
commit: 48e9cb6d3db0e902703b0325bfaa153074f071d8

View File

@@ -0,0 +1,11 @@
#pragma once
#ifndef USE_FASTLED
#define USE_FASTLED
#endif // USE_FASTLED
#define COLOR_ORDER_RBG
#include "src/I2SClockLessLedDriveresp32s3.h"

View File

@@ -0,0 +1,506 @@
#if !__has_include("esp_memory_utils.h")
#error \
"esp_memory_utils.h is not available, are you using esp-idf 4.4 or earlier?"
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"
#pragma GCC diagnostic ignored "-Wvolatile"
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_intr_alloc.h"
#include "esp_memory_utils.h"
#include "esp_pm.h"
#include <stdint.h>
#include <stdio.h> // ok include
#include <string.h>
// #include "esp_lcd_panel_io_interface.h"
// #include "esp_lcd_panel_io.h"
#include "driver/gpio.h"
#include "esp_memory_utils.h"
#include "esp_private/gdma.h"
#include "esp_rom_gpio.h"
#include "hal/dma_types.h"
#include "hal/gpio_hal.h"
#include "soc/rtc.h" // for `rtc_clk_xtal_freq_get()`
#include "soc/soc_caps.h"
// #include "esp_private/periph_ctrl.h"
// #include "esp_lcd_common.h"
#include "hal/lcd_hal.h"
#include "hal/lcd_ll.h"
#include "soc/lcd_periph.h"
#include "esp_heap_caps.h"
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_io_interface.h"
#include "esp_lcd_panel_ops.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "soc/gdma_reg.h"
#include "platforms/esp/esp_version.h"
#define IDF_5_3_OR_EARLIER (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0))
// According to bug reports, this driver does not work well with the new WS2812-v5b. This is
// probably due to the extrrra long reset time requirements of this chipset. so we put in
// a hack that will always add 300 uS to the reset time.
#define FASTLED_EXPERIMENTAL_YVES_EXTRA_WAIT_MICROS 300
#ifndef NUMSTRIPS
#define NUMSTRIPS 16
#endif
#ifndef SNAKEPATTERN
#define SNAKEPATTERN 1
#endif
#ifndef ALTERNATEPATTERN
#define ALTERNATEPATTERN 1
#endif
#define I2S_DEVICE 0
#define AA (0x00AA00AAL)
#define CC (0x0000CCCCL)
#define FF (0xF0F0F0F0L)
#define FF2 (0x0F0F0F0FL)
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#define __OFFSET 0 // (24*3*2*2*2+2)
#define __OFFSET_END (24 * 3 * 2 * 2 * 2 + 2)
#ifndef HARDWARESPRITES
#define HARDWARESPRITES 0
#endif
#if HARDWARESPRITES == 1
#include "hardwareSprite.h"
#endif
#ifdef COLOR_ORDER_GRBW
#define _p_r 1
#define _p_g 0
#define _p_b 2
#define _nb_components 4
#else
#ifdef COLOR_ORDER_RGB
#define _p_r 0
#define _p_g 1
#define _p_b 2
#define _nb_components 3
#else
#ifdef COLOR_ORDER_RBG
#define _p_r 0
#define _p_g 2
#define _p_b 1
#define _nb_components 3
#else
#ifdef COLOR_ORDER_GBR
#define _p_r 2
#define _p_g 0
#define _p_b 1
#define _nb_components 3
#else
#ifdef COLOR_ORDER_BGR
#define _p_r 2
#define _p_g 1
#define _p_b 0
#define _nb_components 3
#else
#ifdef COLOR_ORDER_BRG
#define _p_r 1
#define _p_g 2
#define _p_b 0
#define _nb_components 3
#else
#ifdef COLOR_ORDER_GRB
#define _p_r 1
#define _p_g 0
#define _p_b 2
#define _nb_components 3
#else
#define _p_r 1
#define _p_g 0
#define _p_b 2
#define _nb_components 3
#endif
#endif
#endif
#endif
#endif
#endif
#endif
#ifdef USE_PIXELSLIB
// #include "pixelslib.h"
#else
#include "___pixeltypes.h"
#endif
#define LCD_DRIVER_PSRAM_DATA_ALIGNMENT 64
#define CLOCKLESS_PIXEL_CLOCK_HZ (24 * 100 * 1000)
namespace fl {
static bool IRAM_ATTR flush_ready(esp_lcd_panel_io_handle_t panel_io,
esp_lcd_panel_io_event_data_t *edata,
void *user_ctx);
typedef union {
uint8_t bytes[16];
uint32_t shorts[8];
uint32_t raw[2];
} Lines;
enum colorarrangment {
ORDER_GRBW,
ORDER_RGB,
ORDER_RBG,
ORDER_GRB,
ORDER_GBR,
ORDER_BRG,
ORDER_BGR,
};
enum displayMode {
NO_WAIT,
WAIT,
LOOP,
LOOP_INTERUPT,
};
bool DRIVER_READY = true;
typedef struct led_driver_t led_driver_t;
struct led_driver_t {
size_t (*init)();
void (*update)(uint8_t *colors, size_t len);
};
volatile xSemaphoreHandle I2SClocklessLedDriverS3_sem = NULL;
volatile bool isDisplaying = false;
volatile bool iswaiting = false;
static void IRAM_ATTR transpose16x1_noinline2(unsigned char *A, uint16_t *B) {
uint32_t x, y, x1, y1, t;
y = *(unsigned int *)(A);
#if NUMSTRIPS > 4
x = *(unsigned int *)(A + 4);
#else
x = 0;
#endif
#if NUMSTRIPS > 8
y1 = *(unsigned int *)(A + 8);
#else
y1 = 0;
#endif
#if NUMSTRIPS > 12
x1 = *(unsigned int *)(A + 12);
#else
x1 = 0;
#endif
// pre-transform x
#if NUMSTRIPS > 4
t = (x ^ (x >> 7)) & AA;
x = x ^ t ^ (t << 7);
t = (x ^ (x >> 14)) & CC;
x = x ^ t ^ (t << 14);
#endif
#if NUMSTRIPS > 12
t = (x1 ^ (x1 >> 7)) & AA;
x1 = x1 ^ t ^ (t << 7);
t = (x1 ^ (x1 >> 14)) & CC;
x1 = x1 ^ t ^ (t << 14);
#endif
// pre-transform y
t = (y ^ (y >> 7)) & AA;
y = y ^ t ^ (t << 7);
t = (y ^ (y >> 14)) & CC;
y = y ^ t ^ (t << 14);
#if NUMSTRIPS > 8
t = (y1 ^ (y1 >> 7)) & AA;
y1 = y1 ^ t ^ (t << 7);
t = (y1 ^ (y1 >> 14)) & CC;
y1 = y1 ^ t ^ (t << 14);
#endif
// final transform
t = (x & FF) | ((y >> 4) & FF2);
y = ((x << 4) & FF) | (y & FF2);
x = t;
t = (x1 & FF) | ((y1 >> 4) & FF2);
y1 = ((x1 << 4) & FF) | (y1 & FF2);
x1 = t;
*((uint16_t *)(B)) =
(uint16_t)(((x & 0xff000000) >> 8 | ((x1 & 0xff000000))) >> 16);
*((uint16_t *)(B + 3)) =
(uint16_t)(((x & 0xff0000) >> 16 | ((x1 & 0xff0000) >> 8)));
*((uint16_t *)(B + 6)) =
(uint16_t)(((x & 0xff00) | ((x1 & 0xff00) << 8)) >> 8);
*((uint16_t *)(B + 9)) = (uint16_t)((x & 0xff) | ((x1 & 0xff) << 8));
*((uint16_t *)(B + 12)) =
(uint16_t)(((y & 0xff000000) >> 8 | ((y1 & 0xff000000))) >> 16);
*((uint16_t *)(B + 15)) =
(uint16_t)(((y & 0xff0000) | ((y1 & 0xff0000) << 8)) >> 16);
*((uint16_t *)(B + 18)) =
(uint16_t)(((y & 0xff00) | ((y1 & 0xff00) << 8)) >> 8);
*((uint16_t *)(B + 21)) = (uint16_t)((y & 0xff) | ((y1 & 0xff) << 8));
}
esp_lcd_panel_io_handle_t led_io_handle = NULL;
class I2SClocklessLedDriveresp32S3 {
public:
int testcount;
uint16_t *buffers[2];
uint16_t *led_output = NULL;
uint16_t *led_output2 = NULL;
uint8_t *ledsbuff = NULL;
int num_leds_per_strip;
int _numstrips;
int currentframe;
uint8_t __green_map[256];
uint8_t __blue_map[256];
uint8_t __red_map[256];
uint8_t __white_map[256];
uint8_t _brightness;
float _gammar, _gammab, _gammag, _gammaw;
void setBrightness(int brightness) {
_brightness = brightness;
float tmp;
for (int i = 0; i < 256; i++) {
tmp = powf((float)i / 255, 1 / _gammag);
__green_map[i] = (uint8_t)(tmp * brightness);
tmp = powf((float)i / 255, 1 / _gammag);
__blue_map[i] = (uint8_t)(tmp * brightness);
tmp = powf((float)i / 255, 1 / _gammag);
__red_map[i] = (uint8_t)(tmp * brightness);
tmp = powf((float)i / 255, 1 / _gammag);
__white_map[i] = (uint8_t)(tmp * brightness);
}
}
void setGamma(float gammar, float gammab, float gammag, float gammaw) {
_gammag = gammag;
_gammar = gammar;
_gammaw = gammaw;
_gammab = gammab;
setBrightness(_brightness);
}
void setGamma(float gammar, float gammab, float gammag) {
_gammag = gammag;
_gammar = gammar;
_gammab = gammab;
setBrightness(_brightness);
}
void _initled(uint8_t *leds, const int *pins, int numstrip,
int NUM_LED_PER_STRIP) {
// esp_lcd_panel_io_handle_t init_lcd_driver(unsigned int
// CLOCKLESS_PIXEL_CLOCK_HZ, size_t _nb_components) {
esp_lcd_i80_bus_handle_t i80_bus = NULL;
esp_lcd_i80_bus_config_t bus_config;
bus_config.clk_src = LCD_CLK_SRC_PLL160M;
bus_config.dc_gpio_num = 0;
bus_config.wr_gpio_num = 0;
// bus_config.data_gpio_nums = (int*)malloc(16*sizeof(int));
for (int i = 0; i < numstrip; i++) {
bus_config.data_gpio_nums[i] = pins[i];
}
if (numstrip < 16) {
for (int i = numstrip; i < 16; i++) {
bus_config.data_gpio_nums[i] = 0;
}
}
bus_config.bus_width = 16;
bus_config.max_transfer_bytes =
_nb_components * NUM_LED_PER_STRIP * 8 * 3 * 2 + __OFFSET + __OFFSET_END;
#if IDF_5_3_OR_EARLIER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
// In IDF 5.3, psram_trans_align became deprecated. We kick the can down
// the road a little bit and suppress the warning until idf 5.4 arrives.
bus_config.psram_trans_align = LCD_DRIVER_PSRAM_DATA_ALIGNMENT;
bus_config.sram_trans_align = 4;
#if IDF_5_3_OR_EARLIER
#pragma GCC diagnostic pop
#endif
ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));
esp_lcd_panel_io_i80_config_t io_config;
io_config.cs_gpio_num = -1;
io_config.pclk_hz = CLOCKLESS_PIXEL_CLOCK_HZ;
io_config.trans_queue_depth = 1;
io_config.dc_levels = {
.dc_idle_level = 0,
.dc_cmd_level = 0,
.dc_dummy_level = 0,
.dc_data_level = 1,
};
//.on_color_trans_done = flush_ready,
// .user_ctx = nullptr,
io_config.lcd_cmd_bits = 0;
io_config.lcd_param_bits = 0;
io_config.user_ctx = this;
io_config.on_color_trans_done = flush_ready;
ESP_ERROR_CHECK(
esp_lcd_new_panel_io_i80(i80_bus, &io_config, &led_io_handle));
}
void initled(uint8_t *leds, const int *pins, int numstrip,
int NUM_LED_PER_STRIP) {
currentframe = 0;
_gammab = 1;
_gammar = 1;
_gammag = 1;
_gammaw = 1;
setBrightness(255);
if (I2SClocklessLedDriverS3_sem == NULL) {
I2SClocklessLedDriverS3_sem = xSemaphoreCreateBinary();
}
// esp_lcd_panel_io_handle_t init_lcd_driver(unsigned int
// CLOCKLESS_PIXEL_CLOCK_HZ, size_t _nb_components) {
led_output = (uint16_t *)heap_caps_aligned_alloc(
LCD_DRIVER_PSRAM_DATA_ALIGNMENT,
8 * _nb_components * NUM_LED_PER_STRIP * 3 * 2 + __OFFSET +
__OFFSET_END,
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
memset(led_output, 0,
8 * _nb_components * NUM_LED_PER_STRIP * 3 * 2 + __OFFSET +
__OFFSET_END);
led_output2 = (uint16_t *)heap_caps_aligned_alloc(
LCD_DRIVER_PSRAM_DATA_ALIGNMENT,
8 * _nb_components * NUM_LED_PER_STRIP * 3 * 2 + __OFFSET +
__OFFSET_END,
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
memset(led_output2, 0,
8 * _nb_components * NUM_LED_PER_STRIP * 3 * 2 + __OFFSET +
__OFFSET_END);
buffers[0] = led_output;
buffers[1] = led_output2;
// led_output[0] = 0xFFFF; //the +1 because it's like the first value
// doesnt get pushed do not ask me why for now
// led_output2[0] = 0xFFFF;
led_output2 += __OFFSET / 2;
led_output += __OFFSET / 2;
for (int i = 0; i < NUM_LED_PER_STRIP * _nb_components * 8; i++) {
led_output[3 * i + 1] =
0xFFFF; // the +1 because it's like the first value doesnt get
// pushed do not ask me why for now
led_output2[3 * i + 1] = 0xFFFF;
}
ledsbuff = leds;
_numstrips = numstrip;
num_leds_per_strip = NUM_LED_PER_STRIP;
_initled(leds, pins, numstrip, NUM_LED_PER_STRIP);
}
void transposeAll(uint16_t *ledoutput) {
uint16_t ledToDisplay = 0;
Lines secondPixel[_nb_components];
uint16_t *buff =
ledoutput + 2; //+1 pour le premier empty +1 pour le 1 systématique
uint16_t jump = num_leds_per_strip * _nb_components;
for (int j = 0; j < num_leds_per_strip; j++) {
uint8_t *poli = ledsbuff + ledToDisplay * _nb_components;
for (int i = 0; i < _numstrips; i++) {
secondPixel[_p_g].bytes[i] = __green_map[*(poli + 1)];
secondPixel[_p_r].bytes[i] = __red_map[*(poli + 0)];
secondPixel[_p_b].bytes[i] = __blue_map[*(poli + 2)];
if (_nb_components > 3)
secondPixel[3].bytes[i] = __white_map[*(poli + 3)];
// #endif
poli += jump;
}
ledToDisplay++;
transpose16x1_noinline2(secondPixel[0].bytes, buff);
buff += 24;
transpose16x1_noinline2(secondPixel[1].bytes, buff);
buff += 24;
transpose16x1_noinline2(secondPixel[2].bytes, buff);
buff += 24;
if (_nb_components > 3) {
transpose16x1_noinline2(secondPixel[3].bytes, buff);
buff += 24;
}
}
}
void show() {
transposeAll(buffers[currentframe]);
if (isDisplaying) {
// Serial.println("on display dejà");
iswaiting = true;
if (I2SClocklessLedDriverS3_sem == NULL)
I2SClocklessLedDriverS3_sem = xSemaphoreCreateBinary();
xSemaphoreTake(I2SClocklessLedDriverS3_sem, portMAX_DELAY);
}
isDisplaying = true;
if (FASTLED_EXPERIMENTAL_YVES_EXTRA_WAIT_MICROS) {
delayMicroseconds(FASTLED_EXPERIMENTAL_YVES_EXTRA_WAIT_MICROS);
}
led_io_handle->tx_color(led_io_handle, 0x2C, buffers[currentframe],
_nb_components * num_leds_per_strip * 8 * 3 *
2 +
__OFFSET + __OFFSET_END);
currentframe = (currentframe + 1) % 2;
}
};
static bool IRAM_ATTR flush_ready(esp_lcd_panel_io_handle_t panel_io,
esp_lcd_panel_io_event_data_t *edata,
void *user_ctx) {
// printf("we're here");
DRIVER_READY = true;
isDisplaying = false;
I2SClocklessLedDriveresp32S3 *cont =
(I2SClocklessLedDriveresp32S3 *)user_ctx;
cont->testcount++;
if (iswaiting) {
portBASE_TYPE HPTaskAwoken = 0;
iswaiting = false;
xSemaphoreGiveFromISR(I2SClocklessLedDriverS3_sem, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE)
portYIELD_FROM_ISR(HPTaskAwoken);
}
return false;
}
#pragma GCC diagnostic pop
} // namespace fl
#endif // __has_include("esp_memory_utils.h")

View File

@@ -0,0 +1,334 @@
#include <stdint.h>
#include <string.h>
#ifdef USE_FASTLED
#include "FastLED.h"
#endif
// #include "Arduino.h"
#define _OUT_OF_BOUND -12
namespace fl {
#ifdef COLOR_RGBW
struct Pixel {
union {
uint8_t raw[4];
struct {
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t white;
};
};
inline Pixel(uint8_t r, uint8_t g, uint8_t b, uint8_t w)
__attribute__((always_inline))
: red(r), green(g), blue(b), white(w) {
// brigthness =0xE0 |(br&31);
}
inline Pixel(uint8_t r, uint8_t g, uint8_t b) __attribute__((always_inline))
: red(r), green(g), blue(b) {
white = MIN(red, green);
white = MIN(white, blue);
red = red - white;
green = green - white;
blue = blue - white;
}
inline Pixel() __attribute__((always_inline)) {}
#ifdef USE_FASTLED
inline Pixel &operator=(const CRGB &rhs) __attribute__((always_inline)) {
red = rhs.r;
green = rhs.g;
blue = rhs.b;
white = MIN(red, green);
white = MIN(white, blue);
red = red - white;
green = green - white;
blue = blue - white;
return *this;
}
#endif
inline Pixel(const Pixel &rhs) __attribute__((always_inline)) {
// brigthness=rhs.brigthness;
red = rhs.red;
green = rhs.green;
blue = rhs.blue;
white = rhs.white;
}
inline Pixel &operator=(const uint32_t colorcode)
__attribute__((always_inline)) {
// rgb colorg;
red = (colorcode >> 24) & 0xFF;
green = (colorcode >> 16) & 0xFF;
blue = (colorcode >> 8) & 0xFF;
white = colorcode & 0xFF;
return *this;
}
};
#else
struct Pixel {
union {
uint8_t raw[3];
struct {
uint8_t red;
uint8_t green;
uint8_t blue;
};
};
inline Pixel(uint8_t r, uint8_t g, uint8_t b) __attribute__((always_inline))
: red(r), green(g), blue(b) {
// brigthness =0xE0 |(br&31);
}
inline Pixel() __attribute__((always_inline)) {}
#ifdef USE_FASTLED
inline Pixel &operator=(const CRGB &rhs) __attribute__((always_inline)) {
red = rhs.r;
green = rhs.g;
blue = rhs.b;
return *this;
}
inline Pixel &operator=(CRGB &rhs) __attribute__((always_inline)) {
red = rhs.r;
green = rhs.g;
blue = rhs.b;
return *this;
}
inline Pixel(const CRGB &rhs) __attribute__((always_inline)) {
red = rhs.r;
green = rhs.g;
blue = rhs.b;
}
#endif
inline Pixel(const Pixel &rhs) __attribute__((always_inline)) {
// brigthness=rhs.brigthness;
red = rhs.red;
green = rhs.green;
blue = rhs.blue;
}
inline Pixel &operator=(const uint32_t colorcode)
__attribute__((always_inline)) {
// rgb colorg;
red = (colorcode >> 16) & 0xFF;
green = (colorcode >> 8) & 0xFF;
blue = (colorcode >> 0) & 0xFF;
return *this;
}
inline __attribute__((always_inline)) bool operator==(const Pixel &rhs) {
return (red == rhs.red) && (green == rhs.green) && (blue == rhs.blue);
}
inline __attribute__((always_inline)) bool operator!=(const Pixel &rhs) {
return !((red == rhs.red) && (green == rhs.green) &&
(blue == rhs.blue));
}
};
#endif
enum class leddirection { FORWARD, BACKWARD, MAP };
class Pixels {
public:
inline Pixels() __attribute__((always_inline)) {}
inline Pixels(const Pixels &rhs) __attribute__((always_inline)) {
_size = rhs._size;
_direction = rhs._direction;
_num_strips = rhs._num_strips;
for (int i = 0; i < _num_strips; i++) {
_sizes[i] = rhs._sizes[i];
}
ledpointer = rhs.ledpointer;
mapFunction = rhs.mapFunction;
// parent=rhs.parent;
}
Pixels(int size, Pixel *ledpoi) {
Pixels(size, ledpoi, leddirection::FORWARD);
}
Pixels(int size, Pixel *ledpoi, leddirection direction) {
__Pixels(size, ledpoi, direction, this);
}
void __Pixels(int size, Pixel *ledpoi, leddirection direction,
Pixels *pib) {
pib->_size = size;
pib->ledpointer = ledpoi;
pib->_num_strips = 0;
pib->_direction = direction;
// pib->nb_child=0;
}
Pixels(int num_led_per_strip, int num_strips) {
int sizes[16];
for (int i = 0; i < num_strips; i++) {
sizes[i] = num_led_per_strip;
}
__Pixels(sizes, num_strips, leddirection::FORWARD, this);
}
Pixels(int *sizes, int num_strips) {
__Pixels(sizes, num_strips, leddirection::FORWARD, this);
}
Pixels(int *sizes, int num_strips, leddirection direction) {
__Pixels(sizes, num_strips, direction, this);
}
void __Pixels(int *sizes, int num_strips, leddirection direction,
Pixels *pib) {
int size = 0;
for (int i = 0; i < num_strips; i++) {
size += sizes[i];
pib->_sizes[i] = sizes[i];
}
pib->_num_strips = num_strips;
ledpointer = (Pixel *)calloc(size, sizeof(Pixel));
if (ledpointer == NULL) {
pib->_size = 0;
} else {
pib->_size = size;
}
pib->_direction = direction;
}
Pixel &operator[](int i) {
switch (_direction) {
case (leddirection::FORWARD):
return *(ledpointer + i % _size);
break;
case (leddirection::BACKWARD):
return *(ledpointer + (_size - i % (_size)-1));
break;
case (leddirection::MAP):
if (mapFunction) {
int offset = mapFunction(i, arguments);
// printf("%d %d\n",i,offset);
if (offset == _OUT_OF_BOUND) {
return offPixel;
} else
return *(ledpointer + (mapFunction(i, arguments) % _size));
}
else
return *(ledpointer);
break;
default:
return *(ledpointer);
break;
}
}
void copy(Pixels ori) { copy(ori, leddirection::FORWARD); }
void copy(Pixels ori, leddirection dir) {
leddirection ledd = _direction;
if (_direction == leddirection::MAP)
ledd = leddirection::FORWARD;
for (int i = 0; i < ori._size; i++) {
if (ledd == dir) {
(*this)[i] = ori[i];
} else {
(*this)[i] = ori[ori._size - i % (ori._size) - 1];
}
}
}
Pixels getStrip(int num_strip, leddirection direction) {
if (_num_strips == 0 or _num_strips < num_strip) {
int d[0];
return Pixels(d, 1, direction);
} else {
uint32_t off = 0;
for (int i = 0; i < num_strip % _num_strips; i++) {
off += _sizes[i];
}
return Pixels(_sizes[num_strip], ledpointer + off, direction);
}
}
Pixels getStrip(int num_strip) {
return getStrip(num_strip, leddirection::FORWARD);
}
int *getLengths() { return _sizes; }
int getNumStrip() { return _num_strips; }
uint8_t *getPixels() { return (uint8_t *)ledpointer; }
void clear() {
// memset(ledpointer,0,_size*sizeof(Pixel));
}
Pixels createSubset(int start, int length) {
return createSubset(start, length, leddirection::FORWARD);
}
Pixels createSubset(int start, leddirection direction) {
if (start < 0)
start = 0;
return Pixels(_size, ledpointer + start, direction);
}
Pixels createSubset(int start, int length, leddirection direction) {
if (start < 0)
start = 0;
if (length <= 0)
length = 1;
return Pixels(length, ledpointer + start, direction);
}
/*
Pixels getParent()
{
return *parent;
}
Pixels * getChild(int i)
{
return children[i%nb_child];
}
*/
inline void setMapFunction(int (*fptr)(int i, void *args), void *args,
int size) {
mapFunction = fptr;
if (arguments == NULL)
arguments = (void *)malloc(sizeof(size));
memcpy(arguments, args, size);
}
private:
Pixel *ledpointer;
size_t _size = 0;
int _sizes[16];
int _num_strips = 0;
leddirection _direction;
// int nb_child;
// Pixels *parent;
void *arguments;
// Pixels **children;
int (*mapFunction)(int i, void *args);
/*
* this is the pixel to retuen when out of bound
*/
Pixel offPixel;
};
} // namespace fl