sgtl5000 sample rate capabilities

AlexL

Member
Hello,
currently Audio lib supports only 44.1kHz sample rate (hard coded). However sometimes it is advantageous to use other ones as well (for ex. 22.05kHz or 11.025kHz). It looks like the chip has support for that in its CHIP_CLK_CTRL register (SGTL5000 Datasheet Rev. 7, 1/2022, Table 18 p32). For ex. RATE_MODE field can be set to 1, 2 or 3 which corresponds to setting sample rate Fs to 1/2, 1/4 or 1/6 of SYS_FS field setting. In Audio library SYS_FS = 1 (44.1kHz) so it seems like Fs=22.05 or 11.025kHz are easily possible. However MCLK_FREQ field setting depends upon some value called "Fs" which is not defined anywhere in the document. Does anyone know what "Fs" means when RATE_MODE is not set to 0? And do I need to change input clocks (MCLK, BCLK, LRCLK) from their current settings (11.29MHz, 1.41MHz and 44.1kHz correspondingly) if I choose RATE_MODE=1,2 or 3? Thanks!
 
The Audiolib sample rate is controlled by a #define in AudioStream.h - you can edit this locally in your install.
Code:
#ifndef AUDIO_SAMPLE_RATE_EXACT
#define AUDIO_SAMPLE_RATE_EXACT 44100.0f
#endif
The i2s audio objects between them sort out the chip setup from that value I believe.

Fs = frequency, sampling. Like Rds means resistance, drain-source...
 
The Audiolib sample rate is controlled by a #define in AudioStream.h - you can edit this locally in your install.
Code:
#ifndef AUDIO_SAMPLE_RATE_EXACT
#define AUDIO_SAMPLE_RATE_EXACT 44100.0f
#endif
The i2s audio objects between them sort out the chip setup from that value I believe.

Fs = frequency, sampling. Like Rds means resistance, drain-source...
well, I set
#define AUDIO_SAMPLE_RATE_EXACT 11029.41176
however the tone it produces is much higher pitch (4x, I guess).
(I use Part_1_02_Hardware_Test.ino from Teensy Audio library Examples)

What else is needed to make it work?

PS I understand what Fs generally means. I ask about what it means in the datasheet when RATE_MODE != 0
 
This code will allow you to set the sample rate:
Code:
void setI2SFreq(int freq1)    // merci Franck B.
{
  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (freq1 * 256 * n1);
  double C = ((double)freq1 * 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)                                // &0x07
       | CCM_CS1CDR_SAI1_CLK_PODF(n2-1);                               // &0x3f 
// Serial.printf("SetI2SFreq(%d)\n",freq1);
}

I believe that it will permit at least the following rates and maybe more:
8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000

Pete
 
Thank you very much, but it looks like it can compile only for Teensy4. I need it for Teensy3.2. Is there one? Thanks!
 
Also, excuse me my curiosity but isn't it required to modify sgtl5000 settings as well to support whole bunch of sampling rates that you mention? Thanks!
 
First I must admit it's been several years since I've really looked at the SGTL5000 registers. We've been using this chip with Teensy for at least 11 years. All the code supporting SGTL5000 is very "mature".

Like most codec chips, the explicit clock settings (probably) are only meaningful when the chip runs in master mode (outputs BCLK and LRCLK). But with Teensy we always use SGTL5000 in slave mode (BCLK and LRCLK are inputs) because Teensy transmits those signals. Teensy controlling the sample rate clock is the key ingredient which allows you to use so many different ADC, DAC, codec chips chips together in almost any combination that can physically connect to the pins.

We do have slave mode capability on Teensy. But unlike so many other codec chips, SGTL5000 lacks a crystal oscillator for its master mode. So you would need to disconnect its MCLK from Teensy and supply your own MCLK from an oscillator, if you wanted to use SGTL5000 in master mode and run Teensy in slave mode.

Again, let me give the caveat that I really haven't done register level programming with SGTL5000 for many years. I could be wrong. I did review the datasheet for a few minutes to write this message. I really can't put more time into this. Officially we really only support use of SGTL5000 by the existing audio library code. Unofficially, many of us here on this forum love thinking about alternate ways to do things. But if you really want to dive into configuring the chip in a very different way, you're mostly going to have to experiment because the SGTL5000 datasheet is somewhat lacking in really clear explanation of how some parts of it truly works. It doesn't really say which bits are ignored with slave mode, whether they do anything at all (perhaps modify digital filters which are a fundamental part of all sigma-delta converters) in slave mode. In fact, I don't recall the datasheet even saying whether the internal architecture really is sigma-delta (but it really must be).

