how to use the ics52000 microphone with teensy 4.1

feima0011

Well-known member

------------------------------------
Admin Edit:
ICS-52000 microphones do not appear to work well with Teensy at 44100 Hz sample rate.
------------------------------------


:confused:

We are connecting the ics52000 microphone array with teensy 4.1, and we wish to send the microphone
signal to the pc through usb audio.

Here are the pin connection, note that I have shield the pin 1 3 5 7 9 together at the other side of the microphone array board.
1.jpg2.jpg3.jpg
and the code used
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputTDM            tdm1;           //xy=372.1667022705078,876.5
AudioOutputUSB           usb1;           //xy=608.3333129882812,785.6666870117188
AudioConnection          patchCord1(tdm1, 0, usb1, 0);
AudioConnection          patchCord2(tdm1, 1, usb1, 1);
// GUItool: end automatically generated code
void setup()
{
  AudioMemory(256);
  delay(1000);
}
void loop()
{
  delay(200); 
  }


I have change the port to be "audio" and manully upload the code to teensy.
I am using the https://friture.org/ to inspect the signal,
but I can not see any signal at all.

I do have used the the audio shield and send the signal to the pc as usb audio, and it
works.

I do not understand why it does not work with the ICS52000 TDM microphone.
 
Last edited by a moderator:
the code takes the top 16 bit from the data, so if you are not speaking loud (hitting the microphones) you may not see any signal.
Have not checked HW
 
Last edited:
How to modify?

If you need all 24 bits, than you may need to modify the audio library from 16 bit to 32 bit.
if 16 bit is OK, you could simply change the "memcpy_tdm_rx" function to extract, for example, not the top 16 bits (16 to 32 bit) but, say 14 to 30 bit.
Note the code maybe a little bit more complicated than the existing one.

In how far you have to modify the I2S setup must be checked (BCLK, FSYNC)
 
But first, note that I2S uses also for TDM
Code:
	CORE_PIN4_CONFIG  = 2;  //2:TX_BCLK
	CORE_PIN3_CONFIG  = 2;  //2:TX_SYNC
and not the RX_BCLK and RX_SYNC you have choosen
 
Many thank for your reply.
Could you please provide with some background materials, to help understand how teensy handle i2s and the TDM. We have limited knowledge about it and thus do not know how to start.
 
Many thank for your reply.
Could you please provide with some background materials, to help understand how teensy handle i2s and the TDM. We have limited knowledge about it and thus do not know how to start.

I only read the source code and compare with I2S reference (more or less the same with all Teensy chips) which is a must when you start modifying code.
 
But first, note that I2S uses also for TDM
Code:
	CORE_PIN4_CONFIG  = 2;  //2:TX_BCLK
	CORE_PIN3_CONFIG  = 2;  //2:TX_SYNC
and not the RX_BCLK and RX_SYNC you have choosen

Wrong info:
Pin3/4 are for TDM2
hooked up a Notwrired.com quad board and got with sketch of OP 2nd channel scrambled (16 bit PCM), so data are passing through USB
 
the code takes the top 16 bit from the data, so if you are not speaking loud (hitting the microphones) you may not see any signal.
Have not checked HW

I have tried the following code
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputTDM            tdm1;           //xy=372.1667022705078,876.5
AudioOutputUSB           usb1;           //xy=608.3333129882812,785.6666870117188
AudioConnection          patchCord1(tdm1, 1, usb1, 0);
AudioConnection          patchCord2(tdm1, 1, usb1, 1);
// GUItool: end automatically generated code
void setup()
{
  AudioMemory(256);
  delay(1000);
}
void loop()
{
  delay(200); 
  }

which send the higher 16 bit data to the two usb channel.

I put a loudspeaker the microphone array.
But still there is no signal shown by https://friture.org/

I can the problem is still how teensy 4.1 handle the ICS52000.
But I do not how to proceed.
 
I could NOT get the NotWired.co 4 channel ICS-52000 working, but TDM works with ICS 43434.
I used the following code
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
// WMXZ: modified for tdm test

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

#define MBL 8
#if defined(__IMXRT1062__)
  #define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB
#endif

