I2S DAC generate tone

nameofuser1
Posts: 6
Joined: Mon Oct 15, 2018 9:10 am

I2S DAC generate tone

Postby nameofuser1 » Wed Nov 28, 2018 10:04 am

I am trying to produce sine wave with I2S. There are several strange things I have found I don't know how to overcome.
Here is my code:

Code: Untitled.c Select all


#include <Arduino.h>
#include <driver/i2s.h>
#include <driver/dac.h>

/* I2S DAC */
#define DAC_CHANNEL DAC_CHANNEL_1 // GPIO25 channel

#define I2S_DAC I2S_NUM_0
#define I2S_SAMPLE_RATE 8000
#define I2S_BITS_PER_SAMPLE I2S_BITS_PER_SAMPLE_16BIT
#define I2S_CHANNELS_NUM I2S_CHANNEL_MONO
#define I2S_CHANNEL_FORMAT I2S_CHANNEL_FMT_ONLY_RIGHT
#define I2S_COMM_FORMAT I2S_COMM_FORMAT_I2S_MSB
#define I2S_DAC_CHANNEL I2S_DAC_CHANNEL_RIGHT_EN // corresponds to DAC 1 channel

#define SINE_SAMPLES_NUM 256
#define SINE_800HZ_SAMPLES_NUM 10



/*
const uint16_t i2s_sine_wave_800hz[SINE_800HZ_SAMPLES_NUM] = {
32768, 53830, 65038, 61145, 43975,
21561, 4391, 498, 11706, 32768
};*/


const int16_t i2s_sine_wave_800hz[SINE_800HZ_SAMPLES_NUM] = {
0, 21062, 32269, 28377, 11206,
-11206, -28377, -32269, -21062, 0
};

static bool prepare_i2s_dac();
static bool play_sound();


void setup()
{
Serial.begin(921600);
Serial.println("Hello!");

if(!prepare_i2s_dac())
{
Serial.println("Failed to initialize DAC");
while(true);
}

play_sound();
}

void loop()
{
//delay(10);

}


static bool play_sound()
{
static uint8_t sin_samples[SINE_800HZ_SAMPLES_NUM*2];
for(uint32_t i=0; i<SINE_800HZ_SAMPLES_NUM; i++)
{
sin_samples[i*2] = (i2s_sine_wave_800hz[i]) & 0xFF;
sin_samples[i*2+1] = (i2s_sine_wave_800hz[i] >> 8) & 0xFF;
}

if(i2s_write_bytes(I2S_DAC, (const char*)sin_samples,
SINE_800HZ_SAMPLES_NUM*2, portMAX_DELAY) != SINE_800HZ_SAMPLES_NUM*2)
{
return false;
}

return true;
}


static bool prepare_i2s_dac()
{
i2s_config_t i2s_config = {
mode : (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
sample_rate : I2S_SAMPLE_RATE,
bits_per_sample : I2S_BITS_PER_SAMPLE,
channel_format : I2S_CHANNEL_FORMAT,
communication_format : I2S_COMM_FORMAT,
intr_alloc_flags : 0,
dma_buf_count : 2,
dma_buf_len : 128,
use_apll : false,
fixed_mclk : 0
};

if(i2s_driver_install(I2S_DAC, &i2s_config, 0, NULL) != ESP_OK)
{
Serial.println("Failed to initialize i2s driver for DAC");
return false;
}

if(i2s_set_dac_mode(I2S_DAC_CHANNEL) != ESP_OK)
{
Serial.println("Failed to set i2s DAC channel");
return false;
}

i2s_set_sample_rates(I2S_DAC, I2S_SAMPLE_RATE);

return true;
}
First of all I am totally missing why signal is produced more than once. Image shows that pattern is repeated each ~30ms. Why is that so?
photo_2018-11-28_12-41-17.jpg
Signal repeated
photo_2018-11-28_12-41-17.jpg (100.22 KiB) Viewed 9861 times

Secondly I am not sure how does I2S interpret sent data, should it be signed or unsigned?

However I tried both approaches(you can see two arrays in my code). What for unsinged pattern, it produces the following output:
photo_2018-11-28_12-59-12.jpg
Unsigned DAC signal
photo_2018-11-28_12-59-12.jpg (100.89 KiB) Viewed 9861 times
And here is how it should look like:
Figure_1.png
Unsigned sequence how it should be
Figure_1.png (24.2 KiB) Viewed 9861 times
Clearly sequences of points are not the same.

Could someone clarify what is wrong here?

Sprite
Espressif staff
Espressif staff
Posts: 10617
Joined: Thu Nov 26, 2015 4:08 am

Re: I2S DAC generate tone

Postby Sprite » Wed Nov 28, 2018 11:03 am

1. The I2S needs 16-bit samples, you're stashing (repeated) 8-bit samples into it.
2. The I2S reads the samples as 32-bit words and outputs the high 16-bit first and the low 16-bit second. This means that if you want to send it 16-bit samples, you'll need to swap the even and odd 16-bit words.
3. The DAC only plays the high byte of the 16-bit sample as an unsigned value (0..255, not -128..127).

This should (=untested) work:

Code: Select all

   static uint16_t sin_samples[SINE_800HZ_SAMPLES_NUM];
    for(uint32_t i=0; i<SINE_800HZ_SAMPLES_NUM; i++)
    {
    	uint16_t samp = i2s_sine_wave_800hz[i] + 32768;
        sin_samples[i^1] = samp; //note i XOR with 1 to swap even and odd samples
    }
 
    if(i2s_write_bytes(I2S_DAC, (const char*)sin_samples,
            SINE_800HZ_SAMPLES_NUM*2, portMAX_DELAY) != SINE_800HZ_SAMPLES_NUM*2)
    {
        return false;
    }

nameofuser1
Posts: 6
Joined: Mon Oct 15, 2018 9:10 am

Re: I2S DAC generate tone

Postby nameofuser1 » Tue Dec 11, 2018 10:56 pm

1. The I2S needs 16-bit samples, you're stashing (repeated) 8-bit samples into it.
2. The I2S reads the samples as 32-bit words and outputs the high 16-bit first and the low 16-bit second. This means that if you want to send it 16-bit samples, you'll need to swap the even and odd 16-bit words.
3. The DAC only plays the high byte of the 16-bit sample as an unsigned value (0..255, not -128..127).

This should (=untested) work:

Code: Select all

   static uint16_t sin_samples[SINE_800HZ_SAMPLES_NUM];
    for(uint32_t i=0; i<SINE_800HZ_SAMPLES_NUM; i++)
    {
    	uint16_t samp = i2s_sine_wave_800hz[i] + 32768;
        sin_samples[i^1] = samp; //note i XOR with 1 to swap even and odd samples
    }
 
    if(i2s_write_bytes(I2S_DAC, (const char*)sin_samples,
            SINE_800HZ_SAMPLES_NUM*2, portMAX_DELAY) != SINE_800HZ_SAMPLES_NUM*2)
    {
        return false;
    }
Sorry for the late reply! Thanks a lot for your answer. I did not test your solution since I have found that ESP has hardware cosine waveform generator which solves the issue easily. Nonetheless thanks!

Who is online

Users browsing this forum: No registered users and 2 guests