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 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.
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:
- ///////////////////////////////////////////////////////////
- // config_i2s()
- // This function sets up the important parts of the i2s interface
- // Modify the config structure to make changes
- //
- // In this setup, the nuvoton codec is acting as clock master
- void config_i2s(int rate, int bits, int clk_pin, int frame_pin, int out_pin, int in_pin)
- {
- int i2s_port = I2S_NUM_0; // i2s 1
- i2s_config_t i2s_config =
- {
- .mode = I2S_MODE_SLAVE | 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 = 6,
- .dma_buf_len = 60
- };
- i2s_pin_config_t pin_config =
- {
- .bck_io_num = clk_pin,
- .ws_io_num = frame_pin,
- .data_out_num = out_pin,
- .data_in_num = in_pin
- };
- i2s_driver_install(i2s_port, &i2s_config, 0, NULL); // Install driver
- i2s_set_pin(i2s_port, &pin_config); // Set up pins
- i2s_set_sample_rates(i2s_port, rate); // Set sample rate
- }
- void record_audio(int16_t * buffer, int buffer_size)
- {
- unsigned int bytes_written;
- updateCodec(0x0A, 0x000); // unmute
- i2s_start(I2S_NUM_0);
- i2s_zero_dma_buffer(I2S_NUM_0);
- i2s_read(I2S_NUM_0, buffer, buffer_size, &bytes_written, 100);
- updateCodec(0x0A, 0x040); // remute
- i2s_stop(I2S_NUM_0);
- }
- void play_audio(int16_t * buffer, int buffer_size)
- {
- unsigned int bytes_written;
- updateCodec(0x0A, 0x000); // unmute
- i2s_start(I2S_NUM_0);
- i2s_zero_dma_buffer(I2S_NUM_0);
- i2s_write(I2S_NUM_0, buffer, buffer_size, &bytes_written, 100);
- updateCodec(0x0A, 0x040); // remute
- i2s_stop(I2S_NUM_0);
- }