class I2S_TDM : public AudioStream
{
public:
  I2S_TDM(void) : AudioStream(0, NULL) { begin(); }
  virtual void update(void);
  void begin(void);
  void setShift(int16_t val){I2S_TDM::shift=val;}
protected:  
  static bool update_responsibility;
  static DMAChannel dma;
  static void isr(void);
private:
  static int16_t shift;
  void config_tdm(void);
  static audio_block_t *block_incoming[MBL];
};

// initialize static varaiables
DMAMEM static uint32_t tdm_rx_buffer[2*AUDIO_BLOCK_SAMPLES*MBL];
audio_block_t * I2S_TDM::block_incoming[MBL] = { NULL, NULL, NULL, NULL, NULL , NULL, NULL, NULL};
bool I2S_TDM::update_responsibility = false;
DMAChannel I2S_TDM::dma(false);
int16_t I2S_TDM::shift=8; //8 shifts 24 bit data to LSB


void I2S_TDM::begin(void)
{
  dma.begin(true); // Allocate the DMA channel first

  // TODO: should we set & clear the I2S_RCSR_SR bit here?
  config_tdm();
  
#if defined(KINETISK)
  CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
  dma.TCD->SADDR = &I2S0_RDR0;
  dma.TCD->SOFF = 0;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  dma.TCD->NBYTES_MLNO = 4;
  dma.TCD->SLAST = 0;
  dma.TCD->DADDR = tdm_rx_buffer;
  dma.TCD->DOFF = 4;
  dma.TCD->CITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
  dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
  dma.TCD->BITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
  update_responsibility = update_setup();
  dma.enable();

  I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
  I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
  dma.attachInterrupt(isr);
  
#elif defined(__IMXRT1062__)
  CORE_PIN8_CONFIG  = 3;  //RX_DATA0
  IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
  dma.TCD->SADDR = &I2S1_RDR0;
  dma.TCD->SOFF = 0;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  dma.TCD->NBYTES_MLNO = 4;
  dma.TCD->SLAST = 0;
  dma.TCD->DADDR = tdm_rx_buffer;
  dma.TCD->DOFF = 4;
  dma.TCD->CITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
  dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
  dma.TCD->BITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
  update_responsibility = update_setup();
  dma.enable();

  I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
  dma.attachInterrupt(isr); 
#endif  
}

void I2S_TDM::isr(void)
{
  uint32_t daddr;
  int32_t *src;

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

  if (daddr < (uint32_t)&tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*MBL])
  { // DMA is receiving to the first half of the buffer
    // need to remove data from the second half
    src = &tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*MBL];
  }
  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

  if (block_incoming[0] != NULL) 
  {
    for(int ii=0;ii<AUDIO_BLOCK_SAMPLES;ii++)
    {
      for(int jj=0; jj<MBL; jj++) { block_incoming[jj]->data[ii] = (int16_t) ((*src++)>>I2S_TDM::shift);}
    }
  }
  if (update_responsibility) update_all();
}


void I2S_TDM::update(void)
{
  unsigned int ii, jj;
  audio_block_t *new_block[MBL];
  audio_block_t *out_block[MBL];

  // allocate 5 new blocks.  If any fails, allocate none
  for (ii=0; ii < MBL; ii++) {
    new_block[ii] = allocate();
    if (new_block[ii] == NULL) {
      for (jj=0; jj < ii; jj++) {
        release(new_block[jj]);
      }
      memset(new_block, 0, sizeof(new_block));
      break;
    }
  }
  //
  __disable_irq();
  memcpy(out_block, block_incoming, sizeof(out_block));
  memcpy(block_incoming, new_block, sizeof(block_incoming));
  __enable_irq();
  //
  if (out_block[0] != NULL) {
    // if we got 1 block, all are filled
    for (ii=0; ii < MBL; ii++) {
      transmit(out_block[ii], ii);
      release(out_block[ii]);
    }
  }
}

// following is default TDM config
// MCLK needs to be 48e6 / 1088 * 512 = 22.588235 MHz -> 44.117647 kHz sample rate
//
#if defined (KINETISK)
// for T3.x

#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
  // PLL is at 96 MHz in these modes
  #define MCLK_MULT 4
  #define MCLK_DIV  17
