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

[RESOLVED] I2S-DMA buffer issue (a test app specific) (IDFGH-3814) #300

Closed
valioiv opened this issue Jan 25, 2017 · 8 comments
Closed

[RESOLVED] I2S-DMA buffer issue (a test app specific) (IDFGH-3814) #300

valioiv opened this issue Jan 25, 2017 · 8 comments
Labels
Resolution: Done Issue is done internally Status: Done Issue is done internally

Comments

@valioiv
Copy link

valioiv commented Jan 25, 2017

I'm trying to use the I2S test application as a foundation for some audio project:

https://github.com/espressif/esp-idf/blob/master/examples/peripherals/i2s/main/app_main.c

I did NO modifications on the drivers, I've just connected a WM8731 audio codec chip and it started working properly. I hear the sound, but I noticed it's a bit buzzy. I expected clear triangular and sinusoidal 100Hz waves but they are not. I observe the following waveforms for the left and right channel respectively:

20170125_215905

20170125_215852

It is obvious there are buffers which are not filled with the real data but just zeros.
I really suspect this is some DMA buffer management issue. Most probably the buffer indexes are not updated and/or used properly. I tried to dig in the I2S-DMA affairs but I don't feel myself confident with it. Can somebody share their opinion at least?

@valioiv
Copy link
Author

valioiv commented Jan 25, 2017

And this is my draft code for now (the only changes I did are in the App):

#include "freertos/FreeRTOS.h"
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_event_loop.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "driver/i2s.h"
#include <math.h>

#define I2C_MASTER_SCL_IO    19    /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO    18    /*!< gpio number for I2C master data  */
#define I2C_MASTER_NUM I2C_NUM_1   /*!< I2C port number for master dev */
#define I2C_MASTER_TX_BUF_DISABLE   0   /*!< I2C master do not need buffer */
#define I2C_MASTER_RX_BUF_DISABLE   0   /*!< I2C master do not need buffer */
#define I2C_MASTER_FREQ_HZ    100000     /*!< I2C master clock frequency */

#define WM8731_I2C_ADDR  0x1A    /*!< slave address for WM8731 sensor */
#define WRITE_BIT  I2C_MASTER_WRITE /*!< I2C master write */
#define READ_BIT   I2C_MASTER_READ  /*!< I2C master read */
#define ACK_CHECK_EN   0x1     /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS  0x0     /*!< I2C master will not check ack from slave */
#define ACK_VAL    0x0         /*!< I2C ack value */
#define NACK_VAL   0x1         /*!< I2C nack value */

#define WM8731_LINVOL	0x00
#define WM8731_RINVOL	0x01
#define WM8731_LOUT1V	0x02
#define WM8731_ROUT1V	0x03
#define WM8731_APANA	0x04
#define WM8731_APDIGI	0x05
#define WM8731_PWR		0x06
#define WM8731_IFACE	0x07
#define WM8731_SRATE	0x08
#define WM8731_ACTIVE	0x09
#define WM8731_RESET	0x0f

#define WM8731_SAMPLING_FREQ_8K		8000u
#define WM8731_SAMPLING_FREQ_32K	32000u
#define WM8731_SAMPLING_FREQ_44K1	44100u
#define WM8731_SAMPLING_FREQ_48K	48000u
#define WM8731_SAMPLING_FREQ_88K2	88200u
#define WM8731_SAMPLING_FREQ_96K	96000u

#define WM8731_MCLK 12000000u


//#define CODEC_IS_MASTER


#define SAMPLE_RATE		(36000)
#define I2S_NUM			(0)
#define WAVE_FREQ_HZ	(100)
#define PI				3.14159265

#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ)

typedef struct wm8731_cmd_entry_s {
	unsigned int reg_addr;
	unsigned int reg_val;
} wm8731_cmd_entry_t;

typedef struct _sampling_ctrl {
	long mclk;
	long rate;
	unsigned int fs;
	unsigned char sr:4;
	unsigned char bosr:1;
	unsigned char usb:1;
} wm8731_sampling_ctrl_t;

