New implementation of the ESP32's RMT driver.

This commit is contained in:
Victor Benso
2023-07-01 12:18:07 -03:00
committed by Alan Carvalho de Assis
parent f3e22c0bb0
commit 0c5145b7d1
15 changed files with 1587 additions and 6 deletions
@@ -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 */
+4
View File
@@ -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
+282
View File
@@ -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
@@ -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
@@ -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
@@ -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 */
@@ -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
@@ -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