cancel
Showing results for 
Search instead for 
Did you mean: 

Stereo PDM microphones(2 mics) data acquisition using PDM2PCM middleware, i want to find code example for such situation.

AMukh.3
Associate II

Hello, i would like to know how to use your PDM2PCM middleware for 2 microphones processing data, if you have some examples for it - it would be perfect.

I was able to get the good sound for mono(with 1 mic) when i get the board for stereo sound, i can not get the proper sound signal.

Microphones are connected using i2s peripheral.

I'm using my custom board with 2 IMP34DT05TR PDM MEMs microphones.

Also it is interesting how TIMer code for stereo microphone should look (when should i start the timer and in which mode ?)

MCU: STM32F412CGU6

Micros: IMP34DT05TR

void MX_I2S3_Init(void)

{

 /* USER CODE BEGIN I2S3_Init 0 */

 /* USER CODE END I2S3_Init 0 */

 /* USER CODE BEGIN I2S3_Init 1 */

 /* USER CODE END I2S3_Init 1 */

 hi2s3.Instance = SPI3;

 hi2s3.Init.Mode = I2S_MODE_MASTER_RX;

 hi2s3.Init.Standard = I2S_STANDARD_LSB;

 hi2s3.Init.DataFormat = I2S_DATAFORMAT_32B;

 hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;

 hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_44K; // tried all of the audio freq's

 hi2s3.Init.CPOL = I2S_CPOL_LOW;

 hi2s3.Init.ClockSource = I2S_CLOCK_PLLR;

 hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;

 if (HAL_I2S_Init(&hi2s3) != HAL_OK)

 {

  Error_Handler();

 }

 /* USER CODE BEGIN I2S3_Init 2 */

 /* USER CODE END I2S3_Init 2 */

}

#include <stdio.h>

#include <string.h>

#include "stm32f4xx_hal.h"

#include "pdm2pcm.h"

#include "serial.h"

// Func macros for serial port logging new line and simple one

#define LOGLN(val) serial_println(console, val)

#define LOG(val) serial_print(console, val)

/**

 * Note: if the decimation factor or number of output samples

 * per conversion is changed, these buffer sizes will

 * need to be changed!

 */

#define PCM_BUF_SIZE 48

// For a decimation factor of 64 and 16-bit wordlength ...

#define PDM_BUF_SIZE (PCM_BUF_SIZE * 4)

static I2S_HandleTypeDef *i2s;

// PCM signal buffer

static int16_t pcm[PCM_BUF_SIZE];

// Buffer used to store the raw PDM micro data

static volatile uint16_t rxbufs[2][PDM_BUF_SIZE];

static volatile uint16_t *rxbuf;

static uint32_t sample_count = 0;

static uint16_t audio_buff_size = PDM_BUF_SIZE;

static __IO uint8_t new_data = 0;

extern serial_t *console;

/*

 * @brief: Converter function from PDM to PCM signal

 * also send the signal to the UART

 * @return: Return 0 in case of success

 */

static int convert_audio_to_pcm(void);

int mic_init(void *instance) {

 if(!instance) {

 LOGLN("Error: I2S instance passed NULL ptr in mic_init()");

 return 1;

 }

 i2s = instance;

 MX_PDM2PCM_Init();

 return 0;

}

int mic_start_recording(void) {

 HAL_StatusTypeDef status = HAL_I2S_Receive_DMA(i2s, rxbufs[0], audio_buff_size);

 if (status != HAL_OK) {

  LOGLN("Error: Failed to start I2S transmission in mic_start_recording()");

  return 1;

 }

 sample_count = 0;

// HAL_Delay(10);

 return 0;

}

int mic_stop_recording(void) {

 return HAL_I2S_DMAStop(i2s);

}

void mic_monitor(void) {

 if (new_data) {

  new_data = 0;

  convert_audio_to_pcm();

 }

}

