Продолжаем работать с модулем RMT (Remote Control) и управлением светодиодной лентой, состоящей из цепочки адресных светодиодах WS2812B. И на данном уроке мы научимся собственно управлять свечением данных светодиодов, а именно отправить по сигнальной шине в ленту код, который будет зажигать в ней светодиоды нужными нам цветами с нужной интенсивностью.
Схема урока осталась без изменений: светодиодная лента, плата с контроллером ESP32 и логический анализатор на случай мониторинга сигнала
Подключим плату с контроллером к USB-порту компьютера, также к нему подключим анализатор и подадим питание на ленту с помощью DC/DC-преобразователя, не забыв перед подключением его к ленте выставить необходимое напряжение (не более 5 вольт), и займёмся проектом, который был сделан из проекта прошлого урока с именем WS2812_INIT и получил новое имя WS2812.
Откроем наш проект в Espressif IDE и для начала немного подправим ошибки.
В файле led_strip.c в функции rmt_encode_led_strip присвоим адрес указателю
1 2 |
out: *ret_state = state; |
Также для того, чтобы настройки из файла sdkconfig.defaults применились, удалим файл sdkconfig, чего мы не сделали на прошлом уроке.
Теперь пересоберём наш проект.
Перед тем как работать со свечением ленты, нам желательно отправить такие данные, чтобы все светодиоды в ней перестали светиться, то есть очистить её. В файле led_strip.c выше функции rmt_new_led_strip_encoder добавим функцию для очистки ленты
1 2 3 4 5 6 7 |
//----------------------------------------------------------- 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); } //----------------------------------------------------------- |
Здесь мы проверяем валидность входящего аргумента и с помощью поля переменной свойств ленты вызываем функцию led_strip_rmt_clear.
Объявим в заголовочном файле на нашу функцию прототип и вызовем её в функции configure_led файла main.c
1 2 3 |
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); //Set all LED off to clear all pixels led_strip_clear(led_strip); |
Вернёмся в файл led_strip.c и в функции led_strip_rmt_clear создадим переменную типа глобальной структуры и передадим ей указатель из входящего аргумента
1 2 3 |
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); |
Забьём нулями буфер
1 2 3 |
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); //Write zero to turn off all leds memset((void *)rmt_strip->pixel_buf, 0, (size_t)(rmt_strip->strip_len * rmt_strip->bytes_per_pixel)); |
Далее по коду у нас идёт вызов функции led_strip_rmt_refresh, которая будет передавать наш буфер в ленту. Перейдём в данную функцию и аналогичным образом получим указатель на переменную со свойствами ленты
1 2 3 |
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip) { led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); |
Объявим переменную типа структуры свойств канала передачи и обнулим поле времени передачи в цикле
1 2 3 4 |
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base); rmt_transmit_config_t tx_conf = { .loop_count = 0, }; |
Включим канал передачи
1 2 3 |
.loop_count = 0, }; ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed"); |
Передадим буфер в канал RMT
1 2 3 |
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"); |
Дождёмся окончания передачи и отключим канал
1 2 3 4 |
ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf, rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed"); ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed"); ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed"); |
Соберём код, прошьём контроллер и мы должны увидеть, что в лента нашей не будет светиться ни один светодиод. Правда проверить это смогу пока, видимо, только я, так как у меня уже прошит конечный проект с бегущими огнями
Перейдём в файл main.c и добавим функцию, которая будет зажигать разными цветами наши светодиоды на ленте
1 2 3 4 5 6 |
static led_strip_handle_t led_strip; //============================================================== static void blink_led(uint8_t n) { } //============================================================== |
Мы будем использовать только три варианта свечения ленты, поэтому число n — это номер варианта.
Вызовем нашу функцию в цикле в функции app_main с небольшой задержкой
1 2 3 4 5 6 7 8 |
configure_led(); while (1) { for(uint8_t i=0; i<3; i++) { blink_led(i); vTaskDelay(100 / portTICK_PERIOD_MS); } } |
Вернёмся в функцию blink_led и создадим три переменных для цветов и, в зависимости от варианта, назначим основной цвет
1 2 3 4 5 6 |
static void blink_led(uint8_t n) { uint32_t col1 = 0, col2 = 0, col3 = 0; if (n==0) col1=5; else if (n==1) col2=5; else if (n==2) col3=5; |
Интенсивность свечения мы выставим 5, чтобы светодиоды не светились слишком сильно.
Теперь, прежде чем зажигать светодиоды, нам нужна будет функция, поэтому перейдём в файл led_strip.c и выше функции led_strip_clear добавим такую функцию
1 2 3 4 5 6 7 |
//----------------------------------------------------------- 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); } //----------------------------------------------------------- |
Данная функция также проверит валидность входящего аргумента и вызовет функцию led_strip_rmt_set_pixel.
Ниже добавим ещё одну подобную функцию для работы с другим форматом пикселя
1 2 3 4 5 6 7 |
//----------------------------------------------------------- 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); } //----------------------------------------------------------- |
И ниже также добавим функцию для отправки буфера в ленту
1 2 3 4 5 6 7 |
//----------------------------------------------------------- esp_err_t led_strip_refresh(led_strip_handle_t strip) { ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); return strip->refresh(strip); } //----------------------------------------------------------- |
Ниже функции led_strip_clear добавим функцию удаления ленты
1 2 3 4 5 6 7 |
//----------------------------------------------------------- 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); } //----------------------------------------------------------- |
Объявим на эти четыре функции прототипы в заголовочном файле и в функции led_strip_rmt_set_pixel получим адрес входящего аргумента и проверим количество светодиодов
1 2 3 4 |
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"); |
Получим адрес буфера байтов цветов пикселей
1 2 |
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs"); uint32_t start = index * rmt_strip->bytes_per_pixel; |
Занесём байты пикселей по полученному адресу
1 2 3 4 5 |
uint32_t start = index * rmt_strip->bytes_per_pixel; //In thr order of GRB, as LED strip like WS2812 sends out pixels in this order rmt_strip->pixel_buf[start + 0] = green & 0xFF; rmt_strip->pixel_buf[start + 1] = red & 0xFF; rmt_strip->pixel_buf[start + 2] = blue & 0xFF; |
Если байтов на пиксель в свойствах ленты более трёх, то в остальные занесём нули
1 2 3 4 |
rmt_strip->pixel_buf[start + 2] = blue & 0xFF; if (rmt_strip->bytes_per_pixel > 3) { rmt_strip->pixel_buf[start + 3] = 0; } |
Аналогичный код будет и в функции led_strip_rmt_set_pixel_rgbw, только здесь будет ещё один байт белого цвета
1 2 3 4 5 6 7 8 9 10 11 |
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); 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(rmt_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel"); uint8_t *buf_start = rmt_strip->pixel_buf + index * 4; //SK6812 component order is GRBW *buf_start = green & 0xFF; *++buf_start = red & 0xFF; *++buf_start = blue & 0xFF; *++buf_start = white & 0xFF; |
Осталось дописать нам только функцию led_strip_rmt_del, в которой для начала также получим наш указатель, а затем проверим поле номера канала и поле энкодера
1 2 3 4 5 |
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"); |
Очистим указатель
1 2 |
ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed"); free(rmt_strip); |
Перейдём в main.c и в функции blink_led заполним в буфере цвета всех 144 светодиодов и вызовем функцию передачи в ленту
1 2 3 4 5 6 7 8 9 |
else if (n==2) col3=5; for(int i = 0; i < 48; i++) { led_strip_set_pixel(led_strip, i*3, col1, col2, col3); led_strip_set_pixel(led_strip, i*3 + 1, col2, col3, col1); led_strip_set_pixel(led_strip, i*3 + 2, col3, col1, col2); } //Refresh the strip to send data led_strip_refresh(led_strip); |
Соберём код, прошьём контроллер, и увидим, что цвет наших светодиодов будет циклично меняться по кругу
Также мы видим, как передаются данные в ленту в программе логического анализа
Итак, на данном уроке мы научились управлять свечением адресных светодиодов в ленте, используя при этом модуль RMT. Также мы с данным модулем лишний раз поупражнялись с работой его на передачу.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
Ленты светодиодные WS2812B разные можно приобрести здесь WS2812B
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Добавить комментарий