setI2SFreq fails on T4.x when sampling frequency is 8000Hz

el_supremo

Well-known member
setI2SFreq fails on a T4.x when the required sampling frequency is 8kHz. The reason is that it calculates a value for n2 of 80 but this value must fit in a 6-bit field and therefore must not exceed 63.
I don't know how to fix this properly but I reasoned that if the value of n1 is changed to 8, the resulting value of n2 will fit into 6 bits. And it worked. The change I made does not affect any higher sampling rate so this should be "safe" to use :)
Here's my modified version of setI2SFreq which handles T3.6 as well as T4.x
Code:
#include <Arduino.h>
#include <Audio.h>
#include "setI2SFreq.h"

unsigned int sample_rate_real;

#ifdef __IMXRT1062__
// For set_audioClock on T4.x
#include <utility/imxrt_hw.h>
// Teensy 4.0, 4.1
int setI2SFreq(int freq) {
int n1;
  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  // Fudge to handle 8kHz - El Supremo
  if(freq > 8000) {
    n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  } else {
    n1 = 8;
  }
  int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
  if(n2 > 63) {
    // n2 must fit into a 6-bit field
    Serial.printf("ERROR: n2 exceeds 63 - %d\n",n2);
    return 0;
  }
  double C = ((double)freq * 256 * n1 * n2) / 24000000;
//  Serial.printf("%6d : n1 = %d, n2 = %d, C = %12.6f ",freq,n1,n2,C);
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);
//  Serial.printf("c0 = %d, c1 = %d, c2 = %d\n",c0,c1,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

//START//Added afterwards to make the SAI2 function at the desired frequency as well.

  CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
               | CCM_CS2CDR_SAI2_CLK_PRED(n1 - 1) // &0x07
               | CCM_CS2CDR_SAI2_CLK_PODF(n2 - 1); // &0x3f)
//END//Added afterwards to make the SAI2 function at the desired frequency as well.
  // For compatibility with my T3.6 version
  return freq;
}
#else
// Assume it is T3.6
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;
}
#endif

void set_sample_rate(int sr)
{
  switch(sr) {
  case SAMPLE_RATE_8K:
    sample_rate_real = 8000;
    break;
  case SAMPLE_RATE_11K:
    sample_rate_real = 11025;
    break;
  case SAMPLE_RATE_16K:
    sample_rate_real = 16000;
    break;
  case SAMPLE_RATE_22K:
    sample_rate_real = 22050;
    break;
  case SAMPLE_RATE_32K:
    sample_rate_real = 32000;
    break;
  case SAMPLE_RATE_44K:
    sample_rate_real = 44100;
    break;
  case SAMPLE_RATE_48K:
    sample_rate_real = 48000;
    break;
  case SAMPLE_RATE_88K:
    sample_rate_real = 88200;
    break;
  case SAMPLE_RATE_96K:
    sample_rate_real = 96000;
    break;
  case SAMPLE_RATE_176K:
    sample_rate_real = 176400;
    break;
  case SAMPLE_RATE_192K:
    sample_rate_real = 192000;
    break;
  }
  AudioNoInterrupts();
  sample_rate_real = setI2SFreq(sample_rate_real);
  if(sample_rate_real == 0) {
    Serial.printf("ERROR: failed to set sampling frequency\n");
    while(1);
  }
  delay(200); // this delay seems to be very essential !

  AudioInterrupts();
  delay(20);

} // END function set_sample_rate

Pete
 
Hi,

When working on our Batdetector based on Teensy ( https://forum.pjrc.com/threads/38988-Bat-detector) I had noticed for some time that replaying recordings at lower speeds was not always reliable.
It turned out that below a samplerate of 10K the original code from Frank isnt working. So Ive changed the section from

Code:
// Fudge to handle 8kHz - El Supremo
  if(freq > 8000) {
    n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  } else {
    n1=8;
}
to
Code:
// Handle samplerates below 10K different
  if(freq > 10000) {
    n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  } else {
    n1=8;
}
cheers
Cor
 
Back
Top