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
  • 3) I would recommend printing your buffer as int16_t (signed short), as this is the PCM format generated by the PDM peripheral. When I plot the data it looks somewhat good, but there also seems to be some formatting error going on in the printouts:

    Did you mean this :

    I have created a int16 buffer and printing the same in integer format.  (Previously i was printing %4x on terminal and my buffer was also uint16. 

    )

    int16_t pdm_buf[PDM_BUF_SIZE];
    
        for(int i=0; i<PDM_BUF_SIZE;i++)
        {
          printk("%d, ", pdm_buf[i]);
        }
        printk("\r\n\r\n");
    

    The first parameter (number of channels) in wavfile.setparams is 2, but the data seems to be single channel? How is the PDM peripheral configured in the code?

    Yes its mono configured. On left channel with sampling rate of 16KHz audio frequency.

    Can you suggest me the changes in the python script please.  I created a file and passed  the below data in array to python script.

    940, 703, 241, 470, 907, 1332, 1491, 1254, 1120, 1223, 1231, 1221, 839, 720, 549, 595, 640, 508, 486, 502, 390, 425, 217, 102, 167, 279, 380, 449, 427, 426, 501, 500, 564, 775, 927, 1219, 1290, 1160, 1012, 854, 593, 842, 1200, 1396, 1600, 1589, 1436, 1376, 1458, 1455, 1513, 1380, 1040, 929, 925, 930, 1044, 952, 1153, 1092, 935, 676, 612, 569, 645, 802, 1057, 1162, 1039, 1079, 974, 1054, 1101, 1055, 1095, 1123, 1041, 905, 896, 1024, 1044, 1261, 1498, 1470, 1284, 1051, 870, 775, 655, 724, 561, 458, 500, 487, 493, 601, 776, 702, 642, 589, 638, 562, 441, 423, 409, 345, 374, 327, 451, 591, 705, 709, 676, 687, 527, 278, 247, 62, 130, 434, 387, 422, 76, -152, 18, 175, 164, 130, -51, 18, 101, 379, 465, 287, 262, 281, 119, -122, -170, -51, 15, -153, -547, -773, -770, -738, -545, -442, -361, -354, -389, -424, -584, -581, -666, -807, -887, -894, -832, -718, -665, -803, -856, -894, -1059, -1029, -958, -1018, -1150, -1209, -1267, -1220, -987, -803, -709, -461, -403, -601, -527, -408, -377, -389, -270, -150, -113, -264, -347, -355, -489, -644, -809, -553, -146, -188, -91, -153, -238, -217, -135, -40, -151, -255, -260, -206, 2, 245, 398, 513, 457, 438, 355, 527, 592, 524, 293, 260, 274, 287, 297, 256, 218, 216, 243, 206, 80, -195, -293, -286, -281, -331, -426, -281, -147, -310, -321, -417, -483, -477, -540, -451, -370, -80, -40, 48, 203, 84, 7, 141, 138, 48, -15, -123, -100, -325, -281, -225, -322, -322, -420, -453, -520, -469, -279, -378, -311, -441, -556, -663, -543, -302, -323, -69, -248, -536, -738, -798, -736, -893, -875, -664, -535, -446, -403, -286, -235, -211, -481, -779, -845, -899, -1044, -1059, -984, -573, -629, -574, -775, -927, -733, -591, -507, -577, -514, -344, -215, -521, -598, -644, -577, -514, -358, -431, -644, -659, -634, -418, -363, -557, -910, -1011, -1102, -924, -849, -975, -1143, -1193, -1277, -1193, -974, -1065, -1113, -1010, -791, -673, -706, -489, -398, -240, -10, 62, 55, 39, 11, -101, -116, -238, -173, 22, -80, -96, -309, -487, -354, -255, -370, -338, -153, 119, 200, 219, 83, 56,

    import sys
    import wave
    
    for arg in sys.argv[1:]:
        with open(arg, 'rb') as pcmfile:
            pcmdata = pcmfile.read()
        with wave.open(arg+'.wav', 'wb') as wavfile:
            wavfile.setparams((1, 2, 16000, 0, 'NONE', 'NONE'))
            wavfile.writeframes(pcmdata)

    And here is my main.c for the above example :

    /*
     * 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 24510  //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)
    
    
    int16_t pdm_buf[PDM_BUF_SIZE];
    
    static bool data_ready_flag = false;
    int8_t counter=0;
    bool write_data = false;
    
    
    
    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;
        }
    }
    
    /*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"); 
    
    int16_t pdm_buf[PDM_BUF_SIZE];
    
        for(int i=0; i<PDM_BUF_SIZE;i++)
        {
          printk("%d, ", pdm_buf[i]);
        }
        printk("\r\n\r\n");
    
    
      }
    
    
    
    }
    

Children
  • This seems to work better. Tested using python3.

    import sys
    import wave
    import re
    import struct
    
    with open('0317.test.txt', 'r') as pcmfile:
        # Convert comma-separated hex strings into list of integers
        pcm_list = list(map(lambda x: int(x, 16), re.findall(r'([\w\d]+)', pcmfile.read())))
    
    # Convert list of integers into little endian-encoded byte array
    pcmdata = bytes()
        
    for sample in pcm_list:
        # Original sample is signed 16-bit. Format as uint16, and decode as int16.
        tmpdata = struct.pack('<H', sample)
        tmpdata = struct.unpack('<h', tmpdata)[0]
        pcmdata += struct.pack('<h', tmpdata)
        
    with wave.open('0317.test.wav', 'wb') as wavfile:
        wavfile.setparams((1, 2, 16000, 0, 'NONE', 'NONE'))
        wavfile.writeframes(pcmdata)

Related