Teensy Microphone Module

Neutronned

Well-known member
I've started work on a simple adaptation of the SparkFun Microphone Breakout board. Same basic design with three changes:
1) Bulk cap on the 3.3V supply - cause more is better
2) Added a 1.2V precision reference to get the bias voltage down to 0.6V (versus half supply)
3) Added 0.2 inches to the board length to accommodate the new parts.

Mic draft.jpg

If you have time, please share your thoughts on the design. Eagle files here:

https://www.dropbox.com/sh/960lqz5ub5nsx24/AAAl_404IylyiDKQwaTezwT1a?dl=0

Thanks!
-Leon
 
Just something to consider........

Take a quick look at the MAX9814 Microphone Amplifier with AGC and Low-Noise Microphone Bias

http://datasheets.maximintegrated.com/en/ds/MAX9814.pdf

It appears to integrate inside the chip many of the features you are trying achieve at or below the cost of the OPA344 and LM4041.

Additionally, it provides a lot of extra features that I find useful in a microphone input system such as AGC (which can be disabled if desired).


oops ----- I missed your target output bias level of 0.6v.
This device's output pin is biased at 1.2v DC.
You would have to add a DC blocking cap and a voltage divider from the Low-Noise Microphone Bias supply to achieve your desired output bias level of 0.6v DC to the teensy.. See Cout in the typical application circuit on page 11 of the above data sheet.
 
Last edited:
You would have to add a DC blocking cap and a voltage divider from the Low-Noise Microphone Bias supply to achieve your desired output bias level of 0.6v DC to the teensy.. See Cout in the typical application circuit on page 11 of the above data sheet.

Nice part - I'll start an alternate design and see how it lays out. On the bias, I'm having a bit of a brain-block here, but why couldn't we simply add a resistor divider on the output to cut the 1.24V normal idle voltage in half? As the part output swings from 3mV to 2450mV, the 1/2 divider would go from 1.5mV to 1.225mV, centered at 620mV. It's got a 50 ohm output impedance, so we could drive a couple of 1K ohm resistors no problem. There has to be something I'm missing - need coffee!
 
Quick update - working on the two designs in parallel - the simple Sparkfun derivative based on an op-amp and an Adafruit derivative based on the MAX9814. My Sparkfun microphones came in and I tried the simple R-divider on the output to get the midpoint to 0.6V. Works fine. Still dealing with Teensy digital noise getting in the analog gain chain, so I'm experimenting with power in and audio out filters.

Mic MAX9814 draft.jpg

Mic MAX9814 PCB draft.jpg

It's a little on the large side - 0.8" x 1.3" (large postage stamp) so I'm still working on it.
 
Last edited:
Does FB2 help or hinder? Testing stand-alone is one thing, but this really should be tested in a harsh scenario, like where many WS2812B or APA102 LEDs are being used, or PWM is driving a motor, or RC servo motors are moving. Isolating the grounds when the ADC signal is ground referenced might be more harmful than any reduction of noise to the preamp helps. Maybe?

Likewise, does FB3 cause the ADC to make worse measurements? It generally needs a low impedance at high frequency, since it rapidly switches a capacitor inside the chip to make the measurement.
 
Just for the sake of low impedance driving the ADC, I have a clear preference for a design like in #1 where an opamp drives the ADC directly. But in that design, there should perhaps be a filtering for the microphone bias by adding a 100R + 10uF before R1.

My inner musician says "beware of AGC because it is an abomination!" :p . It leads the wide dynamic range which is theoretically possible with digital recording "ad absurdum" as the Romans said. Background noise captured by the microphone risks to be artificially blown up when your desired signal is weak and most of what you do for having a sophisticated musical expression when playing will be leveled out in the recording by the AGC. I'd rather go for a classic volume potentiometer partially replacing R5 and add some simple code lighting a LED when the sampled values reach the sampling extremes with a distance of ~4dB, thus > 3348 or < 748 when working with a 12bit resolution.
 
