Here's the same for the internal DACs :
Code:void setDACFreq(int freq) { const unsigned config = PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT | PDB_SC_PDBIE | PDB_SC_DMAEN; PDB0_SC = 0; PDB0_IDLY = 1; PDB0_MOD = round((float)F_BUS / freq ) - 1; PDB0_SC = config | PDB_SC_LDOK; PDB0_SC = config | PDB_SC_SWTRIG; PDB0_CH0C1 = 0x0101; }
edit: Added one line
#ifndef AUDIO_SAMPLE_RATE_EXACT
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#define AUDIO_SAMPLE_RATE_EXACT 44117.64706 // 48 MHz / 1088, or 96 MHz * 2 / 17 / 256
#elif defined(__MKL26Z64__)
#define AUDIO_SAMPLE_RATE_EXACT 22058.82353 // 48 MHz / 2176, or 96 MHz * 1 / 17 / 256
#elif defined(__SAMD51__)
//#define AUDIO_CLKRATE (SystemCoreClock >> 6)
//#define AUDIO_PRESCALER TC_CTRLA_PRESCALER_DIV64
//#define AUDIO_SAMPLE_RATE_EXACT 22058.82353 // 120 MHz / 64 / 85
//#define AUDIO_TC_FREQ 22000
#define AUDIO_CLKRATE (VARIANT_GCLK2_FREQ >> 4)
#define AUDIO_PRESCALER TC_CTRLA_PRESCALER_DIV16
#define AUDIO_SAMPLE_RATE_EXACT 44014.085 // 100 MHz / 16 / 142
#define AUDIO_TC_FREQ 44100
#endif // SAMD51
#endif // AUDIO_SAMPLE_RATE_EXACT
#include <utility/imxrt_hw.h>
void setI2SFreq(int freq) { //Teensy 4
// 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
}
I don't know, why. Maybe try to disable the interrupts when switching the rate. I have never tried that - But I know of an application that sets 176khZ in one step, without problems.
//PLL:
int fs = fsamp;
int ovr = 2*(NCHAN_I2S*32);
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n0 = 26; // targeted PLL frequency (n0*24 MHz) n0>=27 && n0<54
int n1, n2;
do
{ n0++;
n1=0;
do
{ n1++;
n2 = 1 + (24'000'000 * n0) / (fs * ovr * n1);
} while ((n2>64) && (n1<=8));
} while ((n2>64 && n0<54));
Serial.printf("fs=%d, n1=%d, n2=%d, %d (>=27 && <54) ", fs, n1,n2,n1*n2*(fs/1000)*ovr/24000);
double C = ((double)fs * ovr * n1 * n2) / (24000000.0f);
Serial.printf(" C=%f\r\n",C);
int c0 = C;
int c2 = 10'000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2, true);
maybe this snippet helps
this relates the MCLK to the sampling frequency by specifying the number of channels (NCHAN_I2S) and word size (32).Code://PLL: int fs = fsamp; int ovr = 2*(NCHAN_I2S*32); // PLL between 27*24 = 648MHz und 54*24=1296MHz int n0 = 26; // targeted PLL frequency (n0*24 MHz) n0>=27 && n0<54 int n1, n2; do { n0++; n1=0; do { n1++; n2 = 1 + (24'000'000 * n0) / (fs * ovr * n1); } while ((n2>64) && (n1<=8)); } while ((n2>64 && n0<54)); Serial.printf("fs=%d, n1=%d, n2=%d, %d (>=27 && <54) ", fs, n1,n2,n1*n2*(fs/1000)*ovr/24000); double C = ((double)fs * ovr * n1 * n2) / (24000000.0f); Serial.printf(" C=%f\r\n",C); int c0 = C; int c2 = 10'000; int c1 = C * c2 - (c0 * c2); set_audioClock(c0, c1, c2, true);
obviously, the allowed ratio MCLK / Fs is given in ADC datasheet
relating sampling frequency(framesync) and Bitclock must also be done in SAI configuration
//MCLK =12.288Mhz, BCLK=1.024Mhz (64xLRCLK) and LRCLK = 16khz (2 Bytes/word)
#define FSAMP 16000
#define NCHAN_I2S 2
#define N_BYTES 2
#define N_BITS (8*N_BYTES)
#if N_BITS==16
typedef uint16_t data_t;
#elif N_BITS==32
typedef uint32_t data_t;
#endif
#define RCR2_DIV 12
#define MCLK_OVR (RCR2_DIV*2*(NCHAN_I2S*N_BITS))
//Bitclock = MasterClock/(2*RCR2_DIV)
//Framclock = Bitclock/(NCHAN_I2S*NBITS)
float mclk,fclk,bclk;
#define NSAMP 128
#define NBUF_I2S (NCHAN_I2S*NSAMP)
DMAMEM data_t tdm_rx_buffer[2*NBUF_I2S] __attribute__((aligned(32)));
//int32_t acq_rx_buffer[NBUF_I2S];
volatile uint32_t acq_count=0;
static void acq_isr(void);
#ifndef I2S_DMA_PRIO
// #define I2S_SAI_PRIO 4*16
#define I2S_DMA_PRIO 5*16
#endif
// ACQ setup functions
#include "DMAChannel.h"
DMAChannel dma;
#define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB
PROGMEM
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4
{
if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
| CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 0: 1/4; 1: 1/2; 2: 1/1
| CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
const int div_post_pll = 1; // other values: 2,4
CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS; //Disable Bypass
Serial.printf("PLL %f\r\n",24.0f*((float)nfact+(float)nmult/(float)ndiv));
}
void acq_init(int fsamp)
{
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing
if (I2S1_RCSR & I2S_RCSR_RE) return;
//PLL:
int fs = fsamp;
int ovr = MCLK_OVR;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n0 = 26; // targeted PLL frequency (n0*24 MHz) n0>=27 && n0<54
int n1, n2;
do
{ n0++;
n1=0;
do
{ n1++;
n2 = 1 + (24'000'000 * n0) / (fs * ovr * n1);
} while ((n2>64) && (n1<=8));
} while ((n2>64 && n0<54));
Serial.printf("fs=%d, n1=%d, n2=%d, %d (>=27 && <54) ", fs, n1,n2,n1*n2*(fs/1000)*ovr/24000);
double C = ((double)fs * ovr * n1 * n2) / (24000000.0f);
Serial.printf(" C=%f\r\n",C);
int c0 = C;
int c2 = 10'000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2, true);
mclk=C*24000000.0/n1/n2;
// 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 // <8
| CCM_CS1CDR_SAI1_CLK_PODF((n2-1)); // &0x3f // <64
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)); //Select MCLK
//configure I2S
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(4);
I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
| I2S_RCR2_BCD | I2S_RCR2_DIV((RCR2_DIV-1));
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((NCHAN_I2S-1)) | I2S_RCR4_SYWD((NCHAN_I2S-1)) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((N_BITS-1)) | I2S_RCR5_W0W((N_BITS-1)) | I2S_RCR5_FBT((N_BITS-1));
// I2S pins
CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
// I2S data pins
CORE_PIN8_CONFIG = 3; //RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
// configure DMA
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE((N_BYTES/2)) | DMA_TCD_ATTR_DSIZE((N_BYTES/2));
dma.TCD->SADDR = &I2S1_RDR0;
dma.TCD->SOFF = 0;
dma.TCD->SLAST = 0;
dma.TCD->NBYTES_MLOFFNO = N_BYTES;
dma.TCD->DADDR = tdm_rx_buffer;
dma.TCD->DOFF = N_BYTES;
dma.TCD->CITER_ELINKNO = 2*NBUF_I2S;
dma.TCD->BITER_ELINKNO = dma.TCD->CITER_ELINKNO;
dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
// NVIC_SET_PRIORITY(DMAMUX_SOURCE_SAI1_RX,I2S_SAI_PRIO);
dma.enable();
//DMA_SERQ = dma.channel;
I2S1_RCSR = I2S_RCSR_FRDE | I2S_RCSR_FR;
dma.attachInterrupt(acq_isr, I2S_DMA_PRIO);
}
void acq_start(void)
{
//DMA_SERQ = dma.channel;
I2S1_RCSR |= (I2S_RCSR_RE | I2S_RCSR_BCE);
}
void acq_stop(void)
{
I2S1_RCSR &= ~(I2S_RCSR_RE | I2S_RCSR_BCE);
//DMA_CERQ = dma.channel;
}
void acq_isr(void)
{
uint32_t daddr;
data_t *src;
dma.clearInterrupt();
asm volatile("dsb");
daddr = (uint32_t) dma.destinationAddress();
if (daddr < ((uint32_t)tdm_rx_buffer + sizeof(tdm_rx_buffer) / 2)) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (data_t*)&tdm_rx_buffer[NBUF_I2S];
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = &tdm_rx_buffer[0];
}
#if IMXRT_CACHE_ENABLED >=1
arm_dcache_delete((void*)src, sizeof(tdm_rx_buffer) / 2);
#endif
//memcpy(acq_rx_buffer,src,sizeof(acq_rx_buffer));
// process data in acq_rx_buffer
acq_count++;
}
void setup() {
// put your setup code here, to run once:
while(!Serial);
acq_init(FSAMP);
bclk=mclk/(2*RCR2_DIV);
fclk=bclk/(NCHAN_I2S*N_BITS);
Serial.printf("MCLK = %.1f\n",mclk);
Serial.printf("FCLK = %.1f\n",fclk);
Serial.printf("BCLK = %.1f\n",bclk);
acq_start();
}
void loop() {
// put your main code here, to run repeatedly:
static uint32_t ic=0;
static uint32_t t0=0;
if(millis()-t0>1000)
{ t0=millis();
Serial.printf("%d %d \n",ic++,acq_count);
acq_count=0;
}
}
On a Teensy4?
Try this:
Code:#include <utility/imxrt_hw.h> void setI2SFreq(int freq) { //Teensy 4 // 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 }
Has anyone got this to work on a Teensy 4 with the Audio Shield?
I tried the code below and it compiled, but AUDIO_SAMPLE_RATE_EXACT is still showing up as 44100. I'm calling setI2SFreq(22050) in setup() after enabling the audio shield.
withteensy41.build.flags.defs=-D__IMXRT1062__ -DTEENSYDUINO=158
teensy41.build.flags.defs=-D__IMXRT1062__ -DTEENSYDUINO=158 -DAUDIO_SAMPLE_RATE_EXACT=22050
___________________________________________ CCM ANALOG _________________________________________________________________________________________
>CCM_CLK1_(P+N) NOTE:Teensy 4.1 NC Alt+0175¯
^
| >OSC_24000000Mhz (XTAL-n)
| ^
| |
| | PLL4_BYPASS_CLK_SCR
| | |¯\ Audio PPL4 PLL4_POST_DIV PLL4_BYPASS
| |->|<\\ |--------------------| |--------------| |¯\ PLL4_AUDIO_DIV
| | | \|--->|24000000*(47+(2/50)=|-- 1,128,960,000Mhz -->| /2 =|--564,480,000--->|<\\ |-------| |--------------| PPL4_MAIN_CLK
|-|->| / | |--------------------| |--------------| | \|-->|PLL4_EN|->| /1 |--564,480,000-->|
| | |_/ | /-->| / |-------| |--------------| |
| | |---------------------------------------------------------------------------/ |_/ |
| | |
______________________________________________________________________________________________________________________________________________ | __
|
______________________________________________ CCM ___________________________________________________________________________________________ | __
|
|<-------564,480,000Mhz-----------------------------------------------------------------------------------------------------------------------<|
|
|
| SAI1_CLK_SEL
| |¯\
| | \ SAI1_CLK_PRED SAI1_CLK_PODF LRCLK-----44,100kHz -4ch stereo
| --PLL3_PFD2_CLK-->| | |--------------| |--------------| SAI1_CLK_ROOT BCLK---5,644,800Mhz -8ch-16b.
|>---[COLOR="#00FF00"]PLL4_MAIN_CLK[/COLOR]-->|<--|--564,480,000->| /5 =|--112,896,000->| /5 =|--22,579,200MHz--> , MCLK--22,579,200Mhz
--PLL5_MAIN_CLK-->| | |--------------| |--------------|
Video PLL5 | /
|_/
___________________________________________________________________________________________________________________________________________________
// Selector for sai1 clock multiplexer
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK)) | CCM_CSCMR1_SAI1_CLK_SEL(2); // (0,1,2): PLL3_PFD2, PLL5, [COLOR="#00FF00"]PLL4[/COLOR]
___________________________________________ CCM ANALOG ________48,000kHz___________________________________________________________________________
>CCM_CLK1_(P+N) NOTE:Teensy 4.1 NC
^
| >OSC_24000000Mhz (XTAL-n)
| ^
| |
| | PLL4_BYPASS_CLK_SCR
| | |¯\ Audio PPL4 PLL4_POST_DIV PLL4_BYPASS
| |->|<\\ |---------------------| |--------------| |¯\ PLL4_AUDIO_DIV
| | | \|--->|24000000*(51+(10/50)=|-- 1,228,800,000Mhz -->| /2 =|--614,400,000--->|<\\ |-------| |--------------| PPL4_MAIN_CLK
|-|->| / | |---------------------| |--------------| | \|-->|PLL4_EN|->| /1 |--614,400,000-->|
| | |_/ | /--->| / |-------| |--------------| |
| | |---------------------------------------------------------------------------/ |_/ |
| | |
_______________________________________________________________________________________________________________________________________________ | __
|
______________________________________________ CCM ____________________________________________________________________________________________ | __
|
|<-------614,400,000Mhz------------------------------------------------------------------------------------------------------------------------<|
|
|
| SAI1_CLK_SEL
| |¯\
| | \ SAI1_CLK_PRED SAI1_CLK_PODF LRCLK-----48,000kHz -4ch stereo
| --PLL3_PFD2_CLK-->| | |--------------| |--------------| SAI1_CLK_ROOT BCLK---6,144,000Mhz -8ch-16b.
|>---[COLOR="#00FF00"]PLL4_MAIN_CLK[/COLOR]-->|<--|--614,400,000->| /5 =|--122,880,000->| /5 =|--24,576,000MHz--> , MCLK--24,576,000Mhz
--PLL5_MAIN_CLK-->| | |--------------| |--------------|
Video PLL5 | /
|_/
___________________________________________________________________________________________________________________________________________________
I need simple way to change the sample rate and master clock at runtime
___________________________________________ CCM ANALOG ________48,000kHz___________________________________________________________________________
>CCM_CLK1_(P+N) NOTE:Teensy 4.1 NC
^
| >OSC_24000000Mhz (XTAL-n)
| ^
| |
| | PLL4_BYPASS_CLK_SCR
| | |¯\ Audio PPL4 PLL4_POST_DIV PLL4_BYPASS
| |->|<\\ |---------------------| |--------------| |¯\ PLL4_AUDIO_DIV
| | | \|--->|24000000*(51+(10/50)=|-- 1,228,800,000Mhz -->| /2 =|--614,400,000--->|<\\ |-------| |--------------| PPL4_MAIN_CLK
|-|->| / | |---------------------| |--------------| | \|-->|PLL4_EN|->| /1 |--614,400,000-->|
| | |_/ | /--->| / |-------| |--------------| |
| | |---------------------------------------------------------------------------/ |_/ |
| | |
_______________________________________________________________________________________________________________________________________________ | __
|
______________________________________________ CCM ____________________________________________________________________________________________ | __
|
|<-------614,400,000Mhz------------------------------------------------------------------------------------------------------------------------<|
|
|
| SAI1_CLK_SEL
| |¯\
| | \ SAI1_CLK_PRED SAI1_CLK_PODF LRCLK-----48,000kHz -4ch stereo
| --PLL3_PFD2_CLK-->| | |--------------| |--------------| SAI1_CLK_ROOT BCLK---6,144,000Mhz -8ch-16b.
|>---PLL4_MAIN_CLK-->|<--|--614,400,000->| /5 =|--122,880,000->| /5 =|--24,576,000MHz-->|, MCLK--24,576,000Mhz
--PLL5_MAIN_CLK-->| | |--------------| |--------------| |
Video PLL5 | / |
|_/ |
_________________________________________________________________________________________________________ | _______________________________________
|
|
___________________________________________ IOMUXC_GPR __________________________________________________ | _______________________________________
|
|<-------SAI1_CLK_ROOT-----------------------------------------------------------------------------------<|
| SAI1_MCLK1_SEL
| |¯\
|>------SAI1_CLK_ROOT-->|<\\
-----SAI2_CLK_ROOT-->| \\
-----SAI3_CLK_ROOT-->| \|
en/ds --SAI1_MCLK-----|>| |------------------SAI1_MCLK1---------------------------------------> , MCLK1-- 24,576,000Mhz for 48,000kHz -4ch stereo
en/ds --SAI2_MCLK---|-->| |
en/ds --SAI3_MCLK-|---->| /
| | | | /
| | | |_/
| | |
TO SAI1_MCLK2_SEL & SAI1_MCLK3_SEL + SAI2_MCLK3_SEL + SAI3_MCLK3_SEL
___________________________________________________________________________________________________________________________________________________
// 11.4.2 GPR1 General Purpose Register (IOMUXC_GPR_GPR1)
// SAI1_MCLK_DIR field descriptions 19
// 0 sai1.MCLK is input signal
// 1 sai1.MCLK is output signal
//
// SAI1_MCLK1_SEL field descriptions 3-0
// 000 ccm.ssi1_clk_root
// 001 ccm.ssi2_clk_root
// 010 ccm.ssi3_clk_root
// 011 iomux.sai1_ipg_clk_sai_mclk
// 100 iomux.sai2_ipg_clk_sai_mclk
// 101 iomux.sai3_ipg_clk_sai_mclk
// 110 Reserved
// 111 Reserved
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)); //Select MCLK