2x PCM1808 ADCs recording LR ch. each (I2S quad input?) on T4 and also 1 DAC UDA1334

Status
Not open for further replies.

muon

Member
Context:
So for my college senior year design project I chose to make noise cancelling headphones(I bought off the shelf active noise cancelling headphones with hybrid system so 4 mics - 2 each side - 1 feed-forward outside the ear-cup and 1 feedback in side the ear-cup). I kept the mics in place but discarded the circuitry as I want to build my own. Initially went with pure analog design but didn't like the results.

Now, after some simulations between two STM32f103c blue-pill boards, one as mic emulator(noisy audio producer) and the other as a DSP emulator (matching delay, phase, and amplitude of Feed Forward Mic with the Feed Back Mic) and using some basic algorithms of a weight-mask convoluting with the Feed Forward data buffer in 'real time' to match the feedback buffer(in simulation I delayed the feedback from feed forward by 6 samples and amplitude *=0.42); Updating the weights each iteration trying to make the error zero.

The Meat:
Using the T4 as DSP.
I want to use 2x PCM1808s @48KHz and 24bits to record 4 channels - 2 per ADC (each ADC is recording 1 FF mic and 1 FB mic). Then do some basic processing on the captured samples and then replay 2 channel output @24bit 48KHz through the UDA1334A DAC by adafruit.
All of this preferably using the built in I2S HW.
I don't think the audio library is going to support this. All I need is some example showing me how to use the IS2 pins for input, setting the bits and ksps, working with the samples. Actually anything you guys think might be helpful is most appreciated.
Thanks!!
Let me know if I am not being clear enough or anything else! First time posting in any forums. :)
 
Would something like this work.. Maybe not the i2s1 output but how about having the quad channel (2 ADCs with Left and Right channels each)??

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2SQuad        i2s_quad1;      //xy=173,257
AudioRecordQueue         queueLeftFeedForward;         //xy=324,135
AudioRecordQueue         queueRightFeedForward;         //xy=359,417
AudioRecordQueue         queueLeftFeedBack;         //xy=370,180
AudioRecordQueue         queueRightFeedBack;         //xy=374,478
AudioOutputI2S           i2s1;           //xy=613,264
AudioConnection          patchCord1(i2s_quad1, 0, queueLeftFeedForward, 0);
AudioConnection          patchCord2(i2s_quad1, 1, queueLeftFeedBack, 0);
AudioConnection          patchCord3(i2s_quad1, 2, queueRightFeedForward, 0);
AudioConnection          patchCord4(i2s_quad1, 3, queueRightFeedBack, 0);
// GUItool: end automatically generated code

like this:
pjrcpost1.png

also I believe the quadi2s pinouts would have to change for the T4:
pjrcquad pinout post2.png
 
I've not used the quad channel stuff. Note, you would have to change the I2S pins from the Teensy 3.x pinout to the Teensy 4.0 pinout. I.e.
  • Instead of pin 9 for BCLK use pin 21/A7;
  • Instead of pin 11 for MCLK use pin 23/A9;
  • Instead of pin 13 for RX use pin 8;
  • Instead of pin 22/A8 for TX use pin 7;
  • Instead of pin 23/A9 for LRCLK use pin 20/A6.

Note, you will need to do a deep dive into the imxrt 1062 datasheet, but the first I2S channel actually has 5 pins that can be used for inputs and outputs. Paul has labeled them:
  • Pin 6 is OUT1D;
  • Pin 7 is OUT1A;
  • Pin 8 is IN1;
  • Pin 9 is OUT1C;
  • Pin 32 on a solder pad under the Teensy is OUT1B.

The datasheets for all of the PJRC products are in:

In the I.MX RT1060 Processor reference, I2S documentation is in section 37.2 which starts on page 2071 and goes through page 2112. While the i.MXrt 1062 has 3 I2S interfaces, evidently only two are brought out to the available pins.

