Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] periph/pdm: propose API and add nrf52 implementation #16125

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boards/adafruit-clue/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ CPU_MODEL = nrf52840xxaa

# Put defined MCU peripherals here (in alphabetical order)
FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_pdm
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_uart
FEATURES_PROVIDED += periph_usbdev
Expand Down
10 changes: 10 additions & 0 deletions boards/adafruit-clue/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ static const spi_conf_t spi_config[] = {
#define SPI_NUMOF ARRAY_SIZE(spi_config)
/** @} */

/**
* @name PDM configuration
* @{
*/
static const pdm_conf_t pdm_config = {
.din_pin = GPIO_PIN(0, 0),
.clk_pin = GPIO_PIN(0, 1),
};
/** @} */

#ifdef __cplusplus
}
#endif
Expand Down
9 changes: 9 additions & 0 deletions cpu/nrf52/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@ void spi_twi_irq_register_spi(NRF_SPIM_Type *bus,
*/
void spi_twi_irq_register_i2c(NRF_TWIM_Type *bus,
spi_twi_irq_cb_t cb, void *arg);

/**
* @brief Structure for PDM configuration data
*/
typedef struct {
uint8_t din_pin; /**< DIN pin */
uint8_t clk_pin; /**< CLK pin */
} pdm_conf_t;

#ifdef __cplusplus
}
#endif
Expand Down
167 changes: 167 additions & 0 deletions cpu/nrf52/periph/pdm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright (C) 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup cpu_nrf52
* @{
*
* @file
* @brief Implementation of the peripheral PDM interface
*
* @author Alexandre Abadie <[email protected]>
*
* @}
*/

#include <assert.h>
#include <errno.h>
#include <inttypes.h>

#include "cpu.h"
#include "periph/gpio.h"
#include "periph/pdm.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

/* The samples buffer is a double buffer */
int16_t _pdm_buf[PDM_BUF_SIZE * 2] = { 0 };
static uint8_t _pdm_current_buf = 0;
static pdm_isr_ctx_t isr_ctx;

