2024-12-25 01:00 PM - edited 2024-12-25 01:04 PM
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.
2024-12-26 12:06 AM
Hello,
Why don't you use the board BSP from F4 CubeHAL?:
2024-12-27 09:36 AM
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:
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?