Skip to content

Commit

Permalink
tests/periph/pdm: Initial test
Browse files Browse the repository at this point in the history
So many things were found and fixed...
I changed the test application so we could verify sample rates.
I added tests that check the values of the sample rates and some sanity check of the actual samples
Added a few changes to the API to make it more testable, such as the variable sample rate and checking the real sample rate.
Implemented variable sample rate.
Cleaned up the python application to only use the output files and removed unneeded dependencies.
Updated the documentation...
  • Loading branch information
MrKevinWeiss committed Apr 25, 2024
1 parent fb5073e commit a712b25
Show file tree
Hide file tree
Showing 16 changed files with 495 additions and 287 deletions.
169 changes: 142 additions & 27 deletions cpu/nrf52/periph/pdm.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <errno.h>
#include <inttypes.h>

#include "container.h"
#include "cpu.h"
#include "periph/gpio.h"
#include "periph/pdm.h"
Expand All @@ -34,40 +35,154 @@ 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)
#define MAX_PDM_CLK_DIV 32
#define MIN_PDM_CLK_DIV 8
#define PDM_CLK_POS 22
#define MAX_PDM_CLK_BITFIELD (0x20000000 >> PDM_CLK_POS)
#define MIN_PDM_CLK_BITFIELD (0x08000000 >> PDM_CLK_POS)

static const uint8_t DIV_TO_BITFIELD_LUT[] = {
114,
103,
94,
86,
79,
74,
69,
65,
61,
57,
54,
52,
49,
47,
45,
43,
41,
40,
38,
37,
36,
35,
34,
33,
32,
};

/* The clock speed that we can assume I guess? */
#define PDM_CLOCK_SPEED 32000000


Check warning on line 75 in cpu/nrf52/periph/pdm.c

View workflow job for this annotation

GitHub Actions / static-tests

too many consecutive empty lines
uint8_t _get_divisor(uint8_t bitfield) {
assert(bitfield >= MIN_PDM_CLK_BITFIELD);
assert(bitfield <= MAX_PDM_CLK_BITFIELD);
for (unsigned i = 0; i < ARRAY_SIZE(DIV_TO_BITFIELD_LUT); i++) {
if (bitfield >= DIV_TO_BITFIELD_LUT[i] ) {
return i + 8;
}
}
/* should never get here */
assert(0);
return 32;
}

uint32_t _get_pdm_sample_rate(uint8_t divisor_tick, uint8_t ratio) {
/* According to the datasheet, there is some sort of mask 22 bits shifted */
/* Here are the examples from the datasheet... Needs a LUT */
/* The divisor_tick is PDM_CLK >> 22. */
/* 0x08000000 PDM_CLK = 32 MHz / 32 = 1.000 MHz
0x08400000 PDM_CLK = 32 MHz / 31 = 1.032 MHz
0x08800000 PDM_CLK = 32 MHz / 30 = 1.067 MHz
0x09800000 PDM_CLK = 32 MHz / 26 = 1.231 MHz
0x0A000000 PDM_CLK = 32 MHz / 25 = 1.280 MHz
0x0A800000 PDM_CLK = 32 MHz / 24 = 1.333 MHz
*/
assert(ratio != 0);

return (uint32_t)PDM_CLOCK_SPEED / _get_divisor(divisor_tick) / ratio;
}

uint8_t _get_divisor_tick(uint32_t rate, uint8_t ratio) {
assert(ratio != 0);
/* Don't be dividing by zeros my friend*/
if (rate == 0) {
return DIV_TO_BITFIELD_LUT[ARRAY_SIZE(DIV_TO_BITFIELD_LUT) - 1];
}
/* We want to do some integer rounding to better approximate the desired */
/* frequency. */
uint8_t divisor = ((uint32_t)PDM_CLOCK_SPEED + (rate * ratio) / 2) / (rate * ratio);

/* Since the mapping of the bitfield is non-linear we will use the LUT */
/* with some bounds... */
if (divisor <= MIN_PDM_CLK_DIV){
return DIV_TO_BITFIELD_LUT[0];
}
if (divisor >= MAX_PDM_CLK_DIV) {
return DIV_TO_BITFIELD_LUT[ARRAY_SIZE(DIV_TO_BITFIELD_LUT) - 1];
}
return DIV_TO_BITFIELD_LUT[divisor - MIN_PDM_CLK_DIV];
}

