Basic I2S Input Example for T4.x

WMXZ

Well-known member
The following code is a simple I2S input example that runs on T4.1.
It can be configured to have multi channel TDM input for ether 16 or 32 bit for (nearly) arbitrary sampling frequency.
It runs without Audio library and is only a demonstration on how it is or can be done.
Edit the lines marked with <<<<<

Obviously using the Audio library is shorter (but maybe less flexible) but here it comes

Code:
#define FSAMP 96000
#define NSAMP 128

#define N_BITS  16  // <<<<< 16 or 32
#define N_WORD  (N_BITS>>3)
#define N_SYNC  1   // <<<<<< 1 for TDM is equal to N_BITS for SAI protocol

// assume to have two audio channels with 3 TDM samples per channel 
#define NPORT_I2S 2 // <<<<<< 1,2,3,4
#define NCHAN_I2S 3 // <<<<<< 
#define NBUF_I2S (NPORT_I2S*NCHAN_I2S*NSAMP)

// assume we want all I2S channels
#define NCHAN_ACQ 6 // <<<<<<<
#define NBUF_ACQ (NCHAN_ACQ*NSAMP)

// assume we want to digitally shift data by 8 bits (e.g. to bring 24 MSB to LSB)
#define adc_shift 0 // <<<<<<< useful for 32 bit data transfer

// define the mapping between I2S and acq buffer (size of NCHA_ACQ)
#if NPORT_I2S == 1
  static int i2sIndex[NCHAN_ACQ]={0,1,2,3,4,5}; 
#elif NPORT_I2S == 2
  static int i2sIndex[NCHAN_ACQ]={0,2,4,1,3,5}; 
#elif NPORT_I2S == 3
  static int i2sIndex[NCHAN_ACQ]={0,3,1,4,2,5}; 
#elif NPORT_I2S == 4
  static int i2sIndex[NCHAN_ACQ]={0,1,2,3,4,5}; 
#endif


static void acq_isr(void);

#if N_BITS==16
  typedef uint16_t data_t;
#elif N_BITS==32
  typedef uint32_t data_t;
#endif

DMAMEM __attribute__((aligned(32)))
static data_t i2s_rx_buffer[2*NBUF_I2S];
static data_t acq_rx_buffer[2*NBUF_ACQ];

#ifndef I2S_DMA_PRIO
  #define I2S_DMA_PRIO 5 // give DMA ISR a higher priority than default (8)
#endif

#include "DMAChannel.h"
static DMAChannel dma;

