Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 5 of 5

Thread: Need help - MEMS mic with Teensy 3.2 and/or audio shield

  1. #1
    Junior Member
    Join Date
    Mar 2018
    Posts
    9

    Need help - MEMS mic with Teensy 3.2 and/or audio shield

    Okay, here's the deal.

    I have a Teensy 3.2.
    I have a 2nd Teensy 3.2 with the audio shield attached.
    And I have an Adafruit SPH0645 MEMS microphone: https://www.adafruit.com/product/3421

    All I want to do is connect up the MEMS microphone to the Teensy and get audio from it, ideally using the audio shield (because I have one now so...), but I can't figure out how to do it. I find the code confusing, I'm struggling finding clear instructions for this specific task...and I'm kinda new to this anyway.

    Can anyone help?

    All the best

  2. #2
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,330
    You cannot easily use audioshield AND them I2S MEMS you are referring too. Both are using I2S.
    (I say easily, because in principle it is possible, as T3.2 has two I2S data ports, and if you control both I2S devices with same MCLK and BitClock)
    So I would say connect first the MEMS mic to the single I2S without audioshield.

  3. #3
    Junior Member
    Join Date
    Mar 2018
    Posts
    9
    Ah okay, thanks for the info. In that case, which pins should I connect and where? Instructions seem to differ from place to place and therefore, so does the code, so I'm a bit lost. Speaking of which, I'm not even sure what code to use.

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,551
    Quote Originally Posted by j.biscuit View Post
    In that case, which pins should I connect and where?
    For Teensy 3.2, connect LRCL to pin 23, DOUT to pin 13, and BCLK to pin 9.

    Connect GND the GND and 3V to 3.3V so that Adafruit module gets power.

    You can connect SEL to either GND or 3.3V. I2S is always a stereo communication protocol. The SEL pin determines which this mic's audio will transmit on the left or right channel. I believe it's also possible to connect 2 of these mics in parallel where one has SEL connected to GND and the other has SEL connected to 3.3V.


    Quote Originally Posted by WMXZ View Post
    in principle it is possible, as T3.2 has two I2S data ports, and if you control both I2S devices with same MCLK and BitClock
    Unfortunately the quad channel I2S is not compatible with most of these MEMS mics. It has BCLK/LRCLK ratio of 32. Most I2S chips automatically work with any ratio (or at least a range). But these MEMS mics tend to require a ratio of 64. Some time ago the stereo I2S was changed to have a ratio of 64 so these mics can work.

    But the quad I2S was never changed and still uses a ratio of 32. Maybe someday it will be changed. I believe it's possible, but requires careful attention to how the I2S and DMA work together. The 32 to 16 bit trick used for stereo won't work for quad channel. If this change ever is made, then it should become possible to use the audio shield and also one (or two) of these mics with the quad I2S. It would also be possible with today's code if you have a mic that can work with BCLK / LRCLK ratio of 32, but as far as I know all of the MEMS mics with I2S require exactly BCLK exactly 64 times LRCLK.

  5. #5
    Junior Member
    Join Date
    Mar 2018
    Posts
    9
    Well, I got it working (I didn't use the audio shield. Just a Teensy 3.2 and the MEMS mic). For those who're trying to accomplish the same thing, here's what I did:

    Wiring (SPH0645 -> Teensy3.2):
    SEL -> GND
    LRCL -> 12
    DOUT -> 13
    BCLK -> 11
    GND -> GND
    3V -> 3.3V

    Teensy I2S library: https://github.com/nodae/teensy_i2s_experimental
    (Place the decompressed 'teensy_i2s_experimental-master' folder in your 'Documents\Arduino\libraries' folder)

    Teensy 3.2 code:
    Code:
    #include <Wire.h>
    #include <i2s.h>
    #define CLOCK_TYPE (I2S_CLOCK_48K_INTERNAL) // 8, 32, 44, 48
    
    uint32_t nRX = 0;
    uint8_t bytes[64];
    
    void i2s_rx_callback(int32_t *pBuf)
    {
      bytes[nRX++] = pBuf[0] & 0xFF;
      bytes[nRX++] = (pBuf[0] >> 8) & 0xFF;
      bytes[nRX++] = (pBuf[0] >> 16) & 0xFF;
      bytes[nRX++] = (pBuf[0] >> 24) & 0xFF;
      if (nRX >= 64)
      {
        Serial.write(bytes, 64);
        nRX = 0;
      }
    }
    
    void setup()
    {
      Serial.begin(9600);
      I2SRx0.begin(CLOCK_TYPE, i2s_rx_callback);
      I2SRx0.start();
    }
    
    void loop()
    {
    
    }
    Visual Studio C# code:
    Code:
    using System.IO.Ports; // place at the beginning of your class file
    
    private void SerialPortMain_DataReceived(object sender, SerialDataReceivedEventArgs e) // Event associated with the SerialPort control
    {
        rC = serialPortMain.BytesToRead; // rC is an int
        serialPortMain.Read(myBuffer, pos, rC); // myBuffer is an array of bytes
        pos += rC; // pos is an int
    }
    In visual studio, create a SerialPort object either through the designer or in code. Set the PortName property to that of the Teensy serial port (eg. "COM5" or something - you can obtain a list of all the ports found on your machine via SerialPort.GetPortNames()). Normally I would say set the BaudRate property of the control to match that of the Teensy, however it seems that the Teensy simulates the serial port connection and that the baud rate is actually irrelevant. You might want to increase the ReadBufferSize property from 4096 (mine was set to 65536), but I don't know if it makes a difference. With BaudRate set to the default 9600, I was transferring data from the Teensy at a rate of 192 KBps (equivalent of 1536000 baud - and it'll probably go higher).

    Create a handler either through the designer or in code for the DataReceived event for your serial port (in the code above, my serial port is called SerialPortMain). This is where all of the transfers from the Teensy to your PC occur. As you can see, I simply made myself an array of bytes and copied the data into it each time the serial port received a transmission. To start recording, simply open the port with the .Open() method. The only other thing you'll want to do is decide what to do with the data as you go. For example you can make that byte array very large and then when you've finished, save all the bytes to a file (using System.IO, File.WriteAllBytes(string path, byte[] bytes)). You can then import the audio via an audio editor like Audacity. To do so, load the program and go to File -> Import -> Raw Data... Select the file and use these settings:

    Encoding: Signed 32-bit PCM
    Byte order: Little-endian
    Channels: 1 Channel (Mono)
    Start offset: 0
    Amount to import: 100%
    Sample rate: 48000

    On a side note, you might want to normalise the audio and adjust for the DC offset if you have one. You can also do this in code on either the Teensy or the PC. It's a matter of subtracting the average sample value from each sample (removes DC offset - do this first) and then multiplying each sample by [the upper limit / the absolute maximum sample value] (normalises, in other words, increases the amplitude). This is fine if you do it after you've finished recording. However to do it live via code, both of these processes are dependant on monitoring a time window and can be a bit tricky due to changes that occur as you go.

    Alternatively, you could periodically save or utilise the data in the buffer (for example for live playback). Just read (or save) the contents of the buffer up to 'pos' and then set 'pos' back to 0. However if you're periodically saving to a file, keeping a file stream open and writing to that might be a better idea. Also, don't forget to close your ports (or check whether they're already open) in your C# program.
    Last edited by j.biscuit; 09-28-2019 at 12:19 AM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •