This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRFX based PDM to PCM implementation (I2S example)

Hello,

I am currently interfacing my nRF52840 DK with i2S based MP34DT05 sensor. I am using nrfx i2s driver's for interfacing with sensor. I am using master clk of 2MHz, LCLK 16KHZ. 

My sensor output PDM data. In my project I am recording audio samples on left channel, so I am currently getting 2*16bit of left data per 32bits as shown in nrf docs. I have got 16bit recorded samples in buffer, but to how to convert this pdm data in pcm format. 

Normally we need to divide the PDM data with a decimation factor, but in I2S we already are dividing the bit sampling rate with ratio value. So is the output from the I2S itself is decimated PCM ?  

Or can anyone give a example of how to convert the PDM to PCM data. Any sort of help is dually appreciated. 

Parents
No Data
Reply
  • Hello,

    i would like to know how to capture song samples continuously. How can i do that in my data handler.

    Currently in my example i have created a single buffer of size 30720 and reading data into that buffer only once. And it gives me good audio sample. The main code and the .wav file is given below.

    void nrfx_pdm_event_handler(nrfx_pdm_evt_t const * p_evt)
    {
        if(p_evt->buffer_requested)
        {
           nrfx_pdm_buffer_set(pdm_buf, PDM_BUF_SIZE);
        }
        if(p_evt->buffer_released != 0)
        {
          write_data = true;
        }
    }
    
    
    
    /*My while loop in main function*/
     
      while(1)
      {
    
        while(!write_data){
          k_sleep(K_MSEC(100));
        }
        
        write_data = false;
    
    //Recording only one sample
        ret = nrfx_pdm_stop();
        if(ret != NRFX_SUCCESS){
          printk("Error Init\r\n");
        }
    
        printk("Displaying buffer\r\n"); 
    
        for(int i=0; i<PDM_BUF_SIZE;i++)
        {
          printk("%4x, ", pdm_buf[i]);
        }
        printk("\r\n\r\n");
    
    
      }

    But how can i continuously record the samples ? If i comment the NRFX_PDM_Stop and store data in buffer continuously. After creating a PCM file from the output terminal by combining multiple DMA buffers when i feed the file into python script it gives me completely mixed song not clear like above.

    Here is the mixed song.wav file:

    Have modified my code a little bit to read dma data in multiple buffers but same issue. Below is the use of multiple buffer in data handler.

    /*
     * Copyright (c) 2020 BayLibre, SAS
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <sys/printk.h>
    #include <sys/util.h>
    #include <string.h>
    #include <usb/usb_device.h>
    #include <drivers/uart.h>
    #include <drivers/gpio.h>
    #include <nrfx_pdm.h>
    
    #define PDM_BUF_SIZE 4096  //length of buffer in 16 bit words
    #define PDM_CLK_PIN  46   // P1.14 (32+14)
    #define PDM_DIN_PIN  45   // P1.13 (32+13)
    
    /* Audio sampling config */
    #define AUDIO_SAMPLING_FREQUENCY            16000
    #define AUDIO_SAMPLES_PER_MS                (AUDIO_SAMPLING_FREQUENCY / 1000)
    #define AUDIO_DSP_SAMPLE_LENGTH_MS          16
    #define AUDIO_DSP_SAMPLE_RESOLUTION         (sizeof(short))
    //#define AUDIO_DSP_SAMPLE_BUFFER_SIZE        (AUDIO_SAMPLES_PER_MS * AUDIO_DSP_SAMPLE_LENGTH_MS * AUDIO_DSP_SAMPLE_RESOLUTION) //4096
    #define AUDIO_DSP_SAMPLE_BUFFER_SIZE        30720
    
    /* Buffers for receiving PDM mic data */
    static uint16_t pdm_buffer_temp[2][AUDIO_DSP_SAMPLE_BUFFER_SIZE] = {0};
    uint16_t *current_buff;
    bool write_data = false;
    
    uint16_t pdm_buf[PDM_BUF_SIZE];
    
    static bool data_ready_flag = false;
    int8_t counter=0;
    
    static uint8_t buf_toggle = 0;
    
    
    //void nrfx_pdm_event_handler(nrfx_pdm_evt_t const * p_evt)
    //{
    //    if(p_evt->buffer_requested)
    //    {
    //       nrfx_pdm_buffer_set(pdm_buf, PDM_BUF_SIZE);
    //    }
    //    if(p_evt->buffer_released != 0)
    //    {
    //      write_data = true;
    //    }
    //}
    
    static void nrfx_pdm_event_handler(nrfx_pdm_evt_t const * p_evt)
    {
        nrfx_err_t err = NRFX_SUCCESS;
    
        if(p_evt->error != 0){
            printk("PDM handler error ocured\n");
            printk("pdm_data_handler error: %d, %d  \n", p_evt->error, p_evt->buffer_requested);
            return;
        }
        if(true == p_evt->buffer_requested){
            buf_toggle ^= 1;
            err = nrfx_pdm_buffer_set(pdm_buffer_temp[buf_toggle], AUDIO_DSP_SAMPLE_BUFFER_SIZE);
            if(err != NRFX_SUCCESS){
                printk("PDM buffer init error: %d\n", err);
            }
        }
        if(p_evt->buffer_released != NULL){
                write_data = true;
                current_buff = pdm_buffer_temp[buf_toggle];           
    
        }
    }
    /*ISR workaround given by Nordic*/
    ISR_DIRECT_DECLARE(pdm_isr_handler)
    {
      nrfx_pdm_irq_handler();
      ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency
                        */
      return 1; /* We should check if scheduling decision should be made */
    }
    
    
    void main(void)
    {
        nrfx_err_t ret = NRFX_SUCCESS;
    
    //USB interface
        const struct device *dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME);
        uint32_t dtr = 0;
    
    
        if (usb_enable(NULL)) {
                return;
        }
    
        //// Poll if the DTR flag was set, optional 
        while (!dtr) {
                uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr);
        }
    
        if (strlen(CONFIG_UART_CONSOLE_ON_DEV_NAME) !=  strlen("CDC_ACM_0") ||
            strncmp(CONFIG_UART_CONSOLE_ON_DEV_NAME, "CDC_ACM_0",
                    strlen(CONFIG_UART_CONSOLE_ON_DEV_NAME))) {
               printk("Error: Console device name is not USB ACM\n");
    
                return;
        }
    
    
    
    //PDM sensor interface
        IRQ_DIRECT_CONNECT(PDM_IRQn, 0, pdm_isr_handler, 0);  //workaround line 
    
        nrfx_pdm_config_t pdm_config = NRFX_PDM_DEFAULT_CONFIG(PDM_CLK_PIN, PDM_DIN_PIN); /*configures CLK to pin 10 and Din to pin 11*/
    
        pdm_config.clock_freq = NRF_PDM_FREQ_1280K; //microphone requirement
        pdm_config.ratio = NRF_PDM_RATIO_80X;       //Decimation Factor to get Sample rate (1280K/80)16Khz
        pdm_config.edge = NRF_PDM_EDGE_LEFTRISING;
        pdm_config.gain_l = NRF_PDM_GAIN_MAXIMUM;
        pdm_config.gain_r = NRF_PDM_GAIN_MAXIMUM;
    
    
        ret = nrfx_pdm_init(&pdm_config, nrfx_pdm_event_handler);
        if(ret != NRFX_SUCCESS){
          printk("PDM Init failed.\r\n");
        }else{
          printk("PDM Init succesfull.\r\n");    
        }
    
        //Turn-on time to get valid output data is 10ms.
        k_sleep(K_MSEC(2000));
    
        ret = nrfx_pdm_start();
        if(ret != NRFX_SUCCESS)
        {
          printk("PDM recording failed\r\n");
        }else{
         printk("PDM recording started succesfully\r\n");    
        }
        
        k_sleep(K_MSEC(2000));
    
     
      while(1)
      {
    
        while(!write_data){
          k_sleep(K_MSEC(100));
        }
        
        write_data = false;
    
        //ret = nrfx_pdm_stop();
        //if(ret != NRFX_SUCCESS){
        //  printk("Error Init\r\n");
        //}
    
        printk("Displaying buffer\r\n"); 
    
        for(int i=0; i<AUDIO_DSP_SAMPLE_BUFFER_SIZE;i++)
        {
          printk("%4x, ", *(current_buff + i));
        }
        printk("\r\n\r\n");
    
    
      }
    
    
    
    }
    

    Can you suggest me how to solve this issue ? How to properly record sample continuously for 10-20 sec minimum audio.

Children
Related