#if defined(__IMXRT1062__)
    #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));
    }

    PROGMEM
    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 = 2*(NCHAN_I2S*N_BITS);

        // 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);

        // 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
        CORE_PIN23_CONFIG = 3;  //1:MCLK // connected to GPIO0 not used
        CORE_PIN21_CONFIG = 3;  //1:RX_BCLK
        CORE_PIN20_CONFIG = 3;  //1:RX_SYNC
  
        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(0);

        I2S1_RCR4 = I2S_RCR4_FRSZ((NCHAN_I2S-1)) | I2S_RCR4_SYWD(N_SYNC-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);

        // DMA
        dma.TCD->SADDR = &I2S1_RDR0;
        dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(N_WORD>>1) | DMA_TCD_ATTR_DSIZE(N_WORD>>1);

  #if NPORT_I2S == 1
        I2S1_RCR3 = I2S_RCR3_RCE;
        CORE_PIN8_CONFIG = 3;   //RX_DATA0
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
  
        dma.TCD->SOFF = 0;
        dma.TCD->NBYTES_MLOFFNO = N_WORD;
        dma.TCD->SLAST = 0;
  
  #elif NPORT_I2S == 2
        I2S1_RCR3 = I2S_RCR3_RCE_2CH;
        CORE_PIN8_CONFIG = 3;   //RX_DATA0
        CORE_PIN6_CONFIG = 3;   //RX_DATA1
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
  
        dma.TCD->SOFF = 4;
        dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                                    DMA_TCD_NBYTES_MLOFFYES_MLOFF(-NPORT_I2S*4) |
                                    DMA_TCD_NBYTES_MLOFFYES_NBYTES(NPORT_I2S*N_WORD);
        dma.TCD->SLAST = -NPORT_I2S*4;

  #elif NPORT_I2S == 3
        I2S1_RCR3 = I2S_RCR3_RCE_3CH;
        CORE_PIN8_CONFIG = 3;   //RX_DATA0
        CORE_PIN6_CONFIG = 3;   //RX_DATA1
        CORE_PIN9_CONFIG = 3;   //RX_DATA2
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874  
        
        dma.TCD->SOFF = 4;
        dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                                    DMA_TCD_NBYTES_MLOFFYES_MLOFF(-NPORT_I2S*4) |
                                    DMA_TCD_NBYTES_MLOFFYES_NBYTES(NPORT_I2S*N_WORD);
        dma.TCD->SLAST = -NPORT_I2S*4;
  
  #elif NPORT_I2S == 4
        I2S1_RCR3 = I2S_RCR3_RCE_4CH;
        CORE_PIN8_CONFIG = 3;   //RX_DATA0
        CORE_PIN6_CONFIG = 3;   //RX_DATA1
        CORE_PIN9_CONFIG = 3;   //RX_DATA2
        CORE_PIN32_CONFIG = 3;  //RX_DATA3
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874  
        IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875        
        
        dma.TCD->SOFF = 4;
        dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                                    DMA_TCD_NBYTES_MLOFFYES_MLOFF(-NPORT_I2S*4) |
                                    DMA_TCD_NBYTES_MLOFFYES_NBYTES(NPORT_I2S*N_WORD);
        dma.TCD->SLAST = -NPORT_I2S*4;
  #endif
  
        dma.TCD->DADDR = i2s_rx_buffer;
        dma.TCD->DOFF = N_WORD;
        dma.TCD->CITER_ELINKNO = dma.TCD->BITER_ELINKNO = 2*NBUF_I2S/NPORT_I2S;
        dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
        
        dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
        dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);        
        dma.enable();

        I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
        dma.attachInterrupt(acq_isr, I2S_DMA_PRIO*16); 
    }

    uint32_t acq_count=0;
    void extract(void *out, void *inp);
    void acq_isr(void)
    {
        uint32_t daddr;
        data_t *src;

        dma.clearInterrupt();
        daddr = (uint32_t)(dma.TCD->DADDR);
        acq_count++;

        if (daddr < (uint32_t)(i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2)) {
            // DMA is receiving to the first half of the buffer
            // need to remove data from the second half
            src = &i2s_rx_buffer[NBUF_I2S];
        } else {
            // DMA is receiving to the second half of the buffer
            // need to remove data from the first half
            src = &i2s_rx_buffer[0];
        }

        #if IMXRT_CACHE_ENABLED >=1
            arm_dcache_delete((void*)src, sizeof(i2s_rx_buffer) / 2);
        #endif

        extract((void *) acq_rx_buffer, (void *) src);
    }

#endif

    void extract(void *out, void *inp)
    {   data_t *dout = (data_t *) out;
        data_t *din  = (data_t *) inp;
        for(int ii=0; ii < NSAMP; ii++)
        {
          for(int jj=0; jj<NCHAN_ACQ; jj++)
          { data_t *dst=&dout[ii*NCHAN_ACQ];
            data_t *src=&din[ii*NPORT_I2S*NCHAN_I2S];
            dst[jj]=src[i2sIndex[jj]]>>adc_shift;
          }
        }
    }

void setup() {
  // put your setup code here, to run once:
  while(!Serial);
  Serial.print(" NPorts "); Serial.print(NPORT_I2S);
  Serial.print("; NChan "); Serial.print(NCHAN_I2S);
  Serial.print("; NBits "); Serial.print(N_BITS);
  Serial.print("; NWord "); Serial.print(N_WORD);
  Serial.println();
  acq_init(FSAMP);
}

void loop() {
  // put your main code here, to run repeatedly:
  static uint32_t to=0;
  if(millis()-to>1000)
  {
    Serial.println(acq_count);
    acq_count=0;
    to=millis();
  }
}
 
background knowledge for this code.

HI WMXZ,

I have been asking a lot of questions and read many post of this forum.

It seems like that the code you posted here is the most appropriate for our project.
We want to connect the Teensy 4.1 with 32 TDM microphones through the 2 TDM port.
We want to have better control of the data and code.

But unfortunately, we have limited knowledge about the inner working theroy of Teensy, :confused:
and thus we have a hard time to under stand your code, the DMA, the acq, and the all kind of parameter.

I am just wandering could you provide us with more background knowledge for this code.:D


The following code is a simple I2S input example that runs on T4.1.
It can be configured to have multi channel TDM input for ether 16 or 32 bit for (nearly) arbitrary sampling frequency.
It runs without Audio library and is only a demonstration on how it is or can be done.
Edit the lines marked with <<<<<

Obviously using the Audio library is shorter (but maybe less flexible) but here it comes

Code:
#define FSAMP 96000
#define NSAMP 128

#define N_BITS  16  // <<<<< 16 or 32
#define N_WORD  (N_BITS>>3)
#define N_SYNC  1   // <<<<<< 1 for TDM is equal to N_BITS for SAI protocol