#elif F_CPU == 72000000
  #define MCLK_MULT 16
  #define MCLK_DIV  51
#elif F_CPU == 120000000
  #define MCLK_MULT 16
  #define MCLK_DIV  85
#elif F_CPU == 144000000
  #define MCLK_MULT 8
  #define MCLK_DIV  51
#elif F_CPU == 168000000
  #define MCLK_MULT 16
  #define MCLK_DIV  119
#elif F_CPU == 180000000
  #define MCLK_MULT 32
  #define MCLK_DIV  255
  #define MCLK_SRC  0
#elif F_CPU == 192000000
  #define MCLK_MULT 2
  #define MCLK_DIV  17
#elif F_CPU == 216000000
  #define MCLK_MULT 16
  #define MCLK_DIV  153
  #define MCLK_SRC  0
#elif F_CPU == 240000000
  #define MCLK_MULT 8
  #define MCLK_DIV  85
#else
  #error "This CPU Clock Speed is not supported by the Audio library";
#endif

#ifndef MCLK_SRC
#if F_CPU >= 20000000
  #define MCLK_SRC  3  // the PLL
#else
  #define MCLK_SRC  0  // system clock
#endif
#endif

void I2S_TDM::config_tdm(void)
{
  SIM_SCGC6 |= SIM_SCGC6_I2S;
  SIM_SCGC7 |= SIM_SCGC7_DMA;
  SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

  // if either transmitter or receiver is enabled, do nothing
  if (I2S0_TCSR & I2S_TCSR_TE) return;
  if (I2S0_RCSR & I2S_RCSR_RE) return;

  // enable MCLK output
  I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
  while (I2S0_MCR & I2S_MCR_DUF) ;
  I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));

  int rsync = 1;
  int tsync = 0; //async

  // configure transmitter
  I2S0_TMR = 0;
  I2S0_TCR1 = I2S_TCR1_TFW(4);
  I2S0_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
    | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  I2S0_TCR3 = I2S_TCR3_TCE;
  I2S0_TCR4 = I2S_TCR4_FRSZ(MBL-1) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
    | I2S_TCR4_FSE | I2S_TCR4_FSD;
  I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  // configure receiver (sync'd to transmitter clocks)
  I2S0_RMR = 0;
  I2S0_RCR1 = I2S_RCR1_RFW(4);
  I2S0_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
    | I2S_RCR2_BCD | I2S_RCR2_DIV(0);
  I2S0_RCR3 = I2S_RCR3_RCE;
  I2S0_RCR4 = I2S_RCR4_FRSZ(MBL-1) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
    | I2S_RCR4_FSE | I2S_RCR4_FSD;
  I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);

  // configure pin mux for 3 clock signals
  CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
  CORE_PIN9_CONFIG  = PORT_PCR_MUX(6); // pin  9, PTC3, I2S0_TX_BCLK
  CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
}

#elif defined(__IMXRT1062__)
/* for T4.x */

FLASHMEM
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv) // 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) // 2: 1/4; 1: 1/2; 0: 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
}

#ifndef AUDIO_SAMPLE_RATE_EXACT
  #define AUDIO_SAMPLE_RATE_EXACT 44100
#endif

#define BPF (8*32) // BitsPerFrame

void I2S_TDM::config_tdm(void)
{
  CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);

  // if either transmitter or receiver is enabled, do nothing
  if (I2S1_TCSR & I2S_TCSR_TE) return;
  if (I2S1_RCSR & I2S_RCSR_RE) return;
