2020-10-29 01:31 AM
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:
2020-10-30 05:39 PM
you said earlier " I can clearly see the graph responding to my clapping" - that's a promising sign. Instead of clapping, use a sine wave generator (or even just a toy musical recorder) to generate the signal. Save the data as unsigned32 and graph it in a spreadsheet - hopefully it is just a data conversion issue, and the sine wave shape will be easier to analyse. I will leave you to it...
2020-10-31 04:29 AM
Well graphing out the data externally is working perfectly fine. The problem is (as mentioned in my first post) that when I do the same on the chip itself, I only get rubbish as a result which is due to the the values shown via UART are not the same I can see in the variables, which does make no sense to me at all. Also I don't get why the values via UART only are correct whenever the transmitted string is exactly 10 characters long.
2020-11-01 03:36 AM
Is your code transmitting through UART proven good?
I.e. forget about I2S at the moment, fill in manually an array with numbers of your choice (e.g. 0x1, 0x2, 0x4, 0x8, 0x10 etc. all way up to 0x80000000) and transmit.
JW
2020-11-01 02:41 PM
... also, consider writing your own basic routines for displaying data via the UART (eg. uint32, int32) - they are handy routines for compact, robust debugging
2020-11-01 11:51 PM
That's a good tip and something I should've done earlier. Thank you!
2020-11-01 11:53 PM
I did that. Worked perfectly fine. I tried it with both a predefined array as well as in a loop. Both worked without any wrong values. I also polled the I2S interface while from within the loop to see if that would throw things off, but it changed nothing. Of course I only did that after I saw the "pure" UART stuff working.
2020-11-02 04:33 AM
Since your answers point towards a mistake on my end instead of a clock issue (like I thought initially), maybe a few lines of code can resolve the issue:
uint16_t data_i2s[2]; // Define a double sized (32 bit) pointer for data.
// Receive two words of 16 bit length into the pointer.
HAL_I2S_Receive(&hi2s1, &data_i2s, 2, 100);
uint32_t sample = 0;
// Build a full 32 bit integer out of both values.
sample = (uint32_t) data_i2s[1] << 16 | data_i2s[0];
When I take a look into the data_i2s array I noticed that I don't always get the specified two 16 bit words. Sometimes it only fills up one array field with a meaningful number while the other just gets a "0". Of course that really messes with the final data. It seems like something is off there. I mean, I can't really get any closer to the actual data, can I?
2020-11-02 06:57 AM
You wrote above:
> I have the I²S set up according to the data sheet. 24 bits on 32 bit frame.
Then why do you expect that
> // Receive two words of 16 bit length into the pointer.
> HAL_I2S_Receive(&hi2s1, &data_i2s, 2, 100);
would receive 16-bit words?
Cube/HAL is open source. Read it.
JW
2020-11-02 07:42 AM
I would expect that because the documentation states:
[...] when a 24-bit data frame or a 32-bit data frame is selected
the Size parameter means the number of 16-bit data length.
That tells me that if I want to read the 32-bit data frame I need to use two lengths of 16-bit data. Size = 2 is also the parameter I have seen in all of the code examples I could find on I²S.
If my lack of knowledge frustrates you feel free to walk away from this thread and do something nice instead. This was by no means my intention. I have spent the better part of the last 10 days on this problem, have read documentations, tried out various solutions, dozens of parameters and have finally brought up the courage to ask my question on this forum despite being a very stubborn and proud person with very little knowledge in terms of C and STM32 microcontrollers.
Thank you nonetheless for your input so far.