// assume to have two audio channels with 3 TDM samples per channel 
#define NPORT_I2S 2 // <<<<<< 1,2,3,4
#define NCHAN_I2S 3 // <<<<<< 
#define NBUF_I2S (NPORT_I2S*NCHAN_I2S*NSAMP)

// assume we want all I2S channels
#define NCHAN_ACQ 6 // <<<<<<<
#define NBUF_ACQ (NCHAN_ACQ*NSAMP)

// assume we want to digitally shift data by 8 bits (e.g. to bring 24 MSB to LSB)
#define adc_shift 0 // <<<<<<< useful for 32 bit data transfer

// define the mapping between I2S and acq buffer (size of NCHA_ACQ)
#if NPORT_I2S == 1
  static int i2sIndex[NCHAN_ACQ]={0,1,2,3,4,5}; 
#elif NPORT_I2S == 2
  static int i2sIndex[NCHAN_ACQ]={0,2,4,1,3,5}; 
#elif NPORT_I2S == 3
  static int i2sIndex[NCHAN_ACQ]={0,3,1,4,2,5}; 
#elif NPORT_I2S == 4
  static int i2sIndex[NCHAN_ACQ]={0,1,2,3,4,5}; 
#endif


static void acq_isr(void);

#if N_BITS==16
  typedef uint16_t data_t;
#elif N_BITS==32
  typedef uint32_t data_t;
#endif

DMAMEM __attribute__((aligned(32)))
static data_t i2s_rx_buffer[2*NBUF_I2S];
static data_t acq_rx_buffer[2*NBUF_ACQ];

#ifndef I2S_DMA_PRIO
  #define I2S_DMA_PRIO 5 // give DMA ISR a higher priority than default (8)
#endif

#include "DMAChannel.h"
static DMAChannel dma;

#if defined(__IMXRT1062__)
    #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));
    }

    PROGMEM
    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 = 2*(NCHAN_I2S*N_BITS);

        // 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);

        // 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
        CORE_PIN23_CONFIG = 3;  //1:MCLK // connected to GPIO0 not used
        CORE_PIN21_CONFIG = 3;  //1:RX_BCLK
        CORE_PIN20_CONFIG = 3;  //1:RX_SYNC
  
        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(0);

        I2S1_RCR4 = I2S_RCR4_FRSZ((NCHAN_I2S-1)) | I2S_RCR4_SYWD(N_SYNC-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);

        // DMA
        dma.TCD->SADDR = &I2S1_RDR0;
        dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(N_WORD>>1) | DMA_TCD_ATTR_DSIZE(N_WORD>>1);

  #if NPORT_I2S == 1
        I2S1_RCR3 = I2S_RCR3_RCE;
        CORE_PIN8_CONFIG = 3;   //RX_DATA0
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
  
        dma.TCD->SOFF = 0;
        dma.TCD->NBYTES_MLOFFNO = N_WORD;
        dma.TCD->SLAST = 0;
  
  #elif NPORT_I2S == 2
        I2S1_RCR3 = I2S_RCR3_RCE_2CH;
        CORE_PIN8_CONFIG = 3;   //RX_DATA0
        CORE_PIN6_CONFIG = 3;   //RX_DATA1
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
  
        dma.TCD->SOFF = 4;
        dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                                    DMA_TCD_NBYTES_MLOFFYES_MLOFF(-NPORT_I2S*4) |
                                    DMA_TCD_NBYTES_MLOFFYES_NBYTES(NPORT_I2S*N_WORD);
        dma.TCD->SLAST = -NPORT_I2S*4;

  #elif NPORT_I2S == 3
        I2S1_RCR3 = I2S_RCR3_RCE_3CH;
        CORE_PIN8_CONFIG = 3;   //RX_DATA0
        CORE_PIN6_CONFIG = 3;   //RX_DATA1
        CORE_PIN9_CONFIG = 3;   //RX_DATA2
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874  
        
        dma.TCD->SOFF = 4;
        dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                                    DMA_TCD_NBYTES_MLOFFYES_MLOFF(-NPORT_I2S*4) |
                                    DMA_TCD_NBYTES_MLOFFYES_NBYTES(NPORT_I2S*N_WORD);
        dma.TCD->SLAST = -NPORT_I2S*4;
  
  #elif NPORT_I2S == 4
        I2S1_RCR3 = I2S_RCR3_RCE_4CH;
        CORE_PIN8_CONFIG = 3;   //RX_DATA0
        CORE_PIN6_CONFIG = 3;   //RX_DATA1
        CORE_PIN9_CONFIG = 3;   //RX_DATA2
        CORE_PIN32_CONFIG = 3;  //RX_DATA3
        IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; // GPIO_B1_00_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA1_SELECT_INPUT = 1; // GPIO_B0_10_ALT3, pg 873
        IOMUXC_SAI1_RX_DATA2_SELECT_INPUT = 1; // GPIO_B0_11_ALT3, pg 874  
        IOMUXC_SAI1_RX_DATA3_SELECT_INPUT = 1; // GPIO_B0_12_ALT3, pg 875        
        
        dma.TCD->SOFF = 4;
        dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_SMLOE |
                                    DMA_TCD_NBYTES_MLOFFYES_MLOFF(-NPORT_I2S*4) |
                                    DMA_TCD_NBYTES_MLOFFYES_NBYTES(NPORT_I2S*N_WORD);
        dma.TCD->SLAST = -NPORT_I2S*4;
  #endif
  
        dma.TCD->DADDR = i2s_rx_buffer;
        dma.TCD->DOFF = N_WORD;
        dma.TCD->CITER_ELINKNO = dma.TCD->BITER_ELINKNO = 2*NBUF_I2S/NPORT_I2S;
        dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
        
        dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
        dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);        
        dma.enable();

        I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
        dma.attachInterrupt(acq_isr, I2S_DMA_PRIO*16); 
    }

    uint32_t acq_count=0;
    void extract(void *out, void *inp);
    void acq_isr(void)
    {
        uint32_t daddr;
        data_t *src;

        dma.clearInterrupt();
        daddr = (uint32_t)(dma.TCD->DADDR);
        acq_count++;

        if (daddr < (uint32_t)(i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2)) {
            // DMA is receiving to the first half of the buffer
            // need to remove data from the second half
            src = &i2s_rx_buffer[NBUF_I2S];
        } else {
            // DMA is receiving to the second half of the buffer
            // need to remove data from the first half
            src = &i2s_rx_buffer[0];
        }

        #if IMXRT_CACHE_ENABLED >=1
            arm_dcache_delete((void*)src, sizeof(i2s_rx_buffer) / 2);
        #endif

        extract((void *) acq_rx_buffer, (void *) src);
    }