//PLL:
  int fs = AUDIO_SAMPLE_RATE_EXACT;
  // 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) / (fs * BPF * n1);

  double C = ((double)fs * BPF * n1 * n2) / 24000000;
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);
  set_audioClock(c0, c1, c2);
  // 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

  n1 = n1 / 2; //Double Speed for TDM

  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

  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

  int rsync = 0; // async
  int tsync = 1;

  // configure transmitter
  I2S1_TMR = 0;
  I2S1_TCR1 = I2S_TCR1_RFW(4);
  I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
    | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  I2S1_TCR3 = I2S_TCR3_TCE;
  I2S1_TCR4 = I2S_TCR4_FRSZ(MBL-1) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
    | I2S_TCR4_FSE | I2S_TCR4_FSD;
  I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  I2S1_RMR = 0;
  I2S1_RCR1 = I2S_RCR1_RFW(4);
  I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
    | I2S_RCR2_BCD | I2S_RCR2_DIV(0);
  I2S1_RCR3 = I2S_RCR3_RCE;
  I2S1_RCR4 = I2S_RCR4_FRSZ(MBL-1) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
    | I2S_RCR4_FSE | I2S_RCR4_FSD;
  I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);

  CORE_PIN23_CONFIG = 3;  //1:MCLK
  CORE_PIN21_CONFIG = 3;  //1:RX_BCLK
  CORE_PIN20_CONFIG = 3;  //1:RX_SYNC
}
#endif

/****************sketch **********************/
#include <Audio.h>

// GUItool: begin automatically generated code
I2S_TDM                  tdm1;
//AudioInputTDM            tdm1; 
AudioOutputUSB           usb1;           
AudioConnection          patchCord1(tdm1, 0, usb1, 0);
AudioConnection          patchCord2(tdm1, 1, usb1, 1);
// GUItool: end automatically generated code
void setup()
{
  AudioMemory(256);
  delay(1000);
//  tdm1.setShift(10);
}
void loop()
{
  delay(200); 
}

where I included a local implementation of the TDM code for both T3.x and T4.x to allow flexible 16-bit extraction from 32 bit words
As said,
ICS 43434 is working
ICS 52000 (4 channel) is not working

by not working I mean that 4 channels are send to teensy but the two channels send to PC are only noise (no acoustic signals)
 
I have designed more than 100 ics52000 PCB, what should I do with them?

I could NOT get the NotWired.co 4 channel ICS-52000 working, but TDM works with ICS 43434.
I used the following code
Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
// WMXZ: modified for tdm test

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

#define MBL 8
#if defined(__IMXRT1062__)
  #define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB
#endif

class I2S_TDM : public AudioStream
{
public:
  I2S_TDM(void) : AudioStream(0, NULL) { begin(); }
  virtual void update(void);
  void begin(void);
  void setShift(int16_t val){I2S_TDM::shift=val;}
protected:  
  static bool update_responsibility;
  static DMAChannel dma;
  static void isr(void);
private:
  static int16_t shift;
  void config_tdm(void);
  static audio_block_t *block_incoming[MBL];
};

// initialize static varaiables
DMAMEM static uint32_t tdm_rx_buffer[2*AUDIO_BLOCK_SAMPLES*MBL];
audio_block_t * I2S_TDM::block_incoming[MBL] = { NULL, NULL, NULL, NULL, NULL , NULL, NULL, NULL};
bool I2S_TDM::update_responsibility = false;
DMAChannel I2S_TDM::dma(false);
int16_t I2S_TDM::shift=8; //8 shifts 24 bit data to LSB


void I2S_TDM::begin(void)
{
  dma.begin(true); // Allocate the DMA channel first

  // TODO: should we set & clear the I2S_RCSR_SR bit here?
  config_tdm();
  
#if defined(KINETISK)
  CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
  dma.TCD->SADDR = &I2S0_RDR0;
  dma.TCD->SOFF = 0;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  dma.TCD->NBYTES_MLNO = 4;
  dma.TCD->SLAST = 0;
  dma.TCD->DADDR = tdm_rx_buffer;
  dma.TCD->DOFF = 4;
  dma.TCD->CITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
  dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
  dma.TCD->BITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
  update_responsibility = update_setup();
  dma.enable();

  I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
  I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
  dma.attachInterrupt(isr);
  
#elif defined(__IMXRT1062__)
  CORE_PIN8_CONFIG  = 3;  //RX_DATA0
  IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
  dma.TCD->SADDR = &I2S1_RDR0;
  dma.TCD->SOFF = 0;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  dma.TCD->NBYTES_MLNO = 4;
  dma.TCD->SLAST = 0;
  dma.TCD->DADDR = tdm_rx_buffer;
  dma.TCD->DOFF = 4;
  dma.TCD->CITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
  dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
  dma.TCD->BITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
  update_responsibility = update_setup();
  dma.enable();

  I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
  dma.attachInterrupt(isr); 
#endif  
}

