Using rev D audio board as simple DAC with variable sampling rate

rvh

Well-known member
Hi,
Can the codec on the rev D audio shield (for teensy 4) be used as a simple DAC with variable sample rate (i.e. more or less continuous sampling frequencies so I don't need to resample/interpolate to adjust playback frequency)? I would like to be able to go as low as a few hundred Hz, and up to about 16kHz.

If so, does anyone know of some example code that sets up the codec in that manner to get me started (presumably not using the audio library) ?

I had a bit of a search but couldn't see anything suitable. Thanks for any pointers.

Cheers,
Richard.
 
The question is, what your exact definition of "simple" is. Best is to take a look at the SGTL-5000 datasheet - and the audio shield schematic - if there are open questions.

Easiest is to use the Audio Library.
You can change it's frequency: https://github.com/TeensyUser/doc/w...-to-change-the-i2s-frequency-on-t4x--micromod

That's excellent Frank. The setI2SFreq code snippet is just what I was looking for. The only thing I have found in my brief test is that while it worked well from 48kHz down to a little under 10kHz, it may have overflowed a register as I continued to go lower because the sample rate skipped way up high again (at 9887hz) and then reduced gradually again form there.

I'll have a look to see if I can do anything about that (please let me know if you already know the answer). But even if not, this method will solve my problem because I can easily add a simple linear 2,4,8, or 16 point interpolator to reach lower frequencies.

Thanks very much.

Cheers,
Richard
 
Hi,
Can the codec on the rev D audio shield (for teensy 4) be used as a simple DAC with variable sample rate (i.e. more or less continuous sampling frequencies so I don't need to resample/interpolate to adjust playback frequency)? I would like to be able to go as low as a few hundred Hz, and up to about 16kHz.

In short, probably not. This is a synchronous I2S chip and relies on quartz-accurate clocking to work - nominally it
supports 8/11.025/12/16/22.05/24/32/44.1/48 and 96kSPS, although I think people have overclocked it, and I
guess it will underclock to some extent too (has anyone tried this?), but you cannot expect to vary the frequency while
it is running/clocking as these synchronous pipelined sigma-delta converters (which I believe both ADC and DAC to be)
rely on precise low jitter clocking for their basic function. However I'm not certain about this, the datasheet doesn't
give much away about the precise implementation other than mentioning its 24 bit, which indicates sigma-delta.

You can stop and restart the clocking at different rates though, but I don't think that quite counts as "simple DAC
with variable sample rate", and I don't know if it will handle 100Hz kinds of rates, internal capacitances might
be present that prevent this, the datasheet doesn't say anything but minimum Fs = 8000

Its basically designed expressly for audio. There is likely no precision in the output gain, for instance, there would be
no point using a voltage reference with such a chip for instance, the two channels won't gain-match exactly...
Furthermore there is gain variation of a percent or so across the pass band due to the internal digital filtering,
so absolute accuracy is about that of a 8 bit converter, but not an issue for audio as this is only 0.12dB pass-band
ripple and inaudible.

What application are you thinking of?
The audio adapter AC-couples all the signals anyway, so its not usable at DC anyway.
 
That's excellent Frank. The setI2SFreq code snippet is just what I was looking for. The only thing I have found in my brief test is that while it worked well from 48kHz down to a little under 10kHz, it may have overflowed a register as I continued to go lower because the sample rate skipped way up high again (at 9887hz) and then reduced gradually again form there.

I'll have a look to see if I can do anything about that (please let me know if you already know the answer). But even if not, this method will solve my problem because I can easily add a simple linear 2,4,8, or 16 point interpolator to reach lower frequencies.

Thanks very much.

Cheers,
Richard

Yes, it should be possible to reach lower frequencies. There are several prescalers in the chain. Best is to take a look at the reference manual.
However, from memory I don't know which ones are already at "maximum", I simply forgot. :)
If it doesn't work to set e.g. "n" to 8, you have to work yourself in there. But it is not too extensive or difficult. Just concentrate on the divisors of the audio PLL and SAI.
 
In short, probably not. This is a synchronous I2S chip and relies on quartz-accurate clocking to work - nominally it
supports 8/11.025/12/16/22.05/24/32/44.1/48 and 96kSPS, although I think people have overclocked it, and I
guess it will underclock to some extent too (has anyone tried this?), but you cannot expect to vary the frequency while
it is running/clocking as these synchronous pipelined sigma-delta converters (which I believe both ADC and DAC to be)
rely on precise low jitter clocking for their basic function. However I'm not certain about this, the datasheet doesn't
give much away about the precise implementation other than mentioning its 24 bit, which indicates sigma-delta.

You can stop and restart the clocking at different rates though, but I don't think that quite counts as "simple DAC
with variable sample rate", and I don't know if it will handle 100Hz kinds of rates, internal capacitances might
be present that prevent this, the datasheet doesn't say anything but minimum Fs = 8000

Its basically designed expressly for audio. There is likely no precision in the output gain, for instance, there would be
no point using a voltage reference with such a chip for instance, the two channels won't gain-match exactly...
Furthermore there is gain variation of a percent or so across the pass band due to the internal digital filtering,
so absolute accuracy is about that of a 8 bit converter, but not an issue for audio as this is only 0.12dB pass-band
ripple and inaudible.

What application are you thinking of?
The audio adapter AC-couples all the signals anyway, so its not usable at DC anyway.

Thanks Mark, much appreciated. Funny enough, my application is actually an 8-bit vintage audio emulation. I don't need to get to DC, somewhere around 300-500 Hz would probably be fine. I saw the codec is able to do 8kHZ, so I'm hoping the I2S routine method proposed by Frank should be plausible down to at least 8kHz, if not lower, which I could combine with a fixed 16pt interpolator (but ideally not).

I haven't looked at how feasible fast changes in sample rate are with that method though. You may well be right that say pitch modulation is not going to work well using that approach. But I'll give it a go.

The alternatives I'm looking at are to wire up a different I2S DAC, either on the Teensy, or possibly even an arduino as the code doesn't really need the speed of a teensy.
 
I tried a few things but no luck still getting Frank's code snippet to work in my code as intended below about Fs=9.9kHz with the rev D codec. And it seems that sample-rate modulation even at modest speeds produces clicks, so I probably will need to find an alternative solution anyway.
 
On a related note, I would be grateful if anyone has suggestions for alternative platforms for me to consider (maybe earlier Teensy boards have simple dac shield options?).

My requirements are not demanding (I thought). I am looking for an Arduino/teensy platform with an option to have continuously variable output rates to a dac that is say at least 10bits, and can accommodate rates from say 500hz to about 22kHz. I hadn't looked into I2C options before, and wrongly assumed the MCP4725 shield would be plenty fast for modest audio like that. But it's appallingly slow as it turns out...

I'm considering an Arduino due, but only as a last resort as it has a lot of features I wouldn't be using.
 
Yup, that code snipped is not intended to be used with constantly changing samplerates or even modulating. Its meant to switch to a fixed frequency.
 
The 3.x Teensys have DACs

Thanks Frank, yes I discovered that last night and have ordered a few 3.2s. The 3.x look like my most promising option so far because, if my understanding is correct, I can just use an 'analogwrite' to the dac whenever I like.

Should we be expecting the teensy 4.x to have buillt in dacs at some point?
 
So it's a year down the track and I have my project running very nicely on a Teensy 3.6. But the board is now out of stock everywhere, so I'm looking or possible alternatives. And the extra CPU power of a Teensy 4.x would be nice anyway. So I'm again soliciting for suggestions for how to add a DAC to a Teensy 4.x that I can write to at any time, rather than at a steady sampling rate (i.e. non i2s). Thanks.

p.s. I'm after just 10bits, but in stereo, and up to about 40-50kHz.
 
The question is, what your exact definition of "simple" is. Best is to take a look at the SGTL-5000 datasheet - and the audio shield schematic - if there are open questions.

Easiest is to use the Audio Library.
You can change it's frequency: https://github.com/TeensyUser/doc/w...-to-change-the-i2s-frequency-on-t4x--micromod
I need to use I2S DAC (pcm5102) together with Teensy 4.1 board and set Fs=8KHz. It looks like this snippet might be helpful. However I'd like to understand the details. Can someone explain magic numbers used in this snippet ( https://github.com/TeensyUser/doc/w...-to-change-the-i2s-frequency-on-t4x--micromod) and where the function set_audioClock(...) is documented? I've tried this code with Fs=8KHz and it does not work. I think I know the reason and have a fix, however I need to understand code better to be sure my fix is correct. Any help highly appreciated. Thanks!
 
What exact "magic numbers" do you mean?
You may want to share your reasoning and your code fix for forum members to elaborate on.

Paul
 
#include <utility/imxrt_hw.h>

[...]

void setI2SFreq(int freq) {
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
double C = ((double)freq * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2, true);
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1)
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1);
}

