cancel
Showing results for 
Search instead for 
Did you mean: 

[Novice] Unable to get I2S data from F407G-DISC1 onboard microphone

APara.6
Associate II

I've tried so much stuff from official examples, unofficial examples, AI, debugging, forum posts, etc., that at this point I think it might be best to describe my understanding of what should be necessary to access the onboard microphone, and hope that there's an expert out there who can correct my mistake.
~~~~

The goal is to set up an I2S channel to receive Pulse-Density-Modulated data from the microphone, which is then written by Direct Memory Access to a buffer in the main memory. Interrupt Requests must be configured to fire when the buffer is filled, because “Polling Mode” is deprecated.

The I2S driver file instructs that the following must be done:

  • Create an I2S handle to be used as a parameter in DMA-related functions.

  • Call HAL_I2S_MspInit() to configure a DMA/SPI handle, among other things.

  • Call HAL_I2S_Init()

  • Call HAL_I2S_Receive_DMA(), specifying a destination buffer as a parameter.

The DMA driver file instructs that the following must be done:

  • “Enable and configure the peripheral to be connected to the DMA Stream.” No instructions are given for this, but I am guessing that this is what’s done in the I2S setup.

  • Create a DMA handle to be used as a parameter in DMA-related functions.

  • Call __HAL_RCC_DMA1_CLK_ENABLE() {Can’t completely verify, because it is defined in a preprocessor conditional segment.}

  • Call HAL_DMA_Init()

  • Call HAL_NVIC_SetPriority() {Breakpoint trips several times?}

  • Call HAL_NVIC_EnableIRQ() {Breakpoint trips several times?}

  • Call HAL_DMA_Start_IT()

I have verified as far as I can that all of the above steps are reached, according to debug break points. Here are what my I2S and DMA handles look like:

 

  hi2s2.Instance = SPI2;
  hi2s2.Init.Mode = I2S_MODE_SLAVE_RX;
  hi2s2.Init.Standard = I2S_STANDARD_MSB;
  hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
  hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
  hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_48K;
  hi2s2.Init.CPOL = I2S_CPOL_LOW;
  hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
  hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
    hdma_spi2_rx.Instance = DMA1_Stream3;
    hdma_spi2_rx.Init.Channel = DMA_CHANNEL_0;
    hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi2_rx.Init.Mode = DMA_CIRCULAR;
    hdma_spi2_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

 

If everything is successful, the callback HAL_DMA_IRQHandler() should fire when a DMA operation is complete.

The current implementation leaves the I2S handle’s state as HAL_I2S_STATE_BUSY_RX, and the DMA handle's state as HAL_DMA_STATE_BUSY which I’m pretty sure is what it’s supposed to be for ongoing reception. However, HAL_DMA_IRQHandler() is never called, and the destination buffer remains empty. Note that I'm not trying to actually do anything with the data, not even convert the PDM to PCM; at this point I just want to see it arriving in the buffer.

 

I was hoping to be teaching myself some DSP techniques by now, but it seems that I'm stuck at the starting line! I've uploaded my project, minus the large "debug" directory, for you to investigate. Any help would be greatly appreciated.

4 REPLIES 4
SofLit
ST Employee

Hello,

Why don't you use the board BSP from F4 CubeHAL?:

https://github.com/STMicroelectronics/stm32f4discovery-bsp/blob/49fcaf442e7d9b5970567eb0c833d66d1333e726/stm32f4_discovery_audio.c

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

I saw some examples not using the official drivers, or not using them in their entirety, so I thought it would be more educational to try and create a “minimum viable” program to access the I2S microphone. I also had some trouble figuring out how to properly import the drivers.

I’m not too proud to use official drivers, though, and, with your encouragement, though, I tried again. I tried two methods. The first method was to follow that github link and add related directories from github according to what was mentioned in the readme files and what was demanded when I tried to build the project. This is the file structure I wound up with:

APara6_1-1735320655624.png

With the ‘Drivers’ directory filled in like this, I was getting the following build error, at the peak of a tall pile of build errors:

In file included from ../Drivers/BSP/STM32f4 Discovery/stm32f4_discovery_audio.h:30,

from ../Drivers/BSP/STM32f4 Discovery/stm32f4_discovery_audio.c:115:

