ESP32 IDF Linux and I2S

buzandy
Posts: 13
Joined: Mon Sep 11, 2017 3:50 pm

ESP32 IDF Linux and I2S

Postby buzandy » Thu Sep 21, 2017 9:15 pm

I have setup the ESP IDF and toolchain etc on Linux and can compile and run some of the simple examples from the ESP-IDF examples folder....but seem to have problem with the I2S example.

Below is part of the code......The example given is to play sound out from I2S port. I dont have an I2S amplifier etc so thought I could set it up to use the BUILT_IN DAC for audio out. The docs show how to do this. I have made the changes to the code as I think but do not get any sound out at all It is supposed to use I2S_num 0 and defaults output to gpio 25 and 26. I am not using any other pins for anything else.
The program compiles, flashes and uploads to the board. ....need some help please........

Code: Select all

void app_main()
{    
    //for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes or 8-bytes each sample)
    //depend on bits_per_sample
    //using 6 buffers, we need 60-samples per buffer
    //if 2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes
    //if 2-channels, 24/32-bit each channel, total buffer is 360*8 = 2880 bytes
    i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,      // Only TX.....I added DAC BUILTIN....XXXXXXX
        .sample_rate = SAMPLE_RATE,
        .bits_per_sample = 16,                                              
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           //2-channels
        .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
        .dma_buf_count = 6,
        .dma_buf_len = 60,                                                      //
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1                                //Interrupt level 1
    };
    i2s_pin_config_t pin_config = {
        .bck_io_num = 26,          
        .ws_io_num = 25,           
        .data_out_num = 22,        
        .data_in_num = -1                                                       //Not used
    };
    i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
    i2s_set_pin(I2S_NUM, NULL);   // Docs say set to NULL for DAC builtin....XXXXXXXXXX

    int test_bits = 16;
    while (1) {
        setup_triangle_sine_waves(test_bits);
        vTaskDelay(5000/portTICK_RATE_MS);
        test_bits += 8;
        if(test_bits > 32)
            test_bits = 16;
        
    } 
    
}

ESP_Sprite
Posts: 9711
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 IDF Linux and I2S

Postby ESP_Sprite » Fri Sep 22, 2017 12:27 am

You may want to add something like i2s_set_dac_mode(I2S_DAC_CHANNEL_LEFT_EN);

buzandy
Posts: 13
Joined: Mon Sep 11, 2017 3:50 pm

Re: ESP32 IDF Linux and I2S

Postby buzandy » Fri Sep 22, 2017 12:48 am

Thanks for reply. I tried that and i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
If I use dacWrite and send values direct to the DAC port I get sound or noise out.
Is there a way to send simple data direct to the I2S which I can read or see blips or sounds at the DAC...The I2S is supposed to be mapped to the DAC when i2s_set_pin(0, NULL); is set.
Rather than the elaborate way most examples do reading from a file or generating a continuous signal..????? or some method of debugging.???

ESP_Sprite
Posts: 9711
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 IDF Linux and I2S

Postby ESP_Sprite » Fri Sep 22, 2017 2:16 am

Fwiw, this code works for me (I only use the left channel)

Code: Select all

	i2s_config_t cfg={
		.mode=I2S_MODE_DAC_BUILT_IN|I2S_MODE_TX|I2S_MODE_MASTER,
		.sample_rate=rate,
		.bits_per_sample=16,
		.channel_format=I2S_CHANNEL_FMT_RIGHT_LEFT,
		.communication_format=I2S_COMM_FORMAT_I2S_MSB,
		.intr_alloc_flags=0,
		.dma_buf_count=4,
		.dma_buf_len=buffsize/4
	};
	i2s_driver_install(0, &cfg, 4, &soundQueue);
	i2s_set_pin(0, NULL);
	i2s_set_dac_mode(I2S_DAC_CHANNEL_LEFT_EN);
	i2s_set_sample_rates(0, cfg.sample_rate);
Then, to send 8-bit samples, I use something like this:

Code: Select all

uint32_t tmpb[SND_CHUNKSZ];
for (int j=0; j<SND_CHUNKSZ; j++) {
	uint8_t s=(put your sample calculation here);
	tmpb[j]=((s)<<8)+((s)<<24);
}
i2s_write_bytes(0, (char*)tmpb, plen*4, portMAX_DELAY);

buzandy
Posts: 13
Joined: Mon Sep 11, 2017 3:50 pm

