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);
}