void I2S_TDM::isr(void)
{
  uint32_t daddr;
  int32_t *src;

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

  if (daddr < (uint32_t)&tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*MBL])
  { // DMA is receiving to the first half of the buffer
    // need to remove data from the second half
    src = &tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*MBL];
  }
  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

  if (block_incoming[0] != NULL) 
  {
    for(int ii=0;ii<AUDIO_BLOCK_SAMPLES;ii++)
    {
      for(int jj=0; jj<MBL; jj++) { block_incoming[jj]->data[ii] = (int16_t) ((*src++)>>I2S_TDM::shift);}
    }
  }
  if (update_responsibility) update_all();
}


void I2S_TDM::update(void)
{
  unsigned int ii, jj;
  audio_block_t *new_block[MBL];
  audio_block_t *out_block[MBL];

  // allocate 5 new blocks.  If any fails, allocate none
  for (ii=0; ii < MBL; ii++) {
    new_block[ii] = allocate();
    if (new_block[ii] == NULL) {
      for (jj=0; jj < ii; jj++) {
        release(new_block[jj]);
      }
      memset(new_block, 0, sizeof(new_block));
      break;
    }
  }
  //
  __disable_irq();
  memcpy(out_block, block_incoming, sizeof(out_block));
  memcpy(block_incoming, new_block, sizeof(block_incoming));
  __enable_irq();
  //
  if (out_block[0] != NULL) {
    // if we got 1 block, all are filled
    for (ii=0; ii < MBL; ii++) {
      transmit(out_block[ii], ii);
      release(out_block[ii]);
    }
  }
}

// following is default TDM config
// MCLK needs to be 48e6 / 1088 * 512 = 22.588235 MHz -> 44.117647 kHz sample rate
//
#if defined (KINETISK)
// for T3.x

#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
  // PLL is at 96 MHz in these modes
  #define MCLK_MULT 4
  #define MCLK_DIV  17
#elif F_CPU == 72000000
  #define MCLK_MULT 16
  #define MCLK_DIV  51
#elif F_CPU == 120000000
  #define MCLK_MULT 16
  #define MCLK_DIV  85
#elif F_CPU == 144000000
  #define MCLK_MULT 8
  #define MCLK_DIV  51
#elif F_CPU == 168000000
  #define MCLK_MULT 16
  #define MCLK_DIV  119
#elif F_CPU == 180000000
  #define MCLK_MULT 32
  #define MCLK_DIV  255
  #define MCLK_SRC  0
#elif F_CPU == 192000000
  #define MCLK_MULT 2
  #define MCLK_DIV  17
#elif F_CPU == 216000000
  #define MCLK_MULT 16
  #define MCLK_DIV  153
  #define MCLK_SRC  0
#elif F_CPU == 240000000
  #define MCLK_MULT 8
  #define MCLK_DIV  85
#else
  #error "This CPU Clock Speed is not supported by the Audio library";
#endif

#ifndef MCLK_SRC
#if F_CPU >= 20000000
  #define MCLK_SRC  3  // the PLL
#else
  #define MCLK_SRC  0  // system clock
#endif
#endif

void I2S_TDM::config_tdm(void)
{
  SIM_SCGC6 |= SIM_SCGC6_I2S;
  SIM_SCGC7 |= SIM_SCGC7_DMA;
  SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

  // if either transmitter or receiver is enabled, do nothing
  if (I2S0_TCSR & I2S_TCSR_TE) return;
  if (I2S0_RCSR & I2S_RCSR_RE) return;

  // enable MCLK output
  I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
  while (I2S0_MCR & I2S_MCR_DUF) ;
  I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));

  int rsync = 1;
  int tsync = 0; //async

  // configure transmitter
  I2S0_TMR = 0;
  I2S0_TCR1 = I2S_TCR1_TFW(4);
  I2S0_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
    | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  I2S0_TCR3 = I2S_TCR3_TCE;
  I2S0_TCR4 = I2S_TCR4_FRSZ(MBL-1) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
    | I2S_TCR4_FSE | I2S_TCR4_FSD;
  I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  // configure receiver (sync'd to transmitter clocks)
  I2S0_RMR = 0;
  I2S0_RCR1 = I2S_RCR1_RFW(4);
  I2S0_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
    | I2S_RCR2_BCD | I2S_RCR2_DIV(0);
  I2S0_RCR3 = I2S_RCR3_RCE;
  I2S0_RCR4 = I2S_RCR4_FRSZ(MBL-1) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
    | I2S_RCR4_FSE | I2S_RCR4_FSD;
  I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);

  // configure pin mux for 3 clock signals
  CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
  CORE_PIN9_CONFIG  = PORT_PCR_MUX(6); // pin  9, PTC3, I2S0_TX_BCLK
  CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
}

