arduino zero and digital MEMS microphone has offset.

Hi All,

I have a Genuino zero hooked up to a digital MEMS mic and currently I am just trying to get the serial output, I get values that are proportional to the input (if I click my fingers or scream into it the numbers all move) andI have also scoped out the connections and signals all seem right except that there is an offset on the output.

The microphone (knowles SPH0645LM4H-B) is 24 bit I2S, 18 bits used, LSBs are 0s. the bits that are changing are 31 >> 18 all other values are 0.

If I shift the numbers down then the offset is approx. -6550, but I cannot for the life of me figure out why the offset is there.

incidentally, using the "arduinosound" libraries yielded different results for essentially the same code, but I have no idea why!

I've been looking at this problem for far too long and the solution is probably staring me in the face but any help/insight much appreciated!

My code is pretty much a copy of the serialplotter example on the arduino site (there is some extra in my attached code which is commented out as I tried to do some manipulation to no avail):

#include <I2S.h>

void setup() {
// Open serial communications and wait for port to open:
// A baud rate of 115200 is used instead of 9600 for a faster data rate
// on non-native USB ports

Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}

// start I2S at 8 kHz with 32-bits per sample
if (!I2S.begin(I2S_PHILIPS_MODE, 32000 , 32)) {
Serial.println("Failed to initialize I2S!");
while (1); // do nothing
}
}

void loop() {

sample = I2S.read(); // get an I2S sample

// if it's non-zero print value to serial
if (sample) {
Serial.print(sample, BIN);
Serial.print("\n");
db = 0;
}
}

i2s_sample_code.ino (1.92 KB)

Check the 32-bit format coming form the mic. If the microphone is putting-out a signed 32-bit integer, make sure you are reading-into a 32-bit signed integer. Then, be careful when you bit-shift because you'll shift the sign bit into a place it doesn't belong.

It's possible that it's putting-out unsigned data, and in that case there would be an offset but an offset of around 6000 seems "odd".

Or, it might just be normal... An analog offset of 6000 out of 32-bits (4 million) is not a lot.

It is sending twos complement integers but I am not sure that the four bytes that it sends are being stored in the right order. It sends the high order byte first. The Arduino processors are little-endian, so it is possible that the four bytes which are being sent in the order A (MSB), B, C, D (LSB) are stored as D, C, B, A
This might explain the oddball results you are getting. I would suggest that you print the 'sample' in HEX by changing this:

    Serial.print(sample, BIN);
    Serial.print("\n");

to this:

    Serial.println(sample, HEX);

Copy and paste some of the output here. One way or another it'll confirm or refute my idea.
P.S. It would help if you can get two samples of the output. One while you are clicking your fingers to get an obvious response and the other during a silent interval.

Pete

You are right it is 2s complement. While your suggestion is sensible I don't think that is the problem.

