cancel
Showing results for 
Search instead for 
Did you mean: 

Not understanding PDM 2 PCM buffer sized

kcire L.
Associate III

Hello,

I posted this in another area but it may be better placed here. I have a PDM MEMS microphone that I am reading in I2S master receive mode. What I would really like to do is store the PCM data into a ping pong buffer and then transmit the first half while the second half is being filled by the PDM2PCM function. I know my code is wrong here, and was hoping someone could steer me in the right direction.

What I am seeing is 64 bytes of data (supposedly) followed by 448 bytes of all zeros. I don't know if my buffer sized are wrong or if I am misunderstanding something else. I would assume 128 bytes of data continuously.

uint16_t pdmRxBuf[128];
uint16_t audio_out[64];
uint16_t MidBuffer[16];
 
void FifoWrite(uint16_t data)
{
	fifoBuf[fifo_w_ptr] = data;
	fifo_w_ptr++;
}
 
uint16_t FifoRead()
{
	uint16_t val = fifoBuf[fifo_r_ptr];
	fifo_r_ptr++;
	return val;
}
 
 
 
 HAL_I2S_Receive_DMA(&hi2s2, &pdmRxBuf[0],64);
 
  HAL_UART_Transmit_DMA(&huart2, (uint8_t *) audio_out, 128);
 
  /* USER CODE END 2 */
 
 
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
 
 
  while (1)
  {
 
	  if (rxstate==1)
	  {
	       	PDM_Filter(&pdmRxBuf[0],&MidBuffer[0], &PDM1_filter_handler);
	       	for (int i=0; i<16;i++)
	       	{ FifoWrite(MidBuffer[i]);
	       	}
	       	if (fifo_w_ptr-fifo_r_ptr > 128)
	       	{
	       		fifo_read_enabled=1;
	       	}
	       	rxstate=0;
 
	       }
 
	       if (rxstate==2)
	       {
	       	PDM_Filter(&pdmRxBuf[64],&MidBuffer[0], &PDM1_filter_handler);
	       	for (int i=0; i<16;i++)
	       	{
	       		FifoWrite(MidBuffer[i]);
	       	}
	       	rxstate=0;
 
	       }
 
	       if(half_complete == 1)
	       {
	 			for (int i=0; i<32;i=i+1)
	 			{
	 				uint16_t data = FifoRead();
	 				audio_out[i] = data;
	 			}
	 			half_complete = 0;
	       }
	       if(full_complete == 1)
	       {
	 			for (int i=32; i<64;i=i+1)
	 			{
	 				uint16_t data = FifoRead();
	 				audio_out[i] = data;
	 			}
	 			full_complete = 0;
	       }
 
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
 
  /* USER CODE END 3 */
}
 
 
 
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
	 half_complete = 1;
}
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	 full_complete = 1;
}
 
void HAL_I2S_RxHalfCpltCallback (I2S_HandleTypeDef *hi2s) {
	rxstate = 1;
}
 
void HAL_I2S_RxCpltCallback (I2S_HandleTypeDef *hi2s)
{
	rxstate = 2;
}

Thanks for any help, I appreciate it.

7 REPLIES 7
Eleon BORLINI
ST Employee

Hi @kcire L.​ ,

as first step, if you have not already it done, I would suggest you to consider the X-CUBE-MEMS1 function pack as reference, and in particular to check:

  • The I2S word length settings (are 16 or 32 bits)?
  • The DMA data size. It can be set equal to 8, 16 or 32 bits.

Depending on the previous settings, especially the DMA ones, the significance of the number used for starting the DMA peripherals can change: I mean, the "64" number can refer to bytes, half words o words, if I well remember.

Please check if this answer can help you.

-Eleon

kcire L.
Associate III

Thanks for the response.

The I2S is setup in 24 bit data on 32 bit frame with a DMA length of half word.

So when pdmRxBuf is half filled it would contain 32 (16 bit values)?

I guess Im still unsure as to the conversion from PDM to PCM? When PDM_Filt is called once, does it produce a signle 16 bit value or multiple since MidBuf is a 16 bit array of 16 values?

Part of this code is not mine so I am a little confused when PDM filt is called but then the for loop seems to be writing 16 half words into the FIFO...

Eleon BORLINI
ST Employee

Hi @kcire L.​ ,

Let me recap briefly the question: you use the I2S with the DMA module, that is the DMA transfers the data from the I2S peripheral to the memory.

For this reason, when you have the half buffer filled, you have half of the samples that you decided to acquire with the DMA reading them from the I2S.

For what concerns the PDM_lib(): there is a parameter defining how many samples are generated for every call from the PDM to PCM. There are many examples that do this, for instance in the FP-AUD-SMARTMIC1 or in the X-CUBE-MEMSMIC1 function packs. The suggestion is to configure the I2S as shown there.

