Manuel Schalk
New member
Hi,
I am trying to program the MIMXRT1062 to use the SAI1 receiver as a TDM stream with 8 words @16 bit & 44.1 kHz. I know the library does this as well, but the future goal is to use the SAI at a different sample rate and bit depths & maybe to use all of the 4 available data lines.
The TDM input stream is generated by 8 PDM MEMS microphones that are converted to a TDM stream by the ADAU7118.
I was able to configure the right frame sync & bit clock at 44.1 kHz and 5,6448 MHz, respectively. I verified the correct TDM stream with a logic analyzer, so there should be valid data on the receive channel. However, when I print the received data to the serial monitor it only prints zeros. I would really appreciate any help!
This is my code:
I am trying to program the MIMXRT1062 to use the SAI1 receiver as a TDM stream with 8 words @16 bit & 44.1 kHz. I know the library does this as well, but the future goal is to use the SAI at a different sample rate and bit depths & maybe to use all of the 4 available data lines.
The TDM input stream is generated by 8 PDM MEMS microphones that are converted to a TDM stream by the ADAU7118.
I was able to configure the right frame sync & bit clock at 44.1 kHz and 5,6448 MHz, respectively. I verified the correct TDM stream with a logic analyzer, so there should be valid data on the receive channel. However, when I print the received data to the serial monitor it only prints zeros. I would really appreciate any help!
This is my code:
Code:
/** @file main.cpp
*
* @brief Configures Teensy 4.1 to use SAI1 Receiver with 1 data line. TDM Mode with 8 words @ 16 bit & 44.1 kHz.
*
* @par
* @author Manuel Schalk
* @date 13.01.2023
*/
#include <Arduino.h>
#include "mimxrt1062.h"
#include "ADAU7118.h"
//#define NDEBUG
volatile static bool new_data_available;
volatile int32_t buf[8];
int32_t data[8];
/*
* @brief ISR reads the RX channel 0 data & stores it to buf.
*/
FASTRUN void SAI1_IRQ_Handler()
{
cli();
new_data_available = true;
for (int i=0; i<8; i++)
{
buf[i] = SAI1_RDR0;
}
sei();
}
void setup() {
// put your setup code here, to run once:
# ifndef NDEBUG
if(!Serial){
Serial.begin(9600);
while(!Serial); // Wait for serial monitor
}
# endif /* NDEBUG */
new_data_available = false;
NVIC_DISABLE_IRQ(IRQ_SAI1);
attachInterruptVector(IRQ_SAI1, &SAI1_IRQ_Handler);
NVIC_ENABLE_IRQ(IRQ_SAI1);
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_00 = 0x3; // Assign SAI1_RX_DATA00 to GPIO_B1_00 (Teensy Pin 8)
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_11 = 0x3; // Assign SAI1_RX_BLCK to GPIO_AD_B1_11 (Teensy Pin 21)
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_10 = 0x3; // Assign SAI1_RX_SYNC to GPIO_AD_B1_10 (Teensy Pin 20)
IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_09 = 0x3; // Assign SAI1_RX_MCLK to GPIO_AD_B1_09 (Teensy Pin 23)
IOMUXC_GPR_GPR1 |= IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0); // use ccm.sai1_clk_root as SAI1_MCLK source
IOMUXC_GPR_GPR1 |= IOMUXC_GPR_GPR1_SAI1_MCLK_DIR; // Sets sai1.MCLK as output signal
/* Configure SAI1 MCLK frequency to be at 11,2896 MHz */
CCM_ANALOG_PLL_AUDIO_CLR |= ((uint32_t) 1)<<16; // do not bypass Audio PLL
CCM_ANALOG_PLL_AUDIO |= ((uint32_t) 30 & 0x7F); // set DIV_SELECT to 30
CCM_ANALOG_PLL_AUDIO_NUM = (((uint32_t) 66) & 0x3FFFFFFF); // set NUM of fractional loop to 66
CCM_ANALOG_PLL_AUDIO_DENOM = (((uint32_t) 625) & 0x3FFFFFFF); // set DENOM of fractional loop to 625
CCM_ANALOG_PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2); // Switcher Clock - Divide PLL Fout by 1
CCM_ANALOG_MISC2_CLR = (uint32_t) 1<<23; // Set MSB of AUDIO_DIV - divide by 1
CCM_ANALOG_MISC2_CLR = (uint32_t) 1<<15; // Set LSB of AUDIO_DIV - divide by 1
CCM_CS1CDR |= CCM_CS1CDR_SAI1_CLK_PRED(7); // Divide by 8
CCM_CS1CDR |= CCM_CS1CDR_SAI1_CLK_PODF(7); // Divide by 8
CCM_ANALOG_PLL_AUDIO |= CCM_ANALOG_PLL_AUDIO_ENABLE; // enable Audio PLL output
CCM_CSCMR1 |= CCM_CSCMR1_SAI1_CLK_SEL(2); // Select Audio PLL as clock source
// SAI1 clock has to be enabled before configuring SAI1
CCM_CCGR5 |= CCM_CCGR5_SAI1(3); // enable SAI1 clock
/*
Configure SAI1
*/
# if 0
// TODO: Why is SR bit not cleared even after writing 0?
SAI1_RCSR |= SAI_RCSR_SR(1); // software reset on receiver
delay(10);
SAI1_RCSR = SAI_RCSR_SR(0); // clear software reset
# endif
SAI1_RCSR |= SAI_RCSR_FRIE; // enable FIFO Request Interrupt
SAI1_RCR1 = 0x6; // set RX FIFO watermark to 7 words - Request flag set when words in FIFO greater than watermark
SAI1_RCR2 |= SAI_RCR2_MSEL(1); // Select MCLK1 as master clock
SAI1_RCR2 |= SAI_RCR2_BCD(1); // Set SAI1 RX to master mode
SAI1_RCR2 |= SAI_RCR2_DIV(0); // Set Bit Clock division value to 2
SAI1_RCR2 |= SAI_RCR2_BCP(1); // Sample inputs on rising edge
SAI1_RCR3 |= SAI_RCR3_RCE(1); // enable SAI1 RX channel 0
SAI1_RCR4 |= SAI_RCR4_FRSZ(7); // 8 words per frame
SAI1_RCR4 |= SAI_RCR4_MF; // MSB is received first
SAI1_RCR4 |= SAI_RCR4_FSD(1); // Frame sync generated internally in master mode
SAI1_RCR4 |= SAI_RCR4_FCONT;
SAI1_RCR5 |= SAI_RCR5_WNW(15); // 16 bits per word for each word except the first word in frame
SAI1_RCR5 |= SAI_RCR5_W0W(15); // 16 bits in first word of frame
SAI1_RCR5 |= SAI_RCR5_FBT(15); // bit 15 is the first bit transmitted
SAI1_RCSR |= SAI_RCSR_RE; // Enable SAI1 receiver
/*
Initialize ADAU7118
*/
ADAU7118_init(ADDR_CNFG1);
delay(50);
}
void loop() {
// put your main code here, to run repeatedly:
cli();
bool copy_new_data_available = new_data_available;
sei();
if (copy_new_data_available)
{
cli();
memcpy(data, (int32_t*) buf, 32);
sei();
Serial.print(data[0]);
Serial.print("\t");
Serial.print(data[1]);
Serial.print("\t");
Serial.print(data[2]);
Serial.print("\t");
Serial.print(data[3]);
Serial.print("\t");
Serial.print(data[4]);
Serial.print("\t");
Serial.print(data[5]);
Serial.print("\t");
Serial.print(data[6]);
Serial.print("\t");
Serial.println(data[7]);
cli();
new_data_available = 0;
sei();
}
}
/*** end of file ***/