Re: ESP32 IDF Linux and I2S

Postby buzandy » Sat Sep 23, 2017 12:56 am

I have got it working......but only on one DAC channel. Using the example posted here..
I have tried various combinations of the settings and still only output on one DAC channel.
I think my orig problem was getting the config settings correct and not sending data correctly.
Basically I dont understand fully the bit of code that sends the data, the rest of the code is config.
Are there any good links or tutorials that explains the audio data, use of pointers, buffers, calcs needed etc..???
I have only tried this using the Arduino IDE and the esp32 tools etc.
Would be grateful if someone could add a few notes to explain how this works and get 2 channels working....

Code: Select all

//
// This is I2S Audio example for Firebeetle ESP32 sending Audio Data to I2S_NUM_0 with BUILT_IN DAC enabled 
// and getting Analogue Audio out from DAC port GPIO25 and GPIO26 for output to amplifier etc.
//
// ..using help gained from forum topic at esp32.com/viewtopic.php?f=13&t=2155 ......
// The topic had the link to....github.com/stevenvdr/esp-adc-bug/blob/master/main/test.c which contained the code......
// Sept 2017......Madified to run on Arduino 1.8.4 with DFRobot Firebeetle ESP32 board and DFRobot toolchain etc.
// ......had some compiling issues with orig. code  eg. needed i2s_mode_t etc etc in places...????

#include "driver/i2s.h"
#include "freertos/queue.h"

#define PI 3.14159265

    uint samples[128];
    float sin_float;

//i2s configuration ....... I put this at the top of the sketch before setup.....???

int i2s_num = 0; // i2s port number
i2s_config_t i2s_config = {
     .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
     .sample_rate = 44100,
     .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = (I2S_COMM_FORMAT_I2S_MSB),
     .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,           // high interrupt priority Interrupt level 1
     .dma_buf_count = 2,
     .dma_buf_len = 128   
    };


void setup()
{
  Serial.begin(115200);
  
  Serial.println("config done.");

        //initialize i2s with configurations above .....I put this bit in setup....????
                 
         i2s_set_pin((i2s_port_t)i2s_num, NULL);        // NULL here needed for BUILT_IN DAC
         
        i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL);
      
      // Did not need the lines below to get it working defaults... one channel ??? Left CH................
        
      //set sample rates of i2s to sample rate of wav file
      
       // i2s_set_sample_rates((i2s_port_t)i2s_num, 8000); 
        
     // esp_err_t i2s_set_dac_mode(i2s_dac_mode_t I2S_DAC_CHANNEL_BOTH_EN);
 
      // esp_err_t dac_i2s_enable();
      
      // i2s_driver_uninstall((i2s_port_t)i2s_num); //stop & destroy i2s driver 
      
        Serial.println("I2S installed and started!");

  
    // Create Data below.....
    // Make sure data is between 0 and 1V
    
    for(uint i = 0; i < 256; i++) {
        sin_float = sin(i / 128.0 * 2 * PI);
        sin_float *= 31;
        sin_float += 32;

        if (i % 2 ) {
            samples[i/2] =  ((char) sin_float) << 8;// ((i % 8) << 8) + ((i % 8) << 24);
        }
        else {
            samples[i/2] =  ((char) sin_float) << 24;// ((i % 8) << 8) + ((i % 8) << 24);
        }
    }
    // Write data to the buffer. This is timing dependant, so be carefull with 
    // changing the configuration
    
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
   
   }

   void loop()
   {
 // nothing to do in loop.........and it plays sound continuously...one channel DAC IO25....
   }

buzandy
Posts: 13
Joined: Mon Sep 11, 2017 3:50 pm

Re: ESP32 IDF Linux and I2S

Postby buzandy » Sat Sep 23, 2017 7:06 pm

This version of modifications give me a sinwave from both DAC at the same time.
But I still do not understand exactly why......

Uint32_t sample(256] ......array size 256..????? datatype changed to Uint32_t.....prob allows left and right together in each sample..??
.dma_buf_count = 2,
.dma_buf_len = 128 ...... whay does this work..?????

samples[i/2] = ((char) sin_float << 8) + ((char) sin_float << 24); CHANGED this lie as per suggested ESPsprite last post..seems to work.

The amplitude of one channel is about 3 time the amplitude of the other......????? why bboth have same sample...one shifted by 8 bits other shifted by 24 bits...????..

