Project in progress
Arduino Zero PDM Microphone

Arduino Zero PDM Microphone © CERN-OHL

Example of the use of a Digital PDM microphone.

  • 542 views
  • 1 comment
  • 2 respects

Components and supplies

Apps and online services

About this project

PDM & Mems Microphones
Mems microphones are hot, used in Cell Phones due to their small package and digital interface. Example is the MP34DT06J from ST Micro, 6 pins Mems micro - interesting reading link.

To use this with an Arduino is a little bit challenging: requires some DSP processing before you have a decent Audio Signal. This is the test circuit diagram:

We use 2 Mems micro's, one for left, one for right channel. Easy connection to build if you use the STMicro eval-board STEVAL-MIC002v1. For practical usage you can snap them out of the PCB-frame and pin them on a breadboard.

PDM Principles
PDM stands for Pulse Density Modulation. Where most audio is send as PCM (every samples has a coded value), PDM is the result of a sigma-Delta ADC and send 1 or 0's as relative density in a clocked bit-stream.

To reconstuct the PCM signals we need to measure the 0's and 1's density, and put the signal through a low-pass filter. This combining is called decimation. ie every XX pulses are filtered and decimated to one value. This means that a 22Khz audio sampling signal needs to have 2x sample speed times the decimator, (ie 64): it has 64x44Khz = 2.8Mhz clock-speed data ... wow. This is interesting reading : STM32 PDM project

Zero I2S connections
To read the PDM clocked data we can use the I2S interface of the Arduino Zero. Please see the details in the spec or AppNote. We can configure the I2S interface for PDM (mono and stereo)., In this case we use stereo (clocked data on every edge - up and down- ) Use the PDMZero-Library to setup the right settings, Data is read in 32-bit value with upper and lower word the left and right channel bit-stream (in reversed order )

Sketch 1 : Decimation Filter
So the I2S interface provides raw data, in this project I use a 128-taps FIR-filter as decimator to re-construct the PDM to PCM data. The filter is instantiated in a loop of 16-bits shift operation, where every 128-bis are FIR-filtered. PCM data is saved in an Array of 2 values (stereo).
As the interface is clocked data (Arduino clocks out, PDM microphone sends data back) the length of your I2S cable should not be too long. 20cm is advised max, use shielded cable to extend this a little bit. In this project we used a 3D-printed Pyramid to lock the Mems Microphone under 45 degrees upwards, extended with 35cm cable.

Sketch 2 : Volume Averaging
In
this example the audio samples are use to calculate a running average Volume, usable for example to make a VU-led-Meter. This volume is adaptive, meaning it tracks the running min and max and calculates the relative volume in between.

Code

PDMZero.zipC/C++
Arduino Library for I2S / PDM
No preview (download only).
PDMZero_VolumeMeter.inoC/C++
PDM example: Volume / VU Meter
/*************************************************************
PDM Microphone example
By JV March 2019 Version 1.00

running volume calculation for VU-Meter usage.
Use 128 decimation for PCM signal.
Auto Gain the volume (keep track of everge max/min volume)

Turn on PLOTTER !!

*************************************************************/
#include <PDMZero.h>
#define DBG 1  // Debug info

// Definitions for Audio PDM Microphone
#define SAMPLERATE_HZ 9375
#define DECIMATION    128
#define PCMTIME 15           // sample evaluatiuon time in milliseconds
#define PCMTRESHOLD 4000
uint16_t sincfilter[DECIMATION] = {0,  0,  1,  2,  4,  7,  10, 15, 19, 25, 31, 39, 47, 56, 66, 77, 89, 103,  118,  134,  151,  170,  189,  211,  233,  258,  282,  309,  337,  366,  396,  428,  460,  493,  527,  562,  598,  634,  670,  707,  743,  780,  816,  852,  888,  922,  956,  988,  1021, 1050, 1079, 1105, 1131, 1153, 1176, 1193, 1211, 1224, 1237, 1245, 1253, 1255, 1258, 1255, 1253, 1245, 1237, 1224, 1211, 1193, 1176, 1153, 1131, 1105, 1079, 1050, 1021, 988,  956,  922,  888,  852,  816,  780,  743,  707,  670,  634,  598,  562,  527,  493,  460,  428,  396,  366,  337,  309,  282,  258,  233,  211,  189,  170,  151,  134,  118,  103,  89, 77, 66, 56, 47, 39, 31, 25, 19, 15, 10, 7,  4,  3,  2,  1,  1,  0,  0,  0};
PDMZero Mypdm = PDMZero(2, A6);   // Create PDM receiver object, with Clock and Data pins used clock=> PA10 = ~2  data => PA07 = A6  MKR1000 / MKR1010
#define PDM_REPEAT_LOOP_16(X) X X X X X X X X X X X X X X X X // a manual loop-unroller!

uint16_t avgl= 0x8000,tavgl = 0x8000, avgr= 0x8000, tavgr = 0x8000,Acoustic_Counter=0; // Global Avarege rolling Audio signal level
uint32_t minvoll=0xffffffff,minvolr=0xffffffff,maxvoll=0,maxvolr=0,avgvolume=0x00001000; // lowest volume level... probably Noise level
uint32_t VolumeLeft = 0, VolumeRight = 0;


void setup()
{ 

/*********** Serial SETUP  **********/
int t=10;  //Initialize serial and wait for port to open, max 10 second waiting
  Serial.begin(921600UL);
  while (!Serial) {
    ; delay(1000);
    if ( (t--)== 0 ) break;
  }
/*********** PDM over I2S Setup ************/
if (!Mypdm.begin()) {
    Serial.println("Failed to initialize I2S/PDM!");
    while (1);
   }
if (!Mypdm.configure(SAMPLERATE_HZ * DECIMATION/16, true)) {
    Serial.println("Failed to configure PDM");
    while (1);
  }
while ( Mypdm.read() ==0) Serial.print("."); // read till first sample are avaialble
Serial.println("PDM Test starts");             
}



