You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2024/02/20 14:46:12 UTC

(nuttx) branch master updated: esp32-sparrow-kit: Add I2S support for the board's microphone

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang 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 bb6f32d610 esp32-sparrow-kit: Add I2S support for the board's microphone
bb6f32d610 is described below

commit bb6f32d610305d1ea1ad61071ddac52b089c6728
Author: simonatoaca <si...@gmail.com>
AuthorDate: Mon Feb 19 09:58:10 2024 +0200

    esp32-sparrow-kit: Add I2S support for the board's microphone
    
    The board's microphone uses 24-bit i2s and this commit also fixes
    the segmentation fault caused by the audio buffer overflow.
    
    arch/xtensa/src/esp32/esp32_i2s.c: Fix bug regarding 24-bit audio and add AUDIOIOC_STOP to ioctl
    drivers/audio/audio_i2s.c: Report number of channels on AUDIOIOC_GETCAPS
    in boards/xtensa/esp32/esp32-sparrow-kit:
            /configs/nsh/defconfig: Add I2S configs
            /src/esp32-sparrow-kit.h: Add the signature of esp32_i2sdev_initialize()
            /src/esp32_bringup.c: Add call to esp32_i2sdev_initialize()
    
    Signed-off-by: simonatoaca <si...@gmail.com>
---
 arch/xtensa/src/esp32/esp32_i2s.c                  | 102 ++++++++++++++++++++-
 .../esp32/esp32-sparrow-kit/configs/nsh/defconfig  |  16 ++++
 .../esp32-sparrow-kit/src/esp32-sparrow-kit.h      |  22 +++++
 .../esp32/esp32-sparrow-kit/src/esp32_bringup.c    |  21 +++++
 drivers/audio/audio_i2s.c                          |   3 +
 5 files changed, 161 insertions(+), 3 deletions(-)

diff --git a/arch/xtensa/src/esp32/esp32_i2s.c b/arch/xtensa/src/esp32/esp32_i2s.c
index 856aafe260..fabf4a0c1a 100644
--- a/arch/xtensa/src/esp32/esp32_i2s.c
+++ b/arch/xtensa/src/esp32/esp32_i2s.c
@@ -361,6 +361,7 @@ static void     i2s_rx_channel_stop(struct esp32_i2s_s *priv);
 static int      i2s_rxchannels(struct i2s_dev_s *dev, uint8_t channels);
 static uint32_t i2s_rxsamplerate(struct i2s_dev_s *dev, uint32_t rate);
 static uint32_t i2s_rxdatawidth(struct i2s_dev_s *dev, int bits);
+static void     i2s_cleanup_queues(struct esp32_i2s_s *priv);
 static int      i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb,
                             i2s_callback_t callback, void *arg,
                             uint32_t timeout);
@@ -1304,6 +1305,7 @@ static void i2s_rx_worker(void *arg)
 
       apb_samp_t samp_size;
       uint32_t data_copied;
+      uint8_t carry_bytes;
       uint8_t padding;
       uint8_t *buf;
       uint8_t *samp;
@@ -1322,23 +1324,48 @@ static void i2s_rx_worker(void *arg)
       samp = &bfcontainer->apb->samp[bfcontainer->apb->curbyte];
       samp_size = (bfcontainer->apb->nbytes - bfcontainer->apb->curbyte);
 
+      data_copied = 0;
+      buf = bfcontainer->buf;
+
+      /* Copy the remaining bytes from previous transfer and
+       * complete the sample so that the next memcpy is aligned
+       * with the sample size.
+       */
+
+      if (priv->rx.carry.bytes)
+        {
+          memcpy(samp, &priv->rx.carry.value, priv->rx.carry.bytes);
+          samp += priv->rx.carry.bytes;
+          data_copied += priv->rx.carry.bytes;
+
+          memcpy(samp, buf, (bytes_per_sample - priv->rx.carry.bytes));
+          buf += (bytes_per_sample - priv->rx.carry.bytes);
+          samp += (bytes_per_sample - priv->rx.carry.bytes);
+          data_copied += (bytes_per_sample - priv->rx.carry.bytes);
+        }
+
       /* If there is no need to add padding bytes, the memcpy may be done at
        * once. Otherwise, the operation must add the padding bytes to each
        * sample in the internal buffer.
        */
 