Still would welcome help please.........

Code: Select all

//
// This is I2S Audio example for Firebeetle ESP32 sending Audio Data to I2S_NUM_0 with BUILT_IN DAC enabled 
// and getting Analogue Audio out from DAC port GPIO25 and GPIO26 for output to amplifier etc.
//
// ..using help gained from forum topic at esp32.com/viewtopic.php?f=13&t=2155 ......
// The topic had the link to....github.com/stevenvdr/esp-adc-bug/blob/master/main/test.c which contained the code......
// Sept 2017......Madified to run on Arduino 1.8.4 with DFRobot Firebeetle ESP32 board and DFRobot toolchain etc.
// ......had some compiling issues with orig. code  eg. needed i2s_mode_t etc etc in places...????

#include "driver/i2s.h"
#include "freertos/queue.h"

#define PI 3.14159265

    uint32_t samples[256];
    float sin_float;

//i2s configuration ....... I put this at the top of the sketch before setup.....???

int i2s_num = 0; // i2s port number
i2s_config_t i2s_config = {
     .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
     .sample_rate = 44100,
     .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = (I2S_COMM_FORMAT_I2S_MSB),
     .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,           // high interrupt priority Interrupt level 1
     .dma_buf_count = 2,
     .dma_buf_len = 128   
    };


void setup()
{
  Serial.begin(115200);
  
  Serial.println("config done.");

        //initialize i2s with configurations above .....I put this bit in setup....????
                 
         i2s_set_pin((i2s_port_t)i2s_num, NULL);        // NULL here needed for BUILT_IN DAC
         
        i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL);
      
      // Did not need the lines below to get it working defaults... one channel ??? Left CH................
        
      //set sample rates of i2s to sample rate of wav file
      
       // i2s_set_sample_rates((i2s_port_t)i2s_num, 8000); 
        
     // esp_err_t i2s_set_dac_mode(i2s_dac_mode_t I2S_DAC_CHANNEL_BOTH_EN);
 
      // esp_err_t dac_i2s_enable();
      
      // i2s_driver_uninstall((i2s_port_t)i2s_num); //stop & destroy i2s driver 
      
        Serial.println("I2S installed and started!");

  
    // Create Data below.....
    // Make sure data is between 0 and 1V
    
    for(unsigned int i = 0; i < 256; i++) {
        sin_float = sin(i / 128.0 * 2 * PI);
        sin_float *= 31;
        sin_float += 32;
/*
        if (i % 2 ) {
            samples[i/2] =  ((char) sin_float) << 8;    //  ((i % 8) << 8) + ((i % 8) << 24);
          }
          else {
            samples[i/2] =  ((char) sin_float) << 24;    // ((i % 8) << 8) + ((i % 8) << 24);
          }
          */
          
          samples[i/2] =  ((char) sin_float << 8) + ((char) sin_float << 24);
    }
    // Write data to the buffer. This is timing dependant, so be carefull with 
    // changing the configuration
    
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
   
   }

ESP_Sprite
Posts: 9711
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 IDF Linux and I2S

Postby ESP_Sprite » Sun Sep 24, 2017 2:46 am

If I recall correctly (it's been a while since I wrote the code I quoted), the I2S-peripheral is operating in stereo 16-bit mode, meaning it will take a 32-bit word each sample time, and distribute the top 16 bit to the left and bottom 16 bit to the right channel. (Or vice versa, I don't entirely remember.) Of those 16-bits, the DACs only look at the top 8 bit. So essentially, by setting the sample to (samp<<24)+(samp<<8), you're sending the same sample to the left and right channel.

No clue on why one channel is 3x as small as the other one... maybe you have some external hardware pulling things down?

buzandy
Posts: 13
Joined: Mon Sep 11, 2017 3:50 pm

Re: ESP32 IDF Linux and I2S

Postby buzandy » Mon Sep 25, 2017 9:49 pm

I have tried various options with the setting and config and cannot get it to work properly.
If I set ...... samples = (sample8) + (sample24); I get the sine wave with what looks like the bottom part inverted as in full wave rectification.....but it outpust sound.

If I set ..... samples[i/2] = (sample8) + (sample24); I get a proper sine wave shape and good sound quality and it plays at all sample rates even up to 384000 samples per second.