static int convert_audio_to_pcm(void) {

 int val = 0;

 uint32_t rc = 0;

 rc = PDM_Filter(&(((uint8_t *)rxbuf)[0]), &(((uint16_t *)pcm)[0]),

 &PDM1_filter_handler);

 if (rc != 0) {

  LOGLN("Error in PDM->PCM conversion first part.");

  return 1;

 }

 rc = PDM_Filter(&(((uint8_t *)rxbuf)[1]), &(((uint16_t *)pcm)[1]),

   &PDM2_filter_handler);

 if (rc != 0) {

   LOGLN("Error in PDM->PCM conversion second part");

   return 1;

 }

 // 2 bytes per sample

 size_t len = 2*PCM_BUF_SIZE;

 // output the values to UART for forming the .vaw file

 serial_print_bytes(console, &(((uint8_t *)pcm)[0]), len);

 sample_count += PCM_BUF_SIZE;

 memset(pcm, 0, len);

 return 0;

}

void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {

// could be used if needed (half transfer done callback)

}

void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s) {

LOGLN("HAL_I2S_ErrorCallback happened on the i2s interface\n");

}

void HAL_I2S_AbortCallback(I2S_HandleTypeDef *hi2s) {

LOGLN("HAL_I2S_AbortCallback happened on the i2s interface\n");

}

void XferM1CpltCallback(I2S_HandleTypeDef *hi2s) {

LOGLN("XferM1CpltCallback happened on the i2s interface\n");

}

void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s) {

if (hi2s != i2s) {

return;

}

rxbuf = rxbufs[0];

new_data = 1;

}

My pdm2pcm code:

/* PDM2PCM init function */

void MX_PDM2PCM_Init(void)

{

 /* USER CODE BEGIN 2 */

 /* USER CODE END 2 */

  /**

 */

 PDM1_filter_handler.bit_order = PDM_FILTER_BIT_ORDER_LSB;

 PDM1_filter_handler.endianness = PDM_FILTER_ENDIANNESS_BE;

 PDM1_filter_handler.high_pass_tap = 2104533974;

 PDM1_filter_handler.in_ptr_channels = 2;

 PDM1_filter_handler.out_ptr_channels = 2;

 PDM_Filter_Init(&PDM1_filter_handler);

 PDM1_filter_config.decimation_factor = PDM_FILTER_DEC_FACTOR_64;

 PDM1_filter_config.output_samples_number = 48;

 PDM1_filter_config.mic_gain = 28;

 PDM_Filter_setConfig(&PDM1_filter_handler, &PDM1_filter_config);

 PDM2_filter_handler.bit_order = PDM_FILTER_BIT_ORDER_LSB;

 PDM2_filter_handler.endianness = PDM_FILTER_ENDIANNESS_BE;

 PDM2_filter_handler.high_pass_tap = 2104533974;

 PDM2_filter_handler.in_ptr_channels = 2;

 PDM2_filter_handler.out_ptr_channels = 2;

 PDM_Filter_Init(&PDM2_filter_handler);

 PDM2_filter_config.decimation_factor = PDM_FILTER_DEC_FACTOR_64;

 PDM2_filter_config.output_samples_number = 48;

 PDM2_filter_config.mic_gain = 28;

 PDM_Filter_setConfig(&PDM2_filter_handler, &PDM2_filter_config);

 /* USER CODE BEGIN 3 */

 /* USER CODE END 3 */

}

 HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);

what i'm doing now - trying to start the timer with such start function.

Clock configurations done according to Interfacing PDM digital microphones using STM32 MCUs and MPUs - Application note.

Timer settings in attachment

Please share your advice or maybe code example for using such PDM stereo micros with your PDM2PCM middleware and CubeMX

3 REPLIES 3
Sebastien DENOUAL
ST Employee

Hi @AMukh.3​ ,

Did you already have a look to this package : X-CUBE-MEMSMIC1 ?

There some code examples (even fro STM32F401) which support multiple microphone acquisition (up to 4)

I would advise this specific code example from package :...\Projects\STM32F401RE-Nucleo\Demonstration\CCA02M2\Microphones_Streaming

it supports from 1 (mono) up to 4 microphones.

Number of microphone can be selected using a compilation flag in cca02M2_conf.h file (as well as sampling freq)

#define AUDIO_IN_CHANNELS 2

#define AUDIO_IN_SAMPLING_FREQUENCY 48000

