I wrote a simple program to try and understand a 250 usec delay I am seeing between the sgtl5000 input to its output. The intent of this test is to take a single sample from the codec ADC into the teensy and then send it back out to the codec DAC. No Audio buffers or Audio library methods are being used for this test.
I am using a simple SAI1 interrupt (no DMA) to handle the data transfers. I configured the i2s clocks and registers almost exactly as is done in the Audio library program except for a small change I made to the I2S1_TCSR register to account for not using DMA. I am also running with an fs = 96000 Hz, which is about a 10.4 usec interrupt rate.
Here is an Arduino sketch of the test program:
#include <Audio.h>
#include "utility\imxrt_hw.h"
void setup() {
AudioControlSGTL5000 sgtl5000_1;
Codec_config_i2s();
attachInterruptVector(IRQ_SAI1, CodecDAC_isr);
NVIC_ENABLE_IRQ(IRQ_SAI1);
I2S1_TCSR |= 1<<8; // start TX interrupts
sgtl5000_1.enable();
}
void loop() {
}
void CodecDAC_isr(void)
{
I2S1_TDR0 = I2S1_RDR0;
__DSB();
}
void Codec_config_i2s(void)
{
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
int fs = 96000.0f;
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, false);
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK)) | CCM_CSCMR1_SAI1_CLK_SEL(2);
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);
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 // master clock is an output and something else?
& ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) |
(IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ((2 - 1)) | I2S_TCR4_SYWD((32 - 1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW((32 - 1)) | I2S_TCR5_W0W((32 - 1)) | I2S_TCR5_FBT((32 - 1));
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((2 - 1)) | I2S_RCR4_SYWD((32 - 1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((32 - 1)) | I2S_RCR5_W0W((32 - 1)) | I2S_RCR5_FBT((32 - 1));
CORE_PIN23_CONFIG = 3; // MCLK
CORE_PIN21_CONFIG = 3; // RX_BCLK
CORE_PIN20_CONFIG = 3; // RX_SYNC
CORE_PIN7_CONFIG = 3; // TX_DATA0
CORE_PIN8_CONFIG = 3; // 1:RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE /* | I2S_TCSR_FRDE <-- not using DMA */;
}
The problem is that the signal I put into codec line input is delayed by 250 usec at the codec line out. With no data buffering, and a 10.4 usec interrupt rate I would expect to see no more than a few tens of microseconds, not 250 usec. Below is a picture of the scope output. The top trace is the sgtl5000 ADC line input and the next trace down is the DAC line out.
I haven't found much information on the guts of the sgtl5000 like amplifier details, or internal buffering or an ADC/DAC pipeline, so the delay is a mystery to me.
If someone has information to explain the delay, I would greatly appreciate it.
Thanks
I am using a simple SAI1 interrupt (no DMA) to handle the data transfers. I configured the i2s clocks and registers almost exactly as is done in the Audio library program except for a small change I made to the I2S1_TCSR register to account for not using DMA. I am also running with an fs = 96000 Hz, which is about a 10.4 usec interrupt rate.
Here is an Arduino sketch of the test program:
#include <Audio.h>
#include "utility\imxrt_hw.h"
void setup() {
AudioControlSGTL5000 sgtl5000_1;
Codec_config_i2s();
attachInterruptVector(IRQ_SAI1, CodecDAC_isr);
NVIC_ENABLE_IRQ(IRQ_SAI1);
I2S1_TCSR |= 1<<8; // start TX interrupts
sgtl5000_1.enable();
}
void loop() {
}
void CodecDAC_isr(void)
{
I2S1_TDR0 = I2S1_RDR0;
__DSB();
}
void Codec_config_i2s(void)
{
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
int fs = 96000.0f;
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, false);
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK)) | CCM_CSCMR1_SAI1_CLK_SEL(2);
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);
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 // master clock is an output and something else?
& ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK)) |
(IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ((2 - 1)) | I2S_TCR4_SYWD((32 - 1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW((32 - 1)) | I2S_TCR5_W0W((32 - 1)) | I2S_TCR5_FBT((32 - 1));
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((2 - 1)) | I2S_RCR4_SYWD((32 - 1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((32 - 1)) | I2S_RCR5_W0W((32 - 1)) | I2S_RCR5_FBT((32 - 1));
CORE_PIN23_CONFIG = 3; // MCLK
CORE_PIN21_CONFIG = 3; // RX_BCLK
CORE_PIN20_CONFIG = 3; // RX_SYNC
CORE_PIN7_CONFIG = 3; // TX_DATA0
CORE_PIN8_CONFIG = 3; // 1:RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE /* | I2S_TCSR_FRDE <-- not using DMA */;
}
The problem is that the signal I put into codec line input is delayed by 250 usec at the codec line out. With no data buffering, and a 10.4 usec interrupt rate I would expect to see no more than a few tens of microseconds, not 250 usec. Below is a picture of the scope output. The top trace is the sgtl5000 ADC line input and the next trace down is the DAC line out.
I haven't found much information on the guts of the sgtl5000 like amplifier details, or internal buffering or an ADC/DAC pipeline, so the delay is a mystery to me.
If someone has information to explain the delay, I would greatly appreciate it.
Thanks