I'm currently finishing the MSc degree in Electronics and I need to store ECG samples in an SD card to perform some tests with simulation circuits. The same SD card should be used to store some test results. The problems are:
the sampling rate should be at least 1kSamples/second
the output wave should present minimal distortion and noise
SPI or I2C rail-to-rail DACs and ADCs with at least 16 bits are preferable over internal DACs and ADCs
I've been using an external DAC, but the output wave appears to have some temporal dispersion.
To sort this problem out I'm trying to play some wave sound files (like music or speech), connecting the DAC outputs to speakers, to see how it sounds, and it's very strange, because, if I use SPI I can't hear anything similar to the original sound, the sound seems like white noise and sometimes loud skeeks and squeals. When I use files with lower sampling frequencies like 6 kHz or 8 kHz, and the internal DACs, the speech is discernible but of very poor quality, similar to older mobile phones, and it's full of noise and distortion. When I use ECG files with fs 1 kHz or 2 kHz the heartbeats seem like little hammers and respiration seems a bit robot like, I don't know why this happens.
I'm using the libraries DueTimer.h, SPI.h and SD.h. The DAC samples are supplied within a timer callback function, I'm also trying to use a buffer, but I don't know if its implementation makes any sense, I'm confused. I also tried the Audio.h library, but it uses the internal DACs and it's not very easy to adapt to your needs, the sound seems ok, but it has a strange interfeerence that makes a strange sound similar to a cricket.
I've made the changes you suggested, the code was really confuse, but the problem seems to persist, I've also noticed, that when I'm using a sample rate greater than 8 kSamples/s, the sound seem to repeat, every 1 seconds or so (a bit like and echo without amplitude decay), that's strange. I suspect something is wrong in the circular buffer implementation, but I don't know what could be. If the buffer is ok, the problem must be the Arduino Due instruction clock that is too slow to provide an adequate flow of samples to the DAC, what do you think?
The code I've made for external SPI DACs doesn't work at all, the sound seems terrible and with a really strange high pitch. I can't set the DAC values inside the timer callback function, because the interrupts are disable, so I think I've made some wrong changes to the code. I'm really confused.
Repeats from a circular buffer mean your producer isn't keeping up with the consumer which overtakes
it and sees previous data a second time.
Make the consumer substitute zero rather than advancing its pointer if the buffer is empty, then
you'll get pauses but still hear all the produced data.
You might have a buffer too small for the bursty producer, in which case a larger buffer will cure
things.
Or you might have a producer whose average rate is less than the consumer rate - in which cases
pauses are the best you can do.
Yes you're absolutely right, the buffer writing code was slower than the reading code, I've optimized the code and now I can reproduce 2 channels, with 16 bits per sample at up to 32kHz, but not higher, and there is another problem. The external DAC doesn't work inside the timer callback function and if I move it to the "while" I'm using to scan the file, I can only hear noise, it seems like there is SPI contention. The SD and the external DACs are connected thru SPI.
There's still a limitation in sampling rate that is 32 kSamples/s, if I could go up to 44.1 or 48 it's better, it seems that the two anolog write functions are taking at least about 10 microseconds, the code to read the wav file is taking about 5 microseconds. It's possible to increase the internal DAC speed? Anyway I really need to use the external SPI DACs.
I guess the default SPI library for the Due is using interrupts for some reason, hence the problem trying
to use it in a timer ISR? If so that's very dumb, the most common things you want to do in an ISR are
talk to external hardware!
Look for another SPI library or for direct register access to the SPI hardware perhaps?
When you tried to drive the DAC from ISR you did guard the SPI bus with a critical section in the main loop?
I'm pretty rusty with the Due I haven't used one for a while, and I know direct register access is pretty
complex (lots of reading of multiple sections of the datasheet seem to be involved for the most basic
actions, alas)
Not sure about SPI contention - you are using single-threaded code if talking to both from the main loop
(why did you not use loop() for the main loop BTW? You seem to have renamed it 'audioWrite()')
MarkT:
I guess the default SPI library for the Due is using interrupts for some reason, hence the problem trying
to use it in a timer ISR? If so that's very dumb, the most common things you want to do in an ISR are
talk to external hardware!
Yes, I suspect that's happening.
MarkT:
Look for another SPI library or for direct register access to the SPI hardware perhaps?
I've tried the turbo SPI library but it seems slower than normal SPI, I'm confuse
MarkT:
When you tried to drive the DAC from ISR you did guard the SPI bus with a critical section in the main loop?
Sorry I don't understand the question.
MarkT:
I'm pretty rusty with the Due I haven't used one for a while, and I know direct register access is pretty
complex (lots of reading of multiple sections of the datasheet seem to be involved for the most basic
actions, alas)
I've seen some implementations on libraries, and yes it's very complex and confusing.
MarkT:
Not sure about SPI contention - you are using single-threaded code if talking to both from the main loop
(why did you not use loop() for the main loop BTW? You seem to have renamed it 'audioWrite()')
I read somewhere that using a while in setup() is faster than using the loop(), but I'm not sure if that's true.
I've attached some code I used to perform timming tests of the SPI libraries.
danny92:
I read somewhere that using a while in setup() is faster than using the loop(), but I'm not sure if that's true.
It is true, main calls loop in a loop that also checks for serial handler functions, whereas an explicit
while loop makes no function calls and checks nothing else.
danny92:
I'm not going to use I2C DACs I've already ordered SPI DACs, and I think the I2S boards can't output DC or am I wrong?
You seem to be confusing I2C and I2S.... No all I2S DACs filter out DC, note, some will have it as an option.
I2S ADCs usually have a high-pass filter though. I2S lowest sampling rate may be too high anyway.
Is it possible to configure both Due DACs to free running mode?
Is it possible to interface I2S devices with Arduino Due?
Some, if not all I2S ICs also have I2C, SPI, or other interface for control purposes.
I've read somewhere that the Arduino Due has some clock limitation, I'm not sure if it's the clock dividers or something related to the exact frequency needed to achieve 44100Hz sampling, that makes it difficult to use with I2S devices.