In addition, the Teensy has a second I2S system:
  • Pin 2 is OUT2;
  • Pin 3 is LRCLK2;
  • Pin 4 is BCLK2;
  • Pin 5 is IN2;
  • Pin 33 on a solder pad under the Teensy is MCLK2.

That means in theory you could have up to 7 pairs of L/R channels.

So rather than using the quad channel stuff for the Teensy 3.x, you could use I2S1 and I2S2. Note, the audio tool has not yet caught up with the new features of the Teensy 4, but if you add I2S1 with the tool and change the I2S1 in the code to I2S2 it should work.

Obviously as you discover useful stuff, please post it.

Here is a real simple example using tone sweep on I2S2 for output. I was using an I2S board that didn't need MCLK2 connected. But if you need MCLK2 there are various ways to get access to the solder pads under the Teensy (look for the threads discussing breakout boards in the General Discussion sub-forum):

Code:
/*
  Demo of the audio sweep function.
  The user specifies the amplitude,
  start and end frequencies (which can sweep up or down)
  and the length of time of the sweep.

  Modified to eliminate the audio shield, and use the second I2S output on the
  Teensy 4.0.

  You can use the Max98357A mono I2S chip.
  https://smile.amazon.com/gp/product/B07PS653CD/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1

  Pins:		Teensy 4.0

  LRCLK:	Pin 3
  BCLK:		Pin 4
  DIN:		Pin 2
  Gain:		see below
  Shutdown:	N/C
  Ground:	Ground
  VIN:		5v

  Other I2S2 pins not used by the Max98357A device:

  MCLK		Pin 33
  VOUT		Pin 6

  Gain setting:

  15dB	if a 100K resistor is connected between GAIN and GND
  12dB	if GAIN is connected directly to GND
   9dB	if GAIN is not connected to anything (this is the default)
   6dB	if GAIN is conneted directly to Vin
   3dB	if a 100K resistor is connected between GAIN and Vin.

  SD setting (documentation from the Adafruit board)

  This pin is used for shutdown mode but is also used for setting which channel
  is output. It's a little confusing but essentially:

  * If SD is connected to ground directly (voltage is under 0.16V) then the amp
    is shut down

  * If the voltage on SD is between 0.16V and 0.77V then the output is (Left +
    Right)/2, that is the stereo average.

  * If the voltage on SD is between 0.77V and 1.4V then the output is just the
    Right channel

  * If the voltage on SD is higher than 1.4V then the output is the Left
    channel.

    This is compounded by an internal 100K pulldown resistor on SD so you need
    to use a pullup resistor on SD to balance out the 100K internal pulldown.

  Or alternatively, use the HiLetgo PCM5102 I2S IIS Lossless Digital Audio DAC
  Decoder which provides stereo output:
  https://smile.amazon.com/gp/product/B07Q9K5MT8/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1
  */

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthToneSweep	tonesweep;		//xy=99,198
AudioMixer4		mixer2;			//xy=280,253
AudioMixer4		mixer1;			//xy=280,175
AudioOutputI2S2		i2s2;			//xy=452,189

AudioConnection		patchCord1(tonesweep, 0, mixer1, 0);
AudioConnection		patchCord2(tonesweep, 0, mixer2, 0);
AudioConnection		patchCord3(mixer2, 0, i2s2, 1);
AudioConnection		patchCord4(mixer1, 0, i2s2, 0);
// GUItool: end automatically generated code

const float	t_ampx	= 0.8;
const int	t_lox	= 10;
const int	t_hix	= 22000;
const float	t_timex	= 10;		// Length of time for the sweep in seconds

