2025-05-19 11:35 AM
Hello,
I have a STM32H747I-DISCO I want to use to record audio. In my test code I tried to record 3 seconds of audio after pressing the JOY_SEL button. I tried to setup the configuration of peripheral SAI4 for PDM and to use DMA and PDM2PCM using the CubeMX tool. I also tried to just use the BSP provided by ST for the discovery board. I was not able to get either the manual configuration or the BSP configuration to work. I have already looked at AN5027 and the examples provided by ST in the STM32Cube_FW_H7_V1.12.1 repository. The examples provide audio recording but for different boards. The DFSDM example for this board is not that useful when using SAI for communication. Any guidance or detailed procedure on how to setup the on-board digital microphone using SAI for the STM32H747I-DISCO is much appreciated!
Manual/BSP Setup:
CPU ICache enabled
CPU DCache enabled
FreeRTOS enabled
GPIOs for joy stick button configured
TIM6 and TIM3 used for sysclks for Core M7 and Core M4 respectively. (because of FreeRTOS)
SAI4 - PDM using MicroPhones pairs (1-2) and CK1 as clock, DMA is also setup, Mode: Circular, Data Widths: Half Word, Increment Memory
Clock Configuration:
For the manual setup, I did not include the stm32h747i_discovery_audio.c and other BSP files in the project.
I use the code generated from STMCubeMX and added some user code on top.
Below I will provide some of the code for the BSP setup and the manual setup
BSP Setup:
This setup allowed me to "record" audio, but when I check the buffer, it is empty and looks like no recorded audio.
Main.c:
I added some code for GPIO initialization, buffers for audio data, and callbacks for DMA transfer.
Define/variables:
#define PCM_BUFFER_SIZE (16000 * 3) // 3 seconds at 16kHz
#define PDM_BUFFER_SIZE (64 * 1000 * 3) // 64 PDM samples per ms at 16kHz
ALIGN_32BYTES(uint8_t pdm_buf[PDM_BUFFER_SIZE]);
ALIGN_32BYTES(int16_t pcm_buf[PCM_BUFFER_SIZE]);
recordtask:
void StartMicRecordTask(void *argument)
{
/* USER CODE BEGIN StartMicRecordTask */
printf("MicRecordTask: Task Started\n");
BSP_AUDIO_Init_t MicInit;
MicInit.Device = AUDIO_IN_DEVICE_DIGITAL_MIC;
MicInit.SampleRate = AUDIO_FREQUENCY_16K;
MicInit.BitsPerSample = AUDIO_RESOLUTION_16B;
MicInit.ChannelsNbr = 1;
MicInit.Volume = 80;
if (BSP_AUDIO_IN_Init(1, &MicInit) != BSP_ERROR_NONE)
{
Error_Handler();
}
printf("MicRecordTask: Microphone initialized\n");
printf("MicRecordTask: Press JOY_SEL to begin 3 second recording\n");
/* Infinite loop */
for(;;)
{
uint32_t now = HAL_GetTick();
HandleButtonEdge(&joy_sel, now);
if (joy_sel.lastStableState == GPIO_PIN_RESET)
{
ResetAudioBuffers();
printf("MicRecordTask: Recording started for ~3 seconds\n");
HAL_GPIO_WritePin(GPIOI, joy_sel.ledMask, GPIO_PIN_RESET); // Green LED ON
if (BSP_AUDIO_IN_RecordPDM(1, pdm_buf, PDM_BUFFER_SIZE) != BSP_ERROR_NONE)
{
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_14, GPIO_PIN_RESET); // Red LED on error
}
osDelay(3000); // Record for 3 seconds
BSP_AUDIO_IN_Stop(1);
printf("MicRecordTask: Recording stopped\n");
HAL_GPIO_WritePin(GPIOI, joy_sel.ledMask, GPIO_PIN_SET); // Green LED OFF
printf("MicRecordTask: Begin PDM to PCM conversion\n");
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_13, GPIO_PIN_RESET); // Orange LED ON
// Convert PDM to PCM
for (int i = 0; i < PCM_BUFFER_SIZE; i += (MicInit.SampleRate / 1000))
{
BSP_AUDIO_IN_PDMToPCM(1, (uint16_t*)&pdm_buf[i], (uint16_t*)&pcm_buf[i]);
}
printf("MicRecordTask: PDM to PCM conversion ended\n");
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_13, GPIO_PIN_SET); // Orange LED OFF
joy_sel.lastStableState = GPIO_PIN_SET; // Reset button state
printf("MicRecordTask: Sample Results:\n");
printf("PDM buffer:\n");
for (int i = 0; i < 100; i++) {
printf("%d,", pdm_buf[i]);
if (i % 10 == 9) printf("\n"); // new line every 10 samples
}
printf("PCM buffer:\n");
for (int i = 0; i < 100; i++) {
printf("%d,", pcm_buf[i]);
if (i % 10 == 9) printf("\n"); // new line every 10 samples
}
}
osDelay(10);
}
/* USER CODE END StartMicRecordTask */
}
Manual Setup:
This setup I was not able to make it past HAL_SAI_Receive_DMA(&hsai_BlockA4, dma_buf, PDM_BYTES_PER_MS / 2) and the LED recording light never turns off.
Main.c:
I added some code for GPIO initialization, buffers for audio data, and callbacks for DMA transfer.
#define PDM_BYTES_PER_MS 128 // 1 ms @ 16 kHz, 64 bits/sample
#define RECORD_MS 3000
#define PCM_SAMPLES_TOTAL (RECORD_MS * 16) // 16 samples/ms = 48,000
#define PDM_TOTAL_BYTES (RECORD_MS * PDM_BYTES_PER_MS)
ALIGN_32BYTES(uint8_t dma_buf[PDM_BYTES_PER_MS]); // 1 ms double buffer
ALIGN_32BYTES(uint8_t pdm_record_buf[PDM_TOTAL_BYTES]); // 3 sec storage
ALIGN_32BYTES(int16_t pcm_buf[PCM_SAMPLES_TOTAL]); // Final PCM output
void StartMicRecordTask(void *argument)
{
/* USER CODE BEGIN StartMicRecordTask */
printf("MicRecordTask: Task Started\n");
PDM_FilterHandler.bit_order = PDM_FILTER_BIT_ORDER_MSB;
PDM_FilterHandler.endianness = PDM_FILTER_ENDIANNESS_LE;
PDM_FilterHandler.high_pass_tap = 2122358088; // default HPF
PDM_FilterHandler.out_ptr_channels = 1;
PDM_FilterHandler.in_ptr_channels = 1;
if (PDM_Filter_Init(&PDM_FilterHandler) != 0)
{
Error_Handler();
}
PDM_FilterConfig.output_samples_number = 16; // 1 ms @ 16kHz
PDM_FilterConfig.mic_gain = 24; // in dB
PDM_FilterConfig.decimation_factor = PDM_FILTER_DEC_FACTOR_64;
if (PDM_Filter_setConfig(&PDM_FilterHandler, &PDM_FilterConfig) != 0)
{
Error_Handler();
}
printf("MicRecordTask: Press JOY_SEL to begin 3 second recording\n");
/* Infinite loop */
for(;;)
{
uint32_t now = HAL_GetTick();
HandleButtonEdge(&joy_sel, now);
if (joy_sel.lastStableState == GPIO_PIN_RESET)
{
ResetAudioBuffers();
pdm_write_offset = 0;
recording_active = true;
printf("MicRecordTask: Recording started for ~3 seconds\n");
HAL_GPIO_WritePin(GPIOI, joy_sel.ledMask, GPIO_PIN_RESET); // Green LED ON
if (HAL_SAI_Receive_DMA(&hsai_BlockA4, dma_buf, PDM_BYTES_PER_MS / 2) != HAL_OK)
{
printf("MicRecordTask: DMA Start Failed\n");
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_14, GPIO_PIN_RESET); // Red LED on error
}
uint32_t start = HAL_GetTick();
while ((HAL_GetTick() - start) < 3000)
{
osDelay(10);
}
HAL_SAI_DMAStop(&hsai_BlockA4);
recording_active = false;
printf("MicRecordTask: Recording stopped\n");
HAL_GPIO_WritePin(GPIOI, joy_sel.ledMask, GPIO_PIN_SET); // Green LED OFF
printf("MicRecordTask: Begin PDM to PCM conversion\n");
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_13, GPIO_PIN_RESET); // Orange LED ON
// Convert PDM to PCM
for (int i = 0; i < PCM_SAMPLES_TOTAL; i += 16)
{
PDM_Filter(&pdm_record_buf[i * 8], &pcm_buf[i], &PDM_FilterHandler);
}
printf("MicRecordTask: PDM to PCM conversion ended\n");
HAL_GPIO_WritePin(GPIOI, GPIO_PIN_13, GPIO_PIN_SET); // Orange LED OFF
joy_sel.lastStableState = GPIO_PIN_SET; // Reset button state
printf("MicRecordTask: Sample Results:\n");
printf("PDM record buffer:\n");
for (int j = 0; j < 5; j++)
{
for (int i = 0; i < 128; i++) {
printf("%d,", pdm_record_buf[i + (j*128)]);
if (i % 16 == 15) printf("\n"); // new line every 16 samples
}
printf("PCM buffer:\n");
for (int i = 0; i < 128; i++) {
printf("%d,", pcm_buf[i+ (j*128)]);
if (i % 16 == 15) printf("\n"); // new line every 16 samples
}
}
}
osDelay(10);
}
/* USER CODE END StartMicRecordTask */
}