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

PDM - nRF52840 SDK 15.2

I'm having trouble with PDM on SDK15.2 with nRF52840.

The raw data buffers are only half full. The last half are zero's, therefore recorded voice has a robotic sound to it.

What I tried:

1) Went thru Thingy firmware thoroughly. As the other forum posts indicate, its in SDK 13 and using the nfx/legacy migration drivers are not 1:1 when handling the buffers.

2) My initial code base was with the nfx driver. It basically works but has the 1/2 data missing issue mentioned above.

3) I rewrote the driver using the same nrf_pdm calls, similar to Thingy's SDK 13 driver. And its having the same 1/2 data issue also.

4) Connected analyzer to the PDM lines and verified that the DATA is not zero during the 1/2 buffer time.

It almost like nRF's PDM is looking for a Right-side mic (stereo) but its set to MONO

nrf_pdm_mode_set((nrf_pdm_mode_t) NRF_PDM_MODE_MONO, (nrf_pdm_edge_t) NRF_PDM_EDGE_LEFTRISING)

Here is my code setup.

#include "pdmDriver.h"

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

#include "gpio.h"
#include "micHandler.h"
#include "nrf_assert.h"
#include "nrf_gpio.h"
#include "nrf_log.h"
#include "nrf_pdm.h"
#include "utility.h"

// Verify SDK configuration.
STATIC_ASSERT(PDM_ENABLED);

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

#define NUM_PDM_BUFFERS		2
static int16_t pdmBuffer[NUM_PDM_BUFFERS][CONFIG_PDM_BUFFER_SIZE_SAMPLES];
static pdmBufferHandler_t bufferHandler;
static uint8_t skipBuffers;
static pdmState_t pdmState = PDM_STATE_IDLE;

void PDM_IRQHandler(void) {
    if (nrf_pdm_event_check(NRF_PDM_EVENT_END)) {
        nrf_pdm_event_clear(NRF_PDM_EVENT_END);

		// Skip initial transient noise
		if (skipBuffers) {
			skipBuffers -= 1;
			return;
		}

        //Buffer is ready to process.
        if (nrf_pdm_buffer_get() == (uint32_t*) pdmBuffer[0]) {
            //NRF_LOG_DEBUG("PDM data:\r\n");
            //NRF_LOG_INFO_ARRAY((uint8_t *)pdmBuffer[1], sizeof(pdmBuffer[1]));
            bufferHandler(pdmBuffer[1], CONFIG_PDM_BUFFER_SIZE_SAMPLES);
        } else {
            //NRF_LOG_DEBUG("PDM data:\r\n");
            //NRF_LOG_INFO_ARRAY((uint8_t *)pdmBuffer[0], sizeof(pdmBuffer[0]));
            bufferHandler(pdmBuffer[0], CONFIG_PDM_BUFFER_SIZE_SAMPLES);
        }
    } else if (nrf_pdm_event_check(NRF_PDM_EVENT_STARTED)) {
        nrf_pdm_event_clear(NRF_PDM_EVENT_STARTED);
        pdmState = PDM_STATE_RUNNING;

        //Swap buffer.
        if (nrf_pdm_buffer_get() == (uint32_t*) pdmBuffer[0]) {
            nrf_pdm_buffer_set((uint32_t*)pdmBuffer[1], CONFIG_PDM_BUFFER_SIZE_SAMPLES);
        } else {
            nrf_pdm_buffer_set((uint32_t*)pdmBuffer[0], CONFIG_PDM_BUFFER_SIZE_SAMPLES);
        }
    } else if (nrf_pdm_event_check(NRF_PDM_EVENT_STOPPED)) {
        nrf_pdm_event_clear(NRF_PDM_EVENT_STOPPED);
        nrf_pdm_disable();
        pdmState = PDM_STATE_IDLE;
    }
}

ret_code_t pdmInit(pdmBufferHandler_t handler) {
    if (handler == NULL) {
        return NRF_ERROR_INVALID_PARAM;
    }

    bufferHandler = handler;
    pdmState = PDM_STATE_IDLE;

    nrf_pdm_buffer_set((uint32_t*)pdmBuffer[0], CONFIG_PDM_BUFFER_SIZE_SAMPLES);
    nrf_pdm_clock_set((nrf_pdm_freq_t) NRF_PDM_FREQ_1032K);
    nrf_pdm_mode_set((nrf_pdm_mode_t) NRF_PDM_MODE_MONO, (nrf_pdm_edge_t) NRF_PDM_EDGE_LEFTRISING);
    nrf_pdm_gain_set(CONFIG_PDM_GAIN, CONFIG_PDM_GAIN);

    nrf_gpio_cfg_output(PIN_MIC_CLK);
    nrf_gpio_pin_clear(PIN_MIC_CLK);
    nrf_gpio_cfg_input(PIN_MIC_DOUT, NRF_GPIO_PIN_PULLDOWN);
    nrf_pdm_psel_connect(PIN_MIC_CLK, PIN_MIC_DOUT);

    nrf_pdm_int_enable(NRF_PDM_INT_STARTED | NRF_PDM_INT_END | NRF_PDM_INT_STOPPED);
    NRFX_IRQ_PRIORITY_SET(PDM_IRQn, PDM_CONFIG_IRQ_PRIORITY);
    NRFX_IRQ_ENABLE(PDM_IRQn);

    return NRF_SUCCESS;
}