-      data_copied = 0;
-      buf = bfcontainer->buf + padding;
+      buf += padding;
 
       if (padding)
         {
-          while (data_copied < samp_size)
+          while (data_copied + bytes_per_sample <= samp_size)
             {
               memcpy(samp, buf, bytes_per_sample);
               buf += (bytes_per_sample + padding);
               samp += bytes_per_sample;
               data_copied += bytes_per_sample;
             }
+
+          /* Store the carry bytes, if any */
+
+          carry_bytes = samp_size - data_copied;
+          memcpy(&priv->rx.carry.value, buf, carry_bytes);
+          priv->rx.carry.bytes = carry_bytes;
         }
       else
         {
@@ -2815,6 +2842,56 @@ errout_with_buf:
 }
 #endif /* I2S_HAVE_RX */
 
+/****************************************************************************
+ * Name: i2s_cleanup_queues
+ *
+ * Description:
+ *   Wait for all buffers to be processed and free them after
+ *
+ ****************************************************************************/
+
+#ifdef I2S_HAVE_RX
+static void i2s_cleanup_queues(struct esp32_i2s_s *priv)
+{
+  irqstate_t flags;
+  struct esp32_buffer_s *bfcontainer;
+
+  while (sq_peek(&priv->rx.done) != NULL)
+    {
+      flags = enter_critical_section();
+      bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.done);
+      leave_critical_section(flags);
+      bfcontainer->callback(&priv->dev, bfcontainer->apb,
+                            bfcontainer->arg, OK);
+      apb_free(bfcontainer->apb);
+      i2s_buf_free(priv, bfcontainer);
+    }
+
+  while (sq_peek(&priv->rx.act) != NULL)
+    {
+      flags = enter_critical_section();
+      bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.act);
+      leave_critical_section(flags);
+      bfcontainer->callback(&priv->dev, bfcontainer->apb,
+                            bfcontainer->arg, OK);
+      apb_free(bfcontainer->apb);
+      i2s_buf_free(priv, bfcontainer);
+    }
+
+  while (sq_peek(&priv->rx.pend) != NULL)
+    {
+      flags = enter_critical_section();
+      bfcontainer = (struct esp32_buffer_s *)sq_remfirst(&priv->rx.pend);
+      leave_critical_section(flags);
+      bfcontainer->apb->flags |= AUDIO_APB_FINAL;
+      bfcontainer->callback(&priv->dev, bfcontainer->apb,
+                            bfcontainer->arg, OK);
+      apb_free(bfcontainer->apb);
+      i2s_buf_free(priv, bfcontainer);
+    }
+}
+#endif /* I2S_HAVE_RX */
+
 /****************************************************************************
  * Name: i2s_ioctl
  *
@@ -2843,6 +2920,25 @@ static int i2s_ioctl(struct i2s_dev_s *dev, int cmd, unsigned long arg)
         }
         break;
 
+      /* AUDIOIOC_STOP - Stop the audio stream.
+       *
+       *   ioctl argument:  Audio session
+       */
+
+      case AUDIOIOC_STOP:
+        {
+          i2sinfo("AUDIOIOC_STOP\n");
+
+#ifdef I2S_HAVE_RX
+          struct esp32_i2s_s *priv = (struct esp32_i2s_s *)dev;
+
+          i2s_cleanup_queues(priv);
+#endif /* I2S_HAVE_RX */
+
+          ret = OK;
+        }
+        break;
+
       /* AUDIOIOC_ALLOCBUFFER - Allocate an audio buffer
        *
        *   ioctl argument:  pointer to an audio_buf_desc_s structure
diff --git a/boards/xtensa/esp32/esp32-sparrow-kit/configs/nsh/defconfig b/boards/xtensa/esp32/esp32-sparrow-kit/configs/nsh/defconfig
index 1cec9907f2..25b9017c95 100644
--- a/boards/xtensa/esp32/esp32-sparrow-kit/configs/nsh/defconfig
+++ b/boards/xtensa/esp32/esp32-sparrow-kit/configs/nsh/defconfig
@@ -22,12 +22,26 @@ CONFIG_ARCH_CHIP_ESP32=y
 CONFIG_ARCH_CHIP_ESP32WROVER=y
 CONFIG_ARCH_STACKDUMP=y
 CONFIG_ARCH_XTENSA=y
+CONFIG_AUDIO=y
+CONFIG_AUDIO_DMA=y
+CONFIG_AUDIO_FORMAT_RAW=y
+CONFIG_AUDIO_I2S=y
 CONFIG_BME680_ENABLE_IIR_FILTER=y
 CONFIG_BOARD_LOOPSPERMSEC=16717
 CONFIG_BUILTIN=y
+CONFIG_DMA=y
+CONFIG_DMA_LINK=y
+CONFIG_DRIVERS_AUDIO=y
 CONFIG_DRIVERS_VIDEO=y
 CONFIG_ESP32_I2C0=y
 CONFIG_ESP32_I2C0_SDAPIN=21
+CONFIG_ESP32_I2S0=y
+CONFIG_ESP32_I2S0_BCLKPIN=25
+CONFIG_ESP32_I2S0_DATA_BIT_WIDTH_24BIT=y
+CONFIG_ESP32_I2S0_DINPIN=26
+CONFIG_ESP32_I2S0_SAMPLE_RATE=8000
+CONFIG_ESP32_I2S0_WSPIN=27
+CONFIG_ESP32_I2S=y
 CONFIG_ESP32_LEDC=y
 CONFIG_ESP32_LEDC_CHANNEL0_PIN=14
 CONFIG_ESP32_LEDC_CHANNEL1_PIN=13
@@ -47,6 +61,7 @@ CONFIG_FS_FAT=y
 CONFIG_FS_PROCFS=y
 CONFIG_HAVE_CXX=y
 CONFIG_HAVE_CXXINITIALIZE=y
+CONFIG_I2S_DMADESC_NUM=4
 CONFIG_IDLETHREAD_STACKSIZE=3072
 CONFIG_INIT_ENTRYPOINT="nsh_main"
 CONFIG_INTELHEX_BINARY=y
@@ -75,6 +90,7 @@ CONFIG_RGBLED_INVERT=y
 CONFIG_RGBLED_LIGHTNESS_CORRECTION=y
 CONFIG_RGBLED_PWM_FREQ=200
 CONFIG_RR_INTERVAL=200
+CONFIG_SCHED_HPWORK=y
 CONFIG_SCHED_WAITPID=y
 CONFIG_SENSORS=y
 CONFIG_SENSORS_BME680=y
diff --git a/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32-sparrow-kit.h b/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32-sparrow-kit.h
index 787ccf9939..c4b87b22d9 100644
--- a/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32-sparrow-kit.h
+++ b/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32-sparrow-kit.h
@@ -106,6 +106,28 @@ int esp32_mmcsd_initialize(int minor);
 
 int esp32_spiflash_init(void);
 
+/****************************************************************************
+ * Name: board_i2sdev_initialize
+ *
+ * Description:
+ *   This function is called by platform-specific, setup logic to configure
+ *   and register the generic I2S audio driver.  This function will register
+ *   the driver as /dev/audio/pcm[x] where x is determined by the I2S port
+ *   number.
+ *
+ * Input Parameters:
+ *   port  - The I2S port used for the device
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+#if defined CONFIG_ESP32_I2S0 || defined CONFIG_ESP32_I2S1
+int board_i2sdev_initialize(int port);
+#endif
+
 /****************************************************************************
  * Name: esp32_gpio_init
  ****************************************************************************/
