Noisy I2S in only one direction

viduata
Posts: 2
Joined: Sun Nov 18, 2018 8:12 pm

Noisy I2S in only one direction

Postby viduata » Sun Nov 18, 2018 8:34 pm

Good afternoon, ESPers,

I've been fighting an I2S problem for about two weeks, and I've finally broken down and decided to ask for help.

Here's the issue:
1) I2S received from my CODEC (NAU8822) is perfect and clean.
2) I2S sent to my CODEC has enough bad transfers to make it sound "crackly," like playing a dusty record.

Some waveforms
dirty_wave.jpg
dirty_wave.jpg (2.28 MiB) Viewed 9911 times
This is an o-scope capture. This wave was generated by using Audacity to create a tone. The tone was then exported as raw PCM data, saved to an SD card, then opened, copied to an array, and then written to I2S. Notice the fairly random ticks. In this case, they're all zeroes, but that's not always true.
clean_wave_from_audacity.png
clean_wave_from_audacity.png (5.83 KiB) Viewed 9911 times
This is a screen cap from Audacity. I used ESP to record an audio signal from the CODEC in I2S, then copied that buffer directly to the SD card. I then imported the data from the SD card into Audacity. Notice that it's perfectly smooth with no "hiccups."


Some Details
1) The CODEC is NAU8822, clocked externally by a high-accuracy 11.2896 MHz oscillator
2) The oscillator is divided by 256 to give a 44.1 KHz sample clock.
3) The system is configured such that the CODEC is the master of all clocks, and ESP32 is the slave.

I have done extensive testing to figure out where the source of the problem is, and I'm pretty sure that it has something to do with the ESP chip. The CODEC works great, sends clear data, and sounds crystal clear when configured in its loop-back test mode.

I have tried writing data from the SD card to the I2S port - Still noisey.
I have tried generating waveforms programmatically and writing them to the I2S port. -Still Noisey
I have tried reading data directly from the I2S port then copying it back. - Still Noisey

Here are some bits of code. Tell me if you see anything wrong here, or have any ideas.

I2S configurator:
  1. ///////////////////////////////////////////////////////////
  2. // config_i2s()
  3. // This function sets up the important parts of the i2s interface
  4. // Modify the config structure to make changes
  5. //
  6. // In this setup, the nuvoton codec is acting as clock master
  7. void config_i2s(int rate, int bits, int clk_pin, int frame_pin, int out_pin, int in_pin)
  8. {
  9.     int i2s_port = I2S_NUM_0; // i2s 1
  10.  
  11.     i2s_config_t i2s_config =
  12.     {
  13.         .mode = I2S_MODE_SLAVE | I2S_MODE_RX | I2S_MODE_TX,
  14.         .sample_rate = rate,
  15.         .bits_per_sample = bits,
  16.         .use_apll = false,
  17.         .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
  18.         .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
  19.         .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_PCM,
  20.         .dma_buf_count =  6,  
  21.         .dma_buf_len = 60      
  22.                                
  23.     };
  24.  
  25.     i2s_pin_config_t pin_config =
  26.     {
  27.     .bck_io_num     = clk_pin,
  28.     .ws_io_num      = frame_pin,
  29.     .data_out_num   = out_pin,
  30.     .data_in_num    = in_pin
  31.     };
  32.  
  33.  
  34.     i2s_driver_install(i2s_port, &i2s_config, 0, NULL);     // Install driver
  35.     i2s_set_pin(i2s_port, &pin_config);                 // Set up pins
  36.     i2s_set_sample_rates(i2s_port, rate);               // Set sample rate
  37. }
Audio recorder
  1. void record_audio(int16_t * buffer, int buffer_size)
  2. {
  3.   unsigned int bytes_written;
  4.   updateCodec(0x0A, 0x000); // unmute
  5.   i2s_start(I2S_NUM_0);
  6.   i2s_zero_dma_buffer(I2S_NUM_0);
  7.   i2s_read(I2S_NUM_0, buffer, buffer_size, &bytes_written, 100);
  8.   updateCodec(0x0A, 0x040); // remute
  9.   i2s_stop(I2S_NUM_0);
  10. }
Audio Player
  1. void play_audio(int16_t * buffer, int buffer_size)
  2. {
  3.   unsigned int bytes_written;
  4.   updateCodec(0x0A, 0x000); // unmute
  5.   i2s_start(I2S_NUM_0);
  6.   i2s_zero_dma_buffer(I2S_NUM_0);
  7.   i2s_write(I2S_NUM_0, buffer, buffer_size, &bytes_written, 100);
  8.   updateCodec(0x0A, 0x040); // remute
  9.   i2s_stop(I2S_NUM_0);
  10. }

jason.mao
Posts: 98
Joined: Mon Nov 19, 2018 2:05 am

Re: Noisy I2S in only one direction

Postby jason.mao » Mon Nov 19, 2018 2:25 am

Hi viduata

Cloud you test with follow configuration? Change ESP32 I2S mode is master, and increase length of `dma_buf_len`.

Code: Select all

    i2s_config_t i2s_config =
    {
        .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX,
        .sample_rate = rate,
        .bits_per_sample = bits,
        .use_apll = false,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
        .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_PCM,
        .dma_buf_count =  3,  
        .dma_buf_len = 300      
                               
    };

colman
Posts: 37
Joined: Mon May 30, 2016 7:41 am

Re: Noisy I2S in only one direction

Postby colman » Mon Nov 19, 2018 2:41 am

ESP32's I2S interface has problem in slave mode. It use its internal clock to clock the state machine, when the internal clock is faster than the mclk, it will cause I2S FIFO underrun. So, you must use the codec as slave and ESP32 as master.

Colman

viduata
Posts: 2
Joined: Sun Nov 18, 2018 8:12 pm

Re: Noisy I2S in only one direction

Postby viduata » Mon Nov 19, 2018 6:53 am

Thanks for your help.

First, the good news:
Changing the setup so that ESP32 is Master works for the application, so I appreciate the advice. I'm getting good data in both directions for the first time.

The bad news:
Getting this to work correctly required me to match the output from the i2s config utilities:
  1. I (216) I2S: PLL_D2: Req RATE: 44100, real rate: 44642.000, BITS: 16, CLKM: 14,
  2. BCK: 8, MCLK: 11289966.924, SCLK: 1428544.000000, diva: 64, divb: 11

Since the ESP32 creates an LRCLK of 44642, I had to spend quite a lot of time tuning my PLL to the right speed, and I'm not sure if I won't have "skips" during long reads. I'll find out later. Also, this is a non-standard recording speed, so it's only useful for internal DSP stuff, and can't reasonbly be used for recording purposes, as it will run about 1.2% too fast.

A final thought:
Will we have a clean I2S Slave mode soon on the ESP32? It should be easy-ish to run it purely on interrupts, or to have it reset its internal timer after every data frame. Even at 1.2% variance, it should be able to run quite a few cycles before it starts to miss its input and output.

Thanks for you help.
-viduata

jason.mao
Posts: 98
Joined: Mon Nov 19, 2018 2:05 am

Re: Noisy I2S in only one direction

Postby jason.mao » Mon Nov 19, 2018 7:39 am

Hi viduata

I think you should set the ".use_apll = true". Audio PLL is suitable all the sample rate.

Who is online

Users browsing this forum: Michaelboeding and 56 guests