Hopefully this comment about master vs slave mode helps at least a little, even though I really can't answer directly about the meaning of specific registers and how they do or don't get used. As you explore further, please try to keep in mind I2S devices running in slave mode really have no control over their sample rate. It's dictated by LRCLK which comes from the I2S master.
 
Last edited:
looks like it can compile only for Teensy4. I need it for Teensy3.2

Teensy 3.2 lacks a PLL which can create any audio clock frequency. The simpler hardware has a clock multiplier and divider, but they're not PLL so multiplying can be quite dodgy. This simple hardware (documented in the reference manual on page 1321 and also pages 165-166) can use a couple different clocks as its input. To edit the code for a different sample rate, you need to find a combination of MCLK_MULT, MCLK_DIV and MCLK_SRC which meet you needs. To edit this stuff for I2S, look in output_i2s.cpp starting at line 286. Best to watch the results on a scope while testing, because some combinations can result in clock signals that stray from 50% duty cycle. Some audio chips can use such clocks, and some say they can't but actually can (perhaps without meeting their full specs), but some can't work at all.
 
Paul,
first of all thanks for your reply.
Here are my comments.
1. Let me clarify just in case. I have understood from reading your code that you're using codec in slave mode only. I do not have intention to change that so there is no need to address this issue.
2. I have verified using scope that all 3 clocks generated by my Teensy3.2 are correct. I've also observed that if I modify MCLK_MULT, MCLK_DIV according to my needs, uC clock multiplier works fine generating correct clocks.
3. All my questions are about codec (sgtl5000) part. I spent a little bit more time reading codec datasheet and I 100% agree with you that it is written in a ... eh-h ... well, a little "convoluted" way (though "convolution" itself is definitely more tractable at least since Monsieur Duhamel's times :)).
The datasheet revision I've found is Rev. 7; it dates back to 1/2022. Which means that until recently they continue to update it again and again. I am surprised however that in 11 years that you closely work with them they were not able to clarify basic functionalities of the chip, like for ex. some of the register usage and chip's functionalities that those registers control.
I understand that for some reason it was not your goal to provide flexibility of choosing sampling rate in your Audio library (or may be it is other way around - you did not implement it because they fail to provide enough info about the chip!). However I do not see how they can claim they can support whole bunch of Fs they mention in Table 8 of their datasheet (8, 11.025, 12, 16, 22.05, 24, 32, 44.1, 48, 96 kHz) without being able to use RATE_MODE > 0? So from that I conclude that RATE_MODE functionality should work OK in the chip but the question remains as to how to use it.
Can anyone on this forum explain what RATE_MODE field of CHIP_CLK_CTRL register actually controls (in the chip's block diagram) and how to use it? Like for ex., is it required to change any of the input clocks to the chip if I attempt to set RATE_MODE > 0? Thank you!
 
I am surprised however that in 11 years that you closely work with them they were not able to clarify basic functionalities of the chip, like for ex. some of the register usage and chip's functionalities that those registers control.

I have never asked NXP (or Freescale before NXP acquired them) any technical questions about the SGTL5000 codec.

Since we started using the Freescale Kinetis in 2012, pretty much all communication has been of a business nature, not technical stuff. Virtually everything I know of their chips has been learned by reading their documentation and performing experiments to fill in the gaps.


So from that I conclude that RATE_MODE functionality should work OK in the chip but the question remains as to how to use it.

Maybe I didn't say this clearly enough. I'm not 100% certain, but my hunch is RATE_MODE and SYS_FS have no effect at all when the chip runs in I2S slave mode.
 
Sorry, posted the wrong one.
Code:
int setI2SFreq(int freq)
{
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } tmclk;

  const int numfreqs = 14;
  const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int)44117.64706 , 48000, 88200, (int)44117.64706 * 2, 96000, 176400, (int)44117.64706 * 4, 192000};

#if (F_PLL==16000000)
  const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
  const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
  const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
  const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
  const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==168000000)
  const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
#elif (F_PLL==180000000)
  const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
  const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
  const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif

  for (int f = 0; f < numfreqs; f++) {
    if ( freq == samplefreqs[f] ) {
      while (I2S0_MCR & I2S_MCR_DUF) ;
      I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
      return round(((float)F_PLL / 256.0) * clkArr[f].mult / clkArr[f].div); //return real freq
    }
  }
  return 0;
}
This is a slightly modified version of one from Frank B. I've forgotten what I did to modify it from the original but note that is returns zero if it fails to set a frequency and it returns an integer frequency if it succeeds.

Pete
 
Sorry, posted the wrong one.
Code:
int setI2SFreq(int freq)
{
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } tmclk;

  const int numfreqs = 14;
  const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int)44117.64706 , 48000, 88200, (int)44117.64706 * 2, 96000, 176400, (int)44117.64706 * 4, 192000};

#if (F_PLL==16000000)
  const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
  const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
  const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
  const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
  const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==168000000)
  const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