void loop()
{
uint16_t vu_left=0,vu_right=0;             // vuMeter Variable
Read_AudioVolume(&vu_left,&vu_right);
//Serial.println(vu_left);                   // turn on your plotter !!
}

// Read audio levels and convert to running Volume
// Rease I2S Samples, Reduce them by Decimator, calcualte volume over Ms Samples read
void Read_AudioVolume(uint16_t* leftchannel,uint16_t* rightchannel) {
uint16_t  t,peakl,peakr;
uint16_t runningsuml, runningsumr;

VolumeLeft=0;VolumeRight=0; // Relative volume per Sampled Time slot PCMTIME

  for (t = 0; t < (SAMPLERATE_HZ * PCMTIME) / 1000; ++t)
  {
    runningsuml=0; runningsumr=0; 
    uint16_t *sinc_ptr = sincfilter; // pointer to 64bit FIR-filer cooeficients

    for (uint8_t samplenum = 0; samplenum < (DECIMATION / 16) ; samplenum++) {
      uint32_t sampler = Mypdm.read();    // we read 32 bits at a time, high and low 16bit stereo
      uint32_t samplel = 0xFFFF & (sampler >> 16);
      samplel = samplel & 0xFFFF;

      PDM_REPEAT_LOOP_16(      // manually unroll loop: for (int8_t b=0; b<16; b++)
      {
        if (samplel & 0x1) {            // start at the LSB which is the 'first' bit to come down the line, chronologically
          runningsuml += *sinc_ptr;     // do the convolution Left channel
        }
        if (sampler & 0x1) {            // start at the LSB which is the 'first' bit to come down the line, chronologically
          runningsumr += *sinc_ptr;     // do the convolution Right channel
        }
        sinc_ptr++;
        samplel >>= 1;                  // right shift :  I2S_SERCTRL_BITREV ,last input bit is at MSB 
        sampler >>= 1;                  // right shift :  I2S_SERCTRL_BITREV last input bit is at MSB 
      }
      )
      
    } // end decimation loop
    // we are only interested in the volume part: summarize the  Sample offset - average
   if ( runningsuml!=0 && runningsumr!=0 ) {
    avgl = (avgl * t + runningsuml) / (t + 1); // calculate running average
    avgr = (avgr * t + runningsumr) / (t + 1);
    peakl = abs(runningsuml - avgl);           // calcualte peak vs average
    peakr = abs(runningsumr - avgr);
       VolumeLeft += peakl;                     // summarize volume by adding all peaks
       VolumeRight += peakr;
    
    } // end sample process !=0
   
  } // end Sample For loop
  
  // process abs volume to relative volume
  if (VolumeLeft!=0 &&  VolumeRight!=0 ){
  avgvolume = (avgvolume*126 +VolumeLeft+VolumeRight)/128;               // average over 32 last samples = 32* 20ms = 1 seconde (uncontinous!)

      if ( maxvoll <= (2*avgvolume) ) maxvoll = 2*avgvolume;           // keep gap high enough : 100% vs Avg
      else  maxvoll = (255*maxvoll)/256;
      maxvolr = maxvoll;
      minvoll = (256*minvoll)/255;  // 1% increase
      minvolr= minvoll;
 
    // housekeeping min max and average
    if (VolumeLeft < minvoll ) minvoll = VolumeLeft;
    if (VolumeRight < minvolr ) minvolr = VolumeRight;
    if (VolumeLeft > maxvoll ) maxvoll = VolumeLeft;
    if (VolumeRight > maxvolr ) maxvolr = VolumeRight;  
    if(VolumeLeft>=PCMTRESHOLD ){*leftchannel =  (uint8_t) ( (100*(VolumeLeft-minvoll)) / (maxvoll-minvoll) ) ;  }
    else{ *leftchannel =  0x00; }
    if(VolumeRight>=PCMTRESHOLD ){ *rightchannel = (uint8_t) ( (100*(VolumeRight-minvolr)) / (maxvolr-minvolr) ) ; }
    else{ *rightchannel = 0x00 ; }
  }
//Serial.print(VolumeLeft);Serial.print(","); 
Serial.print(minvoll);Serial.print(",");
Serial.print(maxvoll);Serial.print(",");
Serial.print(avgvolume);Serial.print(",");
Serial.print((*leftchannel)*32);Serial.print(",");
Serial.print((*rightchannel)*32);Serial.println(",");
}

Custom parts and enclosures

3D Print File
3D STL file for pyramid microphone casing

Schematics

Schematic PDM connect
PDM connect via I2S to Arduino
Schematic pdm pywlcwnsnt
MP34dt06j Datasheet
PDM Microphone - ST Micro

Comments

Similar projects you might like

Monitor Your Energy Bill via Modbus, MKR WiFi 1010 and RS485

Project tutorial by 3 developers

  • 13,510 views
  • 11 comments
  • 62 respects

The Badland Brawler - RC Monster Truck with Arduino

Project tutorial by Jithin Sanal

  • 10,230 views
  • 20 comments
  • 103 respects

Boom Box

Project in progress by Team Trouble

  • 3,040 views
  • 2 comments
  • 7 respects

Making Sound Effects with Arduino

Project in progress by Cory Potter

  • 2,629 views
  • 2 comments
  • 8 respects

Interfacing Arduino MKR or ESP via MQTT - Node-RED 101

Project in progress by Officine Innesto

  • 2,280 views
  • 0 comments
  • 2 respects

Morse Encoder & Displayer

Project showcase by rajdakin

  • 2,241 views
  • 4 comments
  • 13 respects
Add projectSign up / Login