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:

  1. #include <Arduino.h>
  2. #include <driver/i2s.h>
  3. #include <driver/dac.h>
  4.  
  5. /* I2S DAC */
  6. #define  DAC_CHANNEL            DAC_CHANNEL_1                   // GPIO25 channel
  7.  
  8. #define I2S_DAC                 I2S_NUM_0
  9. #define I2S_SAMPLE_RATE         8000
  10. #define I2S_BITS_PER_SAMPLE     I2S_BITS_PER_SAMPLE_16BIT
  11. #define I2S_CHANNELS_NUM        I2S_CHANNEL_MONO
  12. #define I2S_CHANNEL_FORMAT      I2S_CHANNEL_FMT_ONLY_RIGHT
  13. #define I2S_COMM_FORMAT         I2S_COMM_FORMAT_I2S_MSB
  14. #define I2S_DAC_CHANNEL         I2S_DAC_CHANNEL_RIGHT_EN        // corresponds to DAC 1 channel
  15.  
  16. #define SINE_SAMPLES_NUM            256
  17. #define SINE_800HZ_SAMPLES_NUM      10
  18.  
  19.  
  20.  
  21. /*
  22. const uint16_t i2s_sine_wave_800hz[SINE_800HZ_SAMPLES_NUM] = {
  23.     32768, 53830, 65038, 61145, 43975,
  24.     21561,  4391,   498, 11706, 32768
  25. };*/
  26.  
  27.  
  28. const int16_t i2s_sine_wave_800hz[SINE_800HZ_SAMPLES_NUM] = {
  29.         0,       21062,  32269,  28377,     11206,
  30.         -11206, -28377, -32269, -21062,     0
  31. };
  32.  
  33. static bool prepare_i2s_dac();
  34. static bool play_sound();
  35.  
  36.  
  37. void setup()
  38. {
  39.     Serial.begin(921600);
  40.     Serial.println("Hello!");
  41.  
  42.     if(!prepare_i2s_dac())
  43.     {
  44.         Serial.println("Failed to initialize DAC");
  45.         while(true);
  46.     }
  47.  
  48.     play_sound();
  49. }
  50.  
  51. void loop()
  52. {
  53.     //delay(10);
  54.  
  55. }
  56.  
  57.  
  58. static bool play_sound()
  59. {
  60.     static uint8_t sin_samples[SINE_800HZ_SAMPLES_NUM*2];
  61.     for(uint32_t i=0; i<SINE_800HZ_SAMPLES_NUM; i++)
  62.     {
  63.         sin_samples[i*2] = (i2s_sine_wave_800hz[i]) & 0xFF;
  64.         sin_samples[i*2+1] = (i2s_sine_wave_800hz[i] >> 8) & 0xFF;
  65.     }
  66.  
  67.     if(i2s_write_bytes(I2S_DAC, (const char*)sin_samples,
  68.             SINE_800HZ_SAMPLES_NUM*2, portMAX_DELAY) != SINE_800HZ_SAMPLES_NUM*2)
  69.     {
  70.         return false;
  71.     }
  72.  
  73.     return true;
  74. }
  75.  
  76.  
  77. static bool prepare_i2s_dac()
  78. {
  79.     i2s_config_t i2s_config = {
  80.         mode : (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
  81.         sample_rate :  I2S_SAMPLE_RATE,
  82.         bits_per_sample : I2S_BITS_PER_SAMPLE,
  83.         channel_format : I2S_CHANNEL_FORMAT,
  84.         communication_format : I2S_COMM_FORMAT,
  85.         intr_alloc_flags : 0,
  86.         dma_buf_count : 2,
  87.         dma_buf_len : 128,
  88.         use_apll : false,
  89.         fixed_mclk : 0
  90.     };
  91.  
  92.     if(i2s_driver_install(I2S_DAC, &i2s_config, 0, NULL) != ESP_OK)
  93.     {
  94.         Serial.println("Failed to initialize i2s driver for DAC");
  95.         return false;
  96.     }
  97.  
  98.     if(i2s_set_dac_mode(I2S_DAC_CHANNEL) != ESP_OK)
  99.     {
  100.         Serial.println("Failed to set i2s DAC channel");
  101.         return false;
  102.     }
  103.  
  104.     i2s_set_sample_rates(I2S_DAC, I2S_SAMPLE_RATE);
  105.  
  106.     return true;
  107. }
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 7914 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 7914 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 7914 times
Clearly sequences of points are not the same.

Could someone clarify what is wrong here?

ESP_Sprite
Posts: 8921
Joined: Thu Nov 26, 2015 4:08 am

Re: I2S DAC generate tone

Postby ESP_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

ESP_Sprite wrote:
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;
    }
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 72 guests