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.
-
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.Chris Stratton– Chris Stratton01/11/2018 20:53:33Commented Jan 11, 2018 at 20:53
-
Can you give me a short example of how can I do this using timer interrupt?Ashish K– Ashish K01/11/2018 20:54:55Commented Jan 11, 2018 at 20:54
2 Answers 2
In your code, you never set or update previousMicros
value.
-
sorry for missing that out. I have updatedAshish K– Ashish K01/11/2018 20:44:05Commented Jan 11, 2018 at 20:44
-
You need to update
previousMicros
every time you start a new send task. IfpreviousMicros
never gets updated, then your loop runs at top speed, and not metered by theif(currentMicros - previousMicros > 125)
comparison. Try addingpreviousMicros += 125;
just before theSerial.print("Index: ");
line. This will cause your code to trigger approximately every 125 uS.jose can u c– jose can u c01/11/2018 20:46:55Commented Jan 11, 2018 at 20:46 -
There was no change. I can observe in the serial monitor, it is approximately displaying
1000
values per second.Ashish K– Ashish K01/11/2018 20:51:01Commented Jan 11, 2018 at 20:51 -
1Doing 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?jose can u c– jose can u c01/11/2018 20:56:42Commented Jan 11, 2018 at 20:56 -
I think I can hear sound only for 2 sec now after disabling all the
Serial.print
. Also notices a change inwrite
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– Ashish K01/11/2018 21:07:18Commented Jan 11, 2018 at 21:07
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:
- Change the I2C clock to at least 400kHz
- Change
map()
tosoundData[indx++] << 4
- 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.
-
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 readingAshish K– Ashish K01/12/2018 12:52:19Commented Jan 12, 2018 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– Majenko01/12/2018 12:54:18Commented Jan 12, 2018 at 12:54