diff --git a/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32_bringup.c b/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32_bringup.c
index 3665696ccb..4b06008116 100644
--- a/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32_bringup.c
+++ b/boards/xtensa/esp32/esp32-sparrow-kit/src/esp32_bringup.c
@@ -80,6 +80,10 @@
 #  include "esp32_board_i2c.h"
 #endif
 
+#ifdef CONFIG_ESP32_I2S
+#  include "esp32_i2s.h"
+#endif
+
 #ifdef CONFIG_SENSORS_BMP180
 #  include "esp32_bmp180.h"
 #endif
@@ -343,6 +347,23 @@ int esp32_bringup(void)
 
 #endif
 
+#ifdef CONFIG_ESP32_I2S
+
+#ifdef CONFIG_ESP32_I2S0
+
+  /* Configure I2S generic audio on I2S0 */
+
+  ret = board_i2sdev_initialize(ESP32_I2S0);
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "Failed to initialize I2S%d driver: %d\n",
+             CONFIG_ESP32_I2S0, ret);
+    }
+
+#endif  /* CONFIG_ESP32_I2S0 */
+
+#endif /* CONFIG_ESP32_I2S */
+
 #ifdef CONFIG_SENSORS_BMP180
   /* Try to register BMP180 device in I2C0 */
 
diff --git a/drivers/audio/audio_i2s.c b/drivers/audio/audio_i2s.c
index 536a8d599b..c427b6e5ed 100644
--- a/drivers/audio/audio_i2s.c
+++ b/drivers/audio/audio_i2s.c
@@ -185,6 +185,9 @@ static int audio_i2s_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
             /* Report the Sample rates we support */
 
               caps->ac_controls.hw[0] = AUDIO_SAMP_RATE_DEF_ALL;
+
+              caps->ac_channels = 2;
+
               break;
           }