There is other code example available :

  • nuceo-L476 - acquisition on DFSDM interface (HW IP)
  • nucleo-WB55 - SAI

Hope it helps.

Regards,

Sebastien.

This code example is using I2S for stereo acquisition and PDM2PCM library to convert from PDM to PCM.

Regards,

Sebastien.

victagayun
Senior III

Hello,

Were you able to use PDM2PCM in stereo?

I saw in

__weak int32_t CCA02M2_AUDIO_IN_PDMToPCM(uint32_t Instance, uint16_t *PDMBuf, uint16_t *PCMBuf)
{    
  if(Instance != 0U)
  {
    return  BSP_ERROR_WRONG_PARAM;
  }
  else 
  {
#ifdef USE_STM32L4XX_NUCLEO    
    return  BSP_ERROR_WRONG_PARAM;
#else
    uint32_t index;
    
    for(index = 0; index < AudioInCtx[Instance].ChannelsNbr; index++)
    {
      if (AudioInCtx[Instance].SampleRate == 8000U)
      {
        uint16_t Decimate_Out[8U*N_MS_PER_INTERRUPT];
        uint32_t ii;
        uint16_t PDM_Filter_Out[16U*N_MS_PER_INTERRUPT];
        
        (void)PDM_Filter(&((uint8_t*)(PDMBuf))[index], PDM_Filter_Out, &PDM_FilterHandler[index]);
        (void)arm_fir_decimate_q15 (&ARM_Decimator_State[index], (q15_t *)&(PDM_Filter_Out), (q15_t*)&(Decimate_Out), DECIMATOR_BLOCK_SIZE);
        for (ii=0; ii<(8U*N_MS_PER_INTERRUPT); ii++)
        {
          PCMBuf[(ii * AudioInCtx[Instance].ChannelsNbr) + index] = Decimate_Out[ii];
        }
      }
      else
      {
        switch(AudioInCtx[Instance].BitsPerSample)
        {
        case AUDIO_RESOLUTION_16b:
          (void)PDM_Filter(&((uint8_t*)(PDMBuf))[index], (uint16_t*)&(PCMBuf[index]), &PDM_FilterHandler[index]);
          break;
        case AUDIO_RESOLUTION_24b:
          (void)PDM_Filter(&((uint8_t*)(PDMBuf))[index], &((uint8_t*)(PCMBuf))[3U*index], &PDM_FilterHandler[index]);          
          break;
        case AUDIO_RESOLUTION_32b:
          (void)PDM_Filter(&((uint8_t*)(PDMBuf))[index], (uint32_t*)&(PCMBuf[index]), &PDM_FilterHandler[index]);          
          break;
        default:
          break;
        }
      }
    }    
#endif
  }  
  return BSP_ERROR_NONE;
}
 
#endif

It seems that "index" is pointing to the mic channel?

If you have 2 or mics then is it correct to say?

mic1 = 0

mic2 = 1

mic3 = 2

mic4 = 3

PDM_Filter(&((uint8_t*)(PDMBuf))[mic1], (uint16_t*)&(PCMBuf[mic1]), &PDM_FilterHandler[mic1]);
PDM_Filter(&((uint8_t*)(PDMBuf))[mic2], (uint16_t*)&(PCMBuf[mic2]), &PDM_FilterHandler[mic2]);
PDM_Filter(&((uint8_t*)(PDMBuf))[mic3], (uint16_t*)&(PCMBuf[mic3]), &PDM_FilterHandler[mic3]);
PDM_Filter(&((uint8_t*)(PDMBuf))[mic4], (uint16_t*)&(PCMBuf[mic4]), &PDM_FilterHandler[mic4]);

AMukh.3
Associate II

The index is pointing to the corresponding channel in the PDM stream buffer according to the for loop condition. But I did not found any full description of PDM2PCM libraries API, anyway using the code example you refer as is, i was able to get the stereo sound from my microphones and than rewrite it for my needs.

You can also look at this project to record the sound into .vaw file for testing your application.

https://github.com/stm32duino/Python-Wave-Serial-Encoder/blob/main/README.md