Продолжаем работать с модулем RMT (Remote Control) и с сегодняшнего урока мы попытаемся с использованием данного модуля поработать со светодиодной лентой, состоящей из цепочки адресных светодиодах WS2812B.
Как именно управлять такими светодиодами, мы знакомы давно из курса уроков по контроллерам STM32, поэтому нам будет намного проще организовать алгоритм работы и с применением контроллера ESP32. Только управляющие импульсы мы будем отправлять с применением модуля RMT. Почему именно RMT, думаю, и так понятно без объяснений. Дело в том, что если применять задержки, то планировщик операционной системы FreeRTOS по окончанию задержки может запустить совершенно другой код из параллельной задачи и мы рискуем получить неточные тайминги.
Светодиодную ленту мы подключим подобно тому, как мы это делали в уроке 119 по STM32. Только в качестве сигнальной ножки порта у нас будет выступать ножка GPIO4. К данной ножке мы также подключим логический анализатор, чтобы следить за процессом передачи данных. Питать мы ленту будем также от DC-DC преобразователя, так как максимальной силы тока стабилизатора платы может не хватить.
В результате получим вот такую схему
Пока не будем подключать питание ленты, а также отладочную плату к компьютеру, так как нам придется писать очень много кода, а займёмся проектом, который сделан из проекта урока 4 с именем BUTTON01 и назовём его WS2812_INIT.
Откроем наш проект в Espressif IDE и внесём изменения в файл конфигурации Kconfig.projbuild.
Так как кнопка нам не нужна, то у нас не будет данного пункта меню. После изменения файл приобретёт следующий вид
1 2 3 4 5 6 7 8 9 10 |
menu "WS2812 Configuration" config LED_GPIO int "LED GPIO number" range 0 48 default 4 help GPIO number to LED. endmenu |
Также создадим в каталоге проекта файл sdkconfig.defaults следующего содержания
1 2 |
CONFIG_SOC_RMT_SUPPORTED=y CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y |
Удалим полностью весь код из функции app_main файла main.c, а выше добавим функцию конфигурации ленты, также пока пустотелую
1 2 3 4 5 6 |
#include "sdkconfig.h" //============================================================== static void configure_led(void) { } //============================================================== |
Вызовем данную функцию из функции app_main
1 2 3 |
void app_main(void) { configure_led(); |
В каталоге main проекта создадим заголовочный файл main.h следующего содержания
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef MAIN_MAIN_H_ #define MAIN_MAIN_H_ //--------------------------------------------------------------------- #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/gpio.h" #include "esp_log.h" #include "sdkconfig.h" //--------------------------------------------------------------------- #endif /* MAIN_MAIN_H_ */ |
Вернёмся в main.c и удалим подключения заголовочных файлов, а подключим наш новый заголовочный файл main.h
1 |
#include "main.h" |
Создадим ещё один заголовочный файл led_strip.h пока следующего содержания
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef MAIN_LED_STRIP_H_ #define MAIN_LED_STRIP_H_ //----------------------------------------------------------- #include <stdio.h> #include <string.h> #include "esp_err.h" #include <sys/cdefs.h> #include "esp_log.h" #include "esp_check.h" #include "driver/rmt_types.h" #include "driver/rmt_tx.h" //----------------------------------------------------------- #endif /* MAIN_LED_STRIP_H_ */ |
Подключим данный файл в main.h
1 2 |
#include "sdkconfig.h" #include "led_strip.h" |
Создадим также файл led_strip.c, в котором также подключим наш заголовочный файл
1 2 |
#include "led_strip.h" //----------------------------------------------------------- |
Не забываем также данный файл подключить в CMakeLists.txt
set(COMPONENT_SRCS "main.c" "led_strip.c")
Пересоберём проект, перейдём в файл led_strip.h и добавим два перечислимых типа, один для формата пикселя, другой для моделей адресных светодиодов
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include "driver/rmt_tx.h" //----------------------------------------------------------- typedef enum { LED_PIXEL_FORMAT_GRB, //Pixel format: GRB LED_PIXEL_FORMAT_GRBW, //Pixel format: GRBW LED_PIXEL_FORMAT_INVALID //Invalid pixel format } led_pixel_format_t; //----------------------------------------------------------- typedef enum { LED_MODEL_WS2812, //LED strip model: WS2812 LED_MODEL_SK6812, //LED strip model: SK6812 LED_MODEL_INVALID //Invalid LED strip model } led_model_t; //----------------------------------------------------------- |
Добавим структуру для свойств ленты
1 2 3 4 5 6 7 8 9 10 11 12 |
} led_model_t; //---------------------------------------------------------- typedef struct { int strip_gpio_num; //GPIO number that used by LED strip uint32_t max_leds; //Maximum LEDs in a single strip led_pixel_format_t led_pixel_format; //LED pixel format led_model_t led_model; //LED model struct { uint32_t invert_out: 1; //Invert output signal } flags; } led_strip_config_t; //----------------------------------------------------------- |
Назначение каждого поля понятно из комментариев.
В файле main.c в функции configure_led объявим переменную типа нашей структуры и инициализируем некоторые её поля
1 2 3 4 5 6 |
static void configure_led(void) { led_strip_config_t strip_config = { .strip_gpio_num = CONFIG_LED_GPIO, .max_leds = 144, //at least one LED on board }; |
Мы инициализировали ножку и максимальное количество светодиодов в ленте. Остальные поля выставились по умолчанию в 0.
Вернёмся в файл led_strip.h и добавим ещё одну структуру для инициализации свойств канала RMT
1 2 3 4 5 6 7 8 9 10 11 |
#include "driver/rmt_tx.h" //----------------------------------------------------------- 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. struct { uint32_t with_dma: 1; //Use DMA to transmit data */ } flags; } led_strip_rmt_config_t; //----------------------------------------------------------- |
Здесь также назначения полей описаны в комментариях.
Объявим структуру с функциями для управления лентой и одноимённый тип для неё
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "driver/rmt_tx.h" //----------------------------------------------------------- typedef struct led_strip_t led_strip_t; //----------------------------------------------------------- struct led_strip_t { esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue); 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); esp_err_t (*refresh)(led_strip_t *strip); esp_err_t (*clear)(led_strip_t *strip); esp_err_t (*del)(led_strip_t *strip); }; //----------------------------------------------------------- |
Выше объявим тип указателя на тип такой структуры
1 2 3 4 |
#include "driver/rmt_tx.h" //----------------------------------------------------------- typedef struct led_strip_t *led_strip_handle_t; //----------------------------------------------------------- |
Вернёмся в main.c и объявим такой указатель
1 2 3 |
#include "main.h" //============================================================== static led_strip_handle_t led_strip; |
В функции configure_led объявим переменную также типа структуры свойств RMT и инициализируем только одно поле с разрешающей способностью
1 2 3 4 5 |
.max_leds = 144, //at least one LED on board }; led_strip_rmt_config_t rmt_config = { .resolution_hz = 10 * 1000 * 1000, //10MHz }; |
Перейдём в файл led_strip.c и добавим функцию инициализации нового устройства
1 2 3 4 5 6 7 8 9 |
#include "led_strip.h" //----------------------------------------------------------- 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) { esp_err_t ret = ESP_OK; return ret; } //----------------------------------------------------------- |
В заголовочном файле создадим прототип на данную функцию и затем в функции configure_led файла main.c вызовем её
1 2 3 |
.resolution_hz = 10 * 1000 * 1000, //10MHz }; ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); |
В файле led_strip.c также объявим структуру со свойствами ленты
1 2 3 4 5 6 7 8 9 10 11 |
#include "led_strip.h" //----------------------------------------------------------- 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; uint8_t pixel_buf[]; } led_strip_rmt_obj; //----------------------------------------------------------- |
В функции led_strip_new_rmt_device объявим указатель на тип данной структуры
1 2 3 |
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; |
Ниже объявим метку и напишем код. На данный код мы будем переходить в случае определённых ошибок
1 2 3 4 5 6 7 8 9 10 11 |
esp_err_t ret = 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); } |
Выше метки произведём выход с положительным результатом, так как если мы сюда дошли, то у нас ошибок нет
1 2 |
esp_err_t ret = ESP_OK; return ESP_OK; |
Объявим глобальный символьный массив для лога
1 2 3 4 |
#include "led_strip.h" //----------------------------------------------------------- static const char *TAG = "led_strip"; //----------------------------------------------------------- |
Вернёмся в функцию led_strip_new_rmt_device и проверим на валидность входные аргументы
1 2 |
esp_err_t ret = ESP_OK; ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); |
Конструкция ESP_GOTO_ON_FALSE проверяет на TRUE результат выражения в первом аргументе. Если FALSE, то происходит переход по метке, обозначенной в третьем аргументе, переменной ret (это предопределенное имя) присваивается значение из второго аргумента, а также в терминал выводится сообщение из пятого аргумента.
Далее проверим аналогичным способом поле led_pixel_format
1 2 |
ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format"); |
Вычислим количество бит на пиксель
1 2 3 4 5 6 7 8 9 |
ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format"); uint8_t bytes_per_pixel = 3; if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) { bytes_per_pixel = 4; } else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) { bytes_per_pixel = 3; } else { assert(false); } |
Запросим память под буфер
1 2 3 |
assert(false); } rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel); |
Проверим, что память выделилась
1 2 |
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"); |
Объявим глобально разрешающую способность RMT по умолчанию
1 2 3 4 |
#include "led_strip.h" //----------------------------------------------------------- #define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution //----------------------------------------------------------- |
Вернёмся в функцию led_strip_new_rmt_device и объявим переменную, которой присвоим значение разрешающей способности
1 2 |
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; |
Объявим переменную и присвоим ей значение частоты RMT по умолчанию
1 2 |
uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION; rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT; |
А если частота был определена отдельно при конфигурировании, то присвоим её значение нашей переменной
1 2 3 4 |
rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT; if (rmt_config->clk_src) { clk_src = rmt_config->clk_src; } |
Также нам нужен будет размер блока памяти. Объявим также глобально данную величину
1 2 3 4 5 6 |
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 #define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64 #else #define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48 #endif |
Вернёмся в функцию led_strip_new_rmt_device и объявим переменную, которой присвоим величину блока памяти
1 2 3 |
clk_src = rmt_config->clk_src; } size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS; |
А если величина блока была определена при конфигурировании, то присвоим её переменной
1 2 3 4 5 |
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
1 2 |
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution #define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4 |
Вернёмся в функцию led_strip_new_rmt_device и сконфигурируем канал RMT
1 2 3 4 5 6 7 8 9 10 11 |
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, }; |
Проверим конфигурацию канала
1 2 3 |
.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.h объявим структуру для свойств энкодера байтов, в которой в качестве полей будут разрешающая способность в герцах и модель светодиодов
1 2 3 4 5 6 7 |
} led_strip_config_t; //----------------------------------------------------------- typedef struct { uint32_t resolution; //Encoder resolution, in Hz led_model_t led_model; //LED model } led_strip_encoder_config_t; //----------------------------------------------------------- |
Вернёмся в файл led_strip.c и в функции led_strip_new_rmt_device объявим переменную данной структуры и инициализируем её поля
1 2 3 4 5 6 |
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 }; |
Выше добавим функцию для настройки энкодера
1 2 3 4 5 6 7 |
//----------------------------------------------------------- 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; return ret; } //----------------------------------------------------------- |
В функции led_strip_new_rmt_device вызовем данную функцию
1 2 3 |
.led_model = led_config->led_model }; ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed"); |
Объявим глобальную структуру для энкодера
1 2 3 4 5 6 7 8 9 10 |
static const char *TAG = "led_strip"; //----------------------------------------------------------- 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_new_led_strip_encoder объявим указатель на тип только что объявленной структуры
1 2 |
esp_err_t ret = ESP_OK; rmt_led_strip_encoder_t *led_encoder = NULL; |
Добавим аналогичную метку, как в предыдущей функции для перехода по ошибке
1 2 3 4 5 6 7 8 9 10 11 |
rmt_led_strip_encoder_t *led_encoder = NULL; 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); } |
Проверим факт существования входных аргументов, а также валидность модели (принадлежность диапазону значений)
1 2 3 |
rmt_led_strip_encoder_t *led_encoder = NULL; 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"); |
Выделим память под буфер и проверим успешность выделения
1 2 3 |
ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model"); 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"); |
Выше добавим функцию кодирования
1 2 3 4 5 6 7 8 9 |
} led_strip_rmt_obj; //----------------------------------------------------------- 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) { size_t encoded_symbols = 0; out: return encoded_symbols; } //----------------------------------------------------------- |
Объявим указатель на на структуру энкодера
1 2 3 |
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, второй — на энкодер (кодировщик) копирования, который копирует заданные символы в память RMT.
1 2 3 |
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; |
Объявим также две переменные перечислимого типа для состояний
1 2 3 |
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; rmt_encode_state_t session_state = 0; rmt_encode_state_t state = 0; |
В зависимости от состояния нашего энкодера (это немного не те состояния, переменные для которых мы создавали выше, это состояние передачи данных RGB или состояния RESET) произведём преобразование цепочки байтов в код RMT либо кодирование команды RESET и переключим состояние на другое. В телах ответвлений переключателя вариантов как-раз-таки и используются статусы, переменные для которых мы объявляли.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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 } break; // 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 } } |
Ниже добавим функцию удаления энкодера
1 2 3 4 5 6 7 8 9 10 |
//----------------------------------------------------------- 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; } //----------------------------------------------------------- |
Ниже также добавим функцию сброса энкодера
1 2 3 4 5 6 7 8 9 10 |
//----------------------------------------------------------- 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; } //----------------------------------------------------------- |
Вернёмся в функцию rmt_new_led_strip_encoder и инициализируем поля структуры энкодера, показав ему имена наших функций обратного вызова
1 2 3 4 |
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; |
Добавим переменную для свойств передачи байта и сконфигурируем тайминги импульсов в зависимости от модели светодиода
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
led_encoder->base.reset = rmt_led_strip_encoder_reset; rmt_bytes_encoder_config_t bytes_encoder_config; if (config->led_model == LED_MODEL_SK6812) { bytes_encoder_config = (rmt_bytes_encoder_config_t) { .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.6 * config->resolution / 1000000, //T1H=0.6us .level1 = 0, .duration1 = 0.6 * config->resolution / 1000000, //T1L=0.6us }, .flags.msb_first = 1 // SK6812 transfer bit order: G7...G0R7...R0B7...B0(W7...W0) }; } else if (config->led_model == LED_MODEL_WS2812) { //different led strip might have its own timing requirements, following parameter is for WS2812 bytes_encoder_config = (rmt_bytes_encoder_config_t) { .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 }; } else { assert(false); } |
Создадим наш энкодер с помощью специальной функции
1 2 3 |
assert(false); } ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed"); |
Сконфигурируем копирование
1 2 3 |
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"); |
Настроим также кодирование сброса (не менее 50 микросекунд)
1 2 3 4 5 6 7 8 |
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, }; |
Вернём указатель на структуру энкодера, а также положительный результат
1 2 3 4 |
.duration1 = reset_ticks, }; *ret_encoder = &led_encoder->base; return ESP_OK; |
Добавим заготовки под основные функции нашей ленты
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
} 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) { 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) { return ESP_OK; } //----------------------------------------------------------- static esp_err_t led_strip_rmt_refresh(led_strip_t *strip) { return ESP_OK; } //----------------------------------------------------------- static esp_err_t led_strip_rmt_clear(led_strip_t *strip) { return led_strip_rmt_refresh(strip); } //----------------------------------------------------------- static esp_err_t led_strip_rmt_del(led_strip_t *strip) { return ESP_OK; } //----------------------------------------------------------- |
В функции led_strip_new_rmt_device инициализируем поля переменной структуры ленты, указав имена наших основных функций
1 2 3 4 5 6 7 8 |
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->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.clear = led_strip_rmt_clear; rmt_strip->base.del = led_strip_rmt_del; |
Вернём указатель на нашу структуру
1 2 |
rmt_strip->base.del = led_strip_rmt_del; *ret_strip = &rmt_strip->base; |
Вот в принципе и вся инициализация.
На следующем уроке мы постараемся уже конкретно поуправлять свечением различными цветами и различной яркостью наших светодиодов в ленте.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
Ленты светодиодные WS2812B разные можно приобрести здесь WS2812B
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Добавить комментарий