#elif defined(__IMXRT1062__)
/* for T4.x */

FLASHMEM
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv) // 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) // 2: 1/4; 1: 1/2; 0: 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
}

#ifndef AUDIO_SAMPLE_RATE_EXACT
  #define AUDIO_SAMPLE_RATE_EXACT 44100
#endif

#define BPF (8*32) // BitsPerFrame

void I2S_TDM::config_tdm(void)
{
  CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);

  // if either transmitter or receiver is enabled, do nothing
  if (I2S1_TCSR & I2S_TCSR_TE) return;
  if (I2S1_RCSR & I2S_RCSR_RE) return;
//PLL:
  int fs = AUDIO_SAMPLE_RATE_EXACT;
  // 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) / (fs * BPF * n1);

  double C = ((double)fs * BPF * n1 * n2) / 24000000;
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);
  set_audioClock(c0, c1, c2);
  // 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

  n1 = n1 / 2; //Double Speed for TDM

  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

  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

  int rsync = 0; // async
  int tsync = 1;

  // configure transmitter
  I2S1_TMR = 0;
  I2S1_TCR1 = I2S_TCR1_RFW(4);
  I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
    | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  I2S1_TCR3 = I2S_TCR3_TCE;
  I2S1_TCR4 = I2S_TCR4_FRSZ(MBL-1) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
    | I2S_TCR4_FSE | I2S_TCR4_FSD;
  I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  I2S1_RMR = 0;
  I2S1_RCR1 = I2S_RCR1_RFW(4);
  I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
    | I2S_RCR2_BCD | I2S_RCR2_DIV(0);
  I2S1_RCR3 = I2S_RCR3_RCE;
  I2S1_RCR4 = I2S_RCR4_FRSZ(MBL-1) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
    | I2S_RCR4_FSE | I2S_RCR4_FSD;
  I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);

  CORE_PIN23_CONFIG = 3;  //1:MCLK
  CORE_PIN21_CONFIG = 3;  //1:RX_BCLK
  CORE_PIN20_CONFIG = 3;  //1:RX_SYNC
}
#endif

/****************sketch **********************/
#include <Audio.h>

// GUItool: begin automatically generated code
I2S_TDM                  tdm1;
//AudioInputTDM            tdm1; 
AudioOutputUSB           usb1;           
AudioConnection          patchCord1(tdm1, 0, usb1, 0);
AudioConnection          patchCord2(tdm1, 1, usb1, 1);
// GUItool: end automatically generated code
void setup()
{
  AudioMemory(256);
  delay(1000);
//  tdm1.setShift(10);
}
void loop()
{
  delay(200); 
}

where I included a local implementation of the TDM code for both T3.x and T4.x to allow flexible 16-bit extraction from 32 bit words
As said,
ICS 43434 is working
ICS 52000 (4 channel) is not working

by not working I mean that 4 channels are send to teensy but the two channels send to PC are only noise (no acoustic signals)

We are the audio and acoustic group at a unverisity, we need a largr number of microphone for recording a noise source in all directions.

We have designed our own PCB with the ICS52000.
We have 100 of them,which cost several thousand of dollars.
Previously, we want to take advantage of the two TDM port of teensy 4.1
for connecting the microphones.

I do not know what should I do with them
 
Can you help us get the ICS52000 four microphone array working with Teensy 3.6.

Someone from the forum has successfully got the four microphone array working with Teensy 3.6.