It would add quite a bit to the cost & size, but a little 4 to 6 LED level meter to assist with adjusting the gain pot would be a great feature. Ideally, the level meter should have a visual peak holding feature, where the LED for a brief peak stays on for a 10th of a second or so.
 
Driving a little 4 to 6 LED level meter with peak holding feature and a peak LED could be done by the Teensy, using the raw ADC data. In that way, it would cost less (out of a few I/O pins, resistors and LEDs) and it would be optional for those who want it.
 
Paul - agree! The inductors are 2500ohms at 70MHz and less than an ohm in the audio band. Hopefully they don't get in the way but if they do, I'll drop in 0-ohm resistors.

ACG - we should be able to defeat it via the external components. I'll read up more on that.

LEDs - great idea but I'm concerned about size and digital noise. I've used the LM3914/3915 10-segment LED bargraph display drivers in the past and love those things - simple to use and all analog. But they are only available in the large DIP package and would double the size. Any suggestions on similar chips? Otherwise, a small comparator circuit that drives an RGB led might be possible. Green below 0.6Vp-p, yellow up to 0.8Vp-p and red above 0.8Vp-p. Thresholds TBD.

Three-level-comparator.png

Found this via Google. Red/Yellow/Green good enough??

Or - 4 levels:
opamp108.gif
 
Last edited:
Boards on order, parts on order, everything should be here in two weeks. I'll build up a couple to check for major issues and report back.
 
I'd love to be able to use something like this for music response on my Teensy glow-staff. However, as Paul mentioned, immunity to electrical noise is critical. I'm switching up to 800 APA102 LEDs on and off every two milliseconds. I've tried before using other circuits, but been unable to get a useful audio signal given all the junk created by the LED switching.

And I'm fine with AGC, as this is for use at festivals in front of huge Funktion One rigs as well as smaller events, so the range of volume to cover is pretty broad.

Anyway, I hope it works well and I look forward to seeing what you come up with.
 
Status: The boards are due in from OSHPark next week. Components are already in so I should have some built in a couple of weeks, max.
 
First analog-version board built:
1) Changed 100 ohm resistor from 3.3V to mic board to 1 ohm - was getting oscillation on the power supply
2) Changed the two inductors on the Ground and Output to zero ohm resistors

https://youtu.be/PwyCAnzQNIo

https://youtu.be/lwbg7Gb1XIw

Still getting occasional bursts of noise on the 3.3V that punches through my simple filter AND I need to get some Neopixels hooked up to see their impact. More testing to come.
 
Last edited:
First MAX9814-based microphone built and wow, does this work nice!
1) Changed 3.3V power input resistor to 1ohm - same as analog version
2) Changed the two inductors on ground and output to zero ohm resistors - same as analog version

In addition to the LCD, I added a string of 45 Neopixels to test for noise. I have the LEDs running right under the microphone.

IMG_1377.JPGIMG_1378.JPG

https://youtu.be/kaBH-RVNdoU
https://youtu.be/b0YMftwrT4o

The filtered power is rock steady and mic performance seems great. Soldering that MAX9814 IC sucks a bit so building more will take time but I think I'll build up 8 to 10 of these and send them out for people to play with. Let me know if you have time to test in your project.
 
Thanks for working on this, it's very interesting.

You say you removed FB2 and FB3, did you leave FB1 as the 2500 Ohm @ 70 Mhz chip?

Did you use a stencil & oven to reflow the MAX9814 or did you do it by hand?
 
Did you use a stencil & oven to reflow the MAX9814 or did you do it by hand?

For me, the trick to solder these TDFN and QFN style parts by hand is LOTS of flux, a fairly large soldering iron tip, and a 'ball' of solder on the tip. You can then just 'swipe' or 'roll' the solder ball down the edge of the chip. A little of practice will allow you to find a technique that allows the solder to wet the pins nicely but then the solder will flow back to just the pin and pad area as you move the soldering iron tip with the solder 'ball' along the edge.

No shorts, nice look, quick. Similar to what happens in an old wave solder machine. The big solder wave rolls by and then the still warm solder flows back to cover only the pin and pad.

