Forum Rule: Always post complete source code & details to reproduce any issue!
Page 2 of 3 FirstFirst 1 2 3 LastLast
Results 26 to 50 of 74

Thread: asynchronous board synching

  1. #26
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    He wants to add decimals != zero
    i.e. 44100.3456789

    I don't how how that can help (Edit: Even if he changes the code not to use an int). Esp. with the temperature sesivitiy of crystals. two crystals.
    And how exactly he transfers all the fast i2s signals over the air.
    synchronously.

    all too magically for me I'm out.

    have fun i hope you can get it to work. pls. report back!

  2. #27
    Senior Member
    Join Date
    May 2015
    Location
    USA
    Posts
    835
    I see Frank's point. Not sure what I did wrong when I tested it and got a better match between my PC and teensy rate. Perhaps a misleading random change in one of the clocks.

    Looks like it would be easy to change the code to use the decimal portion.

  3. #28
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    It *can* work, if you just don't send I2s over the air.
    Maybe raw data. With sync signals added. Maybe WLAN. Or, best, bluetooth. Edit: something that is fast enough and involves error-correction.
    Even then, changing the #define does'nt work and something more dynamic, and buffering will be needed.
    So, reading the manual makes sense, too, to find a way to keep the dropouts minimal when changing the pll. maybe it works without? I did not read the manual from this point of view
    , and don't plan to, and I can not answer this.
    Last edited by Frank B; 11-26-2020 at 08:34 PM. Reason: wording

  4. #29
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,485
    Quote Originally Posted by Frank B View Post
    The problem is, as Pauls said, you can't modify that at runtime, as the PLL needs to be restarted. This will cause short breaks.
    Has anyone tried anyway? Does the PLL actually ignore writes to its registers while it's turned on, or is that just what we're not supposed to do?

    Or can the change turning the PLL off and back on again be done so quickly that the short breaks are imperceptible to human ears?

  5. #30
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    No, - had no reason to try it

    Maybe damo1976 can tell us wether it works or not, as he works on that anyway.
    I'd be interested to hear the result.

  6. #31
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    Would be interesting for USB-Audio..

  7. #32
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,004
    Quote Originally Posted by PaulStoffregen View Post
    Has anyone tried anyway? Does the PLL actually ignore writes to its registers while it's turned on, or is that just what we're not supposed to do?

    Or can the change turning the PLL off and back on again be done so quickly that the short breaks are imperceptible to human ears?
    Technically I did that when testing SGTL5000 at different sampling speeds
    I first configured the I2S to AUDIO_SAMPLE_RATE_EXACT (44100 Hz) and then I changed the sampling frequency.
    Code:
     void I2S_start(void)
     {
      I2S1_RCSR |= (I2S_RCSR_RE | I2S_RCSR_BCE); 
      I2S1_TCSR |= (I2S_TCSR_TE | I2S_TCSR_BCE); 
     }
     void I2S_stop(void)
     {
       // stop I2S 
      I2S1_RCSR &= ~(I2S_RCSR_RE | I2S_RCSR_BCE); 
      I2S1_TCSR &= ~(I2S_TCSR_TE | I2S_TCSR_BCE); 
     }
    
     void setAudioFrequency(int fs)
     {
       	// 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) / (fs * 256 * n1);
    
    	double C = ((double)fs * 256 * n1 * n2) / 24000000;
    	int c0 = C;
    	int c2 = 10000;
    	int c1 = C * c2 - (c0 * c2);
    	set_audioClock(c0, c1, c2, 1);
    
      	// clear SAI1_CLK register locations
    	CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
    		   | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
    	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
    	// Select MCLK
    	IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1
    		& ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
    		| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));
    
     }
    
     void I2S_modification(uint32_t fsamp, uint16_t nbits) 
    {
      I2S_stop();
      // modify sampling frequency 
      CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
    
      int fs = fsamp;
      setAudioFrequency(fs);
    
      // restart I2S 
      I2S_start();
    }
    No, I did not listen to audio, as this code is executed during setup after standard I2S configuration and I only use ADC

  8. #33
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    Code:
    void setI2SFreq(int freq) {
    #if defined(T4)
      // 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) / (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) // &0x07
           | CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f 
    #else
    
      unsigned tcr5 = I2S0_TCR5;
      unsigned word0width = ((tcr5 >> 24) & 0x1f) + 1;
      unsigned wordnwidth = ((tcr5 >> 16) & 0x1f) + 1;
      unsigned framesize = ((I2S0_TCR4 >> 16) & 0x0f) + 1;
      unsigned nbits = word0width + wordnwidth * (framesize - 1 );
      unsigned tcr2div = I2S0_TCR2 & 0xff; //bitclockdiv
      uint32_t MDR = I2S_dividers(freq, nbits, tcr2div);
      if (MDR > 0) {
        while (I2S0_MCR & I2S_MCR_DUF) {
          ;
        }
        I2S0_MDR = MDR;
      }
    
    ////////////////////////////////////////////////////////////////////////////////  
    /*  typedef struct {
        uint8_t mult;
        uint16_t div;
      } tmclk;
    
      const int numfreqs = 20;
      //  const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int)44117.64706 , 48000, 88200, (int)44117.64706 * 2, 96000, 100000, 176400, (int)44117.64706 * 4, 192000};
      const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int)44117.64706 , 48000, 50223, 88200, (int)44117.64706 * 2,
                                          96000, 100000, 100466, 176400, (int)44117.64706 * 4, 192000, 234375, 281000, 352800
                                        };
    
    #if (F_PLL==180000000)
      //{ 8000, 11025, 16000, 22050, 32000, 44100, (int)44117.64706 , 48000,
      const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875},
        // 50223, 88200, (int)44117.64706 * 2, 96000, 100466, 176400, (int)44117.64706 * 4, 192000, 234375, 281000, 352800};
        {1, 14}, {107, 853}, {32, 255}, {219, 1604}, {224, 1575}, {1, 7}, {214, 853}, {64, 255}, {219, 802}, {1, 3}, {2, 5} , {1, 2}
      };
    /*#elif (F_PLL==192000000)
      const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125},
                                  // { 8000,        11025,    16000,    22050,      32000,      44100,    44117.64706 , 48000,
                                      {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
                                  //     88200, 44117.64706 * 2, 96000, 176400, 44117.64706 * 4, 192000};
    #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;
        }
      }
    ////////////////////////////////////////////////////////////////////////////////////
    */
    
      
    #endif
    Just taken from dd4wh's SDR project.

  9. #34
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,485
    NXP lied to us!! You can change the PLL while it's running!

    Ok, I tried it just now, using this:

    Code:
    #include <Audio.h>
    
    AudioSynthWaveform       waveform1;      //xy=171,84
    AudioOutputI2S           i2s1;           //xy=360,98
    AudioConnection          patchCord1(waveform1, 0, i2s1, 0);
    AudioConnection          patchCord3(waveform1, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=239,232
    
    uint32_t num, denom;
    
    void setup() {
      AudioMemory(10);
    
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.8); // caution: very loud - use oscilloscope only!
    
      waveform1.frequency(440);
      waveform1.amplitude(1.0);
      waveform1.begin(WAVEFORM_SINE);
    
      while (!Serial) ; // wait for Arduino Serial Monitor
      num = CCM_ANALOG_PLL_AUDIO_NUM;
      denom = CCM_ANALOG_PLL_AUDIO_DENOM;
      Serial.printf("PLL: num=%u, denom=%u\n", num, denom);
    }
    
    void loop() {
      CCM_ANALOG_PLL_AUDIO_NUM = num;
      delay(1000);
      CCM_ANALOG_PLL_AUDIO_NUM = num + 4000;
      delay(1000);
    }
    Here's what my oscilloscope sees on the LRCLK pin. The sample rate does change from 44100 to 44725, just be writing to CCM_ANALOG_PLL_AUDIO_NUM.


  10. #35
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    There are no audible artifacts?
    So let's use it for USBAudio

  11. #36
    Senior Member
    Join Date
    May 2015
    Location
    USA
    Posts
    835
    Nice! Kudos for not trusting the manual.

    Add something like PTP and you can get sync, even over wireless.

  12. #37
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,485
    I did some tests to check for how fine we can adjust the frequency. My scope's frequency counter isn't sensitive enough, so I had to connect it to my real frequency counter (BK Precision 1823A) and change the delays to 5 seconds.

    Using the settings the audio library configures, changing the numerator by 1 alters the LRCLK frequency by 0.157 Hz.

    I tried mutliplying both numerator and denominator by 1000, since the audio library is using only such a tiny fraction of the numerical range. I could not measure the change in frequency by changing the numerator by only 1 step. Changing the numerator by 20 was smallest increment I could measure, and even then it's a change approximately as much as the short term fluctuations.

    This is the smallest sort of effect I could see. Indeed the frequency can be adjusted in extremely fine increments with small changes in the numerator when a larger denominator is used.

    Click image for larger version. 

Name:	DSC_1115_web.jpg 
Views:	5 
Size:	59.9 KB 
ID:	22645

    Click image for larger version. 

Name:	DSC_1123_web.jpg 
Views:	6 
Size:	55.4 KB 
ID:	22646

    I want to emphasize the frequency naturally changes almost this much every few readings, and over longer time is drifts much more. This really is the smallest change which can be reliably observed (at least with my test gear) with quite a lot of careful observation to pick apart changes due to the code versus the nature amount of noise / variation.

    This is the code I ran for that test.

    Code:
    #include <Audio.h>
    
    AudioSynthWaveform       waveform1;      //xy=171,84
    AudioOutputI2S           i2s1;           //xy=360,98
    AudioConnection          patchCord1(waveform1, 0, i2s1, 0);
    AudioConnection          patchCord3(waveform1, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=239,232
    
    uint32_t num, denom;
    
    void setup() {
      pinMode(13, OUTPUT);
      AudioMemory(10);
    
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.8); // caution: very loud - use oscilloscope only!
    
      waveform1.frequency(440);
      waveform1.amplitude(1.0);
      waveform1.begin(WAVEFORM_SINE);
    
      while (!Serial) ; // wait for Arduino Serial Monitor
      num = CCM_ANALOG_PLL_AUDIO_NUM;
      denom = CCM_ANALOG_PLL_AUDIO_DENOM;
      Serial.printf("PLL: num=%u, denom=%u\n", num, denom);
    
      num = num * 1000;
      denom = denom * 1000;
      num = num + 3302; // tweak it up to close to 44100 (according to my frequency counter)
      CCM_ANALOG_PLL_AUDIO_NUM = num;
      CCM_ANALOG_PLL_AUDIO_DENOM = denom;
    }
    
    void loop() {
      digitalWrite(13, LOW);
      CCM_ANALOG_PLL_AUDIO_NUM = num;
      delay(10000);
      digitalWrite(13, HIGH);
      CCM_ANALOG_PLL_AUDIO_NUM = num + 20;
      delay(10000);
    }
    Last edited by PaulStoffregen; 11-26-2020 at 10:36 PM. Reason: fix pictures attachments

  13. #38
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,485
    Quote Originally Posted by Frank B View Post
    There are no audible artifacts?
    I did a quick listening test. Sine waves aren't nice for listening.

    On the first test, changing the numerator from 2240 to 6240 while the denominator is 10000, indeed there is a strange noise that seems like some sort of artifact rather than just the sine wave changing frequency.

    So maybe NXP was right, maybe we're not meant to alter the PLL. More testing and listening needed....

    I'm going to stop fiddling / abusing PLL4. I want to get back to fixing some bugs with the serial monitor, in prep for beta6. I might also play with adding larger flash chips support in LittleFS today. Hopefully you & others can explore what realistically can & can't be done by tweaking PLL4.

  14. #39
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    Over the weekend, if I find some time, maybe.

  15. #40
    Senior Member
    Join Date
    May 2015
    Location
    USA
    Posts
    835
    I toggled CCM_ANALOG_PLL_AUDIO_NUM between 2240 and 2241 every second with no audible effect. This is with SPDIF output to an external DAC.

    For most use cases, only small changes are needed - like < 50 ppm. I assume that what I did is a large 446 ppm change. Reducing this with a larger denominator makes sense. Maybe also with dithering.

    I haven't measured yet. Previously I would compare, over a longish period, the PC USB clock to what the teensy was using for audio rate. But as I now know, this is flawed :-). But not 446 ppm flawed.

  16. #41
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,485
    Quote Originally Posted by jonr View Post
    Reduce this with a larger denominator?
    Yes, use a larger denominator.

    The hardware supports a 30 bit number. Currently the audio library is loading the number 10000, using only 14 of those 30 bits!

  17. #42
    Senior Member
    Join Date
    May 2015
    Location
    USA
    Posts
    835
    Sounds good. Change the clock setting code to use a large denominator and floating point. Then anyone wanting to tweak rate can a) change AUDIO_SAMPLE_RATE_EXACT at compile time or b) change CCM_ANALOG_PLL_AUDIO_NUM during run-time.

    Not having a frequency counter, I might have the PC (over longish periods, conditioned to 0.000 ppm of GPS time via NTP) send something via serial/USB to the teensy for comparison.

  18. #43
    Brilliant. I'll try that myself. But I did notice that changing AUDIO_SAMPLE_RATE_EXACT to something like 48000.1 had an effect but 48000.05 did not so... I'm still trying to work out how that floating point is used in the audio code when the clock function strips those decimals..

  19. #44
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    I'd like to suggest to add a function _setI2Sfreq(double or float freq) to utility/imxrt_hw.cpp.
    Then, we can remove all the calculations from all the I2S-using codes and just call the new function: _setI2Sfreq(AUDIO_SAMPLE_RATE_EXACT). It's centralized then, and can be used by the user, too.

  20. #45
    Quote Originally Posted by Frank B View Post
    there must be some magic that restores the decimals from an int value.. and can adjust the pll to exactly these deleted sub-hz..
    AGREED!!
    Thats why I'm asking

  21. #46
    +1

    I'm going to have a look now

  22. #47
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    There is no magic going on .. decimals are ignored
    They are just not needed here, because it was intended for std sampling-freqs.

  23. #48
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    a little excel:
    Attached Files Attached Files

  24. #49
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,658
    Just a wild guess.. maybe 1073739000 (0x3FFF F4F8) is a good value. It's a multiple of 24 and 1000, thus allows 0%error for all std frequencies and maybe the PLL can lock a little bit faster due to the multiple of 24(?!)
    Does this make any sense? I have no Idea about the inner workings of digital PLLs...

  25. #50
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,485
    NXP doesn't tell us how they implemented the PLL, but it's a pretty safe bet the phase comparator is a digital circuit like a R-S latch, which drives an analog amplifier / accumulator, which controls an analog oscillator. The feedback path is almost certainly digital counters. How they implement a 30 bit resolution ratio is a trick I would like to understand someday...

    It would take extra design work for lock detection, which is probably built into the phase comparator, to block writes to the registers. The tools used to design these sort of modern chips automate very complex design when everything is synchronous to the same clock domain, but things are often not so easy when asynchronous signals have to be synchronized. That's why I've long suspected their documentation about not being able to write to the registers while it's running was just a usage guideline rather than a hard technical restriction.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •