Resending DMA buffer to I2S output with ESP32

I am trying to point the DMA buffer to an address in my setup and keep sending the data in an interrupt function (once per execution) without reloading DMA. I know my code works if I put the i2s_write in the interrupt function but that is not my goal. I also tried to find the i2s_write(); definition to define a new function without loading DMA but can only find the declaration in i2s.h and no source code.

here is a summary of my code:

#include "driver/i2s.h"
const i2s_port_t I2S_PORT = I2S_NUM_0;

#define I2S_LRCLK_PIN 15 // 0
#define I2S_BCLK_PIN 27 // 1
#define I2S_TX_PIN 33 // 9
#define I2S_RX_PIN -1 // 2

int32_t waveDMA[WAV_SIZE] = {0}; // scaled waveform

void setup(){
esp_err_t err;
// The I2S config as per the example
const i2s_config_t i2s_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_TX), //
.sample_rate = SAMPLERATE_HZ, //
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, //
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = 0,//ESP_INTR_FLAG_LEVEL1, // Interrupt level 1
.dma_buf_count = 2, // number of buffers
.dma_buf_len = 32,
.use_apll = false,
.tx_desc_auto_clear=true//

};

// The pin config as per the setup
const i2s_pin_config_t pin_config = {
.bck_io_num = I2S_BCLK_PIN, // Serial Clock (SCK)
.ws_io_num = I2S_LRCLK_PIN , // Word Select (WS)
.data_out_num = I2S_TX_PIN, // not used (only for speakers)
.data_in_num = 32 // Serial Data (SD)
};

// Configuring the I2S driver and pins.
// This function must be called before any I2S driver read/write operations.
err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
if (err != ESP_OK) {
Serial.printf("Failed installing driver: %d\n", err);
while (true);
}
err = i2s_set_pin(I2S_PORT, &pin_config);
if (err != ESP_OK) {
Serial.printf("Failed setting pin: %d\n", err);
while (true);
}

Serial.println("I2S driver installed.");
size_t bytesWritten=0;
i2s_write(I2S_PORT, waveDMA, sizeof(waveDMA), &bytesWritten, portMAX_DELAY);

}

void interrupt_function(){
/// how can I just trigger DMA to write to I2S (once) ?

}

Perhaps you did not look deep enough?

#include "esp_types.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "soc/i2s_periph.h"
#include "soc/rtc_periph.h"
#include "soc/soc_caps.h"
#include "hal/i2s_types.h"
#include "driver/periph_ctrl.h"
#include "esp_intr_alloc.h

In one of those h files, hint no need to look into h files without the word i2s, is the thing your are looking for.

You do know that will not work? The ESP32 has 2 ports portA and portB. portB pins are > GPIO_31. portB lacks many features of the lower GPIO pins, such portB pins are not outputs.

You coded the following incorrectly:

This is how to declare and setup a ESP32's configuration under the Arduino IDE:

 pcnt_config_t pcnt_config  = {};
  pcnt_config.pulse_gpio_num = GPIO_NUM_15;// Set PCNT input signal and control GPIOs
  pcnt_config.ctrl_gpio_num  = PCNT_PIN_NOT_USED;
  pcnt_config.channel        = PCNT_CHANNEL_0;
  pcnt_config.unit           = PCNT_UNIT_0;
  // What to do on the positive / negative edge of pulse input?
  pcnt_config.pos_mode       = PCNT_COUNT_INC;   // Count up on the positive edge
  pcnt_config.neg_mode       = PCNT_COUNT_DIS;   // Count down disable
  // What to do when control input is low or high?
  pcnt_config.lctrl_mode     = PCNT_MODE_KEEP; // Keep the primary counter mode if low
  pcnt_config.hctrl_mode     = PCNT_MODE_KEEP;    // Keep the primary counter mode if high
  // Set the maximum and minimum limit values to watch
  pcnt_config.counter_h_lim  = PCNT_H_LIM_VAL;
  pcnt_config.counter_l_lim  = PCNT_L_LIM_VAL;
  pcnt_unit_config(&pcnt_config); // Initialize PCNT unit

pcnt_config_t pcnt_config = {}; <<<< fixes a bug with the way the ESP32 assigns the memory array.

Thank you for your response.
1- I can find where the i2s_write is declared, it is in "driver/i2s.h" , but I cant find the function definition (i.e. in any .cpp files)
2- Regarding the configuring pin33 as the I2S data line, I will try your suggestion if it is better, but 33 works, I can get the output on it.
3- can you please point me to a full example of I2S using this new config, I am newbie and don't know what to do with this config.
4- My main problem that you seem to ignore is that I want to know how two write to I2S without reloading the DMA every time ( someway to repeat the sound bit just by retriggering DMA.

Cheers!

If you can make the DMA transfer happen once, why can't you make it happen again? Presumably the previous transfer did not modify the data buffer. So reset the DMA pointers, counters, controls, etc. Then prepare the I2S interface for another transfer (if necessary). Then kick it off again.

The only way that I have been able to make it work is using i2s_write function which sets up DMA and sends it. In the consequent iterations I don't want to set up the DMA every time, just trigger it to send the data out. I think this should be possible , the problem is that I am a newbie and don't know how to do it. I've done one stupid attempt by calling this for the consequent iterations:

SET_PERI_REG_MASK( I2S_CONF_REG(0) ,I2S_TX_START);

but it doesn't work. If I could find the i2s_write source code I would delete the first half and it would probably work.

My guess is that it's not Open Source. It comes pre-compiled from Espressif as part of the proprietary ESP32 core software.

you are probably right. I have searched my whole PC and has not found it. They have the code for ESP IDF on their github (esp-idf/i2s.c at 3e370c4296247b349aa3b9a0076c05b9946d47dc · espressif/esp-idf · GitHub) .
They have a function called "i2s_start_tx" that hopefully I will be able to declare and use.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.