This technique also works wonders on fine pitch QFP and TQFP pins. You can 'flow solder' a whole side of a chip containing dozens of fine pitch pins in two seconds. Sometimes the last two pins will have a residual solder bridge and require a quick touchup with solder removal braid but that simple touchup is still much faster than trying to do the job a pin at a time.

The exposed pad on the bottom of the chip is a little more problematic as it is difficult to access. I sometimes cheat and put several vias under the chip in the exposed pad area. This gives me some 'holes' from the back side of the board that I can flood with solder. Again lots of flux is your friend. You can easily watch the solder flow down the hole and connect the 'hidden' exposed pad.

I've watched my assemblers hand place and completely hand solder these type of parts faster than I can get the part placed properly on the pads myself.... I guess my older hands are not as steady as they used to be!!
 
Thanks for working on this, it's very interesting.

You say you removed FB2 and FB3, did you leave FB1 as the 2500 Ohm @ 70 Mhz chip?

Did you use a stencil & oven to reflow the MAX9814 or did you do it by hand?

FB1 is still that high-value inductor to hopefully knock any clock noise off the 3.3V line. I only have a 60MHz scope, so I won't be able to see anything too fast but based on what I seen so far, no issues.

I did solder it by hand. I placed a dot of low-temp solder paste on each pad and the center pad, placed the part and heated it up with a small heat gun until the solder reflowed. I then went back around and added a small amount of paste to those pads that looked light on solder and touched them up with a very pointy tip soldering iron.

I have ordered up a stencil and some more parts from DigiKey, but the stencil isn't stocked and it'll be 10days to delivery. I'll probably try a couple more by hand just to test my patience.
 
For me, the trick to solder these TDFN and QFN style parts by hand is LOTS of flux...

I'm a bit scared to try this for fear of creating a large ball of solder with a small IC in the middle! As an experiment, I might try the paste/heat gun technique for the center pad to get the part down and then tackle the side pins using your rolling solder process.

As a side note, that low-temp solder paste is really nice stuff! For the 0805s, I place a dot on the pads with a small syringe tip (one part at a time) and place the parts into the paste. I hold the part down with the tip of the tweezers and use that sharp point soldering iron tip to melt the paste. If I was too light with the paste to begin with, I add a bit more and hit it again.
 
FB1 is still that high-value inductor to hopefully knock any clock noise off the 3.3V line. I only have a 60MHz scope, so I won't be able to see anything too fast but based on what I seen so far, no issues.

I did solder it by hand. I placed a dot of low-temp solder paste on each pad and the center pad, placed the part and heated it up with a small heat gun until the solder reflowed. I then went back around and added a small amount of paste to those pads that looked light on solder and touched them up with a very pointy tip soldering iron.

I have ordered up a stencil and some more parts from DigiKey, but the stencil isn't stocked and it'll be 10days to delivery. I'll probably try a couple more by hand just to test my patience.

Thanks for the info.

Regarding the stencils, I've had good luck with Osh Stencils (https://www.oshstencils.com). I can't recall if I've ever had them make a 0.4mm pitch one but have definitely done several 0.5mm ones with no issue.
 
Sounds like great progress!

I'd be happy to test if you're looking, Nuetronned.

I have a project where I can swap out another mic, and compare results. I'm using an electret wired to the mic and gnd pins of the audio shield, and using I2S for input.

My current project has a pair of 4" wires between the electret mic and the Audio shield.
Would that length of patch wire affect testing?
If you leave the mic unsoldered, I can mount the Mic IC near the Teensy, and string the same wires.
Otherwise, I can mount the Mic IC at the sound source, and route the output from the IC to the Teensy with similar wires.

Let me know if you'd like me to test.
 
Sounds like great progress!

I have a project where I can swap out another mic, and compare results. I'm using an electret wired to the mic and gnd pins of the audio shield, and using I2S for input.

This design needs to connect to an analog input of the Teensy and it probably won't work connecting to the mic input of the audio board. My guess is that the audio board amplifies the electret mic itself.

