cancel
Showing results for 
Search instead for 
Did you mean: 

I2S data gets corrupted

NBees.1
Associate II

Hi there,

I am trying to do some digital signal processing on a STM32G031F6 by interfacing an SPH0645 MEMS microphone via I²S. I'm running into strange issues however, that I haven't been able to solve in the last few days. I am switching to an analogue microphone and ADC for now, but eventually I'd like to use a digital interface to minimize RF interference. I should also mentioned that I'm a beginner to 32 bit Arm MCUs and haven't done a lot of MCU stuff in general.

Anyway. I'm using HAL_I2S_Receive() to poll the values and print them to a serial console via UART. The receive function reads into a unsigned 16 bit integer, so values of around 65000 are expected. I noticed that I only get those if I make the output to the UART console exactly 10 characters long. If it is any shorter, some 32000, 16000s or even zeroes are mixed in. I found this strange, "but maybe that's how it works" I thought to myself.

However, while calculating RMS of the signal, I again noticed strange behaviour, while the calculation itself was implemented correctly. So I repeatedly read 2000 samples into an array and looked at it in the debugger. The readings usually started out stable with values of around 64000 for the first 700 fields. After that, the samples dropped to values of around 650 or lower. Towards the end, sample values rose again to the expected values.

I am suspecting a clock issue, but I have no way to verify that right now. Before I look around for an oscilloscope, I thought I'd try this way.

The MCU is soldered onto a breakout PCB from Adafruit. Both that breakout PCB and the microphone breakout PCB I put into a bread board without crossing data and supply wires. I also tried a few different arrangements, but to no avail.

I don't have an external clock connected. I also wonder if I might need to connect a 12.288 MHz crystal to I2S_CKIN for it to work. Because I don't have one, I tried to use PLLP to at least approximate that frequency with 12.32 MHz. No luck, though.

Thank you for reading this short essay :grinning_face:

18 REPLIES 18

The microphone datasheet says you have to use 32-bit word per side, not 16. Also, why would be the output around 650000?

JW

NBees.1
Associate II

I have the I²S set up according to the data sheet. 24 bits on 32 bit frame. However the function HAL_I2S_Receive() expects a 16 bit unsigned pointer to a buffer for the result. That's why I declared it like this. I switched it to 32 bit unsigned but ran into the same problem, just with different numbers.

I am expecting an output of around 65000 (you had an excess zero in there) because 2^16=65536. When there is no sound (the microphone is "idling") it returns maximum and reduces in value when a sound occurs.

gregstm
Senior III

I've noticed with digital microphones that they can sometimes take a long time to settle. It took me by surprise. I logged the initial data from the microphone and graphed it with a spreadsheet - I too thought something was going wrong. One microphone I used took 30 seconds for the DC offset to disappear. You could try passing the data through a dc-blocking filter. I found this application note very useful in helping me implement my dc-blocking filter.

https://www.knowles.com/docs/default-source/default-document-library/dc-blocking-filter.pdf?sfvrsn=fdb77b1_6

Also, it is easy making mistakes converting the I2S data to a usable form - you could try logging the raw 32 bit data and putting it into a spreadsheet and use it to check that your method of converting it is correct.

The whole DC offset thing is new to me, but wouldn't a dc offset just mean that the microphone would return a slightly biased value in one direction. So instead of a pure midpoint it would report a little over that. If that is the case, it wouldn't explain why the values fluctuate like that.

I also gave the microphone at least a minute to settle down, but that changed nothing. Usually that happens anyway since I need to step away to cope with the frustration :beaming_face_with_smiling_eyes:

I tried to read out the values to a spreadsheet prior to asking this question and it seemed fine. When I plot the output in the Arduino Serial Plotter I can clearly see the graph responding to my clapping or whatever. The problem is that my analysis doesn't work withing the MCU because the data reported to the UART terminal is apparently different from what is stored in my samples array. Or at least, that's what I can see. It's highly likely, that I'm introducing an error. I just don't know how.

So I guess my main concern right now would be to explain the weird UART behaviour. I'm still welcoming all kinds of suggestions and am eager to try =)

I just tried to refresh my memory regarding my SPH0645 experiences... ( I have moved on to using microphones using the DFSDM so that I could use better performing microphones). I found the old spreadsheet where I graphed the DC offset, after 25 seconds it settled to a value of -6700 not zero - but I also seem to remember that this microphone failed completely later, so it might have been on the way out from the start [ I bought my microphone on a little preassembled daughter board - I read somewhere that these boards may have been mistakenly washed after soldering - resulting in these microphones failing after a while. I solder all microphones myself after that experience ]

How did you convert your data from the SAI to a signed 24 bit number? This is the code I used:

if (val >= 0x800000)

val = val - 0x1000000;

// Only upper upper 18 bits of data is valid, divide off lower 6 bits

val = val / 64;

NBees.1
Associate II

Thanks for digging through your records!

I am indeed using a pre-soldered breakout board, but I also have single microphones here that I can try.

So far I haven't converted anything. I just wanted to look at the raw data to get an understanding of what's happening but got slowed down by that issue.

In your example you are converting a 32-bit unsigned into a 24-bit signed, correct? I tried reading the HAL_I2S_Receive() output into a uint32_t buffer but only sometimes received a 32 bit number. Sometimes it's only 16 bit. Am I missing something?

The microphone outputs values as 2s complement, left aligned. I don't know about 'G0, but on older STM32, SPI is only 16-bit, so receiving MSB data inevitably required shuffling the halfwords. Try to draw a timing diagram.

Quiet at 32-bit 2s complement means values alternating between 0xFFFFFxxx (i.e. small negative) and 0x00000xxx (small positive).

Microphone outputs only 18 valid bits so last digits will be insignificant.

JW

This will do the same in a more efficient and simpler way in a constant execution time:

int32_t val;
// ...
val = (val << 8) >> 14;

...the things C will allow you to do! ... the habits of years of writing Pascal/Ada are hard to break....