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