Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The esp32 i2s internal DAC output is not working with the release 2.0.1 #5938

Closed
cicciocb opened this issue Nov 28, 2021 · 41 comments
Closed
Assignees
Labels
Area: ESP-IDF related ESP-IDF related issues Area: Peripherals API Relates to peripheral's APIs. Resolution: Awaiting response Waiting for response of author
Milestone

Comments

@cicciocb
Copy link

The audio output using I2S works both with an external DAC or in PDM mode but not using the internal DAC.
This seems to be related to the ESP-IDF https://github.com/espressif/esp-idf/issues/7684

@VojtechBartoska
Copy link
Contributor

PTAL, @PilnyTomas.

relates to: espressif/esp-idf#7684

@FedericoBusero
Copy link
Contributor

It did work until V2.0.0-rc2

@Jason2866
Copy link
Collaborator

Do you have news for this @PilnyTomas ?

@PilnyTomas
Copy link
Contributor

Hi, sorry for the late response - I was working on other tasks.
I can confirm that in version 2.0.1 and newer it doesn't work and that version 2.0.0. does work.
I will investigate to pinpoint the breaking commit and find the reason for this issue.

@PilnyTomas
Copy link
Contributor

I found the braking commit, from Oct 1, 2021: 00214d5c2a1c2b1904f2caf6f0d5ddfe952331ff

@VojtechBartoska
Copy link
Contributor

We are now discussing the issue with ESP-IDF team, we will let you know about the update.

@Jason2866
Copy link
Collaborator

@PilnyTomas PilnyTomas self-assigned this Jan 27, 2022
@s-hadinger
Copy link
Contributor

I did some more tests, on ESP32 Rev1 and Pico D4. Actually the DAC output works, but the speed is 11x more than real time. I.e. I sent PCM at 16K samples/s, and it was played at ~176K samples/s.

Original waveform is 660ms long
Orig

The signal recorded on GPIO26 is playing in 60ms
DAC

@cicciocb
Copy link
Author

Maybe this could be related to ?
espressif/esp-idf@1d02e63

@s-hadinger
Copy link
Contributor

I doubt. I did some detour on i2s_hal_tx_clock_config() to check what clock values were calculated, and force-injected some different clock values until the sound was good again. I will work backwards to see why these values are wrong in the first place.

@cicciocb
Copy link
Author

@s-hadinger What is the version of the arduino core that you are using?

@s-hadinger
Copy link
Contributor

s-hadinger commented Jan 31, 2022

I use 2.0.2 with IDF 4.4. I believe the problem is in idf, not Arduino Core.

One highly possible cause is that the calculated mclk_div is 1250 when using 16000Hz audio on DAC. I'm not sure yet what are the values of a and b divider, but I realize now that mclk_div is 8-bit long. 1250 is clearly an overflow.

Values calculated by IDF 4.4 for 16000Hz audio, DAC, 2 channels
sclk=160000000 mclk=128000 bclk=64000 mclk_div=1250 bclk_div=2

Hardware registers:

    union {
        struct {
            uint32_t clkm_div_num: 8;
            uint32_t clkm_div_b:   6;
            uint32_t clkm_div_a:   6;
            uint32_t clk_en:       1;
            uint32_t clka_en:      1;
            uint32_t reserved22:  10;
        };
        uint32_t val;
    } clkm_conf;

The overflow may explain why I got random results, and why I only could get something to work by increasing bclk_div

Edit: for information, bclk_div uses 6 bits

            uint32_t tx_bck_div_num: 6;
            uint32_t rx_bck_div_num: 6;

Edit2: 1250 (0x4E2) would be truncated to 226 (0xE2) which could explain why the playback was 11x times the normal rate.

@s-hadinger
Copy link
Contributor

I have now confirmation. For 16000Hz, 2 channels, internal DAC is 8 bits.

Knowing that the sclk is 160MHz
esp-idf set the following mclk_div=1250 and bclk_div=2 which makes an overall divider around at 1250*2 = 2500, i.e. a bit frequency of 160000000/2500 = 64000 bps. But the problem is that mclk is overflowing the 8 bits.

My working version sets: mclk_div=156, bclk_div=32, so the overall divider is 156*32 = 4992. So the bit frequency is 160000000/4992 = 32051 bps, which is in line with 16000 * 2 (for 2 channels).

I'm still unclear whether a and b factors have an influence.

@s-hadinger
Copy link
Contributor

