mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-10 04:43:33 +00:00
feat(rmt): Add simple callback encoder
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(led_strip_simple_encoder)
|
56
examples/peripherals/rmt/led_strip_simple_encoder/README.md
Normal file
56
examples/peripherals/rmt/led_strip_simple_encoder/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
# RMT Transmit Example -- LED Strip
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
Almost any waveform can be generated by RMT peripheral, as long as a proper encoder is implemented. In this example, the simple callback RMT encoder is used to convert RGB pixels into format that can be recognized by hardware.
|
||||
|
||||
This example shows how to drive an addressable LED strip [WS2812](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf) by implementing a callback that can be used by the simple callback RMT encoder.
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
|
||||
* A USB cable for Power supply and programming
|
||||
* A WS2812 LED strip
|
||||
|
||||
Connection :
|
||||
|
||||
```
|
||||
--- 5V
|
||||
|
|
||||
+
|
||||
RMT_LED_STRIP_GPIO_NUM +------ +---|>| (WS2812 LED strip)
|
||||
DI +
|
||||
|
|
||||
--- GND
|
||||
```
|
||||
|
||||
The GPIO number used in this example can be changed according to your board, by the macro `RMT_LED_STRIP_GPIO_NUM` defined in the [source file](main/led_strip_example_main.c). The number of LEDs can be changed as well by `EXAMPLE_LED_NUMBERS`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Console Output
|
||||
|
||||
```
|
||||
I (302) cpu_start: Starting scheduler on PRO CPU.
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (323) example: Create RMT TX channel
|
||||
I (343) example: Create simple callback-based encoder
|
||||
I (353) example: Start LED rainbow chase
|
||||
```
|
||||
|
||||
|
||||
After you seeing this log, you should see a rainbow chasing demonstration pattern. To change the chasing speed, you can update the `EXAMPLE_ANGLE_INC_FRAME` value in [source file](main/led_strip_example_main.c). To change the density of colors, you can change `EXAMPLE_ANGLE_INC_LED` in the same file.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "led_strip_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 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"
|
||||
|
||||
#define RMT_LED_STRIP_RESOLUTION_HZ 10000000 // 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
|
||||
#define RMT_LED_STRIP_GPIO_NUM 8
|
||||
|
||||
#define EXAMPLE_LED_NUMBERS 24
|
||||
|
||||
#define EXAMPLE_FRAME_DURATION_MS 20
|
||||
#define EXAMPLE_ANGLE_INC_FRAME 0.02
|
||||
#define EXAMPLE_ANGLE_INC_LED 0.3
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
static uint8_t led_strip_pixels[EXAMPLE_LED_NUMBERS * 3];
|
||||
|
||||
static const rmt_symbol_word_t ws2812_zero = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.3 * RMT_LED_STRIP_RESOLUTION_HZ / 1000000, // T0H=0.3us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.9 * RMT_LED_STRIP_RESOLUTION_HZ / 1000000, // T0L=0.9us
|
||||
};
|
||||
|
||||
static const rmt_symbol_word_t ws2812_one = {
|
||||
.level0 = 1,
|
||||
.duration0 = 0.9 * RMT_LED_STRIP_RESOLUTION_HZ / 1000000, // T1H=0.9us
|
||||
.level1 = 0,
|
||||
.duration1 = 0.3 * RMT_LED_STRIP_RESOLUTION_HZ / 1000000, // T1L=0.3us
|
||||
};
|
||||
|
||||
//reset defaults to 50uS
|
||||
static const rmt_symbol_word_t ws2812_reset = {
|
||||
.level0 = 1,
|
||||
.duration0 = RMT_LED_STRIP_RESOLUTION_HZ / 1000000 * 50 / 2,
|
||||
.level1 = 0,
|
||||
.duration1 = RMT_LED_STRIP_RESOLUTION_HZ / 1000000 * 50 / 2,
|
||||
};
|
||||
|
||||
static size_t encoder_callback(const void *data, size_t data_size,
|
||||
size_t symbols_written, size_t symbols_free,
|
||||
rmt_symbol_word_t *symbols, bool *done, void *arg)
|
||||
{
|
||||
// We need a minimum of 8 symbol spaces to encode a byte. We only
|
||||
// need one to encode a reset, but it's simpler to simply demand that
|
||||
// there are 8 symbol spaces free to write anything.
|
||||
if (symbols_free < 8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We can calculate where in the data we are from the symbol pos.
|
||||
// Alternatively, we could use some counter referenced by the arg
|
||||
// parameter to keep track of this.
|
||||
size_t data_pos = symbols_written / 8;
|
||||
uint8_t *data_bytes = (uint8_t*)data;
|
||||
if (data_pos < data_size) {
|
||||
// Encode a byte
|
||||
size_t symbol_pos = 0;
|
||||
for (int bitmask = 0x80; bitmask != 0; bitmask >>= 1) {
|
||||
if (data_bytes[data_pos]&bitmask) {
|
||||
symbols[symbol_pos++] = ws2812_one;
|
||||
} else {
|
||||
symbols[symbol_pos++] = ws2812_zero;
|
||||
}
|
||||
}
|
||||
// We're done; we should have written 8 symbols.
|
||||
return symbol_pos;
|
||||
} else {
|
||||
//All bytes already are encoded.
|
||||
//Encode the reset, and we're done.
|
||||
symbols[0] = ws2812_reset;
|
||||
*done = 1; //Indicate end of the transaction.
|
||||
return 1; //we only wrote one symbol
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Create RMT TX channel");
|
||||
rmt_channel_handle_t led_chan = NULL;
|
||||
rmt_tx_channel_config_t tx_chan_config = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT, // select source clock
|
||||
.gpio_num = RMT_LED_STRIP_GPIO_NUM,
|
||||
.mem_block_symbols = 64, // increase the block size can make the LED less flickering
|
||||
.resolution_hz = RMT_LED_STRIP_RESOLUTION_HZ,
|
||||
.trans_queue_depth = 4, // set the number of transactions that can be pending in the background
|
||||
};
|
||||
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan));
|
||||
|
||||
ESP_LOGI(TAG, "Create simple callback-based encoder");
|
||||
rmt_encoder_handle_t simple_encoder = NULL;
|
||||
const rmt_simple_encoder_config_t simple_encoder_cfg = {
|
||||
.callback = encoder_callback
|
||||
//Note we don't set min_chunk_size here as the default of 64 is good enough.
|
||||
};
|
||||
ESP_ERROR_CHECK(rmt_new_simple_encoder(&simple_encoder_cfg, &simple_encoder));
|
||||
|
||||
ESP_LOGI(TAG, "Enable RMT TX channel");
|
||||
ESP_ERROR_CHECK(rmt_enable(led_chan));
|
||||
|
||||
ESP_LOGI(TAG, "Start LED rainbow chase");
|
||||
rmt_transmit_config_t tx_config = {
|
||||
.loop_count = 0, // no transfer loop
|
||||
};
|
||||
float offset = 0;
|
||||
while (1) {
|
||||
for (int led = 0; led < EXAMPLE_LED_NUMBERS; led++) {
|
||||
// Build RGB pixels. Each color is an offset sine, which gives a
|
||||
// hue-like effect.
|
||||
float angle = offset + (led * EXAMPLE_ANGLE_INC_LED);
|
||||
const float color_off = (M_PI * 2) / 3;
|
||||
led_strip_pixels[led * 3 + 0] = sin(angle + color_off * 0) * 127 + 128;
|
||||
led_strip_pixels[led * 3 + 1] = sin(angle + color_off * 1) * 127 + 128;
|
||||
led_strip_pixels[led * 3 + 2] = sin(angle + color_off * 2) * 117 + 128;;
|
||||
}
|
||||
// Flush RGB values to LEDs
|
||||
ESP_ERROR_CHECK(rmt_transmit(led_chan, simple_encoder, led_strip_pixels, sizeof(led_strip_pixels), &tx_config));
|
||||
ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY));
|
||||
vTaskDelay(pdMS_TO_TICKS(EXAMPLE_FRAME_DURATION_MS));
|
||||
//Increase offset to shift pattern
|
||||
offset += EXAMPLE_ANGLE_INC_FRAME;
|
||||
if (offset > 2 * M_PI) {
|
||||
offset -= 2 * M_PI;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user