wm8731_sampling_ctrl_t sampling_ctrl_tbl[] = {
	/* 48k */
	{12288000, 48000, 256, 0x0, 0x0, 0x0},
	{18432000, 48000, 384, 0x0, 0x1, 0x0},
	{12000000, 48000, 250, 0x0, 0x0, 0x1},
	{6144000,  48000, 256, 0x7, 0x0, 0x0},

	/* 32k */
	{12288000, 32000, 384, 0x6, 0x0, 0x0},
	{18432000, 32000, 576, 0x6, 0x1, 0x0},
	{12000000, 32000, 375, 0x6, 0x0, 0x1},

	/* 8k */
	{12288000, 8000, 1536, 0x3, 0x0, 0x0},
	{18432000, 8000, 2304, 0x3, 0x1, 0x0},
	{11289600, 8000, 1408, 0xb, 0x0, 0x0},
	{16934400, 8000, 2112, 0xb, 0x1, 0x0},
	{12000000, 8000, 1500, 0x3, 0x0, 0x1},

	/* 96k */
	{12288000, 96000, 128, 0x7, 0x0, 0x0},
	{18432000, 96000, 192, 0x7, 0x1, 0x0},
	{12000000, 96000, 125, 0x7, 0x0, 0x1},

	/* 44.1k */
	{11289600, 44100, 256, 0x8, 0x0, 0x0},
	{16934400, 44100, 384, 0x8, 0x1, 0x0},
	{12000000, 44100, 272, 0x8, 0x1, 0x1},

	/* 88.2k */
	{11289600, 88200, 128, 0xf, 0x0, 0x0},
	{16934400, 88200, 192, 0xf, 0x1, 0x0},
	{12000000, 88200, 136, 0xf, 0x1, 0x1},
};

wm8731_cmd_entry_t wm8731_cmd_entries[] = {
	{ WM8731_PWR,		0x80 },
	{ WM8731_RESET,		0x00 },
	{ WM8731_ACTIVE,	0x00 },
	{ WM8731_APANA,		0x12 },
	{ WM8731_APDIGI,	0x00 },
	{ WM8731_PWR,		0x00 },
	{ WM8731_IFACE,		0x02 },
	{ WM8731_LINVOL, 	0x17 },
	{ WM8731_RINVOL, 	0x17 },
	{ WM8731_LOUT1V, 	0x7F },
	{ WM8731_ROUT1V, 	0x7F },
	{ WM8731_ACTIVE, 	0x01 },
};

