Initial commit
This commit is contained in:
3
main/CMakeLists.txt
Normal file
3
main/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "led_strip_example_main.c" "led_strip_encoder.c"
|
||||
PRIV_REQUIRES esp_driver_rmt
|
||||
INCLUDE_DIRS ".")
|
||||
126
main/led_strip_encoder.c
Normal file
126
main/led_strip_encoder.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "led_strip_encoder.h"
|
||||
|
||||
static const char *TAG = "led_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;
|
||||
|
||||
RMT_ENCODER_FUNC_ATTR
|
||||
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 = RMT_ENCODING_RESET;
|
||||
rmt_encode_state_t state = RMT_ENCODING_RESET;
|
||||
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 = RMT_ENCODING_RESET; // 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;
|
||||
}
|
||||
|
||||
RMT_ENCODER_FUNC_ATTR
|
||||
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 = RMT_ENCODING_RESET;
|
||||
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;
|
||||
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 = rmt_alloc_encoder_mem(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;
|
||||
// different led strip might have its own timing requirements, following parameter is for WS2812
|
||||
rmt_bytes_encoder_config_t bytes_encoder_config = {
|
||||
.bit0 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||
},
|
||||
.bit1 = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
|
||||
},
|
||||
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
|
||||
};
|
||||
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(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
|
||||
|
||||
uint32_t reset_ticks = config->resolution / 1000000 * 50 / 2; // reset code duration defaults to 50us
|
||||
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;
|
||||
}
|
||||
36
main/led_strip_encoder.h
Normal file
36
main/led_strip_encoder.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "driver/rmt_encoder.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of led strip encoder configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t resolution; /*!< Encoder resolution, in Hz */
|
||||
} 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
118
main/led_strip_example_main.c
Normal file
118
main/led_strip_example_main.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "led_strip_encoder.h"
|
||||
|
||||
#define RMT_LED_STRIP_RESOLUTION_HZ 10000000 // 10MHz resolution
|
||||
#define RMT_LED_STRIP_GPIO_NUM 10 // GPIO10
|
||||
|
||||
#define EXAMPLE_LED_NUMBERS 22 // 22个LED
|
||||
#define REFRESH_MS 20 // 刷新间隔,20ms = 50fps
|
||||
#define BREATH_CYCLE_MS 3000 // 呼吸周期(淡入+淡出)
|
||||
#define CHASE_SPEED_DEG 5 // 每次色环偏移角度
|
||||
|
||||
static const char *TAG = "rainbow_breath";
|
||||
|
||||
static uint8_t led_strip_pixels[EXAMPLE_LED_NUMBERS * 3];
|
||||
|
||||
/**
|
||||
* @brief HSV转RGB
|
||||
*/
|
||||
void led_strip_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b)
|
||||
{
|
||||
h %= 360;
|
||||
uint32_t rgb_max = v * 255 / 100;
|
||||
uint32_t rgb_min = rgb_max * (100 - s) / 100;
|
||||
|
||||
uint32_t i = h / 60;
|
||||
uint32_t diff = h % 60;
|
||||
|
||||
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||
|
||||
switch (i) {
|
||||
case 0: *r = rgb_max; *g = rgb_min + rgb_adj; *b = rgb_min; break;
|
||||
case 1: *r = rgb_max - rgb_adj; *g = rgb_max; *b = rgb_min; break;
|
||||
case 2: *r = rgb_min; *g = rgb_max; *b = rgb_min + rgb_adj; break;
|
||||
case 3: *r = rgb_min; *g = rgb_max - rgb_adj; *b = rgb_max; break;
|
||||
case 4: *r = rgb_min + rgb_adj; *g = rgb_min; *b = rgb_max; break;
|
||||
default: *r = rgb_max; *g = rgb_min; *b = rgb_max - rgb_adj; break;
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Create RMT TX channel on GPIO%d", RMT_LED_STRIP_GPIO_NUM);
|
||||
rmt_channel_handle_t led_chan = NULL;
|
||||
rmt_tx_channel_config_t tx_chan_config = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.gpio_num = RMT_LED_STRIP_GPIO_NUM,
|
||||
.mem_block_symbols = 64,
|
||||
.resolution_hz = RMT_LED_STRIP_RESOLUTION_HZ,
|
||||
.trans_queue_depth = 4,
|
||||
};
|
||||
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan));
|
||||
|
||||
ESP_LOGI(TAG, "Install led strip encoder");
|
||||
rmt_encoder_handle_t led_encoder = NULL;
|
||||
led_strip_encoder_config_t encoder_config = {
|
||||
.resolution = RMT_LED_STRIP_RESOLUTION_HZ,
|
||||
};
|
||||
ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&encoder_config, &led_encoder));
|
||||
|
||||
ESP_LOGI(TAG, "Enable RMT TX channel");
|
||||
ESP_ERROR_CHECK(rmt_enable(led_chan));
|
||||
|
||||
ESP_LOGI(TAG, "Start Rainbow Breath Chase, LED count: %d", EXAMPLE_LED_NUMBERS);
|
||||
rmt_transmit_config_t tx_config = {
|
||||
.loop_count = 0,
|
||||
};
|
||||
|
||||
uint16_t hue_offset = 0; // 彩虹色环偏移(跑马灯)
|
||||
int breath_tick = 0; // 呼吸时间计数器
|
||||
|
||||
while (1) {
|
||||
// 计算当前呼吸亮度系数:0.0 ~ 1.0
|
||||
float breath_phase = (2.0f * 3.1415926f * breath_tick * REFRESH_MS) / BREATH_CYCLE_MS;
|
||||
float breath_brightness = (sinf(breath_phase) + 1.0f) / 2.0f; // 0.0 ~ 1.0
|
||||
|
||||
// 为每个LED计算颜色:彩虹分布 + 呼吸亮度
|
||||
for (int i = 0; i < EXAMPLE_LED_NUMBERS; i++) {
|
||||
// 每个LED在色环上均匀分布,加上偏移实现流动
|
||||
uint16_t hue = ((i * 360 / EXAMPLE_LED_NUMBERS) + hue_offset) % 360;
|
||||
|
||||
uint32_t r, g, b;
|
||||
// 先计算全亮彩虹色
|
||||
led_strip_hsv2rgb(hue, 100, 100, &r, &g, &b);
|
||||
|
||||
// 叠加呼吸亮度
|
||||
r = (uint32_t)(r * breath_brightness);
|
||||
g = (uint32_t)(g * breath_brightness);
|
||||
b = (uint32_t)(b * breath_brightness);
|
||||
|
||||
// GRB顺序写入
|
||||
led_strip_pixels[i * 3 + 0] = (uint8_t)g;
|
||||
led_strip_pixels[i * 3 + 1] = (uint8_t)b;
|
||||
led_strip_pixels[i * 3 + 2] = (uint8_t)r;
|
||||
}
|
||||
|
||||
// 发送数据到灯带
|
||||
ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, led_strip_pixels, sizeof(led_strip_pixels), &tx_config));
|
||||
ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY));
|
||||
|
||||
// 更新跑马灯偏移
|
||||
hue_offset = (hue_offset + CHASE_SPEED_DEG) % 360;
|
||||
|
||||
// 更新呼吸计数器
|
||||
breath_tick++;
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(REFRESH_MS));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user