I opened a new issue with these information espressif/esp-idf#8326

@s-hadinger
Copy link
Contributor

Recompiling esp-idf with the fix above(thanks @Jason2866) makes Audio DAC working again.

Here are the new values computed:
sclk=160000000 mclk=1024000 bclk=32000 mclk_div=156 bclk_div=32

@Jason2866
Copy link
Collaborator

Jason2866 commented Feb 1, 2022

The framework with the idf i2s fix is here if anyone wants to try.
There are some different sdkconfig settings choosen to optimize for our needs.
The build can directly used with platformio using platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.2/platform-tasmota-espressif32-2.0.2.zip

@cicciocb
Copy link
Author

cicciocb commented Feb 1, 2022

@Jason2866 Thanks for your framework but what is exactly the fix done inside the idf?
Thanks

@pjmi1
Copy link

pjmi1 commented May 12, 2022

This is still broken (I2S using I2S_MODE_DAC_BUILT_IN) - it still does not work for Arduino ESP32 core v. 2.0.3

@PilnyTomas
Copy link
Contributor

This is still broken (I2S using I2S_MODE_DAC_BUILT_IN) - it still does not work for Arduino ESP32 core v. 2.0.3

@pjmi1 Please open a new issue with specifications for your problem. Please include a complete sketch that demonstrates the issue.

@ifrew
Copy link

ifrew commented Jun 11, 2022

I can confirm this is still broken in Arduino 2.0.4, IDF 4.4.1

Not sure why we need to open a new bug for this @PilnyTomas?

Here is the sketch I used. If I change from internal DAC to PDM it works although really noisy.

// Arduino Zero / Feather M0 I2S audio tone generation example.
// Author: Tony DiCola
//
// Connect an I2S DAC or amp (like the UDA1334A) to the Arduino Zero
// and play back simple sine, sawtooth, triangle, and square waves.
// Makes your Zero sound like a NES!
//
// NOTE: The I2S signal generated by the Zero does NOT have a MCLK /
// master clock signal.  You must use an I2S receiver that can operate
// without a MCLK signal (like the UDA1334A).
//
// For an Arduino Zero / Feather M0 connect it to you I2S hardware as follows:
// - Digital 0 -> I2S LRCLK / FS (left/right / frame select clock)
// - Digital 1 -> I2S BCLK / SCLK (bit / serial clock)
// - Digital 9 -> I2S DIN / SD (data output)
// - Ground
//
// Released under a MIT license: https://opensource.org/licenses/MIT


//modified for M5Stack Gray Core

#include <M5Stack.h>
#include <driver/i2s.h>




#define SPEAKER_I2S_NUMBER I2S_NUM_0


#define SAMPLERATE_HZ 44100  // The sample rate of the audio.  Higher sample rates have better fidelity,
                             // but these tones are so simple it won't make a difference.  44.1khz is
                             // standard CD quality sound.

#define AMPLITUDE     ((1<<31)-1)   // Set the amplitude of generated waveforms.  This controls how loud
                             // the signals are, and can be any value from 0 to 2**31 - 1.  Start with
                             // a low value to prevent damaging speakers!

#define WAV_SIZE      256    // The size of each generated waveform.  The larger the size the higher
                             // quality the signal.  A size of 256 is more than enough for these simple
                             // waveforms.


// Define the frequency of music notes (from http://www.phy.mtu.edu/~suits/notefreqs.html):
#define C4_HZ      261.63
#define D4_HZ      293.66
#define E4_HZ      329.63
#define F4_HZ      349.23
#define G4_HZ      392.00
#define A4_HZ      440.00
#define B4_HZ      493.88

// Define a C-major scale to play all the notes up and down.
float scale[] = { C4_HZ, D4_HZ, E4_HZ, F4_HZ, G4_HZ, A4_HZ, B4_HZ, A4_HZ, G4_HZ, F4_HZ, E4_HZ, D4_HZ, C4_HZ };

// Store basic waveforms in memory.
int32_t sine[WAV_SIZE]     = {0};
int32_t sawtooth[WAV_SIZE] = {0};
int32_t triangle[WAV_SIZE] = {0};
int32_t square[WAV_SIZE]   = {0};
size_t writeSize;

// Create I2S audio transmitter object.
//Adafruit_ZeroI2S i2s;

#define Serial Serial