As you can see there are many numbers hardcoded in this code snippet: 24000000, 27, 256, 10000. What is their meaning? Also where is this function - set_audioClock(c0, c1, c2, true); - documented and what its parameters mean? How the author guessed correct spelling of the MCU's register name (CCM_CS1CDR) and its fields? Where these names are defined? Are you the author and can you shed some light on all these things? Thanks!

PS. I have found that this code works for 16KHz sampling rate but not for 8KHz. However if I change n1 to 5 (instead of setting it to 4) it magically starts working for 8KHz as well. I have found this is because when freq=8000 and n1=4 , then the n2 value does not fit CCM_CS1CDR_SAI1_CLK_PODF field, but for n1=5 - it does! However I am cautious to recommend this fix to people who need it to work for Fs < 10KHz (which is apparently current function limitation) because I am not sure about potential side effects it may have. For ex. this thoughtful comment in the code says that "if n1=4 then (n1*n2) is multiple of 4" ... Why is it important to be "multiple of 4"? What if its gonna be "multiple of 5 or 6 or whatever" instead?
 
I think I've found answers to most of the questions I had: 27 is min value of PLL4 (Audio PLL) loop divider ; 24000000 is
PLL4 reference clock in Hz; 10000 is a chosen CCM_ANALOG_PLL_AUDIO_DENOM register setting. The resulting PLL4 output frequency is 655358400Hz (according to spec it should be between (650MHz and 1.3GHz).
The remaining question I have is why PLL4 output frequency should be set to this value?
BTW it seems like there is a typo in set_audioClock() function comment. Post divider is set, it seems, to 1, not 1/4. I think the comment should read:
CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 3: reserved; 2: 1; 1: 1/2; 0: 1/4
Please double check and fix if you agree.
 
BTW it seems like there is a typo in set_audioClock() function comment. Post divider is set, it seems, to 1, not 1/4. I think the comment should read:
CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 3: reserved; 2: 1; 1: 1/2; 0: 1/4

The comment "3: reserved; 2: 1; 1: 1/2; 0: 1/4" seems to match the reference manual info on page 1105.

1733070080256.png


Indeed it looks like the comment in imxrt_hw.cpp is incorrect.
 
Back
Top