From 52ec336c558901af8754f405c9dbfa13681f98aa Mon Sep 17 00:00:00 2001 From: Victor Benso Date: Tue, 25 Jul 2023 22:12:13 -0300 Subject: [PATCH] Example for controlling a chain of WS2812 LEDs using ESP32 RMT peripheral --- examples/ws2812esp32rmt/Kconfig | 23 ++ examples/ws2812esp32rmt/Make.defs | 23 ++ examples/ws2812esp32rmt/Makefile | 32 ++ examples/ws2812esp32rmt/ws2812esp32rmt_main.c | 279 ++++++++++++++++++ 4 files changed, 357 insertions(+) create mode 100644 examples/ws2812esp32rmt/Kconfig create mode 100644 examples/ws2812esp32rmt/Make.defs create mode 100644 examples/ws2812esp32rmt/Makefile create mode 100644 examples/ws2812esp32rmt/ws2812esp32rmt_main.c diff --git a/examples/ws2812esp32rmt/Kconfig b/examples/ws2812esp32rmt/Kconfig new file mode 100644 index 000000000..60245d7be --- /dev/null +++ b/examples/ws2812esp32rmt/Kconfig @@ -0,0 +1,23 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_WS2812_ESP32_RMT + tristate "ws2812 Demo Program using ESP32_RMT" + depends on ESP32_RMT + default n + ---help--- + Enable the ws2812 demo, using ESP32 RMT peripheral to generate the pulses + +if EXAMPLES_WS2812_ESP32_RMT + +config EXAMPLES_WS2812_ESP32_RMT_PRIORITY + int "Task Priority" + default 100 + +config EXAMPLES_WS2812_ESP32_RMT_STACKSIZE + int "Stack Size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/examples/ws2812esp32rmt/Make.defs b/examples/ws2812esp32rmt/Make.defs new file mode 100644 index 000000000..6d98a56af --- /dev/null +++ b/examples/ws2812esp32rmt/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/examples/ws2812/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_WS2812_ESP32_RMT),) +CONFIGURED_APPS += $(APPDIR)/examples/ws2812esp32rmt +endif diff --git a/examples/ws2812esp32rmt/Makefile b/examples/ws2812esp32rmt/Makefile new file mode 100644 index 000000000..f2d6a8e2e --- /dev/null +++ b/examples/ws2812esp32rmt/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/examples/ws2812/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# MTD R/W buffer test Example + +PROGNAME = ws2812esp32rmt +PRIORITY = $(CONFIG_EXAMPLES_WS2812_ESP32_RMT_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_WS2812_ESP32_RMT_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_WS2812_ESP32_RMT) + +MAINSRC = ws2812esp32rmt_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/ws2812esp32rmt/ws2812esp32rmt_main.c b/examples/ws2812esp32rmt/ws2812esp32rmt_main.c new file mode 100644 index 000000000..60657cf83 --- /dev/null +++ b/examples/ws2812esp32rmt/ws2812esp32rmt_main.c @@ -0,0 +1,279 @@ +/**************************************************************************** + * apps/examples/ws2812esp32rmt/ws2812esp32rmt_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define APB_PERIOD (12.5) + +#define T0H ((uint16_t)(350 / APB_PERIOD)) // ns +#define T0L ((uint16_t)(900 / APB_PERIOD)) // ns +#define T1H ((uint16_t)(900 / APB_PERIOD)) // ns +#define T1L ((uint16_t)(350 / APB_PERIOD)) // ns +#define RES ((uint16_t)(60000 / APB_PERIOD)) // ns + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct led_s + { + uint8_t r; + uint8_t g; + uint8_t b; + }; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int fd; +static uint32_t *rmt_buffer; +static uint32_t rmt_buffer_len_in_words; +static uint32_t leds_buffer_len_in_bytes; +static struct led_s *leds_buffer; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint32_t map_byte_to_words(uint8_t byte, uint32_t *dst) +{ + uint32_t ret = 0; + uint8_t mask = 0x80; + for (int i = 0; i < 8; i++) + { + uint8_t bit = byte&mask; + mask >>= 1; + uint32_t word; + if (bit) + { + word = (T1L << 16) | (0x8000 | T1H); + } + else + { + word = (T0L << 16) | (0x8000 | T0H); + } + + *dst = word; + dst++; + ret++; + } + + return ret; +} + +static int map_leds_to_words(struct led_s *leds, + uint32_t n_leds, + uint32_t *dst, + uint32_t dst_len_in_words + ) +{ + int ret = OK; + + if (!dst || !leds) + { + return -1; + } + + uint32_t required_words = n_leds * 3 * 8; + + if (required_words >= dst_len_in_words) + { + _err("Required %d words, but buffer has only:%d", + required_words, dst_len_in_words + ); + return -1; + } + + uint32_t dst_offset = 0; + for (uint32_t led_idx = 0; led_idx < n_leds; led_idx++) + { + dst_offset += map_byte_to_words(leds[led_idx].g, dst + dst_offset); + dst_offset += map_byte_to_words(leds[led_idx].r, dst + dst_offset); + dst_offset += map_byte_to_words(leds[led_idx].b, dst + dst_offset); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * ws2812esp32rmt_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + if (argc < 2) + { + printf("usage: %s channel led_count\n", argv[0]); + exit(1); + } + + int ch_idx = atoi(argv[1]); + if (ch_idx >= 8) + { + printf("channel must be int the range 0~7\n"); + exit(1); + } + + uint32_t n_leds = atoi(argv[2]); + + char dev_name[50]; + snprintf(dev_name, 50, "/dev/rmt%d", ch_idx); + fd = open(dev_name, O_WRONLY); + if (fd < 0) + { + fprintf(stderr, + "ws2812esp32rmt: open %s failed: %d\n", + dev_name, + errno); + goto errout_with_dev; + } + + _info("size of (struct led_s) is:%d", sizeof(struct led_s)); + + leds_buffer_len_in_bytes = sizeof(struct led_s)*n_leds; + leds_buffer = (struct led_s *)malloc(leds_buffer_len_in_bytes); + _info("leds:%d - leds_buffer %p len:%d\n", + n_leds, + leds_buffer, + leds_buffer_len_in_bytes + ); + + if (!leds_buffer) + { + _err("failure allocating %d bytes for LED buffer", + leds_buffer_len_in_bytes + ); + goto errout; + } + + rmt_buffer_len_in_words = (leds_buffer_len_in_bytes * 8 + 1); + rmt_buffer = (uint32_t *)malloc(rmt_buffer_len_in_words * 4); + _info("words:%d - rmt_buffer %p rmt_buffer_len_in_words:%d\n", + rmt_buffer_len_in_words, + rmt_buffer, + rmt_buffer_len_in_words + ); + + if (!rmt_buffer) + { + _err("failure allocating %d words for RMT word buffer", + rmt_buffer_len_in_words + ); + goto errout; + } + + int pos = 0; + int direction = 0; + + /* Run the display loop */ + + while (1) + { + int n_loops = 30; + for (int i = 0; i < n_loops; ++i) + { + for (int l = 0; l < n_leds; l++) + { + if (!direction) + leds_buffer[l].r = i; + else + leds_buffer[l].r = 30 - i; + + leds_buffer[l].g = 0x00; + leds_buffer[l].b = 0x00; + + if (l == pos) + { + leds_buffer[l].r = 0x00; + leds_buffer[l].g = 0xff; + leds_buffer[l].b = 0x00; + } + } + + if (pos++ >= n_leds) + pos = 0; + int ret; + ret = map_leds_to_words(leds_buffer, + n_leds, + rmt_buffer, + rmt_buffer_len_in_words + ); + + if (ret) + { + _err("error mapping leds to words"); + goto errout; + } + + ret = write(fd, rmt_buffer, rmt_buffer_len_in_words * 4); + + if (ret < 0) + { + _err("error writing to device, %d errno:%d", ret, errno); + goto errout_with_dev; + } + +/**************************************************************************** + * Must sleep so the WS2812 can understand the EOT + ****************************************************************************/ + + usleep(50000); + } + + direction = !direction; + } + + free(leds_buffer); + free(rmt_buffer); + close(fd); + fflush(stdout); + return OK; + +errout_with_dev: + close(fd); + +errout: + fflush(stdout); + return ERROR; +}