The microphone sends 24 bits with only 18 used. and there are 24 "used" bits on the upper end of 'sample' which implies the bits are in the right place. I have changed my serial output to hex and a few samples (which are typical of what i've been getting so far) are below in a relatively quiet environment:

F93D8000
F93E8000
F9404000
F93FC000
F93E8000
F93E8000
F93E8000
F93F4000
F93F4000
F93F8000
F9404000
F93F4000

It's fairly difficult to catch the numbers when clicking my fingers as this is spewing out data in the order of kHz (I think) but I hope you can take my word for it that the numbers do change in response and it is the its with lower significance that change (the MSB 'F' does change to 'E' with a VERY loud signal), if not i'll try and capture it.

Interestingly, if I set the I2S mode to be 16 bit, I would expect the microphone 2 LSbs of data to get chopped but instead I get "FFFF8000" EVERY time regardless of audio input, this suggests some ignorance about how the I2S library works (true) and looks slightlly like instead of the 16 bit per channel it is actually 8 bit per channel into 16 bits.

As far as the microphone just naturally having an offset, while possible I find it unlikely as this is a straight digital output. While there is no mention in the datasheet there there is no offset I would expect a special mention in the datasheet if there was (which there isn't.... unless i've missed some smallprint somewhere... relly don't want to read it for a 17th time).

Thanks for suggestions so far and further thanks for any more to come!

While your suggestion is sensible I don't think that is the problem

Yeah, I was pondering the problem just before I went to sleep last night and I realized that it would be really dumb to write an I2S library that provided the data the wrong way round :slight_smile:
Is the I2S library you are using part of the Zero's IDE or did you have to install it yourself? If possible, can you provide a link to the library? It is really strange that 16-bit mode always returns FFFF8000. I would have expected it to return the top 16 bits of what it would have given you in 32-bit mode - F93F, F940, etc.

Pete

My ignorance about header files and libraries may become clear here...

I have used to seperate programs/sketches to get data from the mic.

the program for my "mainly" used one (the one that produces this offset) is part of the arduino IDE, reference link below:

I found it odd that arduino didn't seem to provide the I2S.h file but found an arduino related one which I have attached.

the other library is "arduinosound" which i installed through the arduino IDE by searching "I2S" in the library manager. this library provides a few different exampes but using "AmplitudeSerialPlotter" the data from the microphone fills up all 32 bits which is clearly wrong. below are a few hex outputs when using this program:

4C20CFA
4B65F30
4C07E3C
4BCF730
4AEBE1F
4A8456A
4B6045A
4BE1FF2
4C6A6A8
4B7D413
4AFCE57
4B63ED3
4A953FF

i2s.h (2.18 KB)

I also need the i2c.cpp you are using

BTW. I haven't got a Zero so I can't (don't want to) install it and/or I2C drivers into my IDE.

Pete

el_supremo:
I also need the i2c.cpp you are using

BTW. I haven't got a Zero so I can't (don't want to) install it and/or I2C drivers into my IDE.

Pete

Sorry el_supremo, you lost me at ".cpp" all i know is the code from:

https://www.arduino.cc/en/Tutorial/I2SInputSerialPlotter

includes <i2s.h>

I used the header file previously attached to get it to compile. I simply stuck the header file into libraries in the arduino directory. is a .cpp file an optional thing? how would I find what .cpp file is being used?

I have done a search for .cpp files in my arduino directory and they all relate to the "arduinosound" library. I have GUESSED at a .cpp file that is being used as it is the most sensible one I can find.

AmplitudeAnalyzer.cpp (1.66 KB)

I was looking for the I2S library file I2S.cpp which would accompany I2S.h, but I think I have found it online anyway at ArduinoCore-samd/libraries/I2S/src at master · arduino/ArduinoCore-samd · GitHub
The AmplitudeAnalyzer is a different library.

the data from the microphone fills up all 32 bits which is clearly wrong

Not necessarily. The datasheet shows that when the low order 8 bits are being transmitted, the chip simply tri-states its output. This could mean that the Zero will read random data in those low order bits. This will not matter because you're going to have to shift the whole number right by 14 bits to correct the value anyway and that will remove those bits.
The troubling part is that the datasheet also shows that bits 19-24 (where the most significant bit is #1) are sent as zero, but none of your samples have a consistent block of zero in bits 19-24.

Pete

el_supremo:
Not necessarily. The datasheet shows that when the low order 8 bits are being transmitted, the chip simply tri-states its output.

Not my immediate problem but good point, I was wrongly assuming 0s.

the link you posted for i2s.h and i2s.cpp is a different source, they may be the same code from a different place but I will try using that library tomorrow to see if it yields different results.

I thought this was going to be easy.......

I have included the i2s.h and i2s.cpp file from you're linked source el_supremo. the output has retained its inexplicable offset.

At this point I Am wondering if there is a hardware issue that i've missed. The code is so simple there is nothing I can debug, I doubt the headers are wrong as I seem to be the only person in the world with this issue (although I do find it odd that I get a different output when using the arduinosound library).

For what it's worth, I am also seeing an offset of ~6500 with this microphone. I'm using an Adafruit Feather M0.

I don't understand how you can get samples at 64000 a second to print on a 115200 baud serial
line.

I2S is stereo so 32000 sample rate means 64000 samples a second in total. You must be dropping
samples dramatically.

You shouldn't be worrying about an offset in the data, just estimate the offset with a low-pass digital
filter and subtract it out.

If you are dropping any samples at random due to serial delays the sound quality you get will be awful.