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

nRF52840 PDK - PDM - Where to call nrfx_pdm_buffer_set()?

Hi,

I'm using nRF5 SDK v15.0.0, and I'm trying to use the PDM peripheral for a digital microphone. I'm modifying the driver from ThingySDK, but I can't seem to find where nrfx_pdm_buffer_set() is set. From what I understood from the PDM driver description, it has to be called after the buffer is requested. However I believe my implementation is incorrect, as a hard fault occurs during the audio buffer handling.

What am I doing wrong?

#include "app_error.h"
#include "nrf_log.h"
#include "nrf_gpio.h"
#include "nrf_drv_pdm.h"
#include "nrfx_pdm.h"
#include "pdm.h"
#include "drv_audio_config.h"
#include "macros_common.h"

STATIC_ASSERT(CONFIG_PDM_BUFFER_SIZE_SAMPLES == (1 * CONFIG_AUDIO_FRAME_SIZE_SAMPLES));

// Sampling rate depends on PDM configuration.
STATIC_ASSERT(PDM_CONFIG_CLOCK_FREQ == NRF_PDM_FREQ_1032K);
#define SAMPLING_RATE   (1032 * 1000 / 64)
#define PDM_BUF_NUM 1

static pdm_buf_t                    m_pdm_buf[PDM_BUF_NUM];
static int16_t                      m_pdm_buff[2][CONFIG_PDM_BUFFER_SIZE_SAMPLES];
static bool                         m_audio_enabled;          ///< Audio enabled flag.
static uint8_t                      m_skip_buffers;
static drv_audio_buffer_handler_t   m_buffer_handler;