#endif

    void extract(void *out, void *inp)
    {   data_t *dout = (data_t *) out;
        data_t *din  = (data_t *) inp;
        for(int ii=0; ii < NSAMP; ii++)
        {
          for(int jj=0; jj<NCHAN_ACQ; jj++)
          { data_t *dst=&dout[ii*NCHAN_ACQ];
            data_t *src=&din[ii*NPORT_I2S*NCHAN_I2S];
            dst[jj]=src[i2sIndex[jj]]>>adc_shift;
          }
        }
    }

void setup() {
  // put your setup code here, to run once:
  while(!Serial);
  Serial.print(" NPorts "); Serial.print(NPORT_I2S);
  Serial.print("; NChan "); Serial.print(NCHAN_I2S);
  Serial.print("; NBits "); Serial.print(N_BITS);
  Serial.print("; NWord "); Serial.print(N_WORD);
  Serial.println();
  acq_init(FSAMP);
}

void loop() {
  // put your main code here, to run repeatedly:
  static uint32_t to=0;
  if(millis()-to>1000)
  {
    Serial.println(acq_count);
    acq_count=0;
    to=millis();
  }
}
 
when trying to verify teh code I get an error message:

Arduino: 1.8.13 (Windows 10), TD: 1.53, Board: "Teensy 4.1, Serial, 600 MHz, Faster, US English"

bla bla .....

^

Basic_I2S_Input_Example_for_T4:214: error: no matching function for call to 'DMAChannel::attachInterrupt(void (&)(), int)'

dma.attachInterrupt(acq_isr, I2S_DMA_PRIO*16);

^

In file included from C:\Users\thijs\Documents\Arduino\Basic_I2S_Input_Example_for_T4\Basic_I2S_Input_Example_for_T4.ino:58:0:

C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy4/DMAChannel.h:533:7: note: candidate: void DMAChannel::attachInterrupt(void (*)())

void attachInterrupt(void (*isr)(void)) {

^

C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy4/DMAChannel.h:533:7: note: candidate expects 1 argument, 2 provided

no matching function for call to 'DMAChannel::attachInterrupt(void (&)(), int)'





Do I miss a librabrary for this 'DMAChannel::attachInterrupt function?
 
Thank you! Verified and uploaded successfully on the first try.
Now to learn how to understand it...
Can you point me toward any resource that can help me take the next step?
Regardless, this is a treasure trove for me. Thanks again.
 
Back
Top