-
Notifications
You must be signed in to change notification settings - Fork 2k
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||||||
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); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
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)]; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
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)]); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
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(); | ||||||
} |
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 */ | ||
/** @} */ |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.