void generateSine(int32_t amplitude, int32_t* buffer, uint16_t length) {
  // Generate a sine wave signal with the provided amplitude and store it in
  // the provided buffer of size length.
  for (int i=0; i<length; ++i) {
    buffer[i] = int32_t(float(amplitude)*sin(2.0*PI*(1.0/length)*i));
  }
}
void generateSawtooth(int32_t amplitude, int32_t* buffer, uint16_t length) {
  // Generate a sawtooth signal that goes from -amplitude/2 to amplitude/2
  // and store it in the provided buffer of size length.
  float delta = float(amplitude)/float(length);
  for (int i=0; i<length; ++i) {
    buffer[i] = -(amplitude/2)+delta*i;
  }
}

void generateTriangle(int32_t amplitude, int32_t* buffer, uint16_t length) {
  // Generate a triangle wave signal with the provided amplitude and store it in
  // the provided buffer of size length.
  float delta = float(amplitude)/float(length);
  for (int i=0; i<length/2; ++i) {
    buffer[i] = -(amplitude/2)+delta*i;
  }
    for (int i=length/2; i<length; ++i) {
    buffer[i] = (amplitude/2)-delta*(i-length/2);
  }
}

void generateSquare(int32_t amplitude, int32_t* buffer, uint16_t length) {
  // Generate a square wave signal with the provided amplitude and store it in
  // the provided buffer of size length.
  for (int i=0; i<length/2; ++i) {
    buffer[i] = -(amplitude/2);
  }
    for (int i=length/2; i<length; ++i) {
    buffer[i] = (amplitude/2);
  }
}

void playWave(int32_t* buffer, uint16_t length, float frequency, float seconds) {
  // Play back the provided waveform buffer for the specified
  // amount of seconds.
  // First calculate how many samples need to play back to run
  // for the desired amount of seconds.
  uint32_t iterations = seconds*SAMPLERATE_HZ;
  // Then calculate the 'speed' at which we move through the wave
  // buffer based on the frequency of the tone being played.
  float delta = (frequency*length)/float(SAMPLERATE_HZ);
  // Now loop through all the samples and play them, calculating the
  // position within the wave buffer for each moment in time.
  for (uint32_t i=0; i<iterations; ++i) {
    uint16_t pos = uint32_t(i*delta) % length;
    int32_t sample = buffer[pos];
    // Duplicate the sample so it's sent to both the left and right channel.
    // It appears the order is right channel, left channel if you want to write
    // stereo sound.
    i2s_write(SPEAKER_I2S_NUMBER, &sample, sizeof(sample), &writeSize, portMAX_DELAY);
  }
}

void setup() {
  // Configure serial port.
  Serial.begin(115200);

  Serial.printf("ESP-IDF Version %d.%d.%d \r\n", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH);
  Serial.printf("Ardunio Version %d.%d.%d \r\n", ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH);

  M5.begin(true, true, true, true);

  Serial.println("Audio Tone Generator");


  // Initialize the I2S transmitter.
  esp_err_t err = ESP_OK;

  //i2s_driver_uninstall(SPEAKER_I2S_NUMBER);
  i2s_config_t i2s_config = {
      .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
      .sample_rate = SAMPLERATE_HZ ,
      .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB
      .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
      .communication_format = I2S_COMM_FORMAT_I2S,
      .intr_alloc_flags = 0,
      .dma_buf_count = 2,
      .dma_buf_len = 256
  };
  
  i2s_config.use_apll = false;
  i2s_config.tx_desc_auto_clear = true;



  err += i2s_driver_install(SPEAKER_I2S_NUMBER, &i2s_config, 0, NULL);
  i2s_pin_config_t tx_pin_config;

  tx_pin_config.mck_io_num = 0;
  tx_pin_config.bck_io_num = 12;
  tx_pin_config.ws_io_num = 13;
  tx_pin_config.data_out_num = 15;
  tx_pin_config.data_in_num = 34;

  err += i2s_set_pin(SPEAKER_I2S_NUMBER, NULL);

  err += i2s_set_clk(SPEAKER_I2S_NUMBER, SAMPLERATE_HZ, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);

  
  

  // Generate waveforms.
  generateSine(AMPLITUDE, sine, WAV_SIZE);
  generateSawtooth(AMPLITUDE, sawtooth, WAV_SIZE);
  generateTriangle(AMPLITUDE, triangle, WAV_SIZE);
  generateSquare(AMPLITUDE, square, WAV_SIZE);
}

