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