If you need more support, you can contact the (STM32) OLS from the contact page on st.com.

-Eleon

frnt
Senior

Dear @Eleon BORLINI​ ,

I still have some doubts about the PDM2PCM library.

Consider the STM32F401RE Nucleo board connected to the IMP34DT05 PDM microphone with L/R to GND.

Through Mx, the I2S peripheral is configured as follows:

0693W00000GYxgwQAD.pngThen, the DMA is configured as a circular buffer, half-word:

0693W00000GYxe3QAD.pngFinally, I have configured the PDM2PCM library as follows:

0693W00000GYxi9QAD.pngStarting from that configuration I'm having trouble understanding why the PDM2PCM is not working as expected.

Here is the skeleton of the code:

uint16_t pdmRxBuf[128];
uint16_t MidBuffer[16];
uint16_t pcm_data[4096];
uint8_t txstate = 0;
uint8_t rxstate = 0;
 
int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
  
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2S2_Init();
  MX_I2S3_Init();
  MX_CRC_Init();
  MX_PDM2PCM_Init();
  /* USER CODE BEGIN 2 */
 
 pcm_index = 0;
  /* USER CODE END 2 */
  
  HAL_I2S_Receive_DMA(&hi2s2, &pdmRxBuf[0],64);
 
  /* Infinite loop */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (rxstate==1) {
    	PDM_Filter(&pdmRxBuf[0],&MidBuffer[0], &PDM1_filter_handler);
    	for(uint8_t i = 0; i < 16 ; i++){
		pcm_data[pcm_index] = mid_buffer[i];
		pcm_index++;
	}
    	rxstate=0;
 
    }
 
    if (rxstate==2) {
    	PDM_Filter(&pdmRxBuf[64],&MidBuffer[0], &PDM1_filter_handler);
    	for(uint8_t i = 0; i < 16 ; i++){
	   pcm_data[pcm_index] = mid_buffer[i];
           pcm_index++;
        }
        rxstate=0;
    }
 
   if(pcm_index==4096){
	//Stop the DMA
	HAL_I2S_DMAPause(&hi2s2);
        
       // Send to UART or Store Data 
       pcm_index = 0;
       
       
 
    }
  /* USER CODE END 3 */
}
 
void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s){
	rx_state = 1;
}
 
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s){
	rx_state = 2;
}

The program is very stupid, tries to get 4096 samples into the pcm_data to be saved or sent through the UART.

Having configured the DMA as a circular buffer, I would expect that at every interrupt Half of the buffer is filled with new data, while the other half is used to run the PDM_Filter function and complete the PCM conversion and copy the data out to the pcm_data.

However, running the code above I observe that only half of the pdmRxBuf is filled and the other half has all zeros. Why is that happening?

Did I misunderstand some configurations?

Because what I get at the output is simply noise.

Thanks

Hi @frnt​ ,

Unfortunately I'm not an expert of the interface of audio sensors with microcontrollers, sorry.

I might suggest you to have a look to existing examples, instead of trying to configure the peripherals for the acquisition from scratch.

For example, you can have a look to the Microphones_Streaming example with the STM32F401RE-Nucleo that you can find in the X-CUBE-MEMSMIC1 function pack, folder: \Projects\STM32F401RE-Nucleo\Demonstration\CCA02M2\Microphones_Streaming.

If it is not suitable for you, try to post this question (a new one) in the STM32 group, topic STM32F401RE Nucleo, so that experts can easily reach you.

-Eleon

Dear @Eleon BORLINI​ ,

thanks for your reply. I have partially solved the issue.

Now I have 16kHz selected audio frequency, 16-bit data on 16 data frame, 64 decimation. Therefore the I2S clock should be 1.024MHz.

I record the audio of a pure tone (e.g. 500Hz) and then I convert it to a 16bit, 16kHz .wav file. The problem is that the recorded audio, played has double the frequency of the pure tone (1000Hz in this case). How could it be possible?

Changing the data and dataframe format to 24-bit in 32 data frame solves this issue.

But I don't undestand why!

Why with 16-bit data the pure tone appears with a double frequency?

Do you have any suggestions?

Hi @frnt​ ,

the I2S clock usually takes into account double of the frequency because it could deal with a stereo configuration.

In this configuration the two (Left and Right) mics share the same clock and data line, so that the clock samples the data on the rising edge of the digital pattern for the Right mic and on the falling edge for the Left mic.

Not sure however why you solved the issue by changing the data and dataframe format to 24-bit in 32 data frame.

Can you apply this configuration for your application?

-Eleon

** Note: I'll refer from now on to this thread, and will close this one **