#elif (F_PLL==180000000)
  const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
  const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
  const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif

  for (int f = 0; f < numfreqs; f++) {
    if ( freq == samplefreqs[f] ) {
      while (I2S0_MCR & I2S_MCR_DUF) ;
      I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
      return round(((float)F_PLL / 256.0) * clkArr[f].mult / clkArr[f].div); //return real freq
    }
  }
  return 0;
}
This is a slightly modified version of one from Frank B. I've forgotten what I did to modify it from the original but note that is returns zero if it fails to set a frequency and it returns an integer frequency if it succeeds.

Pete
Thank you very much for this code. Unfortunately it may not work for Fs I am interested in. For ex. if we assume F_PLL=96MHz (as in Teensy3.2) your code gives:
Fs=8000Hz {8, 375} -> MCLK = 96MHz*8/375 = 2,048,000Hz
Fs=11025Hz {73, 2483}->MCLK = 96MHz*73/2483 = 2,822,392Hz
As you can see both are well below 8MHz which is the minimum supported by sgtl5000 (according to datasheet).
 
I have never asked NXP (or Freescale before NXP acquired them) any technical questions about the SGTL5000 codec.

Since we started using the Freescale Kinetis in 2012, pretty much all communication has been of a business nature, not technical stuff. Virtually everything I know of their chips has been learned by reading their documentation and performing experiments to fill in the gaps.




Maybe I didn't say this clearly enough. I'm not 100% certain, but my hunch is RATE_MODE and SYS_FS have no effect at all when the chip runs in I2S slave mode.
Even if you're right about RATE_MODE and SYS_FS, the question still remains: what these setting do in the master mode? BTW it looks like you have implemented Master mode for Teensy4.x, correct? So maybe we can be in a better shape with Teensy4.x? :)
 
You may have a look at https://github.com/WMXZ-EU/test_sgtl5000
where I did these test s7 years ago.
Thank you for the link. In your code you address sample rates 32kHz and above. I have no issues with those sample rates. I tried to set Fs=8000Hz and your function returns mclk=2,051,282Hz which is below minimum supported by sgtl5000 (again, according to their datasheet, which - as Paul says - may not be accurate). Have you tried Fs < 32kHz yourself? Thanks!
 
I have used that code to sample at 8000Hz and it worked. I've just tried it on a T3.6 to pass the audio from line-in through to the headphones.

Pete
P.S. I think that should work on a T3.2?
 
I have used that code to sample at 8000Hz and it worked. I've just tried it on a T3.6 to pass the audio from line-in through to the headphones.

Pete
P.S. I think that should work on a T3.2?
Thank you very much, Pete. Miraculously it works on my 3.2 board!
But man, honestly, I do not understand, why... I measure mclk clock and it is indeed 2MHz. Their datasheet must be totally screwed up!
 
I don't understand the grimy details of that code either. I just took what @FrankB provided and used it.
Glad that it worked for you.

Pete
 
I don't understand the grimy details of that code either. I just took what @FrankB provided and used it.
Glad that it worked for you.

Pete
Pete, again many thanks to you and everyone participated in this thread! I will try to get explanations from NXP and report here if they're helpful
 
I'm really curious to hear of how you contact NXP and what answers they give?

If it's just a question posted on their community forum, please share the link so anyone who later finds this thread by search can also find the related messages on NXP's site.
 
Sure, but so far they're not so helpful...
Here is the link:
 
Hello everyone,
as promised I come back to share my experiences with NXP on that chip.
I've tried to squeeze out any useful information from NXP using both their forum as well as direct dialogue with supporting AEs. After about 3 weeks of messaging back and forth I have to declare complete fiasco. I was not able to locate people there who know this product. Here is my guess, why.
This chip has been developed by Freescale Inc. and reached market in 2008. In 2015 Freescale has been acquired by NXP and this product became orphaned. I guess all retention packages are gone by now (after 10 years!) :) and nobody left in the company who can understand the design.

Sorry for bad news!
 
It is not clear from sgtl5000 datasheet what RATE_MODE actually controls inside the chip (they simply do not disclose it!). Suppose it controls front end interpolating/decimating filter only to produce the 1/2,1/4 or 1/6Fs "effective" sampling rate samples while assuming the same (i.e. Fs) LRCLK frequency. Then the acquired samples should be repeated in the MCU's buffer 2, 4 or 6 times. Is my understanding correct? Then MCU I2S DMA engine should support samples decimation. But does it? If my "decryption" is correct, why do they never mention that in their document?! Otherwise I do not see how they can support their claim of supporting lower sample rates (like for ex. 8000Hz etc.) given their own input clock restrictions. Is there other possible option that I miss? Any thoughts?
 
Back
Top