We have tried their code and did not get any success. :(
 
i saw some one' code as shown below
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputTDM            tdm1;           //xy=359,317
//AudioRecordQueue         queue13;        //xy=620,615
//AudioRecordQueue         queue14;        //xy=620,649
//AudioRecordQueue         queue15;        //xy=620,682
//AudioRecordQueue         queue12;        //xy=621,581
//AudioRecordQueue         queue16;        //xy=622,716
//AudioRecordQueue         queue9;         //xy=623,480
//AudioRecordQueue         queue10;        //xy=623,514
//AudioRecordQueue         queue11;        //xy=623,547
//AudioRecordQueue         queue4;         //xy=624,315
//AudioRecordQueue         queue5;         //xy=624,348
//AudioRecordQueue         queue6;         //xy=624,381
//AudioRecordQueue         queue7;         //xy=624,414
//AudioRecordQueue         queue3;         //xy=625,281
//AudioRecordQueue         queue8;         //xy=625,447
AudioRecordQueue         queue2;         //xy=626,248
AudioRecordQueue         queue1;         //xy=627,214
AudioOutputUSB           usb1;           //xy=629,126
AudioConnection          patchCord1(tdm1, 0, queue1, 0);
AudioConnection          patchCord2(tdm1, 0, usb1, 0);
AudioConnection          patchCord3(tdm1, 0, usb1, 1);
AudioConnection          patchCord4(tdm1, 1, queue2, 0);
//AudioConnection          patchCord5(tdm1, 2, queue3, 0);
//AudioConnection          patchCord6(tdm1, 3, queue4, 0);
//AudioConnection          patchCord7(tdm1, 4, queue5, 0);
//AudioConnection          patchCord8(tdm1, 5, queue6, 0);
//AudioConnection          patchCord9(tdm1, 6, queue7, 0);
//AudioConnection          patchCord10(tdm1, 7, queue8, 0);
//AudioConnection          patchCord11(tdm1, 8, queue9, 0);
//AudioConnection          patchCord12(tdm1, 9, queue10, 0);
//AudioConnection          patchCord13(tdm1, 10, queue11, 0);
//AudioConnection          patchCord14(tdm1, 11, queue12, 0);
//AudioConnection          patchCord15(tdm1, 12, queue13, 0);
//AudioConnection          patchCord16(tdm1, 13, queue14, 0);
//AudioConnection          patchCord17(tdm1, 14, queue15, 0);
//AudioConnection          patchCord18(tdm1, 15, queue16, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=369,479
// GUItool: end automatically generated code

void setup() {
  
  Serial.begin(9600);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();

  while(!Serial);
  Serial.println("Testing tdm1 channel 1 most significant 16 bits to USB");

}

void loop() {
  // put your main code here, to run repeatedly:

}

There are two line about sgtl5000
I am just wondering does this sgtl5000 matters.?
 
HI WMXZ;

I also check the code from https://forum.pjrc.com/threads/48563-Cheap-ICS-52000-Microphone-Array-Board-for-TDM-evaluation
which successfully connected the Teensy 3.6 with the ICS52000 microphone array.

In their code, they also seems to contain several lines about the sgtl5000

Maybe this the source of the problem.

no, the sgtl5000 is for the pjrc audio board
re ICS52000 see https://forum.pjrc.com/threads/4856...TDM-evaluation?p=218184&viewfull=1#post218184
it seems that it is the NotWired.co board that is not working-
if you build a board following the original design, maybe the ICS52000 is working
 
maybe you can confirm that there are no other parts than the one shown in this picture
 

Attachments

  • ics52000.png
    ics52000.png
    48.1 KB · Views: 71
Yes, I confirm that, there are no other parts than the one shown in this picture.

maybe you can confirm that there are no other parts than the one shown in this picture

Yes, I confirm that, there are no other parts than the one shown in this picture.
 
maybe you can confirm that there are no other parts than the one shown in this picture

I think maybe it is the 100k resistor 's problems !!

In the pcb of the 4 ICS52000 microphone array, they do not have the resistor.
Thus we use the array, we may have to put a 100k resistor there. :D
 
We tried to connect our own ics52000 PCB with the Teensy 4.1.
But no luck, it is still not working. :confused:

I think maybe it is the ICS52000 microphone's problem.
 
Back
Top