void _set_best_pdm_rate(uint32_t rate) {
/* Calculate the divisor tick with with ratio 80 given the sample rate. */
uint8_t divisor_tick_80;
uint32_t real_rate_80;
uint32_t abs_diff_80;
divisor_tick_80 = _get_divisor_tick(rate, 80);
real_rate_80 = _get_pdm_sample_rate(divisor_tick_80, 80);
if (real_rate_80 < rate) {
abs_diff_80 = -(real_rate_80 - rate);
}
else {
abs_diff_80 = real_rate_80 - rate;
}

/* Now let's do the same for the 64 ratio. */
uint8_t divisor_tick_64;
uint32_t real_rate_64;
uint32_t abs_diff_64;
divisor_tick_64 = _get_divisor_tick(rate, 64);
real_rate_64 = _get_pdm_sample_rate(divisor_tick_64, 64);
if (real_rate_64 < rate) {
abs_diff_64 = -(real_rate_64 - rate);
}
else {
abs_diff_64 = real_rate_64 - rate;
}

/* One we have both, let's see which one is the best to use. */
/* If we have more ratios, probably better to refactor this. */
if (abs_diff_80 <= abs_diff_64) {
DEBUG("[PDM] PDM_CLK = %lu Hz, ratio 80\n", real_rate_80);
NRF_PDM->RATIO = ((PDM_RATIO_RATIO_Ratio80 << PDM_RATIO_RATIO_Pos) & PDM_RATIO_RATIO_Msk);
NRF_PDM->PDMCLKCTRL = ((uint32_t)divisor_tick_80 << PDM_CLK_POS);
}
else {
DEBUG("[PDM] PDM_CLK = %lu Hz, ratio 64\n", real_rate_64);
NRF_PDM->RATIO = ((PDM_RATIO_RATIO_Ratio64 << PDM_RATIO_RATIO_Pos) & PDM_RATIO_RATIO_Msk);
NRF_PDM->PDMCLKCTRL = ((uint32_t)divisor_tick_64 << PDM_CLK_POS);
}
}

uint32_t get_pdm_sample_rate(void) {
if (((NRF_PDM->RATIO & PDM_RATIO_RATIO_Msk) >> PDM_RATIO_RATIO_Pos) == PDM_RATIO_RATIO_Ratio80) {

Check warning on line 168 in cpu/nrf52/periph/pdm.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
return _get_pdm_sample_rate(NRF_PDM->PDMCLKCTRL >> PDM_CLK_POS, 80);
}
if (((NRF_PDM->RATIO & PDM_RATIO_RATIO_Msk) >> PDM_RATIO_RATIO_Pos) == PDM_RATIO_RATIO_Ratio64) {

Check warning on line 171 in cpu/nrf52/periph/pdm.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
return _get_pdm_sample_rate(NRF_PDM->PDMCLKCTRL >> PDM_CLK_POS, 64);
}
return 0;
}

int pdm_init(pdm_mode_t mode, uint32_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
//#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;
}
_set_best_pdm_rate(rate);

/* Configure mode (Mono or Stereo) */
switch (mode) {
Expand Down
13 changes: 0 additions & 13 deletions dist/tools/pdm_check/README.md

This file was deleted.

3 changes: 0 additions & 3 deletions dist/tools/pdm_check/requirements.txt

This file was deleted.

91 changes: 0 additions & 91 deletions dist/tools/pdm_check/testing_pdm_conversion.py

This file was deleted.

2 changes: 2 additions & 0 deletions dist/tools/pdm_to_wav/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
output.png
output.wav
21 changes: 21 additions & 0 deletions dist/tools/pdm_to_wav/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# PDM to WAV tool

This tool takes the saved output of the `tests/periph/pdm` firmware, filters
through it to find recorded samples, then formats the samples into a wav file.

## Requirements

To run this script one only needs python3.7+.
If `matplotlib` is installed, then a plot will be generated along with the wav
file.


## Usage
Point to log file with the output from the test, here we provide an example.
```
python pdm_to_wav.py example.txt
```

## Misc

See the [firmware README](../../../tests/periph/pdm) for more info.
1 change: 1 addition & 0 deletions dist/tools/pdm_to_wav/example.txt

Large diffs are not rendered by default.

Loading

0 comments on commit a712b25

Please sign in to comment.