Here is my test code - sorry for the length!!

// Spectrum Analyzer using a Teensy 3.2 & the Adafruit 1.8" TFT module w/ ST7735S chip
// Test microphone is connected to A3
// Adding some Neopixel code to test interaction

//*********************************************************************************** INIT
//
#define TFT_SCLK 14 // SCLK can also use pin 13
#define TFT_MOSI 7 // MOSI can also use pin 11
#define TFT_CS 10 // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define TFT_DC 20 // but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define TFT_RST 8 // RST can use any pin
#define SD_CS 4 // CS for SD card, can use any pin
#define MIC_GAIN 2 // multiplier for the specific mic

#include <Adafruit_GFX.h> // Core graphics library
#include <ST7735_t3.h> // Hardware-specific library for the ST7735 LCD controller
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>
#include <Adafruit_NeoPixel.h>

// create the LCD object
ST7735_t3 tft = ST7735_t3(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

// Assign human-readable names to some common 16-bit color values:
#define WHITE 0xFFFF
#define BLACK 0x0000
#define GREEN 0x07E0

//Set-up the audio library functions
AudioInputAnalog adc1(A3); //default for adc1 is A2
AudioAnalyzeFFT1024 fft1024_1;
AudioConnection patchCord1(adc1, fft1024_1);

// An array to hold the 16 frequency bands
float level[16];

#define LEDPIN 2 // digital output pin for the LEDs
#define NUMLEDS 45 // number of LEDs
#define LEDBRIGHTNESS 64 // display brightness
byte colorMap = 160; // the offset position for the color map
byte colorScale = 2; // the variation in the color map - 0=one color, 4=full spectrum

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMLEDS, LEDPIN, NEO_GRB + NEO_KHZ800);


//*********************************************************************************** SETUP
void setup() {
strip.begin();
strip.show(); // Initialize all pixels to 'off'
strip.setBrightness(LEDBRIGHTNESS);

tft.initR(INITR_BLACKTAB); // initialize a ST7735S chip, black tab
tft.fillScreen(ST7735_BLACK);

tft.setRotation(1); // 1=270
tft.setTextColor(WHITE);
tft.setTextSize(1);
tft.print("TFT Spectrum Analyzer ");

// Audio connections require memory to work.
AudioMemory(12);

// Configure the window algorithm to use
fft1024_1.windowFunction(AudioWindowHanning1024);

}