void i2c_master_init()
{
	int i2c_master_port = I2C_MASTER_NUM;
	i2c_config_t conf;
	conf.mode = I2C_MODE_MASTER;
	conf.sda_io_num = I2C_MASTER_SDA_IO;
	conf.sda_pullup_en = GPIO_PULLUP_DISABLE;
	conf.scl_io_num = I2C_MASTER_SCL_IO;
	conf.scl_pullup_en = GPIO_PULLUP_DISABLE;
	conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
	i2c_param_config(i2c_master_port, &conf);
	i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

esp_err_t wm8731_config(i2c_port_t i2c_num, wm8731_cmd_entry_t* cmd_entries, unsigned int entries_count, unsigned int relaxation_time)
{
	i2c_cmd_handle_t cmd;
	int ret;

	for (unsigned int i = 0; i < entries_count; i++)
	{
		unsigned char byte1 = (cmd_entries[i].reg_addr << 1) | (cmd_entries[i].reg_val >> 8);
		unsigned char byte2 = cmd_entries[i].reg_val & 0xff;

		cmd = i2c_cmd_link_create();
		i2c_master_start(cmd);
		i2c_master_write_byte(cmd, WM8731_I2C_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
		i2c_master_write_byte(cmd, byte1, ACK_CHECK_EN);
		i2c_master_write_byte(cmd, byte2, ACK_CHECK_EN);
		i2c_master_stop(cmd);
		ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
		i2c_cmd_link_delete(cmd);
		if (ret == ESP_FAIL) {
			return ESP_FAIL;
		}
		vTaskDelay(relaxation_time / portTICK_RATE_MS);
	}

	return ESP_OK;
}

void app_main(void)
{
	int ret;

	nvs_flash_init();

	//vTaskDelay(500 / portTICK_RATE_MS);

	i2c_master_init();

	//vTaskDelay(500 / portTICK_RATE_MS);

	ret = wm8731_config(I2C_MASTER_NUM, wm8731_cmd_entries, sizeof(wm8731_cmd_entries) / sizeof(wm8731_cmd_entry_t), 1);

	printf("*******************\n");
	printf("WM8731 initialization\n");
	printf("*******************\n");
	if (ret == ESP_OK) {
		printf("Codec initialization success!\n");
	} else {
		printf("No ack, codec not connected...skip...\n");
	}

	vTaskDelay(500 / portTICK_RATE_MS);

	unsigned int i, sample_val;
	float sin_float, triangle_float, triangle_step = 65536.0 / SAMPLE_PER_CYCLE;
	//for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes each sample)
	//using 6 buffers, we need 60-samples per buffer
	//2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes
	i2s_config_t i2s_config = {
		.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
		.sample_rate = SAMPLE_RATE,
		.bits_per_sample = 16,                                                  //16-bit per channel
		.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           //2-channels
		.communication_format = I2S_COMM_FORMAT_I2S,
		.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 = 23,
		.ws_io_num = 21,
		.data_out_num = 22,
		.data_in_num = -1                                                       //Not used
	};

	i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
	i2s_set_pin(I2S_NUM, &pin_config);

	triangle_float = -32767;

	for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
		sin_float = sin(i * PI / 180.0);
		if(sin_float >= 0)
			triangle_float += triangle_step;
		else
			triangle_float -= triangle_step;
		sin_float *= 32767;

		sample_val = 0;
		sample_val += (short)triangle_float;
		sample_val = sample_val << 16;
		sample_val += (short) sin_float;

		//printf("0x%x\n", sample_val);

		i2s_push_sample(I2S_NUM, (char *)&sample_val, portMAX_DELAY);
	}
}

@valioiv
Copy link
Author

valioiv commented Jan 26, 2017

@tuanpmt , can you comment on this? Do you have any observations like that?

@tuanpmt
Copy link
Contributor

tuanpmt commented Jan 27, 2017

@valiolv
I also met a similar problem, but other reasons. I will find out the cause next week. Currently, I'n on Lunar New Year vacation and not have any hardware to test

@valioiv
Copy link
Author

valioiv commented Jan 27, 2017

Great! Happy new year and thanks in advance!

@tuanpmt
Copy link
Contributor

tuanpmt commented Feb 6, 2017

@valioiv Because i2s tx faster than sinwave calculation. Please re-calculate to a buffer first, then using i2s_write_bytes, or loop and push

@valioiv
Copy link
Author

valioiv commented Feb 8, 2017

Oh sure, I see... I'll test tomorrow and will close the "issue". Thanks for the support!

@valioiv
Copy link
Author

valioiv commented Feb 9, 2017

Hi :)

I've just modified the part of generation and sending via I2S as you suggested like this:

...
    unsigned int sample_val_arr[SAMPLE_PER_CYCLE] = { 0 };

    for(i = 0; i < SAMPLE_PER_CYCLE; i++) {
        sin_float = sin(i * PI / 180.0);
        if(sin_float >= 0)
            triangle_float += triangle_step;
        else
            triangle_float -= triangle_step;
        sin_float *= 32767;

        sample_val_arr[i] = 0;
        sample_val_arr[i] += (short)triangle_float;
        sample_val_arr[i] = sample_val_arr[i] << 16;
        sample_val_arr[i] += (short) sin_float;

        //printf("0x%x\n", sample_val);

        // i2s_push_sample(I2S_NUM, (char *)&sample_val, portMAX_DELAY);
    }

    i2s_write_bytes(I2S_NUM, sample_val_arr, sizeof(sample_val_arr), portMAX_DELAY);
...

Here are the diagrams:

20170209_214127

20170209_214137

It works fine, man! Thanks a lot!

@valioiv valioiv closed this as completed Feb 9, 2017
@valioiv valioiv changed the title I2S-DMA buffer issue [RESOLVED] I2S-DMA buffer issue (a test app specific) Feb 9, 2017
@AutoEM
Copy link

AutoEM commented Aug 13, 2020

Dear valioiv,

I am trying also to connect my esp32 to wm8731 audio codec board with no success. Could you please clarify how did you connect your esp32 pins to the codec?

Are the three i2s pins enough or do I have to set up the mclk also?
like here: espressif/arduino-esp32#3932

Thanks in advance!

@github-actions github-actions bot changed the title [RESOLVED] I2S-DMA buffer issue (a test app specific) [RESOLVED] I2S-DMA buffer issue (a test app specific) (IDFGH-3814) Aug 13, 2020
@espressif-bot espressif-bot added Resolution: Done Issue is done internally Status: Done Issue is done internally labels Jun 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution: Done Issue is done internally Status: Done Issue is done internally
Projects
None yet
Development

No branches or pull requests

4 participants