Search code examples
caudioembeddedstm32

Recording Audio Using DFSDM on an STM microcontroller


On the STM32L4 Discovery kit for IoT node (B-L47E-IOT0A1), there are two MP34DT01 microphones: one with LR pulled to VDD and the second with LR pulled low. DFSDM1_CKOUT and DFSDM1_DATIN2 are connected for both. I have this board, with the system clock set at 80MHz, and DFSDM CH2 enabled In DMA mode. Additionally I have the filter set in continuous mode with Fosr set at 52, and Iosr at 8. I can get values so I know that the microphones are working, but trying to play back the audio results in a bunch of static. I believe my sampling rate should be 48KHz because the interal SPI clock divider is set to 4. -- (80MHz/4)/(52 * 8) = 48KHz] -- My setup is below:

// Audio.c
#define AUDIO_LEN 240
#define H_AUDIO_LEN 120

int32_t micRecBuf[AUDIO_LEN];
int32_t micAudioBuf[AUDIO_LEN];
volatile uint8_t audioFilterHalfCplt = 0;
volatile uint8_t audioFilterCplt = 0;

void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) {
    audioFilterHalfCplt = 1;
}
void HAL_DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) {
     audioFilterCplt = 1;
 }

void micInit(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) {
    HAL_DFSDM_FilterRegularStart_DMA(hdfsdm_filter, micRecBuf, AUDIO_LEN);
}

while(true) {
   // first half of DMA is filled, now process
    if (audioFilterHalfCplt == 1) {
      // fill the audio buffer
      for (i = 0; i < H_AUDIO_LEN; i++) {
        micAudioBuf[i] = micRecBuf[i] << 8; // remove meta data
      }
      // reset the flag
      audioFilterHalfCplt = 0;
    }
    // second half is filled, finish process
    if (audioFilterCplt == 1) {
      // fill the audio buffer
      for (i = H_AUDIO_LEN; i < AUDIO_LEN; i++) {
        micAudioBuf[i] = micRecBuf[i] << 8; // remove meta data
      }
}

I am able to retrieve the data and save it to a binary file using this shell command:

screen -S audio -L -Logfile audio.bin /dev/ttyACM1 9600, cs8

And I save the data as a wav file using this python script:

#!/usr/bin/env python3

import wave

with open("audio.bin", "rb") as input_file:
    data = input_file.read()
    with wave.open("output.wav", "wb") as output_file:
        output_file.setnchannels(1)  # Assuming mono
        output_file.setsampwidth(4)  # Assuming 16-bit samples (2 bytes)
        output_file.setframerate(48077)  # Set the sample rate
        output_file.writeframesraw(data)

When I play back the audio file it is all static. Where am I going wrong in this process?


Solution

  • Caveat: Not a complete solution, but some things to try ...

    Although, it may be an issue with the wiring or DMA setup, etc. there are some things to check first to ensure your capture and transfer process is correct.

    1. From my top comment: You're outputting mono 16 bit which is 2 bytes but you specify 4? Also, bitrates are usually 44100 (pc audio) or 48000 (mp4 audio), so 48077 seems strange to me.

    2. Can you verify that your PC captures the data correctly? That is, the baud rate, etc. are correct. Are both the PC and STM32 set correctly?

    3. Have the STM32 output a fixed string of known bytes at the beginning. (e.g.) for (i = 0; i < 256; ++i) buf[i] = i; That fixed [and verifiable] pattern should start the .bin file. You can examine this with a hex editor.

    4. If you set up your PC to output [through its speakers] a pure tone (e.g. sine wave) and record that, when you get the .bin back file on your PC, you should be able to plot the raw data and see the sine wave.

    5. Can you guarantee that when your DMA completes, that it completely fills the buffer? Is there a function you can call to get the number of bytes actually transferred by the DMA? (e.g.) Could you get a completion that has fewer bytes than you requested?

    6. Could you have a race condition between the DMA and your code? Is your buffering scheme good enough to prevent the DMA from doing output to first and second halves, but go so fast that it tries to fill the first buffer [partially] again?

    7. In other words, your base waits for a "DMA complete" flag (e.g. audioFilterHalfCplt) and grabs the data. But, what prevents the DMA from writing the next chunk of data into that buffer while you are pulling it out in your loop?