//*********************************************************************************** LOOP
void loop() {
byte pColor; // a color map position
byte Channel; // specific frequency channel
byte Red1; // temporary Red value
byte Green1; // temporary Green value
byte Blue1; // temporary Blue value

//****************************************************************************** INPUT
if (fft1024_1.available()) {
// each time new FFT data is available, bin it into 16 channels
// Note- response doesn't seem flat so each is normalized <experimenting!
// Note- these calculations go very fast!!

level[0] = fft1024_1.read(0) * 1.21 * MIC_GAIN;
level[1] = fft1024_1.read(1) * 1.18 * MIC_GAIN;
level[2] = fft1024_1.read(2, 3) * 1.15 * MIC_GAIN;
level[3] = fft1024_1.read(4, 6) * 1.12 * MIC_GAIN;
level[4] = fft1024_1.read(7, 10) * 1.09 * MIC_GAIN;
level[5] = fft1024_1.read(11, 15) * 1.06 * MIC_GAIN;
level[6] = fft1024_1.read(16, 22) * 1.03 * MIC_GAIN;
level[7] = fft1024_1.read(23, 32) * 1.00 * MIC_GAIN;
level[8] = fft1024_1.read(33, 46) * 0.96 * MIC_GAIN;
level[9] = fft1024_1.read(47, 66) * 0.93 * MIC_GAIN;
level[10] = fft1024_1.read(67, 93) * 0.90 * MIC_GAIN;
level[11] = fft1024_1.read(94, 131) * 0.87 * MIC_GAIN;
level[12] = fft1024_1.read(132, 184) * 0.84 * MIC_GAIN;
level[13] = fft1024_1.read(185, 257) * 0.81 * MIC_GAIN;
level[14] = fft1024_1.read(258, 359) * 0.78 * MIC_GAIN;
level[15] = fft1024_1.read(360, 511) * 0.75 * MIC_GAIN;

//****************************************************************************** LED OUTPUT
for (int i = 0; i < NUMLEDS; i++){ // cycle through all the LEDs
pColor = i * colorScale * 4 + colorMap; // this results in a color map position
Channel = i/3 ; // there are 16 frequency bands - not perfect - needs better math

Red1 = (WheelR(pColor) * level[Channel]) ; // brightness proportionally to the loudness
Green1 = (WheelG(pColor) * level[Channel]) ; // of the individual channel
Blue1 = (WheelB(pColor) * level[Channel]) ;

strip.setPixelColor(i, Red1, Green1, Blue1); // color the pixel with the given color

}

strip.show(); //show the new strip colors - FYI - takes rough 4.66 milliseconds on the Uno


//****************************************************************************** LCD OUTPUT
for (byte i = 0; i < 16; i++){ // cycle through the 16 channels

// takes about 1.66msec on a Teensy 3.2 to draw two fillRect 9-pixels wide - one
// black to erase any old information and one for the new data
// takes about 190usec per line on a Teensy 3.2 to draw the two drawFastVLines
// needed to erase the old information and draw a new line

int line1 = level * 100;
if (line1 > 100){
line1 = 100;
}

tft.fillRect( i*10, 10, 10, 128-line1, BLACK); //erase old information
tft.drawRect( i*10, 128-line1, 10, line1, WHITE); //paint new bar

for (byte j = 1; j < 9; j++){ // draw muliple lines to create the bar
//tft.drawFastVLine( i*10 + j, 10, 128-line1, BLACK);
tft.drawFastVLine( i*10 + j, 128-line1, line1, Wheel565(i * 15 + 60));
}

}
}
}


//*******************************************************************************************/
// Wheel565: standard color look-up routine - Input a value 0 to 255 to get an RGB color value.
// NOTE: The colors are a transition Green (0) to Red (85) to Blue (170) back to Green
// RGB565 format!!
// R4-R3-R2-R1-R0-G5-G4-G3-G2-G1-G0-B4-B3-B2-B1-B0 Bits
// |-----------|-----------|-----------|-----------| Bytes
//*******************************************************************************************/
long Wheel565(byte WheelPos) {
if(WheelPos < 85) {
return ((((WheelPos * 3) / 8) * 0x0800) + ((63 - ((WheelPos * 3) / 4)) * 0x0020));
} else if(WheelPos < 170) {
WheelPos -= 85;
return (((31 - ((WheelPos * 3) / 8)) * 0x0800) + (0 * 0x0020) + (WheelPos * 3 / 8));
} else {
WheelPos -= 170;
return ((((WheelPos * 3) / 4) * 0x0020) + (31 - ((WheelPos * 3) / 8)));
}
}

//******************************************************************************************/
// WheelR: Neopixel RGB color wheel - returns the Red byte (not the entire RGB word)
byte WheelR(byte WheelPos) {
if(WheelPos < 85) {
return WheelPos * 3;
}
else if(WheelPos < 170) {
WheelPos -= 85;
return 255 - WheelPos * 3;
} else {
return 0;
}
}
//*******************************************************************************************/
// WheelG: same as above but just returns the Green byte
byte WheelG(byte WheelPos) {
if(WheelPos < 85) {
return 255 - WheelPos * 3;
} else if(WheelPos < 170) {
return 0;
} else {
WheelPos -= 170;
return WheelPos * 3;
}
}
//*******************************************************************************************/
// WheelB: same as above but just returns the Blue byte
byte WheelB(byte WheelPos) {
if(WheelPos < 85) {
return 0;
} else if(WheelPos < 170) {
WheelPos -= 85;
return WheelPos * 3;
} else {
WheelPos -= 170;
return 255 - WheelPos * 3;
}
}
 
Back
Top