// Do a sweep in both directions, enabling or disabling the left/right speakers
void do_sweep (int i)
{
  int do_left  = (i & 1) != 0;
  int do_right = (i & 2) != 0;
  float gain   = (do_left && do_right) ? 0.5f : 1.0f;

  Serial.printf ("Sweep up,   left = %c, right = %c\n",
		 (do_left)  ? 'Y' : 'N',
		 (do_right) ? 'Y' : 'N');

  mixer1.gain (0, do_left  ? gain : 0.0f);
  mixer2.gain (0, do_right ? gain : 0.0f);

  if (!tonesweep.play (t_ampx, t_lox, t_hix, t_timex)) {
    Serial.println ("ToneSweep - play failed");
    while (1)
      ;
  }

  // wait for the sweep to end
  while (tonesweep.isPlaying ())
    ;

  // and now reverse the sweep
  Serial.printf ("Sweep down, left = %c, right = %c\n",
		 (do_left)  ? 'Y' : 'N',
		 (do_right) ? 'Y' : 'N');

  if (!tonesweep.play (t_ampx, t_hix, t_lox, t_timex)) {
    Serial.println("ToneSweep - play failed");
    while (1)
      ;
  }

  // wait for the sweep to end
  while (tonesweep.isPlaying ())
    ;

  Serial.println ("Sweep done");
}

void setup(void)
{
  // Wait for at least 3 seconds for the USB serial connection
  Serial.begin (9600);
  while (!Serial && millis () < 3000)
    ;

  AudioMemory (8);
  Serial.println ("setup done");

  for (int i = 1; i <= 3; i++)
    do_sweep (i);

  Serial.println ("Done");
}

void loop (void)
{
}
 
Last edited:
Thanks so much! I'll update as I progress. I am thinking of using the I2S1 and I2S2 audioinput instances with an external 12.288MHz clock for a common MCLK... so I think the ADCs will be in Master Mode, I dont know how well the T4 handles async sampling (dropped frames and stuff?!) Now I just need to figure out ways of controlling the resolution and sample rate to 24bits@48KHz but I read somewhere that working with anything over 16bits is a whole different story, the T4 processes in 16bit chunks and it will get very slow. Lets see how it goes. Thanks again.
 
So I'm having a couple of thoughts...

Here I am testing instances of AudioInputI2S and AudioInputI2S2 for capturing samples in a queue and printing them(mostly copy code from recording example). But this is all gonna take place at 16bit @44.1KHz while I want 24bit@48KHz So...
Code:
#include <Audio.h>

AudioInputI2S             i2s1;
AudioInputI2S2            i2s2;
AudioRecordQueue          queue1;
AudioRecordQueue          queue2;
AudioConnection           patchCord1(i2s1, 0, queue1, 0);
AudioConnection           patchCord2(i2s2, 0, queue2, 0);

void setup() {
  Serial.begin(115200);
  AudioMemory(60);
  queue1.begin();
  queue2.begin();
}

void loop() {
  byte buffer1[512];
  byte buffer2[512];

  memcpy(buffer1, queue1.readBuffer(), 256);
  queue1.freeBuffer();
  memcpy(buffer1 + 256, queue1.readBuffer(), 256);
  queue1.freeBuffer();
  memcpy(buffer2, queue2.readBuffer(), 256);
  queue2.freeBuffer();
  memcpy(buffer2 + 256, queue2.readBuffer(), 256);
  queue2.freeBuffer();
  for (int i = 0; i < 512; i++)
  {
    Serial.println(buffer1[i]);
  }
  for (int i = 0; i < 512; i++)
  {
    Serial.println(buffer2[i]);
  }
}

I think what I might do is inside the "audio_block_struct" in AudioStream.h,
audiostream.png
change the data[AUDIO_BLOCK_SAMPLES] data type from int16_t to int32_t and change the
#define AUDIO_SAMPLE_RATE_EXACT 44100.0f
to
#define AUDIO_SAMPLE_RATE_EXACT 48000.0f

I don't know how deep the implications of this might be.
What do you folks think?
 
Did you het the PCM1808 ADC working on the teensy 4? I am. interested because I am workong on an Audio to ethernet brdge and would like to exoerimemt with the PCM1808 before I go to some multichannel variant.
 
Last edited:
Status
Not open for further replies.
Back
Top