void pdmUninit(void) {
    nrf_pdm_disable();
    nrf_pdm_psel_disconnect();
    NRF_LOG_INFO("PDM Uninitialized\r\n");
}

ret_code_t pdmEnable(void) {
    // Skip buffers with invalid data.
    skipBuffers = MAX(1, ROUNDED_DIV((CONFIG_PDM_TRANSIENT_STATE_LEN * SAMPLING_RATE),
                                        (1000 * CONFIG_AUDIO_FRAME_SIZE_SAMPLES)));
	NRF_LOG_INFO("pdmEnable skip: %d\r\n", skipBuffers);

    ret_code_t err_code;
    if (pdmState != PDM_STATE_IDLE) {
        if (pdmState == PDM_STATE_RUNNING) {
            err_code = NRF_SUCCESS;
            NRF_LOG_INFO("Function: %s, error code: %s.\r\n", (uint32_t)__func__, (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code));
            return err_code;
        }
        err_code = NRF_ERROR_BUSY;
        NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", (uint32_t)__func__, (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code));
        return err_code;
    }
    pdmState = PDM_STATE_TRANSITION;

    nrf_pdm_enable();
    nrf_pdm_event_clear(NRF_PDM_EVENT_STARTED);
    nrf_pdm_task_trigger(NRF_PDM_TASK_START);
    err_code = NRF_SUCCESS;
    return err_code;
}

ret_code_t pdmDisable(void) {
    ret_code_t err_code;
    if (pdmState != PDM_STATE_RUNNING) {
        if (pdmState == PDM_STATE_IDLE) {
            nrf_pdm_disable();
            err_code = NRF_SUCCESS;
            NRF_LOG_INFO("Function: %s, error code: %s.\r\n", (uint32_t)__func__, (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code));
            return err_code;
        }
        err_code = NRF_ERROR_BUSY;
        NRF_LOG_WARNING("Function: %s, error code: %s.\r\n", (uint32_t)__func__, (uint32_t)NRF_LOG_ERROR_STRING_GET(err_code));
        return err_code;
    }
    pdmState = PDM_STATE_TRANSITION;
    nrf_pdm_task_trigger(NRF_PDM_TASK_STOP);
    err_code = NRF_SUCCESS;
    return err_code;
}

static void bufferHandler(int16_t *p_buffer, uint16_t samples) {
	// Handler for PDM interface ready events. This event handler is called on a buffer request, an error or when a buffer is full and ready to be processed.
	if (samples == 0) {
		return;
	}

	pdm_buf_t *ptrBuffer = NULL;
	for (uint32_t i = 0; i < PDM_BUF_NUM; i++) {
		ptrBuffer = &pdmBuffer[i];
		if (ptrBuffer->free) {
			ptrBuffer->free = false;
			ptrBuffer->samples = samples;
			memcpy(ptrBuffer->buf, p_buffer, samples);

			ret_code_t err_code = app_sched_event_put((void*) &ptrBuffer, sizeof(pdm_buf_t*), processAudio);
			APP_ERROR_CONTINUE(err_code);
			return;
		}
	}

	NRF_LOG_WARNING("bufferHandler: BUFFER FULL!!\r\n");
}

static void processAudio(void *p_event_data, uint16_t event_size) {
	pdm_buf_t *pdm = *(pdm_buf_t**) p_event_data;

	uint8_t nested;
	//app_util_critical_region_enter(&nested);

	#if CONFIG_AUDIO_EQUALIZER_ENABLED
		drv_audio_dsp_equalizer((q15_t *)pdm->buf, CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
	#endif

	#if CONFIG_AUDIO_GAIN_CONTROL_ENABLED
		drv_audio_dsp_gain_control((q15_t *)pdm->buf, CONFIG_AUDIO_FRAME_SIZE_SAMPLES);
	#endif

	m_audio_frame_t frame_buf;
	drv_audio_coder_encode(pdm->buf, &frame_buf);
	pdm->free = true;
	//app_util_critical_region_exit(nested);

	NRF_LOG_DEBUG("raw: %d", (CONFIG_AUDIO_FRAME_SIZE_SAMPLES * sizeof(int16_t)));
	NRF_LOG_INFO_ARRAY((uint8_t *)pdm->buf, (CONFIG_AUDIO_FRAME_SIZE_SAMPLES * sizeof(int16_t)));
	NRF_LOG_DEBUG("\r\n");
	NRF_LOG_DEBUG("frame: %d", frame_buf.data_size);
	NRF_LOG_INFO_ARRAY((uint8_t *)frame_buf.data, frame_buf.data_size);
	NRF_LOG_DEBUG("\r\n");

	// Send audio
	sendAudioResponsePacket(frame_buf.data, frame_buf.data_size);
}

Parents Reply Children
No Data
Related