static void m_audio_process(void * p_event_data, uint16_t event_size)
{
    int16_t         * p_buffer;
    ret_code_t        status;
    m_audio_frame_t   frame_buf;
    pdm_buf_t       * p_pdm_buf = (pdm_buf_t *)(*(uint32_t *)p_event_data);

    APP_ERROR_CHECK_BOOL(p_event_data != NULL);
    APP_ERROR_CHECK_BOOL(event_size > 0);
    p_buffer = p_pdm_buf->buf;

#if CONFIG_AUDIO_EQUALIZER_ENABLED
    drv_audio_dsp_equalizer((q15_t *)p_buffer, CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
#endif /* CONFIG_AUDIO_EQUALIZER_ENABLED */

#if CONFIG_AUDIO_GAIN_CONTROL_ENABLED
    drv_audio_dsp_gain_control((q15_t *)p_buffer, CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
#endif /* CONFIG_AUDIO_GAIN_CONTROL_ENABLED */

    uint8_t nested;
    app_util_critical_region_enter(&nested);
    drv_audio_coder_encode(p_buffer, &frame_buf);
    p_pdm_buf->free = true;
    app_util_critical_region_exit(nested);

    // Schedule audio transmission. It cannot be done from this context.
    status = m_data_handler(&frame_buf);
    if (status != NRF_SUCCESS)
    {
        NRF_LOG_WARNING("Cannot schedule audio frame transmission!\r\n");
        /*
         * Do not clear CONFIG_IO_DBG_PCM. This will make debugging pulse wider
         * than expected and easier to spot on the logic analyzer.
         */
    }
}

void m_audio_buffer_handler(int16_t *p_buffer, uint16_t samples)
{
    uint32_t     err_code;
    pdm_buf_t  * p_pdm_buf = NULL;
    uint32_t     pdm_buf_addr;

    for(uint32_t i = 0; i < PDM_BUF_NUM; i++)
    {
        if ( m_pdm_buf[i].free == true )
        {
            m_pdm_buf[i].free    = false;
            m_pdm_buf[i].samples = samples;

            for (uint32_t j = 0; j < samples; j++)
            {
                m_pdm_buf[i].buf[j] = p_buffer[j];
            }

            p_pdm_buf = &m_pdm_buf[i];
            pdm_buf_addr = (uint32_t)&m_pdm_buf[i];

            break;
        }
    }

    if (p_pdm_buf != NULL)
    {
        err_code = app_sched_event_put(&pdm_buf_addr, sizeof(pdm_buf_t *), m_audio_process);
        APP_ERROR_CHECK(err_code);
    }
    else
    {
        NRF_LOG_WARNING("m_audio_buffer_handler: BUFFER FULL!!\r\n");
    }
}


void pdm_event_handler(uint32_t *p_buffer, uint16_t length)
{
    uint32_t err_code;
    err_code = nrfx_pdm_buffer_set((int16_t *)p_buffer, CONFIG_PDM_BUFFER_SIZE_SAMPLES);

    if (m_skip_buffers)
    {
        m_skip_buffers -= 1;
    }
    else
    {
        m_buffer_handler((int16_t *)p_buffer, CONFIG_PDM_BUFFER_SIZE_SAMPLES);
    }
}


ret_code_t drv_mic_init(drv_mic_data_handler_t data_handler, drv_audio_buffer_handler_t buffer_handler)
{

    if (buffer_handler == NULL)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    m_audio_enabled = false;
    m_data_handler  = data_handler;

    for(uint32_t i = 0; i < PDM_BUF_NUM; i++)
    {
        m_pdm_buf[i].free = true;
    }

    /* Configure MIC power pin */
    nrf_gpio_cfg_output(MIC_PWR_CTRL);
    nrf_gpio_pin_clear(MIC_PWR_CTRL);
    
    /* Configure DATA power pin */
    nrf_gpio_cfg_input(MIC_DATA, NRF_GPIO_PIN_PULLDOWN);

    /* PDM Configuration */
    nrf_drv_pdm_config_t pdm_cfg = NRF_DRV_PDM_DEFAULT_CONFIG(MIC_CLK, MIC_DATA);
    m_buffer_handler    = buffer_handler;
    pdm_cfg.gain_l      = CONFIG_PDM_GAIN;
    pdm_cfg.gain_r      = CONFIG_PDM_GAIN;
    pdm_cfg.mode        = NRF_PDM_MODE_MONO;
    pdm_cfg.edge        = NRF_PDM_EDGE_LEFTFALLING;

    return nrf_drv_pdm_init(&pdm_cfg, pdm_event_handler);
}


ret_code_t drv_mic_enable(void)
{
    if(m_audio_enabled == true)
    {
        return NRF_SUCCESS;
    }

    ret_code_t status;

    /* Set MIC power pin */
    nrf_gpio_pin_set(MIC_PWR_CTRL);

    /* Configure DATA power pin */
    nrf_gpio_cfg_input(MIC_DATA, NRF_GPIO_PIN_NOPULL);

    /* Skip buffers with invalid data. */
    m_skip_buffers = MAX(1, ROUNDED_DIV((CONFIG_PDM_TRANSIENT_STATE_LEN * SAMPLING_RATE),
                                        (1000 * CONFIG_AUDIO_FRAME_SIZE_SAMPLES)));

    /* Start PDM */
    status = nrfx_pdm_start();

    if (status == NRF_SUCCESS)
    {
        m_audio_enabled = true;
    }

    NRF_LOG_DEBUG("m_audio: Enabled\r\n");

    return status;
}

ret_code_t drv_mic_disable(void)
{
    ret_code_t status;

    NRF_LOG_DEBUG("m_audio: Disabled\r\n");

    if(m_audio_enabled == false)
    {
        return NRF_SUCCESS;
    }

    /* Stop PDM */
    status = nrfx_pdm_stop();

    /* Clear MIC power pin */
    nrf_gpio_pin_clear(MIC_PWR_CTRL);

    /* Configure DATA power pin */
    nrf_gpio_cfg_input(MIC_DATA, NRF_GPIO_PIN_PULLDOWN);
    
    if (status == NRF_SUCCESS)
    {
        m_audio_enabled = false;
    }

    return status;
}


Parents Reply Children
  • Hi,

    I've updated the driver, however now I'm facing the problem that the buffer is not being updated with new values. I have a single buffer now (of 800 samples) that should be filled periodically. I'm trying to discontinue sampling as soon as the buffer is full, and then wait for the next cycle to start over again.

    Please see the following code:

    #include "stdint.h"
    #include "stdbool.h"
    #include "stdlib.h"
    #include "string.h"
    
    #include "nrf_assert.h"
    #include "nrf_error.h"
    #include "nrf_gpio.h"
    #include "nrf_delay.h"
    
    #include "pdm.h"
    #include "nrfx_pdm.h"
    #include "nrf_drv_pdm.h"
    #include "nrf_pdm.h"
    
    #include "math.h"
    
    int                  sound_level = 0;
    static volatile bool buffer_full = false;
    int16_t buffer[BUFFER_SIZE];      
    
    static void pdm_event_handler(nrfx_pdm_evt_t const * const p_evt)
    {	
    
        ret_code_t err_code;
    
        if(p_evt->buffer_requested)
        {
            err_code =  nrfx_pdm_buffer_set(buffer, BUFFER_SIZE);
            APP_ERROR_CHECK(err_code);
        }
    
        if(p_evt->buffer_released)
        {
            
    	/* Stop PDM */
            err_code = nrfx_pdm_stop();
            buffer_full = true;
    
            /* Process current buffer */
            int sum_of_squares = 0;
            int16_t *p_buffer_8 = (int16_t*)p_evt->buffer_released;
    
            for( int16_t i = 0 ; i < BUFFER_SIZE ; i++ )
            {
                sum_of_squares += p_buffer_8[0] * p_buffer_8[0];
                p_buffer_8++;
            }
    
            sound_level = sqrt(sum_of_squares / BUFFER_SIZE) ;
    
    
            /* Clear MIC power pin */
            nrf_gpio_pin_clear(MIC_PWR_CTRL);
    
            /* Configure DATA power pin */
            nrf_gpio_cfg_default(MIC_DATA);
            
        }
    }
    
    //get PDM buffer
    uint32_t get_PDM_buffer(void)
    {
        ret_code_t err_code;
    
        /* Set MIC power pin */
        nrf_gpio_pin_set(MIC_PWR_CTRL);
    
        /* Wait for 50ms (for Knowles microphone) to wake up. Add more delay afterwards to ignore useless data */
        //nrf_delay_ms(50);
    
        /* Start PDM */
        err_code = nrfx_pdm_start();
        APP_ERROR_CHECK(err_code);
    
        return err_code;
    }
    
    
    uint32_t drv_audio_enable(void)
    {
        ret_code_t err_code;
    
        /* Set MIC power pin */
        nrf_gpio_pin_set(MIC_PWR_CTRL);
    
        /* Wait for 50ms (for Knowles microphone) to wake up. Add more delay afterwards to ignore useless data */
        nrf_delay_ms(50);
    
        err_code = nrfx_pdm_start();
        
        APP_ERROR_CHECK(err_code);
    
        while (!buffer_full)
        {
          __WFE();
        }
        buffer_full = false;
    
        return err_code;
    }
    
    
    uint32_t drv_audio_disable(void)
    {
        ret_code_t err_code;
    
        /* Clear MIC power pin */
        nrf_gpio_pin_clear(MIC_PWR_CTRL);
    
        /* Configure DATA power pin */
        nrf_gpio_cfg_default(MIC_DATA);
    
        return err_code;
    }
    
    
    uint32_t drv_audio_init(void)
    {
        nrfx_pdm_config_t pdm_cfg = NRFX_PDM_DEFAULT_CONFIG(MIC_CLK, MIC_DATA);
    
        pdm_cfg.gain_l          = CONFIG_AUDIO_PDM_GAIN;
        pdm_cfg.gain_r          = CONFIG_AUDIO_PDM_GAIN;
    
        return nrf_drv_pdm_init(&pdm_cfg, pdm_event_handler);
    }

  • Hey Abdul,

    did you ever manage to get your code working?

    Best

Related