void loop() {
  Serial.println("Sine wave");
  for (int i=0; i<sizeof(scale)/sizeof(float); ++i) {
    // Play the note for a quarter of a second.
    playWave(sine, WAV_SIZE, scale[i], 0.25);
    // Pause for a tenth of a second between notes.
    delay(100);
  }
  Serial.println("Sawtooth wave");
  for (int i=0; i<sizeof(scale)/sizeof(float); ++i) {
    // Play the note for a quarter of a second.
    playWave(sawtooth, WAV_SIZE, scale[i], 0.25);
    // Pause for a tenth of a second between notes.
    delay(100);
  }
  Serial.println("Triangle wave");
  for (int i=0; i<sizeof(scale)/sizeof(float); ++i) {
    // Play the note for a quarter of a second.
    playWave(triangle, WAV_SIZE, scale[i], 0.25);
    // Pause for a tenth of a second between notes.
    delay(100);
  }
  Serial.println("Square wave");
  for (int i=0; i<sizeof(scale)/sizeof(float); ++i) {
    // Play the note for a quarter of a second.
    playWave(square, WAV_SIZE, scale[i], 1.0);
    // Pause for a tenth of a second between notes.
    //delay(1);
    delay(100);
  }
}

@PilnyTomas
Copy link
Contributor

Hi @ifrew I tried your code and it takes 110 seconds to generate that 0.25sec tone. Please try improving tone generation.

@ifrew
Copy link

ifrew commented Jun 21, 2022

@PilnyTomas It isn't actually my code, I was just using an example I found to show the potential issue. However, I did find the issue in the example. as I stated in #6856. Thanks for getting back though.

@PilnyTomas
Copy link
Contributor

PilnyTomas commented Jul 20, 2022

I have tried IDF example and the output is very laggy, so I opened an issue for the IDF team
espressif/esp-idf#9389

@JoseEnriqueFA
Copy link

The esp32 I2s internal DAC output is NOT working with the release 2.0.5

I tested yesterday the last Board Manager release 2.0.5 and a sketch to generate a sinus signal with I2S and the internal DAC output. Same result: Not Working!

HW: Adafruit ESP32 feather and an oscilloscope

With the Board Manager 2.0.0 the sketch works but all the Board Manager > 2.0.0 have same bug

Is there any work around?

Note: I discovered the bug trying to evaluate the Adafruit ESP32 feather V2. You need to upgrade the Board Manager to 2.0.4
Same issue is shown in both boards Adafruit ESP32 feather and Adafruit ESP32 feather V2

@PilnyTomas
Copy link
Contributor

PilnyTomas commented Oct 17, 2022

@cicciocb, @ifrew, @pjmi1, can you please try this sketch if it works for you? It does for me.

@JoseEnriqueFA
Copy link

JoseEnriqueFA commented Oct 17, 2022 via email

@PilnyTomas PilnyTomas added the Resolution: Awaiting response Waiting for response of author label Oct 18, 2022
@PilnyTomas
Copy link
Contributor

The topic seems to be dead, so I am closing it.

Repository owner moved this from In Progress to Done in Arduino ESP32 Core Project Roadmap Nov 9, 2022
@McMornan
Copy link

McMornan commented Nov 9, 2022

Hi, I ran into this issue yesterday. Took me hours of testing different boards and max chips, checking wiring and pin layout etc. to finally come to the conclusion that the only difference of my hitherto working code vs. non working code and device was 1.0.6 vs 2.0.6 esp32 framework.

Arduino 1.8.6, esp32 2.0.6 framework, esp32 dev board, max dac chip.

Downgraded to 1.0.6, dac works again immediately.

@JoseEnriqueFA
Copy link

JoseEnriqueFA commented Nov 10, 2022 via email

@PilnyTomas
Copy link
Contributor

Please understand the difference between IDF and arduino-esp32.
The function i2s_driver_install is IDF (please read the documentation) - if something doesn't work, please report it to IDF repository

From version 2.0.3 of arduino-esp32 there is I2S library using Arduino API (Arduino Documentation)
I am currently working on some fixes and updates for this library - you can follow them in PR #7117

Arduino-esp32 is based on IDF so you can always use IDF functions and combine them with Arduino API. We pull updates done in IDF from time to time, which could cause breaking changes or introduce bugs, but most of the time they are fixing previous bugs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: ESP-IDF related ESP-IDF related issues Area: Peripherals API Relates to peripheral's APIs. Resolution: Awaiting response Waiting for response of author
Projects
Development

No branches or pull requests

10 participants