int pdm_init(pdm_mode_t mode, pdm_sample_rate_t rate, int8_t gain,
pdm_data_cb_t cb, void *arg)
{
if (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {
NRF_CLOCK->TASKS_HFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {}
}

/* Configure sampling rate */
switch (rate) {
case PDM_SAMPLE_RATE_16KHZ:
#ifdef CPU_MODEL_NRF52840XXAA
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#ifdef CPU_MODEL_NRF52840XXAA
#ifdef PDM_PDMCLKCTRL_FREQ_1280K

NRF_PDM->RATIO = ((PDM_RATIO_RATIO_Ratio80 << PDM_RATIO_RATIO_Pos) & PDM_RATIO_RATIO_Msk);
NRF_PDM->PDMCLKCTRL = PDM_PDMCLKCTRL_FREQ_1280K;
#else
NRF_PDM->PDMCLKCTRL = PDM_PDMCLKCTRL_FREQ_Default; /* 1.033MHz */
#endif
break;
case PDM_SAMPLE_RATE_20KHZ:
NRF_PDM->PDMCLKCTRL = 0x0A000000; /* CLK= 1.280 MHz (32 MHz / 25) => rate= 20000 Hz */
break;
case PDM_SAMPLE_RATE_41KHZ:
NRF_PDM->PDMCLKCTRL = 0x15000000; /* CLK= 2.667 MHz (32 MHz / 12) => rate= 41667 Hz */
break;
case PDM_SAMPLE_RATE_50KHZ:
NRF_PDM->PDMCLKCTRL = 0x19000000; /* CLK= 3.200 MHz (32 MHz / 10) => rate= 50000 Hz */
break;
case PDM_SAMPLE_RATE_60KHZ:
NRF_PDM->PDMCLKCTRL = 0x20000000; /* CLK= 4.000 MHz (32 MHz / 8) => rate= 60000 Hz */
break;
default:
DEBUG("[pdm] init: sample rate not supported\n");
return -ENOTSUP;
}

/* Configure mode (Mono or Stereo) */
switch (mode) {
case PDM_MODE_MONO:
NRF_PDM->MODE = PDM_MODE_OPERATION_Mono;
break;
case PDM_MODE_STEREO:
NRF_PDM->MODE = PDM_MODE_OPERATION_Stereo;
break;
default:
DEBUG("[pdm] init: mode not supported\n");
return -ENOTSUP;
}

/* Configure gain */
if (gain > PDM_GAIN_MAX) {
gain = PDM_GAIN_MAX;
}

if (gain < PDM_GAIN_MIN) {
gain = PDM_GAIN_MIN;
}

NRF_PDM->GAINR = (gain << 1) + 40;
NRF_PDM->GAINL = (gain << 1) + 40;

/* Configure CLK and DIN pins */
gpio_init(pdm_config.clk_pin, GPIO_OUT);
gpio_clear(pdm_config.clk_pin);
gpio_init(pdm_config.din_pin, GPIO_IN);

NRF_PDM->PSEL.CLK = pdm_config.clk_pin;
NRF_PDM->PSEL.DIN = pdm_config.din_pin;

/* clear pending events */
NRF_PDM->EVENTS_STARTED = 0;
NRF_PDM->EVENTS_STOPPED = 0;
NRF_PDM->EVENTS_END = 0;

/* Enable end/started/stopped events */
NRF_PDM->INTENSET = ((PDM_INTEN_END_Enabled << PDM_INTEN_END_Pos) |
(PDM_INTEN_STARTED_Enabled << PDM_INTEN_STARTED_Pos) |
(PDM_INTEN_STOPPED_Enabled << PDM_INTEN_STOPPED_Pos));

/* Configure internal RAM buffer size, divide by 2 for stereo mode */
NRF_PDM->SAMPLE.MAXCNT = (PDM_BUF_SIZE >> mode);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NRF_PDM->SAMPLE.MAXCNT = (PDM_BUF_SIZE >> mode);
NRF_PDM->SAMPLE.MAXCNT = (PDM_BUF_SIZE);

According to the datasheet and also logically the SAMPLE.MAXCNT shouldn't change if we run the pdm in stereo mode, because we still want to fill the double buffer _pdm_buff and not only half of it.

The only thing that changes is that in stereo mode "the buffer in RAM can contain half the stereo sampling time as compared to the mono sampling time".


isr_ctx.cb = cb;
isr_ctx.arg = arg;

/* enable interrupt */
NVIC_EnableIRQ(PDM_IRQn);

/* Enable PDM */
NRF_PDM->ENABLE = (PDM_ENABLE_ENABLE_Enabled << PDM_ENABLE_ENABLE_Pos);

return 0;
}

void pdm_start(void)
{
NRF_PDM->SAMPLE.PTR = (uint32_t)_pdm_buf;
DEBUG("[PDM] MAXCNT: %lu\n", NRF_PDM->SAMPLE.MAXCNT);

NRF_PDM->TASKS_START = 1;
}

void pdm_stop(void)
{
NRF_PDM->TASKS_STOP = 1;
}

void isr_pdm(void)
{
if (NRF_PDM->EVENTS_STARTED == 1) {
NRF_PDM->EVENTS_STARTED = 0;
uint8_t next_buf_pos = (_pdm_current_buf + 1) & 0x1;
NRF_PDM->SAMPLE.PTR = (uint32_t)&_pdm_buf[next_buf_pos * (PDM_BUF_SIZE >> 1)];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NRF_PDM->SAMPLE.PTR = (uint32_t)&_pdm_buf[next_buf_pos * (PDM_BUF_SIZE >> 1)];
NRF_PDM->SAMPLE.PTR = (uint32_t)&_pdm_buf[next_buf_pos * (PDM_BUF_SIZE)];

Here the buffer shouldn't be divided by 2 because the _pdm_buf is PDM_BUF_SIZE * 2. So dividing PDM_BUF_SIZE here would result in us only looping one quarter of the whole buffer.

}

if (NRF_PDM->EVENTS_STOPPED == 1) {
NRF_PDM->EVENTS_STOPPED = 0;
_pdm_current_buf = 0;
}

if (NRF_PDM->EVENTS_END == 1) {
NRF_PDM->EVENTS_END = 0;

/* Process received samples frame */
isr_ctx.cb(isr_ctx.arg, &_pdm_buf[_pdm_current_buf * (PDM_BUF_SIZE >> 1)]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
isr_ctx.cb(isr_ctx.arg, &_pdm_buf[_pdm_current_buf * (PDM_BUF_SIZE >> 1)]);
isr_ctx.cb(isr_ctx.arg, &_pdm_buf[_pdm_current_buf * (PDM_BUF_SIZE)]);

Similarly to the change on line 148 dividing the PDM_BUF_SIZE here would only return one quarter of the double buffer _pdm_buff


/* Set next buffer */
_pdm_current_buf = (_pdm_current_buf + 1) & 0x1;
}

cortexm_isr_end();
}
127 changes: 127 additions & 0 deletions drivers/include/periph/pdm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright (C) 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @defgroup drivers_periph_pdm Pulse Density Modulation (PDM) driver
* @ingroup drivers_periph
* @brief Low-level Pulse Density Modulation (PDM) driver
*
* @{
* @file
*
* @author Alexandre Abadie <[email protected]>
*
*/

#ifndef PERIPH_PDM_H
#define PERIPH_PDM_H

#include <stdint.h>

#include "periph_cpu.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Default PDM mode values
* @{
*/
#ifndef HAVE_PDM_MODE_T
typedef enum {
PDM_MODE_MONO = 0, /**< Mono mode */
PDM_MODE_STEREO, /**< Stereo mode */
} pdm_mode_t;
#endif
/** @} */

/**
* @brief Default PDM sampling rate values
* @{
*/
#ifndef HAVE_PDM_SAMPLE_RATE_T
typedef enum {
PDM_SAMPLE_RATE_16KHZ = 0, /**< 16kHz */
PDM_SAMPLE_RATE_20KHZ, /**< 20kHz */
PDM_SAMPLE_RATE_41KHZ, /**< 41.6kHz */
PDM_SAMPLE_RATE_50KHZ, /**< 50kHz */
PDM_SAMPLE_RATE_60KHZ, /**< 60kHz */
} pdm_sample_rate_t;
#endif
/** @} */

/**
* @brief Default PDM min gain values (in dB)
*/
#ifndef PDM_GAIN_MIN
#define PDM_GAIN_MIN (-20)
#endif

/**
* @brief Default PDM max gain values (in dB)
*/
#ifndef PDM_GAIN_MAX
#define PDM_GAIN_MAX (20)
#endif

/**
* @brief Default PDM samples frame buffer size
*/
#ifndef PDM_BUF_SIZE
#define PDM_BUF_SIZE (128U)
#endif

/**
* @brief Signature for data received interrupt callback
*
* @param[in] arg context to the callback (optional)
* @param[in] buf the buffer containing the current samples frame
*/
typedef void(*pdm_data_cb_t)(void *arg, int16_t *buf);

/**
* @brief Interrupt context for a PDM device
*/
#ifndef HAVE_PDM_ISR_CTX_T
typedef struct {
pdm_data_cb_t cb; /**< data received interrupt callback */
void *arg; /**< argument to both callback routines */
} pdm_isr_ctx_t;
#endif

/**
* @brief Initialize the PDM peripheral
*
* @param[in] rate sample rate
* @param[in] gain gain
* @param[in] cb data received callback function
* @param[in] arg context passed to the callback function
*
* @return 0 on successful initialization
* @return <0 on error
*/
int pdm_init(pdm_mode_t mode, pdm_sample_rate_t rate, int8_t gain,
pdm_data_cb_t cb, void *arg);

/**
* @brief Start the PDM peripheral
*/
void pdm_start(void);

/**
* @brief Stop the PDM peripheral
*/
void pdm_stop(void);

#ifdef __cplusplus
}
#endif

#endif /* PERIPH_PDM_H */
/** @} */
5 changes: 5 additions & 0 deletions tests/periph_pdm/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include ../Makefile.tests_common

FEATURES_REQUIRED = periph_pdm

include $(RIOTBASE)/Makefile.include
Loading