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

PDM code implementation and testing

static void bsp_event_handler(bsp_event_t event)
{
    ret_code_t err_code;

    switch (event)
    {
        case BSP_EVENT_SLEEP:
            sleep_mode_enter();
            break; // BSP_EVENT_SLEEP

        case BSP_EVENT_DISCONNECT:
            err_code = sd_ble_gap_disconnect(m_conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break; // BSP_EVENT_DISCONNECT

        case BSP_EVENT_WHITELIST_OFF:
            if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
            {
                err_code = ble_advertising_restart_without_whitelist(&m_advertising);
                if (err_code != NRF_ERROR_INVALID_STATE)
                {
                    APP_ERROR_CHECK(err_code);
                }
            }
            break; // BSP_EVENT_KEY_0
          case BSP_EVENT_KEY_1:
            NRF_LOG_INFO("\n Press Button key 1\n");
            NRF_LOG_INFO("\n Erase the flash content !!\n");
//            err_code = pcm_pstorage_eraseall();
//            APP_ERROR_CHECK(err_code);
            break;

         case BSP_EVENT_KEY_2:
            NRF_LOG_INFO("\n Start:PDM --> PCM\n");
            err_code =  nrf_drv_pdm_start(); 
            APP_ERROR_CHECK(err_code);
            break;

         case BSP_EVENT_KEY_3:
            NRF_LOG_INFO("\n Stop:PDM --> PCM\n");
            // stop audio sample
            nrf_drv_pdm_stop();
            break;

        default:
            break;
    }
}









static void drv_audio_pdm_event_handler(uint32_t *p_buffer, uint16_t length)
{
	m_buffer_handler((int16_t *)p_buffer, length);
        int i;
//        for(i=0;i<= length;i++)
//        {
//          NRF_LOG_INFO("%s ",&p_buffer[i]);
//        }
//        NRF_LOG_INFO("\n ");
          NRF_LOG_INFO("pdm event handler \n");

}


uint32_t drv_audio_init(drv_audio_buffer_handler_t buffer_handler)
{
//    nrf_drv_pdm_config_t pdm_cfg = NRF_DRV_PDM_DEFAULT_CONFIG(CONFIG_IO_PDM_CLK,
//                                                              CONFIG_IO_PDM_DATA,
//                                                              m_pdm_buff[0],
//                                                              m_pdm_buff[1],
//                                                              CONFIG_PDM_BUFFER_SIZE_SAMPLES);
    nrf_drv_pdm_config_t pdm_cfg = NRF_DRV_PDM_DEFAULT_CONFIG(CONFIG_IO_PDM_CLK,CONFIG_IO_PDM_DATA);
    
    if (buffer_handler == NULL)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    
    m_buffer_handler    = buffer_handler;
    pdm_cfg.gain_l      = CONFIG_PDM_GAIN;
    pdm_cfg.gain_r      = CONFIG_PDM_GAIN;
    

    return nrf_drv_pdm_init(&pdm_cfg, drv_audio_pdm_event_handler);
}










int main(void)
{
    bool erase_bonds;

    // Initialize.
    log_init();
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    advertising_init();
    services_init();
    conn_params_init();
    peer_manager_init();

    // Start execution.
    NRF_LOG_INFO("Template example started.");
    application_timers_start();

//    advertising_start(erase_bonds);
    uint32_t err_code;
    err_code = drv_audio_init(drv_audio_pdm_event_handler);
    APP_ERROR_CHECK(err_code);
    
    // Enter main loop.
    for (;;)
    {
        idle_state_handle();
    }
}

I have to connect a MIC  working on PDM to nrf52 DK Board.For that, I have written the above code.  But when I am testing I am not able to get the clock pulses at the defined pin. Also do not know at which Freq it is working?

And when I read the pdm documentation it says I need to change the PSEL.CLK register value? Then how should I do it and with what value? same for PSEL.DIN register. I am confused a lot. Please suggest me the solution to all my doubts.I have checked the clk pin on Oscilloscope, the clock is not being generated.In order to generate clk at pin number 22 of nrf52 DK board should I define pin clk  as #define CONFIG_IO_PDM_CLK 0x22 or 0x16 (hex value of 22 )instead o rtf 0x22?

Parents
  • The PDM driver will call nrf_pdm_psel_connect who'll set the PSEL.CLK and PSEL.DIN. I suggest you read the implementation of nrfx_pdm_init and see how the driver and HAL configures the PDM registers. This will give you valuable insight into how our drivers and HALs work in relation to the peripherals HW documentation.

    "should I define pin clk  as #define CONFIG_IO_PDM_CLK 0x22 or 0x16 (hex value of 22 )instead o rtf 0x22?"
    pin numbers are decimal, so either 22 or 0x16. 

  • Ok. But even though I am doing #define PDM_CONFIG_CLOCK_FREQ 134217728 which is 1MHz, Iam not getting the clock signal at pin 22. For this what should I do?

  • Thanks, Torbjorn

    Yes, I want to echo the data which i receive from the PDM which will be PCM data(I guess).

    I was using ble_app_template example code but since I was not getting PDM clock I had removed everything from it and had only kept the PDM related functions.

    For the Uarte code when I had used the code given to me the board was rebooting. But then while I was debugging when I ad only initialised uarte, I got to know it hangs in the while condition of the below code :

    static void serial_tx(void const * p_context, char const * p_buffer, size_t len)
    {
    uint8_t len8 = (uint8_t)(len & 0x000000FF);
    m_xfer_done = false;
    ret_code_t err_code = nrf_drv_uart_tx(&m_uart, (uint8_t *)p_buffer, len8);
    APP_ERROR_CHECK(err_code);
    /* wait for completion since buffer is reused*/
    while (m_async_mode && (m_xfer_done == false))
    {

    }

    }

    And I am attaching the code when system enters fatal error when I try transmitting data on terminal in PDM handler..

    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    
    #include "nordic_common.h"
    #include "nrf.h"
    #include "app_error.h"
    #include "ble.h"
    #include "ble_hci.h"
    #include "ble_srv_common.h"
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "nrf_sdh.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_sdh_ble.h"
    #include "app_timer.h"
    #include "fds.h"
    #include "peer_manager.h"
    #include "peer_manager_handler.h"
    #include "bsp_btn_ble.h"
    #include "sensorsim.h"
    #include "ble_conn_state.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_ble_qwr.h"
    #include "nrf_pwr_mgmt.h"
    #include "boards.h"
    #include "nrf_gpio.h"
    #include "app_scheduler.h"
    
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "app_fifo.h"
    
    #include "nrf_drv_pdm.h"
    #include "nrf_fstorage.h"
    #include "nrf_fstorage_sd.h"
    #include "drv_audio.h"
    #include "app_uart.h"
    #include "nrfx_uarte.h"
    
    
    
    #if defined (UART_PRESENT)
    #include "nrf_uart.h"
    #endif
    #if defined (UARTE_PRESENT)
    #include "nrf_uarte.h"
    #endif
    
    #define DEVICE_NAME                     "Nordic_Template"                       /**< Name of device. Will be included in the advertising data. */
    #define MANUFACTURER_NAME               "NordicSemiconductor"                   /**< Manufacturer. Will be passed to Device Information Service. */
    #define APP_ADV_INTERVAL                 1600//300                                     /**< The advertising interval (in units of 0.625 ms. This value corresponds to 187.5 ms). */
    
    #define APP_ADV_DURATION                500//18000                                   /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
    #define APP_BLE_OBSERVER_PRIO           3                                       /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    #define APP_BLE_CONN_CFG_TAG            1                                       /**< A tag identifying the SoftDevice BLE configuration. */
    
    #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(100, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.1 seconds). */
    #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(200, UNIT_1_25_MS)        /**< Maximum acceptable connection interval (0.2 second). */
    #define SLAVE_LATENCY                   0                                       /**< Slave latency. */
    #define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         /**< Connection supervisory timeout (4 seconds). */
    
    #define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                   /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                  /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT    3                                       /**< Number of attempts before giving up the connection parameter negotiation. */
    
    #define SEC_PARAM_BOND                  1                                       /**< Perform bonding. */
    #define SEC_PARAM_MITM                  0                                       /**< Man In The Middle protection not required. */
    #define SEC_PARAM_LESC                  0                                       /**< LE Secure Connections not enabled. */
    #define SEC_PARAM_KEYPRESS              0                                       /**< Keypress notifications not enabled. */
    #define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
    #define SEC_PARAM_OOB                   0                                       /**< Out Of Band data not available. */
    #define SEC_PARAM_MIN_KEY_SIZE          7                                       /**< Minimum encryption key size. */
    #define SEC_PARAM_MAX_KEY_SIZE          16                                      /**< Maximum encryption key size. */
    
    #define DEAD_BEEF                       0xDEADBEEF                              /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */
    #define UART_TX_BUF_SIZE                256                                         /**< UART TX buffer size. */
    #define UART_RX_BUF_SIZE                256                                         /**< UART RX buffer size. */
    
    
    #define CONFIG_PDM_BUFFER_SIZE_SAMPLES 64//320
    
    // <o> PDM Decimation Filter Gain <0x00-0x50>
    // <i> For details on the PDM decimation filter, see the 'Decimation filter' section in the nRF52 Product Specification document.
    //#define CONFIG_PDM_GAIN 0x28
    //
    //
    //// <o> PDM CLK Pin
    //#define CONFIG_IO_PDM_CLK  29
    //
    //// <o> PDM DATA Pin
    //#define CONFIG_IO_PDM_DATA   5 //0x05
    
    // <o> PDM Microphone Power Control Pin
    #define CONFIG_IO_PDM_MIC_PWR_CTRL 0x1A
    
    
    //
    //typedef struct
    //{
    //    uint8_t buffer[CONFIG_AUDIO_FRAME_SIZE_BYTES];
    //    int     buffer_size;
    //    bool    buffer_free_flag;
    //} drv_audio_frame_t;
    
    unsigned char enabled = 0;
    /**@brief Audio buffer handler. */
    typedef void (*drv_audio_buffer_handler_t)(int16_t *p_buffer, uint16_t samples);
    
    static int16_t                      input_pdm_buff[2][CONFIG_PDM_BUFFER_SIZE_SAMPLES];
    //volatile drv_audio_frame_t       m_audio_frame[CONFIG_AUDIO_FRAME_BUFFERS];
    
    static int32_t                      *output_pdm_buff;
    static drv_audio_buffer_handler_t   m_buffer_handler;
    //uint16_t			m_frame_index = 0;
    //uint16_t                        m_next_frame_index = 0;
    static app_sched_event_handler_t        m_audio_frame_handler = NULL;
    
    
    const uint8_t leds_list[LEDS_NUMBER] = LEDS_LIST;
    void startDMA();
    
    
    /**@brief  Function for initializing the UART module.
     */
    /**@snippet [UART Initialization] */
    //nrfx_uarte uarte0 = NRFX_UARTE_INSTANCE(0);
    
    nrfx_uarte_t uarte0 = NRFX_UARTE_INSTANCE(0);
    void uarte_handler(nrfx_uarte_event_t const * p_event, void *p)
    {
        //Handle uarte events if needed
    }
    
    void uarte_init(void)
    {
        nrfx_err_t err_code = NRFX_SUCCESS;
        nrfx_uarte_config_t uarte_cfg = NRFX_UARTE_DEFAULT_CONFIG; //Set config in sd_config.h
        
    //    err_code = nrfx_uarte_init(&uarte0, &uarte_cfg, uarte_handler);
    //    APP_ERROR_CHECK(err_code);
      
    //    uint8_t txBuffer[20];
    //    uint8_t rxBuffer[20];
    //    
    //    memset(txBuffer,'A',sizeof(txBuffer));
    //
        NRF_UARTE0->BAUDRATE = NRF_UARTE_BAUDRATE_115200;
        NRF_UARTE0->PSEL.TXD = TX_PIN_NUMBER;
        NRF_UARTE0->PSEL.RXD = RX_PIN_NUMBER;
        NRF_UARTE0->CONFIG = NRF_UARTE_HWFC_DISABLED;
        NRF_UARTE0->ENABLE = (UARTE_ENABLE_ENABLE_Enabled << UARTE_ENABLE_ENABLE_Pos);
    
    //    NRF_UARTE0->TXD.PTR = (uint32_t)((uint8_t *) txBuffer);
    //    NRF_UARTE0->TXD.MAXCNT = sizeof(txBuffer);
    //    NRF_UARTE0->TASKS_STARTTX = 0;
    //
    //    NRF_UARTE0->RXD.PTR = (uint32_t)((uint8_t *) rxBuffer);
    //    NRF_UARTE0->RXD.MAXCNT = sizeof(rxBuffer);
    //    NRF_UARTE0->TASKS_STARTRX = 1;
    //    NRF_UARTE0->TASKS_STARTTX = 1;
    
    
        NRF_UARTE0->INTENCLR = 0xFFFFFFFF;
        NRF_UARTE0->INTENSET = NRF_UARTE_INT_ENDRX_MASK;
    
        NVIC_ClearPendingIRQ(UARTE0_UART0_IRQn);
        NVIC_SetPriority(UARTE0_UART0_IRQn, 1);
        NVIC_EnableIRQ(UARTE0_UART0_IRQn);
    //
    //
    //    LEDS_CONFIGURE(LEDS_MASK);
    //    LEDS_OFF(LEDS_MASK);
    //    if(NRF_UARTE0->EVENTS_RXSTARTED){
    //        LEDS_INVERT(1 << leds_list[1]);
    //    }
    
    
    
    }
    
    
    /**@brief Function for initializing the nrf log module.
     */
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    
    
    
    //static void nrf52_pdm_irq(void)
    //{
    //    // Simply pass on to the driver component handler.
    ////    if (nrf52_pdm_driver)
    ////        nrf52_pdm_driver->irq();
    //      irq();
    //}
    
    /**
     * Constructor for an instance of a PDM input (typically microphone),
     *
     * @param sd The pin the PDM data input is connected to.
     * @param sck The pin the PDM clock is conected to.
     * @param sampleRate the rate at which samples are generated in the output buffer (in Hz)
     * @param id The id to use for the message bus when transmitting events.
     */
    //                
    
    
    #define BUFFER_SIZE  64
    
    void startDMA()
    {
    
    //   NRF_LOG_INFO("startDMA   \n");   
       NRF_PDM->SAMPLE.PTR = (uint32_t)&input_pdm_buff[0];
    }
    
    void pdm_enable()
    {
        if (!enabled)
        {
            enabled = true;
    
            NRF_PDM->ENABLE = 1;
    
            startDMA();
    
            NRF_PDM->TASKS_START = 1;
    //        NRF_PDM->EVENTS_STARTED = 1;
        }
    }
    
    /**
     * Disable this component
     */
    void pdm_disable()
    {
        // Schedule all DMA transfers to stop after the next DMA transaction completes.
        enabled = false;
        NRF_PDM->ENABLE = 0;
    //    NRF_PDM->TASKS_START = 1;
    //    NRF_PDM->EVENTS_STOPPED = 0;
    //    NRF_PDM_TASK_STOP = 0;
    }
    
    
    
    void PDM_IRQHandler()
    {
    
    //    NRF_LOG_INFO(" INTERRUPT HANDLER \n"); 
        if (NRF_PDM->EVENTS_STARTED) 
        {
    //         NRF_LOG_INFO(" EVENTS_STARTED \n"); 
            // We've just started receiving data the inputBuffer.
            // So we don't miss any data, line up the next buffer.
            // (unless we've been asked to stop).
            if (enabled)
            {
              uint16_t *finished_buffer;
              finished_buffer = input_pdm_buff[0];
              nrfx_uarte_tx(&uarte0, &input_pdm_buff, CONFIG_AUDIO_FRAME_SIZE_BYTES);
              startDMA();
    
              NRF_PDM->EVENTS_STARTED = 0;
            }
        }
    
        if (NRF_PDM->EVENTS_END)
        {
    //      NRF_LOG_INFO(" EVENTS_END \n"); 
    //        if (outputBuffer.length() > 0)
    //            output.pullRequest();
    
            NRF_PDM->EVENTS_END = 0;
        }
    
        if (NRF_PDM->EVENTS_STOPPED)
        NRF_LOG_INFO(" EVENTS_STOPPED \n"); 
            NRF_PDM->EVENTS_STOPPED = 0;
    
        // Erratum.
        // JF: Not sure what this is working around? Seems harmless, so leaving it in for now...
        (void) NRF_PDM->EVENTS_STOPPED;
        
    }
    
    void pdm_init()
    {
       
        // Define our output stream as non-bocking.
    //    output.setBlocking(false);
    
       
        pdm_disable();
      
        nrf_gpio_cfg_output(CONFIG_IO_PDM_CLK);
        nrf_gpio_cfg_input(CONFIG_IO_PDM_DATA,NRF_GPIO_PIN_NOPULL);
    
        // Map the PDM peripheral onto the given pins...
        NRF_PDM->PSEL.CLK = CONFIG_IO_PDM_CLK;
    //    pdm_cfg.edge = NRF_PDM_EDGE_LEFTRISING;
    //   nrf_pdm_mode_set(nrf_pdm_mode_t pdm_mode, nrf_pdm_edge_t pdm_edge)
    
         NRF_PDM->MODE = ((PDM_CONFIG_MODE << PDM_MODE_OPERATION_Pos) & PDM_MODE_OPERATION_Msk)
                        | ((NRF_PDM_EDGE_LEFTRISING << PDM_MODE_EDGE_Pos) & PDM_MODE_EDGE_Msk);
    
    
        NRF_PDM->PSEL.DIN = CONFIG_IO_PDM_DATA;
    
        // Configure for DMA enabled, single channel PDM input.
    //  NVIC_SetVector(PDM_IRQn, (uint32_t )nrf52_pdm_irq);
    
        /* Set up interrupts */
        NRF_PDM->INTENSET = (PDM_INTENSET_STARTED_Enabled << PDM_INTENSET_STARTED_Pos) |
            (PDM_INTENSET_END_Enabled << PDM_INTENSET_END_Pos ) |
            (PDM_INTENSET_STOPPED_Enabled << PDM_INTENSET_STOPPED_Pos);
    
        NVIC_SetPriority( PDM_IRQn, 1);
        NVIC_ClearPendingIRQ( PDM_IRQn );
        NVIC_EnableIRQ( PDM_IRQn );
    
    
        // Configure for a 1.032MHz PDM clock.
        NRF_PDM->PDMCLKCTRL = (PDM_PDMCLKCTRL_FREQ_Default << PDM_PDMCLKCTRL_FREQ_Pos );
    
        // Mono operation.
        NRF_PDM->MODE = ( PDM_MODE_EDGE_LeftRising  << PDM_MODE_EDGE_Pos ) |
            (PDM_MODE_OPERATION_Mono << PDM_MODE_OPERATION_Pos);
    
        // Set default gain of 0dbm.
        NRF_PDM->GAINL = CONFIG_AUDIO_PDM_GAIN;
        NRF_PDM->GAINR = CONFIG_AUDIO_PDM_GAIN;
    
    
        // Configure buffer size.
       
        NRF_PDM->SAMPLE.PTR = (uint32_t)&input_pdm_buff[0];
        NRF_PDM->SAMPLE.MAXCNT = CONFIG_PDM_BUFFER_SIZE_SAMPLES;
        pdm_enable();
    
        // Record our sample rate for future computation.
        // This is a constant of the PDM samplerate / 64 (as defined in nrf52 specification, seciton 44).
        // For the configuration above, this translates to approx 16kHz.
    //    this->sampleRate = 1032000 / 64;
    }
    
    
    
       
    
    
    
    
    /**
     * Initiate a DMA transfer into the raw data buffer.
     */
    
    static void idle_state_handle(void)
    {
        if (NRF_LOG_PROCESS() == false)
        {
            nrf_pwr_mgmt_run();
        }
    }
    
    
    /**@brief Function for application main entry.
     */
    int main(void)
    {
       // Start execution.
    
     
        log_init();
    //
    
        uarte_init();
    //      uart_init();
    
        NRF_LOG_INFO("Template example started.");
        pdm_init();
        
    
    for (;;)
        {
            idle_state_handle();
        }
      
    }
    
    
    
    
    /**
     * Update our reference to a downstream component.
     * Pass through any connect requests to our output buffer component.
     *
     * @param component The new downstream component for this PDM audio source.
     */
    
    
    //static NRF52PDM *nrf52_pdm_driver = NULL;
    
    
    
    
    
    

  • For me, only the data matters that is how to read the PDM data either on a terminal or from RAM anything will do.You can guide me on either way but need the PDM data.

  • Hi Sara

    I don't think the UART driver is very happy if you try to access the driver from a high priority interrupt. 

    Could you try to reduce the interrupt priority of the PDM interrupt handler and see if it works better?

    A higher IRQ priority number corresponds to lower interrupt priority, with 7 being the lowest and 0 the highest. 

    Best regards
    Torbjørn

  • Hi, i had made the interrupt priorities changes as below , but still my device resets and goes into Fatal error.

    #ifndef NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY
    #define NRFX_UART_DEFAULT_CONFIG_IRQ_PRIORITY 5
    #endif

    #ifndef PDM_CONFIG_IRQ_PRIORITY
    #define PDM_CONFIG_IRQ_PRIORITY 7
    #endif

  • Hi Sara

    Any particular reason you are not using the PDM driver provided with the SDK?

    I tried to add the PDM driver to the ble_app_uart example, and was able to get it running very quickly. I can print out parts of the PDM data without any issues (I didn't try to print out all the data, as this might be too much for the UART to handle). 

    You can download my project here if you want to have a look:
    ble_app_uart_pdmtest.zip

    Best regards
    Torbjørn

Reply Children
Related