I was using Audacity to record the sound and measure it, I had Audacity set at same rate as the ESP board. I only get 64 samples per cycle but good quality. One channel is at about 1/3 the amplitude of the other.

According to the schematic for the Firebeetle there is nothing connected to the DAC pins and I have nothing else connected to the board.

I dont understand why I need 4 I2S write bytes to send the data...?????

I printed out the array calculations in Binary and all look OK... It must still be something to do with settings...???

I couldnt figure out how to post an image or I would post a screen dump...???? how do you do it..???



Code: Select all

#include "driver/i2s.h"
#include "freertos/queue.h"

#define PI 3.14159265

    uint32_t samples[128];
    uint32_t sample8[128];
    uint32_t sample24[128];
    
    float sin_float;

//i2s configuration ....... I put this at the top of the sketch before setup.....???

int i2s_num = 0; // i2s port number
i2s_config_t i2s_config = {
     .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
     .sample_rate =44100,
     .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = (I2S_COMM_FORMAT_I2S_MSB),
     .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,           // high interrupt priority Interrupt level 1
     .dma_buf_count = 2,
     .dma_buf_len = 128   
    };


void setup()
{
  Serial.begin(115200);
  
  Serial.println("config done.");

        //initialize i2s with configurations above .....I put this bit in setup....????
                 
         i2s_set_pin((i2s_port_t)i2s_num, NULL);        // NULL here needed for BUILT_IN DAC
         
        i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL);
      
           
        Serial.println("I2S installed and started!");

  
    // Create Data below.....
    // Make sure data is between 0 and 1V
    
    for(int i = 0; i < 128; i++) {
       sin_float = sin(i / 128.0 * 2 * PI);  // this bit gets sine wave values for 1 cycle  + and - parts of wave...
       sin_float *= 31;
       sin_float += 32;                   // this mult by 32 and add 32 makes the whole wave positive values  1 to 63


    sample8[i] =  ((char) sin_float) << 8;     //
    sample24[i] =  ((char) sin_float) << 24;  // I seperated out the individual arrays 
    
       

        //   samples[i] = ((uint32_t)(sin_float) << 8) + ((uint32_t)(sin_float) << 24);

          samples[i] = (sample8[i]) + (sample24[i]);    //  now combine the arrays to get 32 bits, 16 left, 16 right
    }
    
    // Write data to the buffer. This is timing dependant, so be carefull with 
    // changing the configuration
    
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);
    i2s_write_bytes((i2s_port_t)i2s_num, (char*) &samples, 256, portMAX_DELAY);   // why is there 4 I2s write bytes...???
      
   }

ESP_Sprite
Posts: 9711
Joined: Thu Nov 26, 2015 4:08 am

Re: ESP32 IDF Linux and I2S

Postby ESP_Sprite » Tue Sep 26, 2017 3:10 am

Your distortion and 'why do I need to write 4 bytes' is probably because you're confusing bytes and samples. Each sample is a 32-bit value. You have 256 of them (looking at your

Code: Select all

uint32_t sample[256]
line). i2s_write_bytes, however, expects the samples and the sample length in *bytes*, however. (Why is this? Mostly because the I2S hardware is capable of having samples of multiple byte lengths, but the smallest granularity will always be one byte.) Because of this, you also need to feed it the length of your sample array *in bytes*. 1 32-bit word equals 4 8-bit bytes, so your 256-element array of 32-bit words equals (256*4=)1024 bytes. So change the 3rd argument to your i2s_write_bytes call to 1024, remove the extra 3 calls, and it should work as you expect it to.

buzandy
Posts: 13
Joined: Mon Sep 11, 2017 3:50 pm

Re: ESP32 IDF Linux and I2S

Postby buzandy » Tue Sep 26, 2017 2:15 pm

Yes, thanks, It works now...I was either mixed up or confused...???.....I think I simply didn't know...??

Got it also working with 128 sample array and change i2s writebytes to 512 also had to change i2s_config.dma_buf_len to 64...but it worked..... How do you determine the dma_buf_len and number of buffers to be 64 len x 2 or 128 len x 2 ????. must we always have at least 2 buffers..??.

The amplitude of one channel is still about 1/4 that of the other and it is 180 degrees out of phase, but no distortion. And If I send to one channel only there is no crosstalk to the other..???.

I was meaning before I started typing this to send samples with DACwrite to see the same is happening....I will do that now and post back....Thanks

Who is online

Users browsing this forum: No registered users and 95 guests