../Drivers/BSP/STM32f4 Discovery/../Components/cs43l22/cs43l22.h:189:8: error: unknown type name 'AUDIO_DrvTypeDef'

189 | extern AUDIO_DrvTypeDef cs43l22_drv;

| ^~~~~~~~~~~~~~~~

When I look around, I find that Drivers/BSP/Components/Common, sourced from here, contains a definition for struct type called “AUDIO_Drv_t,” which looks very similar to what cs4l22.h is looking for. Trying to rename the struct to DrvTypeDef causes different warnings and errors which I understand even less, of course.

So, then, the second method was to follow this guide and copy all the relevant files from the repository which STM32CubeIDE downloads in order to start a project. This seemed promising, because the audio.h file in here actually does contain a definition for AUDIO_DrvTypeDef, as well as a definition for CODEC_STANDARD, which is another missing definition error near the top of the error pile.

Alas, this just leads to different build errors, not fewer: (there are dozens)

../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_timebase_rtc_alarm_template.c:82:1: error: unknown type name 'RTC_HandleTypeDef'; did you mean 'DMA_HandleTypeDef'?

82 | RTC_HandleTypeDef hRTC_Handle;

| ^~~~~~~~~~~~~~~~~

| DMA_HandleTypeDef

../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_timebase_rtc_alarm_template.c: In function 'HAL_InitTick':

../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_timebase_rtc_alarm_template.c:149:16: error: request for member 'Instance' in something not a structure or union

149 | hRTC_Handle.Instance = RTC;

| ^

So, this is where I’m stuck. I spent some time trying to work through the errors by enabling components, adding things to the compiler path, deleting unneeded stuff, etc. but ultimately there are so many errors that it seems more likely that I’ve done the driver import wrong than that the drivers are somehow broken. This is supposed to be just a copy-paste, refresh, compile operation, isn’t it?

APara.6
Associate II

Someone with very sharp eyes noticed that the last revision of the STM32F407VG (there doesn't seem to be a manual for the STM32F407-just-G) the microphone was changed. There's no date on the revision, but is it possible that the drivers, examples, etc. haven't been updated to account for this change, and attempt to communicate with an I2S microphone which is no longer there?

Both of my boards are the "E revision.

APara.6
Associate II

I've made a little bit of progress. I read the BSP-import guide closer, and saw that it was a mistake to decline CubeIDE's offer to "initialize all peripherals in their default state." I also was mistakenly replacing the "CMSIS" and "STM32Fxx_HAL_Driver" directories with those from the repository, when really I was supposed to not touch those, and just move the "BSP" directory. Starting a new project and doing those two things differently solved most of the missing header errors.

The remaining errors were solved by adding new header locations to the compiler search path, and enabling the "PDM2PCD" software pack through CubeMX (this requires "CRC" to be turned on before it can be enabled).

With all this done, I am able to start the program and see a stream of data moving through the audio buffer array. However, this data doesn't seem related to the sound which should be hitting the microphone. The numbers appear to be random 16-bit values, though I've also never seen a value below "3000", which should be happening like 1-in-64 times, and that seems odd. If these are PCM values, they shouldn't appear random. If these are PDM values, they should be mostly multiples of 2 when there is near-silence, but they aren't. This is the case even when I lower the bit resolution sent to BSP_AUDIO_IN_Init to just 2 bits, which I would think would round silence to zero.

I do see this sentence from the top of the audio driver: "User needs to implement user callbacks to retrieve data saved in the record buffer (AUDIO_IN_RxHalfCpltCallback/BSP_AUDIO_IN_ReceiveComplete_CallBack)" However, there are a few reasons that I suspect this isn't the issue:

  1. AUDIO_IN_RxHalfCpltCallback and BSP_AUDIO_IN_ReceiveComplete_CallBack aren't function names mentioned anywhere in the code base, making me doubt the relevance of the  advice.
  2. The apparent real callback functions, BSP_AUDIO_IN_TransferComplete_CallBack, which is called by HAL_I2S_RxCpltCallback, never get called.
  3. The fact that data is moving through the buffer makes me think that not implementing the callback isn't stopping the I2S process.

I have attached the project.