This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 0c5145b7d1 New implementation of the ESP32's RMT driver.
0c5145b7d1 is described below
commit 0c5145b7d1e81288a24b06b7acf58b9b057c0f0c
Author: Victor Benso <[email protected]>
AuthorDate: Sat Jul 1 12:18:07 2023 -0300
New implementation of the ESP32's RMT driver.
---
Kconfig | 38 ++
arch/xtensa/src/esp32/Make.defs | 4 +
arch/xtensa/src/esp32/esp32_rmt.c | 436 +++++++++++++++++++++
arch/xtensa/src/esp32/esp32_rmt.h | 164 ++++++++
arch/xtensa/src/esp32/hardware/esp32_rmt.h | 266 +++++++++++++
boards/xtensa/esp32/common/include/esp32_rmt.h | 67 ++++
boards/xtensa/esp32/common/src/Make.defs | 4 +
boards/xtensa/esp32/common/src/esp32_rmt.c | 282 +++++++++++++
.../esp32-devkitc/configs/wifi_smp_rmt/defconfig | 121 ++++++
boards/xtensa/esp32/esp32-devkitc/src/Make.defs | 4 +
.../xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h | 26 ++
.../xtensa/esp32/esp32-devkitc/src/esp32_bringup.c | 33 +-
.../xtensa/esp32/esp32-devkitc/src/esp32_ws2812.c | 124 ++++++
drivers/leds/ws2812.c | 6 +-
include/debug.h | 18 +
15 files changed, 1587 insertions(+), 6 deletions(-)
diff --git a/Kconfig b/Kconfig
index f4067ad39e..5400b2d080 100644
--- a/Kconfig
+++ b/Kconfig
@@ -1619,6 +1619,44 @@ config DEBUG_RC_INFO
endif # DEBUG_RC
+config DEBUG_RMT
+ bool "RMT Debug Features"
+ default n
+ depends on ESP32_RMT
+ ---help---
+ Enable RMT debug features.
+
+ Support for this debug option is architecture-specific and may
not
+ be available for some MCUs.
+
+if DEBUG_RMT
+
+config DEBUG_RMT_ERROR
+ bool "RMT Error Output"
+ default n
+ depends on DEBUG_ERROR
+ ---help---
+ Enable RMT driver error output to SYSLOG.
+
+ Support for this debug option is architecture-specific and may
not
+ be available for some MCUs.
+
+config DEBUG_RMT_WARN
+ bool "RMT Warnings Output"
+ default n
+ depends on DEBUG_WARN
+ ---help---
+ Enable RMT driver warning output to SYSLOG.
+
+config DEBUG_RMT_INFO
+ bool "RMT Informational Output"
+ default n
+ depends on DEBUG_INFO
+ ---help---
+ Enable RMT driver informational output to SYSLOG.
+
+endif # DEBUG_RMT
+
config DEBUG_RTC
bool "RTC Debug Features"
default n
diff --git a/arch/xtensa/src/esp32/Make.defs b/arch/xtensa/src/esp32/Make.defs
index f9732cff90..f42dfb3bcc 100644
--- a/arch/xtensa/src/esp32/Make.defs
+++ b/arch/xtensa/src/esp32/Make.defs
@@ -82,6 +82,10 @@ ifeq ($(CONFIG_ESP32_PCNT_AS_QE),y)
CHIP_CSRCS += esp32_qencoder.c
endif
+ifeq ($(CONFIG_ESP32_RMT),y)
+CHIP_CSRCS += esp32_rmt.c
+endif
+
ifeq ($(CONFIG_ESP32_SPI),y)
CHIP_CSRCS += esp32_spi.c
ifeq ($(CONFIG_SPI_SLAVE),y)
diff --git a/arch/xtensa/src/esp32/esp32_rmt.c
b/arch/xtensa/src/esp32/esp32_rmt.c
new file mode 100644
index 0000000000..05bf21c00c
--- /dev/null
+++ b/arch/xtensa/src/esp32/esp32_rmt.c
@@ -0,0 +1,436 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32/esp32_rmt.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 <nuttx/config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <arch/board/board.h>
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/spinlock.h>
+
+#include "xtensa.h"
+
+#include "esp32_gpio.h"
+#include "esp32_rmt.h"
+#include "esp32_irq.h"
+#include "esp32_clockconfig.h"
+
+#include "hardware/esp32_dport.h"
+#include "hardware/esp32_gpio_sigmap.h"
+
+#ifdef CONFIG_ESP32_RMT
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* RMT methods */
+
+static void rmt_reset(struct rmt_dev_s *dev);
+static int rmt_setup(struct rmt_dev_s *dev);
+IRAM_ATTR static int rmt_interrupt(int irq, void *context, void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+struct rmt_dev_s g_rmt_dev =
+{
+ .periph = ESP32_PERIPH_RMT,
+ .irq = ESP32_IRQ_RMT,
+ .cpu = 0,
+ .cpuint = -ENOMEM,
+ .lock = 0
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rmt_reset
+ *
+ * Description:
+ * Reset the RMT device. Called early to initialize the hardware. This
+ * function is called, before esp32_rmt_setup().
+ *
+ * Input Parameters:
+ * dev - An instance of the "upper half" RMT driver state structure.
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void rmt_reset(struct rmt_dev_s *dev)
+{
+ irqstate_t flags;
+
+ flags = spin_lock_irqsave(&dev->lock);
+
+ modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST, 1);
+ modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST, 0);
+
+ /* Clear any spurious IRQ Flag */
+
+ putreg32(0xffffffff, RMT_INT_CLR_REG);
+
+ /* Enable memory wrap-around */
+
+ modifyreg32(RMT_APB_CONF_REG, 0 , BIT(1));
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/****************************************************************************
+ * Name: rmt_setup
+ *
+ * Description:
+ * Configure the RMT. This method is called the first time that the RMT
+ * device is opened. This will occur when the port is first opened.
+ * This setup includes configuring and attaching RMT interrupts.
+ *
+ * Input Parameters:
+ * dev - An instance of the "upper half" RMT driver state structure.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+static int rmt_setup(struct rmt_dev_s *dev)
+{
+ irqstate_t flags;
+ int ret = OK;
+
+ flags = spin_lock_irqsave(&dev->lock);
+
+ if (dev->cpuint != -ENOMEM)
+ {
+ /* Disable the provided CPU Interrupt to configure it. */
+
+ up_disable_irq(dev->cpuint);
+ }
+
+ dev->cpu = up_cpu_index();
+ dev->cpuint = esp32_setup_irq(dev->cpu, dev->periph,
+ 1, ESP32_CPUINT_LEVEL);
+ if (dev->cpuint < 0)
+ {
+ /* Failed to allocate a CPU interrupt of this type. */
+
+ ret = dev->cpuint;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return ret;
+ }
+
+ ret = irq_attach(dev->irq, rmt_interrupt, dev);
+
+ if (ret != OK)
+ {
+ /* Failed to attach IRQ, so CPU interrupt must be freed. */
+
+ esp32_teardown_irq(dev->cpu, dev->periph, dev->cpuint);
+ dev->cpuint = -ENOMEM;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return ret;
+ }
+
+ /* Enable the CPU interrupt that is linked to the RMT device. */
+
+ up_enable_irq(dev->irq);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: rmt_load_tx_buffer
+ *
+ * Description:
+ * Copies chunks of data from the buffer to the RMT device memory
+ * This function can also be called on the first transmition data chunk
+ *
+ * Input Parameters:
+ * channel - Pointer to the channel to be reloaded
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+IRAM_ATTR void rmt_load_tx_buffer(struct rmt_dev_channel_s *channel)
+{
+ uint32_t *src = channel->src;
+ uint32_t dst_mem;
+ uint32_t buffer_size;
+
+ if (channel->src_offset == 0)
+ {
+ buffer_size = channel->available_words;
+ dst_mem = channel->start_address;
+ channel->next_buffer = 0;
+ }
+ else
+ {
+ buffer_size = channel->reload_thresh;
+ dst_mem = channel->start_address +
+ 4*channel->next_buffer*channel->reload_thresh;
+
+ /* only swap buffers after the first call */
+
+ if (channel->next_buffer == 0)
+ {
+ channel->next_buffer = 1;
+ }
+ else
+ {
+ channel->next_buffer = 0;
+ }
+ }
+
+ while (channel->src_offset < channel->words_to_send && buffer_size > 0)
+ {
+ uint32_t word_to_send = *(src + channel->src_offset);
+ putreg32(word_to_send, dst_mem);
+
+ channel->src_offset++;
+ dst_mem += 4;
+ buffer_size--;
+ }
+
+ /* Adding 0x00 on RMT's buffer marks the EOT */
+
+ if (channel->src_offset == channel->words_to_send && buffer_size > 0)
+ {
+ putreg32(0x00, dst_mem);
+ }
+}
+
+/****************************************************************************
+ * Name: rmt_interrupt
+ *
+ * Description:
+ * RMT TX interrupt handler
+ *
+ * Input Parameters:
+ * irq - The IRQ number of the interrupt.
+ * context - The register state save array at the time of the interrupt.
+ * arg - The pointer to driver structure.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+IRAM_ATTR static int rmt_interrupt(int irq, void *context, void *arg)
+{
+ struct rmt_dev_s *dev = (struct rmt_dev_s *)arg;
+ uint32_t regval = getreg32(RMT_INT_ST_REG);
+
+ uint8_t error_flag = 0;
+
+ int flags = spin_lock_irqsave(&dev->lock);
+
+ for (int ch_idx = 0; ch_idx < RMT_NUMBER_OF_CHANNELS; ch_idx++)
+ {
+ struct rmt_dev_channel_s *channel_data =
+ (struct rmt_dev_channel_s *) &(dev->channels[ch_idx]);
+
+ /* IRQs from channels with no pins, should be ignored */
+
+ if (channel_data->output_pin < 0)
+ {
+ putreg32(RMT_CHN_TX_THR_EVENT_INT_CLR(ch_idx), RMT_INT_CLR_REG);
+ putreg32(RMT_CHN_TX_END_INT_CLR(ch_idx), RMT_INT_CLR_REG);
+ continue;
+ }
+
+ if (regval & RMT_CHN_TX_THR_EVENT_INT_ST(ch_idx))
+ {
+ putreg32(RMT_CHN_TX_THR_EVENT_INT_CLR(ch_idx), RMT_INT_CLR_REG);
+
+ /* buffer refill */
+
+ rmt_load_tx_buffer(channel_data);
+ }
+ else if (regval & RMT_CHN_TX_END_INT_ST(ch_idx))
+ {
+ /* end of transmition */
+
+ modifyreg32(RMT_INT_ENA_REG,
+ RMT_CHN_TX_END_INT_ENA(ch_idx) |
+ RMT_CHN_TX_THR_EVENT_INT_ENA(ch_idx),
+ 0
+ );
+
+ putreg32(RMT_CHN_TX_END_INT_CLR(ch_idx), RMT_INT_CLR_REG);
+ putreg32(RMT_CHN_TX_THR_EVENT_INT_CLR(ch_idx), RMT_INT_CLR_REG);
+
+ /* release the lock so the write function can return */
+
+ nxsem_post(&channel_data->tx_sem);
+ }
+ }
+
+ if (error_flag)
+ {
+ /* clear any spurious IRQ flag */
+
+ putreg32(0xffffffff, RMT_INT_CLR_REG);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: esp32_rmtinitialize
+ *
+ * Description:
+ * Initialize the selected RMT device
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ * Valid RMT device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct rmt_dev_s *esp32_rmtinitialize(void)
+{
+ struct rmt_dev_s *rmtdev = &g_rmt_dev;
+ irqstate_t flags;
+
+ flags = spin_lock_irqsave(&rmtdev->lock);
+
+ modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_RMT_CLK_EN);
+ modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST, 0);
+
+ spin_unlock_irqrestore(&rmtdev->lock, flags);
+
+ rmt_reset(rmtdev);
+ rmt_setup(rmtdev);
+
+ rmtdev->channels = kmm_zalloc(
+ sizeof(struct rmt_dev_channel_s)*
+ RMT_NUMBER_OF_CHANNELS
+ );
+
+ if (!rmtdev->channels)
+ {
+ rmterr("Failed to allocate memory for RMT Channels");
+ return NULL;
+ }
+
+ for (int ch_idx = 0; ch_idx < RMT_NUMBER_OF_CHANNELS; ch_idx++)
+ {
+ struct rmt_dev_channel_s *channel_data =
+ (struct rmt_dev_channel_s *) &(rmtdev->channels[ch_idx]);
+
+ channel_data->open_count = 0;
+ channel_data->ch_idx = ch_idx;
+ channel_data->output_pin = -1;
+
+ channel_data->available_words = 64;
+ uint32_t start_addr_chn = RMT_DATA_BASE_ADDR +
+ RMT_DATA_MEMORY_BLOCK_WORDS * 4 * ch_idx;
+
+ channel_data->start_address = start_addr_chn;
+ channel_data->reload_thresh = channel_data->available_words / 2;
+ channel_data->parent_dev = rmtdev;
+ }
+
+ return rmtdev;
+}
+
+/****************************************************************************
+ * Name: rmt_attach_pin_to_channel
+ *
+ * Description:
+ * Binds a gpio pin to a RMT channel
+ *
+ * Input Parameters:
+ * rmtdev - pointer the rmt device, needed for the locks
+ * output_pin - the pin used for output
+ * channel - the RMT's channel that will be used
+ *
+ * Returned Value:
+ * Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int rmt_attach_pin_to_channel(struct rmt_dev_s *rmtdev, int ch_idx, int pin)
+{
+ irqstate_t flags;
+
+ if (ch_idx >= RMT_NUMBER_OF_CHANNELS || pin < 0)
+ {
+ return -EINVAL;
+ }
+
+ flags = spin_lock_irqsave(&rmtdev->lock);
+
+ struct rmt_dev_channel_s *channel_data =
+ (struct rmt_dev_channel_s *) &(rmtdev->channels[ch_idx]);
+
+ channel_data->output_pin = pin;
+ nxsem_init(&channel_data->tx_sem, 0, 1);
+
+ /* Configure RMT GPIO pin */
+
+ esp32_gpio_matrix_out(pin, RMT_SIG_OUT0_IDX + ch_idx, 0, 0);
+ esp32_configgpio(pin, OUTPUT_FUNCTION_1);
+
+ spin_unlock_irqrestore(&rmtdev->lock, flags);
+
+ return OK;
+}
+
+#endif
diff --git a/arch/xtensa/src/esp32/esp32_rmt.h
b/arch/xtensa/src/esp32/esp32_rmt.h
new file mode 100644
index 0000000000..a9391aa103
--- /dev/null
+++ b/arch/xtensa/src/esp32/esp32_rmt.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32/esp32_rmt.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_XTENSA_SRC_ESP32_ESP32_RMT_H
+#define __ARCH_XTENSA_SRC_ESP32_ESP32_RMT_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <semaphore.h>
+#include <nuttx/spinlock.h>
+#include "hardware/esp32_rmt.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct rmt_dev_channel_s
+{
+ /* Parameters for each RMT channel */
+
+ int open_count;
+ int ch_idx;
+ int output_pin;
+ int next_buffer;
+ sem_t tx_sem;
+
+ uint32_t *src;
+ uint32_t src_offset;
+ size_t words_to_send;
+ uint32_t available_words;
+ uint32_t start_address;
+ uint32_t reload_thresh;
+ void *parent_dev;
+};
+
+struct rmt_dev_s
+{
+ /* Device configuration */
+
+ uint8_t periph; /* Peripheral ID */
+ uint8_t irq; /* IRQ associated with this RMT */
+ uint8_t cpu; /* CPU ID */
+ int cpuint; /* CPU interrupt assigned to this RMT */
+ spinlock_t lock;
+
+ struct rmt_dev_channel_s *channels;
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+#if defined(CONFIG_ESP32_RMT)
+
+/****************************************************************************
+ * Name: rmt_load_tx_buffer
+ *
+ * Description:
+ * Copies chunks of data from the buffer to the RMT device memory
+ * This function can also be called on the first transmition data chunk
+ *
+ * Input Parameters:
+ * channel - Pointer to the channel to be reloaded
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+IRAM_ATTR void rmt_load_tx_buffer(struct rmt_dev_channel_s *channel);
+
+/****************************************************************************
+ * Name: esp32_rmtinitialize
+ *
+ * Description:
+ * Initialize the selected RMT device
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ * Valid RMT device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct rmt_dev_s *esp32_rmtinitialize(void);
+
+/****************************************************************************
+ * Name: rmt_attach_pin_to_channel
+ *
+ * Description:
+ * Binds a gpio pin to a RMT channel
+ *
+ * Input Parameters:
+ * rmtdev - pointer the rmt device, needed for the locks
+ * output_pin - the pin used for output
+ * channel - the RMT's channel that will be used
+ *
+ * Returned Value:
+ * Zero on success; a negated errno on failure
+ *
+ ****************************************************************************/
+
+int rmt_attach_pin_to_channel(struct rmt_dev_s *rmtdev, int ch_idx, int pin);
+
+/****************************************************************************
+ * Name: board_rmt_initialize
+ *
+ * Description:
+ * Initialize RMT driver and register the channel/pin pair at /dev/rtm0
+ *
+ * Input Parameters:
+ * output_pin - the output pin to assing to the channel
+ * channel - the channel that will be initialized
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success; A negated errno value is returned
+ * to indicate the nature of any failure.
+ *
+ ****************************************************************************/
+
+int board_rmt_initialize(int output_pin, int channel);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ARCH_XTENSA_SRC_ESP32_ESP32_RMT_H */
diff --git a/arch/xtensa/src/esp32/hardware/esp32_rmt.h
b/arch/xtensa/src/esp32/hardware/esp32_rmt.h
new file mode 100644
index 0000000000..7f3c097269
--- /dev/null
+++ b/arch/xtensa/src/esp32/hardware/esp32_rmt.h
@@ -0,0 +1,266 @@
+/****************************************************************************
+ * arch/xtensa/src/esp32/hardware/esp32_rmt.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_XTENSA_SRC_ESP32_HARDWARE_ESP32_RMT_H
+#define __ARCH_XTENSA_SRC_ESP32_HARDWARE_ESP32_RMT_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "esp32_soc.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* RMT Peripheral constants */
+#define RMT_NUMBER_OF_CHANNELS 8
+#define RMT_DATA_BASE_ADDR 0x3ff56800
+#define RMT_DATA_MEMORY_BLOCK_WORDS 64
+
+/* RMT Channel configuration registers */
+
+#define RMT_CHNCONF_REG_BASE (DR_REG_RMT_BASE+0x20)
+#define RMT_CHNCONF0_REG(n) (RMT_CHNCONF_REG_BASE + 8*n)
+#define RMT_CHNCONF1_REG(n) (RMT_CHNCONF0_REG(n) + 4)
+
+#define RMT_INT_RAW_REG (DR_REG_RMT_BASE+0x00A0)
+#define RMT_INT_ST_REG (DR_REG_RMT_BASE+0x00A4)
+#define RMT_INT_ENA_REG (DR_REG_RMT_BASE+0x00A8)
+#define RMT_INT_CLR_REG (DR_REG_RMT_BASE+0x00AC)
+
+#define RMT_CHNCARRIER_DUTY_REG(n) (DR_REG_RMT_BASE + 0x00B0+4*n)
+#define RMT_CHN_TX_LIM_REG(n) (DR_REG_RMT_BASE + 0x00D0+4*n)
+
+#define RMT_APB_CONF_REG (DR_REG_RMT_BASE + 0x00F0)
+
+/* RMT_CHNCONF0_REG Bits */
+
+/* RMT_MEM_PD: This bit is used to power down the entire RMT RAM block.
+ * (It only exists in RMT_CH0CONF0).
+ * 1: power down memory; 0: power up memory. (R/W)
+ */
+
+#define RMT_MEM_PD BIT(30)
+#define RMT_MEM_PD_M (RMT_MEM_PD_V << RMT_MEM_PD_S)
+#define RMT_MEM_PD_V 0x00000001
+#define RMT_MEM_PD_S 30
+
+/* RMT_CARRIER_OUT_LV_CHN This bit is used for configuration when the
+ * carrier wave is being transmitted. Transmit on low output level with 0,
+ * and transmit on high output level with 1. (R/W)
+ */
+
+#define RMT_CARRIER_OUT_LV_CHN BIT(29)
+#define RMT_CARRIER_OUT_LV_CHN_M (RMT_CARRIER_OUT_LV_CHN_V <<
RMT_CARRIER_OUT_LV_CHN_S)
+#define RMT_CARRIER_OUT_LV_CHN_V 0x00000001
+#define RMT_CARRIER_OUT_LV_CHN_S 29
+
+/* RMT_CARRIER_EN_CHN This is the carrier modulation enable-control bit
+ * for channel n. Carrier modulation is enabled with 1, while carrier
+ * modulation is disabled with 0. (R/W)
+ */
+
+#define RMT_CARRIER_EN_CHN BIT(28)
+#define RMT_CARRIER_EN_CHN_M (RMT_CARRIER_EN_CHN_V << RMT_CARRIER_EN_CHN_S)
+#define RMT_CARRIER_EN_CHN_V 0x00000001
+#define RMT_CARRIER_EN_CHN_S 28
+
+/* RMT_MEM_SIZE_CHN This register is used to configure the amount of
+ * memory blocks allocated to channel n. (R/W)
+ */
+
+#define RMT_MEM_SIZE_CHN BIT(24)
+#define RMT_MEM_SIZE_CHN_M (RMT_MEM_SIZE_CHN_V << RMT_MEM_SIZE_CHN_S)
+#define RMT_MEM_SIZE_CHN_V 0x00000001
+#define RMT_MEM_SIZE_CHN_S 24
+
+/* RMT_IDLE_THRES_CHN In receive mode, when no edge is detected on
+ * the input signal for longer than REG_IDLE_THRES_CHN channel clock cycles,
+ * the receive process is finished. (R/W)
+ */
+
+#define RMT_IDLE_THRES_CHN BIT(8)
+#define RMT_IDLE_THRES_CHN_M (RMT_IDLE_THRES_CHN_V << RMT_IDLE_THRES_CHN_S)
+#define RMT_IDLE_THRES_CHN_V 0x00000001
+#define RMT_IDLE_THRES_CHN_S 8
+
+/* RMT_DIV_CNT_CHN This register is used to set the divider for the channel
+ * clock of channel n. (R/W)
+ */
+
+#define RMT_DIV_CNT_CHN BIT(0)
+#define RMT_DIV_CNT_CHN_M (RMT_DIV_CNT_CHN_V << RMT_DIV_CNT_CHN_S)
+#define RMT_DIV_CNT_CHN_V 0x00000001
+#define RMT_DIV_CNT_CHN_S 0
+
+/* RMT_CHNCONF1_REG Bits */
+
+/* RMT_IDLE_OUT_EN_CHN This is the output enable-control bit for channel n
+ * in IDLE state. (R/W)
+ */
+
+#define RMT_IDLE_OUT_EN_CHN BIT(19)
+#define RMT_IDLE_OUT_EN_CHN_M (RMT_IDLE_OUT_EN_CHN_V << RMT_IDLE_OUT_EN_CHN_S)
+#define RMT_IDLE_OUT_EN_CHN_V 0x00000001
+#define RMT_IDLE_OUT_EN_CHN_S 19
+
+/* RMT_IDLE_OUT_LV_CHN This bit configures the level of output signals
+ * in channel n when the latter is in IDLE state. (R/W)
+ */
+
+#define RMT_IDLE_OUT_LV_CHN BIT(18)
+#define RMT_IDLE_OUT_LV_CHN_M (RMT_IDLE_OUT_LV_CHN_V << RMT_IDLE_OUT_LV_CHN_S)
+#define RMT_IDLE_OUT_LV_CHN_V 0x00000001
+#define RMT_IDLE_OUT_LV_CHN_S 18
+
+/* RMT_REF_ALWAYS_ON_CHN This bit is used to select the channel's base
+ * clock. 1:clk_apb; 0:clk_ref. (R/W)
+ */
+
+#define RMT_REF_ALWAYS_ON_CHN BIT(17)
+#define RMT_REF_ALWAYS_ON_CHN_M (RMT_REF_ALWAYS_ON_CHN_V <<
RMT_REF_ALWAYS_ON_CHN_S)
+#define RMT_REF_ALWAYS_ON_CHN_V 0x00000001
+#define RMT_REF_ALWAYS_ON_CHN_S 17
+
+/* RMT_REF_CNT_RST_CHN Setting this bit resets the clock divider of channel
+ * n. (R/W)
+ */
+
+#define RMT_REF_CNT_RST_CHN BIT(16)
+#define RMT_REF_CNT_RST_CHN_M (RMT_REF_CNT_RST_CHN_V << RMT_REF_CNT_RST_CHN_S)
+#define RMT_REF_CNT_RST_CHN_V 0x00000001
+#define RMT_REF_CNT_RST_CHN_S 16
+
+/* RMT_RX_FILTER_THRES_CHN In receive mode, channel n ignores input
+ * pulse when the pulse width is smaller than this value in APB clock
+ * periods. (R/W)
+ */
+
+#define RMT_RX_FILTER_THRES_CHN BIT(8)
+#define RMT_RX_FILTER_THRES_CHN_M (RMT_RX_FILTER_THRES_CHN_V <<
RMT_RX_FILTER_THRES_CHN_S)
+#define RMT_RX_FILTER_THRES_CHN_V 0x00000001
+#define RMT_RX_FILTER_THRES_CHN_S 8
+
+/* RMT_RX_FILTER_EN_CHN This is the receive filter's enable-bit for channel
+ * n. (R/W)
+ */
+
+#define RMT_RX_FILTER_EN_CHN BIT(7)
+#define RMT_RX_FILTER_EN_CHN_M (RMT_RX_FILTER_EN_CHN_V <<
RMT_RX_FILTER_EN_CHN_S)
+#define RMT_RX_FILTER_EN_CHN_V 0x00000001
+#define RMT_RX_FILTER_EN_CHN_S 7
+
+/* RMT_TX_CONTI_MODE_CHN If this bit is set, instead of going to an idle
+ * state when transmission ends, the transmitter will restart transmission.
+ * This results in a repeating output signal. (R/W)
+ */
+
+#define RMT_TX_CONTI_MODE_CHN BIT(6)
+#define RMT_TX_CONTI_MODE_CHN_M (RMT_TX_CONTI_MODE_CHN_V <<
RMT_TX_CONTI_MODE_CHN_S)
+#define RMT_TX_CONTI_MODE_CHN_V 0x00000001
+#define RMT_TX_CONTI_MODE_CHN_S 6
+
+/* RMT_MEM_OWNER_CHN This bit marks channel n's RAM block ownership.
+ * Number 1 indicates that the receiver is using the RAM, while 0 indicates
+ * that the transmitter is using the RAM. (R/W)
+ */
+
+#define RMT_MEM_OWNER_CHN BIT(5)
+#define RMT_MEM_OWNER_CHN_M (RMT_MEM_OWNER_CHN_V << RMT_MEM_OWNER_CHN_S)
+#define RMT_MEM_OWNER_CHN_V 0x00000001
+#define RMT_MEM_OWNER_CHN_S 5
+
+/* RMT_MEM_RD_RST_CHN Set this bit to reset the read-RAM address for channel
+ * n by accessing the transmitter. (R/W)
+ */
+
+#define RMT_MEM_RD_RST_CHN BIT(3)
+#define RMT_MEM_RD_RST_CHN_M (RMT_MEM_RD_RST_CHN_V << RMT_MEM_RD_RST_CHN_S)
+#define RMT_MEM_RD_RST_CHN_V 0x00000001
+#define RMT_MEM_RD_RST_CHN_S 3
+
+/* RMT_MEM_WR_RST_CHN Set this bit to reset the write-RAM address for
+ * channel n by accessing the receiver. (R/W)
+ */
+
+#define RMT_MEM_WR_RST_CHN BIT(2)
+#define RMT_MEM_WR_RST_CHN_M (RMT_MEM_WR_RST_CHN_V << RMT_MEM_WR_RST_CHN_S)
+#define RMT_MEM_WR_RST_CHN_V 0x00000001
+#define RMT_MEM_WR_RST_CHN_S 2
+
+/* RMT_RX_EN_CHN Set this bit to enable receiving data on channel n. (R/W) */
+
+#define RMT_RX_EN_CHN BIT(1)
+#define RMT_RX_EN_CHN_M (RMT_RX_EN_CHN_V << RMT_RX_EN_CHN_S)
+#define RMT_RX_EN_CHN_V 0x00000001
+#define RMT_RX_EN_CHN_S 1
+
+/* RMT_TX_START_CHN Set this bit to start sending data on channel n. (R/W) */
+
+#define RMT_TX_START_CHN(n) BIT(n)
+#define RMT_TX_START_CHN_M (RMT_TX_START_CHN_V << RMT_TX_START_CHN_S)
+#define RMT_TX_START_CHN_V 0x00000001
+#define RMT_TX_START_CHN_S 0
+
+/* RMT_INT_RAW_REG Bits */
+
+/* RMT_CHN_TX_THR_EVENT_INT_RAW The raw interrupt status bit for the
+ * RMT_CHN_TX_THR_EVENT_INT interrupt. (RO)
+ */
+
+#define RMT_CHN_TX_THR_EVENT_INT_RAW(n) BIT(24+n)
+
+/* RMT_CHN_ERR_INT_RAW The raw interrupt status bit for the RMT_CHN_ERR_INT
+ * interrupt. (RO)
+ */
+
+#define RMT_CHN_ERR_INT_RAW(n) BIT(3*n+2)
+
+/* RMT_CHN_RX_END_INT_RAW The raw interrupt status bit for
+ * the RMT_CHN_RX_END_INT interrupt. (RO)
+ */
+#define RMT_CHN_RX_END_INT_RAW(n) BIT(3*n+1)
+
+/* RMT_CHN_TX_END_INT_RAW The raw interrupt status bit for the
+ * RMT_CHN_TX_END_INT interrupt. (RO)
+ */
+#define RMT_CHN_TX_END_INT_RAW(n) BIT(3*n)
+
+/* RMT_INT_ST_REG Bits */
+#define RMT_CHN_TX_THR_EVENT_INT_ST(n) BIT(24+n)
+#define RMT_CHN_ERR_INT_ST(n) BIT(3*n+2)
+#define RMT_CHN_RX_END_INT_ST(n) BIT(3*n+1)
+#define RMT_CHN_TX_END_INT_ST(n) BIT(3*n)
+
+/* RMT_INT_ENA_REG Bits */
+#define RMT_CHN_TX_THR_EVENT_INT_ENA(n) BIT(24+n)
+#define RMT_CHN_ERR_INT_ENA(n) BIT(3*n+2)
+#define RMT_CHN_RX_END_INT_ENA(n) BIT(3*n+1)
+#define RMT_CHN_TX_END_INT_ENA(n) BIT(3*n)
+
+/* RMT_INT_CLR_REG Bits */
+#define RMT_CHN_TX_THR_EVENT_INT_CLR(n) BIT(24+n)
+#define RMT_CHN_ERR_INT_CLR(n) BIT(3*n+2)
+#define RMT_CHN_RX_END_INT_CLR(n) BIT(3*n+1)
+#define RMT_CHN_TX_END_INT_CLR(n) BIT(3*n)
+
+#endif /* __ARCH_XTENSA_SRC_ESP32_HARDWARE_ESP32_RMT_H */
diff --git a/boards/xtensa/esp32/common/include/esp32_rmt.h
b/boards/xtensa/esp32/common/include/esp32_rmt.h
new file mode 100644
index 0000000000..560c64f29f
--- /dev/null
+++ b/boards/xtensa/esp32/common/include/esp32_rmt.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+ * boards/xtensa/esp32/common/include/esp32_rmt.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __BOARDS_XTENSA_ESP32_COMMON_INCLUDE_ESP32_RMT_H
+#define __BOARDS_XTENSA_ESP32_COMMON_INCLUDE_ESP32_RMT_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Type Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Inline Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BOARDS_XTENSA_ESP32_COMMON_INCLUDE_ESP32_RMT_H */
diff --git a/boards/xtensa/esp32/common/src/Make.defs
b/boards/xtensa/esp32/common/src/Make.defs
index c3f39c2f9d..ed718e251a 100644
--- a/boards/xtensa/esp32/common/src/Make.defs
+++ b/boards/xtensa/esp32/common/src/Make.defs
@@ -124,6 +124,10 @@ ifeq ($(CONFIG_RGBLED),y)
CSRCS += esp32_rgbled.c
endif
+ifeq ($(CONFIG_ESP32_RMT),y)
+ CSRCS += esp32_rmt.c
+endif
+
DEPPATH += --dep-path src
VPATH += :src
CFLAGS +=
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)src
diff --git a/boards/xtensa/esp32/common/src/esp32_rmt.c
b/boards/xtensa/esp32/common/src/esp32_rmt.c
new file mode 100644
index 0000000000..86bf860197
--- /dev/null
+++ b/boards/xtensa/esp32/common/src/esp32_rmt.c
@@ -0,0 +1,282 @@
+/****************************************************************************
+ * boards/xtensa/esp32/common/src/esp32_rmt.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 <nuttx/config.h>
+
+#include <errno.h>
+#include <debug.h>
+#include <stdio.h>
+
+#include "xtensa.h"
+
+#include <nuttx/kmalloc.h>
+#include "esp32_rmt.h"
+
+#ifdef CONFIG_ESP32_RMT
+
+/****************************************************************************
+ * 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
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+static int rmt_open(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct rmt_dev_channel_s *dev_data = inode->i_private;
+
+ FAR struct rmt_dev_s *parent_dev =
+ (struct rmt_dev_s *)dev_data->parent_dev;
+ int ret;
+ irqstate_t flags;
+ DEBUGASSERT(parent_dev);
+
+ nxsem_wait(&dev_data->tx_sem);
+
+ flags = spin_lock_irqsave(&parent_dev->lock);
+
+ if (dev_data->open_count == 0)
+ {
+ int ch_idx = dev_data->ch_idx;
+
+ uint32_t reg0_addr = RMT_CHNCONF0_REG(ch_idx);
+ uint32_t reg1_addr = RMT_CHNCONF1_REG(ch_idx);
+ uint32_t reg_val = 0x00;
+
+ /* a single memory block with double buffering is enough */
+
+ uint32_t mem_blocks = 1;
+ dev_data->available_words = RMT_DATA_MEMORY_BLOCK_WORDS*mem_blocks;
+ dev_data->reload_thresh = dev_data->available_words / 2;
+ uint32_t start_addr_chn = RMT_DATA_BASE_ADDR +
+ RMT_DATA_MEMORY_BLOCK_WORDS * 4 * ch_idx;
+
+ dev_data->start_address = start_addr_chn;
+
+ reg_val = (mem_blocks) << 24;
+ uint32_t clock_divider = 1;
+ reg_val |= (clock_divider);
+ putreg32(reg_val, reg0_addr);
+ reg_val = 0;
+
+ /* use APB clock */
+
+ reg_val |= RMT_REF_ALWAYS_ON_CHN;
+
+ /* memory block in transmission mode */
+
+ reg_val &= ~RMT_MEM_OWNER_CHN;
+ putreg32(reg_val, reg1_addr);
+
+ /* set when the buffer swapping IRQ must be generated */
+
+ uint32_t reload_addr = RMT_CHN_TX_LIM_REG(ch_idx);
+ rmtinfo("Setting thr limit at %08X to %d",
+ reload_addr, dev_data->reload_thresh);
+ putreg32(dev_data->reload_thresh, reload_addr);
+
+ /* allow direct access to RMT's memory */
+
+ modifyreg32(RMT_APB_CONF_REG, 0, BIT(0));
+ }
+ else
+ {
+ rmtwarn("Be careful on opening this channel multiple times");
+ }
+
+ dev_data->open_count += 1;
+
+ ret = OK;
+
+ spin_unlock_irqrestore(&parent_dev->lock, flags);
+ nxsem_post(&dev_data->tx_sem);
+
+ return ret;
+}
+
+static int rmt_close(FAR struct file *filep)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct rmt_dev_channel_s *dev_data = inode->i_private;
+
+ FAR struct rmt_dev_s *parent_dev =
+ (struct rmt_dev_s *)dev_data->parent_dev;
+
+ int ret;
+ irqstate_t flags;
+ DEBUGASSERT(parent_dev);
+ nxsem_wait(&dev_data->tx_sem);
+ flags = spin_lock_irqsave(&parent_dev->lock);
+
+ dev_data->open_count -= 1;
+
+ ret = OK;
+
+ spin_unlock_irqrestore(&parent_dev->lock, flags);
+ nxsem_post(&dev_data->tx_sem);
+ return ret;
+}
+
+static ssize_t rmt_write(FAR struct file *filep,
+ FAR const char *data,
+ size_t len)
+{
+ FAR struct inode *inode = filep->f_inode;
+ FAR struct rmt_dev_channel_s *dev_data = inode->i_private;
+
+ FAR struct rmt_dev_s *parent_dev =
+ (struct rmt_dev_s *)dev_data->parent_dev;
+
+ irqstate_t flags;
+ size_t len_in_words = len / 4;
+
+ DEBUGASSERT(parent_dev);
+
+ if (data == NULL || (len_in_words == 0) || (len % 4))
+ {
+ return -EINVAL;
+ }
+
+ flags = spin_lock_irqsave(&parent_dev->lock);
+
+ /* set RMT's memory as writable */
+
+ uint32_t reg1_addr = RMT_CHNCONF1_REG(dev_data->ch_idx);
+ modifyreg32(reg1_addr, 0, RMT_MEM_RD_RST_CHN);
+ modifyreg32(reg1_addr, RMT_MEM_RD_RST_CHN, 0);
+
+ dev_data->src = (uint32_t *)data;
+ dev_data->src_offset = 0;
+ dev_data->words_to_send = len_in_words;
+
+ /* enable IRQs for buffer refill and End-of-Transmition (EOT) */
+
+ modifyreg32(
+ RMT_INT_ENA_REG,
+ 0,
+ RMT_CHN_TX_THR_EVENT_INT_ENA(dev_data->ch_idx) |
+ RMT_CHN_TX_END_INT_ENA(dev_data->ch_idx));
+
+ rmt_load_tx_buffer(dev_data);
+
+ /* tell RMT to start the transmition */
+
+ modifyreg32(reg1_addr, 0, RMT_TX_START_CHN(dev_data->ch_idx));
+
+ spin_unlock_irqrestore(&parent_dev->lock, flags);
+
+ /* wait for the transmition to finish */
+
+ nxsem_wait(&dev_data->tx_sem);
+ nxsem_post(&dev_data->tx_sem);
+
+ return len;
+}
+
+/****************************************************************************
+ * Name: board_rmt_initialize
+ *
+ * Description:
+ * Initialize and register the RMT driver
+ *
+ * Input Parameters:
+ * devno - The device number, used to build the device path as /dev/rmtN
+ * rmt_dev - Pointer to the RMT device that will be used
+ * nleds - number of LEDs
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static const struct file_operations g_rmt_channel_fops =
+{
+ rmt_open, /* open */
+ rmt_close, /* close */
+ NULL, /* read */
+ rmt_write, /* write */
+ NULL, /* seek */
+ NULL, /* ioctl */
+};
+
+int board_rmt_initialize(int channel, int output_pin)
+{
+ struct rmt_dev_s *rmt_dev = esp32_rmtinitialize();
+ DEBUGASSERT(rmt_dev);
+
+ char devpath[13];
+ int ret;
+
+ rmt_attach_pin_to_channel(rmt_dev, channel, output_pin);
+
+ struct rmt_dev_channel_s *channel_data = &(rmt_dev->channels[channel]);
+
+ /* Register the WS2812 driver at the specified location. */
+
+ snprintf(devpath, sizeof(devpath), "/dev/rmt%d", channel);
+
+ /* Register the character driver */
+
+ ret = register_driver(devpath, &g_rmt_channel_fops, 0666, channel_data);
+
+ if (ret < 0)
+ {
+ rmterr("ERROR: board_rmt_initialize(%s) failed: %d\n",
+ devpath, ret);
+ return ret;
+ }
+
+ return OK;
+}
+#endif
diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/wifi_smp_rmt/defconfig
b/boards/xtensa/esp32/esp32-devkitc/configs/wifi_smp_rmt/defconfig
new file mode 100644
index 0000000000..426563624d
--- /dev/null
+++ b/boards/xtensa/esp32/esp32-devkitc/configs/wifi_smp_rmt/defconfig
@@ -0,0 +1,121 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed
.config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that
includes your
+# modifications.
+#
+# CONFIG_ARCH_LEDS is not set
+# CONFIG_NSH_ARGCAT is not set
+# CONFIG_NSH_CMDOPT_HEXDUMP is not set
+CONFIG_ALLOW_BSD_COMPONENTS=y
+CONFIG_ARCH="xtensa"
+CONFIG_ARCH_BOARD="esp32-devkitc"
+CONFIG_ARCH_BOARD_COMMON=y
+CONFIG_ARCH_BOARD_ESP32_DEVKITC=y
+CONFIG_ARCH_CHIP="esp32"
+CONFIG_ARCH_CHIP_ESP32=y
+CONFIG_ARCH_CHIP_ESP32WROVER=y
+CONFIG_ARCH_INTERRUPTSTACK=2048
+CONFIG_ARCH_SETJMP_H=y
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_ARCH_XTENSA=y
+CONFIG_BOARD_LOOPSPERMSEC=16717
+CONFIG_BUILTIN=y
+CONFIG_CODECS_HASH_MD5=y
+CONFIG_DEBUG_ASSERTIONS=y
+CONFIG_DEBUG_FEATURES=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DEV_URANDOM=y
+CONFIG_DRIVERS_IEEE80211=y
+CONFIG_DRIVERS_WIRELESS=y
+CONFIG_ESP32_SPIFLASH=y
+CONFIG_ESP32_SPIFLASH_SPIFFS=y
+CONFIG_ESP32_STORAGE_MTD_SIZE=0x80000
+CONFIG_ESP32_UART0=y
+CONFIG_ESP32_WIFI=y
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_EXAMPLES_WEBSERVER=y
+CONFIG_FS_PROCFS=y
+CONFIG_HAVE_CXX=y
+CONFIG_HAVE_CXXINITIALIZE=y
+CONFIG_IDLETHREAD_STACKSIZE=2048
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_INIT_STACKSIZE=3072
+CONFIG_INTELHEX_BINARY=y
+CONFIG_IOB_NBUFFERS=128
+CONFIG_MM_REGIONS=4
+CONFIG_NAME_MAX=48
+CONFIG_NETDB_DNSCLIENT=y
+CONFIG_NETDB_DNSCLIENT_NAMESIZE=64
+CONFIG_NETDEV_LATEINIT=y
+CONFIG_NETDEV_PHY_IOCTL=y
+CONFIG_NETDEV_WIRELESS_IOCTL=y
+CONFIG_NETUTILS_CJSON=y
+CONFIG_NETUTILS_CODECS=y
+CONFIG_NETUTILS_HTTPD_DIRLIST=y
+CONFIG_NETUTILS_HTTPD_SENDFILE=y
+CONFIG_NETUTILS_IPERF=y
+CONFIG_NETUTILS_NTPCLIENT_STACKSIZE=4096
+CONFIG_NETUTILS_TELNETD=y
+CONFIG_NETUTILS_WEBCLIENT=y
+CONFIG_NETUTILS_WEBSERVER=y
+CONFIG_NET_BROADCAST=y
+CONFIG_NET_ETH_PKTSIZE=1518
+CONFIG_NET_ICMP=y
+CONFIG_NET_ICMP_SOCKET=y
+CONFIG_NET_STATISTICS=y
+CONFIG_NET_TCP=y
+CONFIG_NET_TCP_WRITE_BUFFERS=y
+CONFIG_NET_UDP=y
+CONFIG_NFS=y
+CONFIG_NSH_ARCHINIT=y
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_CODECS_BUFSIZE=2048
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_LINELEN=300
+CONFIG_NSH_READLINE=y
+CONFIG_NSH_WGET_BUFF_SIZE=2048
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_PTHREAD_MUTEX_TYPES=y
+CONFIG_RAM_SIZE=114688
+CONFIG_RAM_START=0x20000000
+CONFIG_READLINE_CMD_HISTORY=y
+CONFIG_RR_INTERVAL=200
+CONFIG_RTC=y
+CONFIG_RTC_ALARM=y
+CONFIG_RTC_DRIVER=y
+CONFIG_RTC_NALARMS=2
+CONFIG_SCHED_LPWORK=y
+CONFIG_SIG_DEFAULT=y
+CONFIG_SMP=y
+CONFIG_SMP_NCPUS=2
+CONFIG_SPI=y
+CONFIG_SPIFFS_NAME_MAX=48
+CONFIG_STACK_COLORATION=y
+CONFIG_START_DAY=18
+CONFIG_START_MONTH=2
+CONFIG_START_YEAR=2021
+CONFIG_SYSLOG_BUFFER=y
+CONFIG_SYSLOG_TIMESTAMP=y
+CONFIG_SYSTEM_DHCPC_RENEW=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_SYSTEM_NTPC=y
+CONFIG_SYSTEM_PING=y
+CONFIG_SYSTEM_SYSTEM=y
+CONFIG_SYSTEM_TASKSET=y
+CONFIG_TESTING_GETPRIME=y
+CONFIG_TESTING_OSTEST=y
+CONFIG_TESTING_SMP=y
+CONFIG_TLS_TASK_NELEM=4
+CONFIG_UART0_SERIAL_CONSOLE=y
+CONFIG_WIRELESS=y
+CONFIG_WIRELESS_WAPI=y
+CONFIG_WIRELESS_WAPI_CMDTOOL=y
+CONFIG_WIRELESS_WAPI_INITCONF=y
+CONFIG_WIRELESS_WAPI_STACKSIZE=4096
+CONFIG_EXPERIMENTAL=y
+CONFIG_ESP32_RMT=y
+CONFIG_EXAMPLES_WS2812_WITH_ESP32_RMT=y
+
diff --git a/boards/xtensa/esp32/esp32-devkitc/src/Make.defs
b/boards/xtensa/esp32/esp32-devkitc/src/Make.defs
index 815c5b0500..784eaf1ea2 100644
--- a/boards/xtensa/esp32/esp32-devkitc/src/Make.defs
+++ b/boards/xtensa/esp32/esp32-devkitc/src/Make.defs
@@ -45,6 +45,10 @@ ifeq ($(CONFIG_USERLED),y)
CSRCS += esp32_userleds.c
endif
+ifeq ($(CONFIG_WS2812),y)
+CSRCS += esp32_ws2812.c
+endif
+
ifeq ($(CONFIG_ARCH_BUTTONS),y)
CSRCS += esp32_buttons.c
endif
diff --git a/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
b/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
index 8e5e9d9d08..ae9f3edbb5 100644
--- a/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
+++ b/boards/xtensa/esp32/esp32-devkitc/src/esp32-devkitc.h
@@ -66,6 +66,11 @@
#define ONESHOT_TIMER 1
#define ONESHOT_RESOLUTION_US 1
+/* RMT gpio */
+
+#define RMT_OUTPUT_PIN 4
+#define RMT_CHANNEL 0
+
/****************************************************************************
* Public Types
****************************************************************************/
@@ -201,5 +206,26 @@ int board_i2sdev_initialize(int port, bool enable_tx, bool
enable_rx);
int esp32_cs4344_initialize(int port);
#endif
+/****************************************************************************
+ * Name: board_ws2812_initialize
+ *
+ * Description:
+ * This function may called from application-specific logic during its
+ * to perform board-specific initialization of the ws2812 device
+ *
+ *
+ ****************************************************************************/
+
+# ifdef CONFIG_WS2812
+# ifndef CONFIG_WS2812_NON_SPI_DRIVER
+int board_ws2812_initialize(int devno, int spino, uint16_t nleds);
+# else
+int board_ws2812_initialize(
+ int devno,
+ uint16_t nleds,
+ void *dev);
+# endif
+# endif
+
#endif /* __ASSEMBLY__ */
#endif /* __BOARDS_XTENSA_ESP32_ESP32_DEVKITC_SRC_ESP32_DEVKITC_H */
diff --git a/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
b/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
index 0042829b30..a06dea4323 100644
--- a/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
+++ b/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c
@@ -41,6 +41,7 @@
#endif
#include <nuttx/fs/fs.h>
#include <nuttx/himem/himem.h>
+#include <nuttx/board.h>
#if defined(CONFIG_ESP32_EFUSE)
#include "esp32_efuse.h"
@@ -56,7 +57,7 @@
#endif
#ifdef CONFIG_TIMER
-#include <esp32_tim_lowerhalf.h>
+# include <esp32_tim_lowerhalf.h>
#endif
#ifdef CONFIG_ONESHOT
@@ -152,6 +153,10 @@
# include "esp32_max6675.h"
#endif
+#ifdef CONFIG_ESP32_RMT
+# include "esp32_rmt.h"
+#endif
+
#include "esp32-devkitc.h"
/****************************************************************************
@@ -298,7 +303,7 @@ int esp32_bringup(void)
if (ret)
{
syslog(LOG_ERR, "ERROR: Failed to initialize Wi-Fi and BT "
- "coexistence support\n");
+ "coexistence support\n");
}
#endif
@@ -319,9 +324,9 @@ int esp32_bringup(void)
}
#endif
-/* First, register the timer drivers and let timer 1 for oneshot
- * if it is enabled.
- */
+ /* First, register the timer drivers and let timer 1 for oneshot
+ * if it is enabled.
+ */
#ifdef CONFIG_TIMER
@@ -624,6 +629,14 @@ int esp32_bringup(void)
}
#endif
+#ifdef CONFIG_ESP32_RMT
+ ret = board_rmt_initialize(RMT_CHANNEL, RMT_OUTPUT_PIN);
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "ERROR: board_rmt_initialize() failed: %d\n", ret);
+ }
+#endif
+
#ifdef CONFIG_RTC_DRIVER
/* Instantiate the ESP32 RTC driver */
@@ -644,6 +657,16 @@ int esp32_bringup(void)
ESP32_SPI2, ret);
}
# endif
+#endif
+
+#ifdef CONFIG_WS2812
+# ifndef CONFIG_WS2812_NON_SPI_DRIVER
+ ret = board_ws2812_initialize(0, ESP32_SPI3, CONFIG_WS2812_LED_COUNT);
+ if (ret < 0)
+ {
+ syslog(LOG_ERR, "Failed to initialize ws2812 driver\n");
+ }
+# endif
#endif
/* If we got here then perhaps not all initialization was successful, but
diff --git a/boards/xtensa/esp32/esp32-devkitc/src/esp32_ws2812.c
b/boards/xtensa/esp32/esp32-devkitc/src/esp32_ws2812.c
new file mode 100644
index 0000000000..41e5ffcb41
--- /dev/null
+++ b/boards/xtensa/esp32/esp32-devkitc/src/esp32_ws2812.c
@@ -0,0 +1,124 @@
+/****************************************************************************
+ * boards/xtensa/esp32/esp32-devkitc/src/esp32_ws2812.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 <nuttx/config.h>
+
+#include <errno.h>
+#include <debug.h>
+#include <stdio.h>
+
+#include "xtensa.h"
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/leds/ws2812.h>
+
+#ifdef CONFIG_WS2812
+
+/****************************************************************************
+ * 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
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+# ifndef CONFIG_WS2812_NON_SPI_DRIVER
+/****************************************************************************
+ * Name: board_ws2812_initialize
+ *
+ * Description:
+ * Initialize and register the WS2812 LED driver.
+ *
+ * Input Parameters:
+ * devno - The device number, used to build the device path as /dev/leddrvN
+ * spino - SPI port number
+ * nleds - number of LEDs
+ *
+ * Returned Value:
+ * Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int board_ws2812_initialize(int devno, int spino, uint16_t nleds)
+{
+ struct spi_dev_s *spi;
+ char devpath[13];
+ int ret;
+
+ spi = esp32_spibus_initialize(spino);
+ if (spi == NULL)
+ {
+ return -ENODEV;
+ }
+
+ /* Register the WS2812 driver at the specified location. */
+
+ snprintf(devpath, sizeof(devpath), "/dev/leds%d", devno);
+ ret = ws2812_leds_register(devpath, spi, nleds);
+ if (ret < 0)
+ {
+ lederr("ERROR: ws2812_leds_register(%s) failed: %d\n",
+ devpath, ret);
+ return ret;
+ }
+
+ return OK;
+}
+# else
+int board_ws2812_initialize(int devno, int spino, uint16_t nleds)
+{
+ return -1;
+}
+# endif
+
+#endif
diff --git a/drivers/leds/ws2812.c b/drivers/leds/ws2812.c
index d352347279..87bec5f1b5 100644
--- a/drivers/leds/ws2812.c
+++ b/drivers/leds/ws2812.c
@@ -55,7 +55,11 @@
* Pre-processor Definitions
****************************************************************************/
-#define WS2812_RW_PIXEL_SIZE 4
+#ifdef WS2812_HAS_WHITE
+# define WS2812_RW_PIXEL_SIZE 4
+#else
+# define WS2812_RW_PIXEL_SIZE 3
+#endif
#ifdef CONFIG_WS2812_NON_SPI_DRIVER
diff --git a/include/debug.h b/include/debug.h
index 659c5dd6c0..d1411af6cb 100644
--- a/include/debug.h
+++ b/include/debug.h
@@ -629,6 +629,24 @@
# define rcinfo _none
#endif
+#ifdef CONFIG_DEBUG_RMT_ERROR
+# define rmterr _err
+#else
+# define rmterr _none
+#endif
+
+#ifdef CONFIG_DEBUG_RMT_WARN
+# define rmtwarn _warn
+#else
+# define rmtwarn _none
+#endif
+
+#ifdef CONFIG_DEBUG_RMT_INFO
+# define rmtinfo _info
+#else
+# define rmtinfo _none
+#endif
+
#ifdef CONFIG_DEBUG_RTC_ERROR
# define rtcerr _err
#else