1

I have a buffer which contains 16000 PCM samples of 8Khz 8-bit mono. I am trying to play it using 12 bit MCP4725 DAC. I have tried using micros() to control the write interval for the DAC. Here's my code:

uint8_t soundData[16000] = { 234,206,79,255,249,....,210,222 }; // 8Khz 8-bit mono
long previousMicros = 0; // Sorry for missing this out!!

void setup() {

Serial.begin(115200);
Wire.begin(D2, D1);
delay(100); // delay 100 ms
Serial.flush();
delay(1000);

} 

void value_write(uint16_t temp){

  Wire.beginTransmission(0x62);
  Wire.write(64); 
  Wire.write(temp >> 4);        // the 8 most significant bits...
  Wire.write((temp & 15) << 4); // the 4 least significant bits...
  Wire.endTransmission();

}

void loop() {

  unsigned long currentMicros = micros();

// 125 uS sampling for 8KHz signal    
if(currentMicros - previousMicros > 125) {
  Serial.print("Index: ");
  Serial.println(indx);
  uint16_t temp = map(soundData[indx++], 0, 255, 0, 4095);

  value_write(temp);
}

}

The write should logically be completed within 2 seconds but takes way more time. Any help regarding successfully writing the PCM values to the DAC at 8000Hz is greatly appreciated.

2
  • This is going to be a very jittery way to do things. Typically you'd use a timer interrupt, or even (if you can afford to shut everything else off) a calibrated busy wait. Jan 11 '18 at 20:53
  • Can you give me a short example of how can I do this using timer interrupt?
    – Ashish K
    Jan 11 '18 at 20:54
1

In your code, you never set or update previousMicros value.

10
  • sorry for missing that out. I have updated
    – Ashish K
    Jan 11 '18 at 20:44
  • You need to update previousMicros every time you start a new send task. If previousMicrosnever gets updated, then your loop runs at top speed, and not metered by the if(currentMicros - previousMicros > 125) comparison. Try adding previousMicros += 125; just before the Serial.print("Index: "); line. This will cause your code to trigger approximately every 125 uS. Jan 11 '18 at 20:46
  • There was no change. I can observe in the serial monitor, it is approximately displaying 1000 values per second.
    – Ashish K
    Jan 11 '18 at 20:51
  • 1
    Doing a lot of serial printing may be messing up your timing. Do things work better if you disable all the Serial.print(...) commands in loop? Jan 11 '18 at 20:56
  • I think I can hear sound only for 2 sec now after disabling all the Serial.print. Also notices a change in write rate no sooner I removed one. The sample is actually unknown to me (the original audio) so could not confirm but could hear the beap only for 2 seconds. When I will be working ahead in the project, i will test it using known voice audio. Anyway, thankyou so much.
    – Ashish K
    Jan 11 '18 at 21:07
4

One sample takes 38 I2C clock cycles to write. That's:

1 START
7 Address
1 R/W
1 ACK
8 Byte
1 ACK
8 Byte
1 ACK
8 Byte
1 ACK
1 STOP

At the default 100kHz clock speed that's 100,000/38 = 2631.58 samples per second absolute maximum.

You can increase the clock speed to 400kHz, which would allow you 400,000 / 38 = 10526 samples per second absolute maximum. That would just about be do-able.

I am unsure what the ESP8266 can support, but the DAC also supports "High Speed" mode - 3.4MHz I2C. If the ESP8266 supports that you can then run at 3,400,000 / 38 = 89474 samples per second, which is better still.

There's also the matter of the map() function which is just nasty in this situation. You can just bit-shift.

So besides just removing the Serial commands (which will slow things down massively) you should also:

  1. Change the I2C clock to at least 400kHz
  2. Change map() to soundData[indx++] << 4
  3. Consider using a timer to trigger the sending of the samples

But, with all that said. an I2C DAC is really not suited to audio playback. I2C is just too slow and clunky for audio. You'd be better off with an SPI DAC which can run much much faster than I2C. Better still you should investigate I2S on the ESP8266. This is designed specifically for audio, although it's much harder to set up owing to the increased number of special clocks you need to provide ("master" clock, "bit" clock and "l/r" clock). Also the DACs (also known as CODECs) tend to be at least 16 bit or even higher, and capable of proper audio sampling frequencies, like 44100ksps and above.

2
  • Thanks @Majenko , I actually don't have to deal with a very high frequency as I am trying to play a voice sample recorded at 8KHz 8 bit mono and sound quality won't be an issue. Do I have to still go for I2S or I2C DAC would do the job for me.? And I would be going for 8000x38 = 304KHz for wire.setClock() but wish to do it using timers. It would be great if you can suggest me some reading
    – Ashish K
    Jan 12 '18 at 12:52
  • You want the clock frequency to be as fast as you can get. I2C specifications limit you to specific frequencies though - you can't just use any arbitrary value.
    – Majenko
    Jan 12 '18 at 12:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.