Unable to get PDM Mic working with NRF54L15 Custom Board

Hi Everyone,

I am trying a very simple task of reading PDM Mic with NRF54L Custom Board and streaming the Audio via Bluetooth.

I was able to do it very easily with the NRF52840 Custom Board, but currently, we are trying to upgrade to NRF54L15.

MIC CLK > P1.12
MIC DATA > P1.13
Mic L/R  > GND 


The Mic and Controller are powered using a stable 3.3V Supply.
Care has been taken for Noise, Decoupling Capacitor, etc.


Problem:
Everything in the Board works except the PDM Mic.
I have tested LED Blink, BLE Advertisement, BLE Scanning, BLE Data Transmission, Verified HFXO by generating different frequencies on P1.08 CLK Pin and many more things.

But whenever I try to connect the PDM Mic, it outputs static data with some cracking sounds in between.
It is to be noted that the Data is not 0. It is a static value of around -300 and fluctuates by a value of 5-6.


I have tried different NCS versions 2.9.2 and 3.x.x also.
I tried PDM and DMIC APIs.
I tried Bare Metal Code as well.
I tried a different Board just to make sure the Mic is not Bad. We have used the same Mic on previous NRF52840 boards, which worked without any issue.

I am just finding the experience with NRF54L very buggy and complicated.

Please Help.

----------------------------------------------------------------------------------------------------------------------------------------------------------------

Update on 4 Mar 2026

I am currently using NRF Connect SDK 3.2.3

As of now, we are using NRF54L15-DK to rule out any hardware issues.

I am using just the sample DMIC code, but added a small code before clearing the buffer to do a simple analysis on all the values of the buffer to understand whether the Mic is giving any real output data.

The Problem remains the same as what was described in the Main Question. The output of Mic is just some noise.


Current Code [Sample DMIC Code with added Analysis Block]:

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <zephyr/audio/dmic.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(dmic_sample);

#define MAX_SAMPLE_RATE  16000
#define SAMPLE_BIT_WIDTH 16
#define BYTES_PER_SAMPLE sizeof(int16_t)
/* Milliseconds to wait for a block to be read. */
#define READ_TIMEOUT     1000

/* Size of a block for 100 ms of audio data. */
#define BLOCK_SIZE(_sample_rate, _number_of_channels) \
	(BYTES_PER_SAMPLE * (_sample_rate / 10) * _number_of_channels)

/* Driver will allocate blocks from this slab to receive audio data into them.
 * Application, after getting a given block from the driver and processing its
 * data, needs to free that block.
 */
#define MAX_BLOCK_SIZE   BLOCK_SIZE(MAX_SAMPLE_RATE, 2)
#define BLOCK_COUNT      4
K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, 4);

/* Simple integer square root (binary search). Used in Analysis */
static uint32_t isqrt64(int64_t val)
{
	if (val <= 0) {
		return 0;
	}
	uint32_t lo = 1;
	uint32_t hi = 65535;
	while (lo <= hi) {
		uint32_t mid = lo + (hi - lo) / 2;
		uint64_t sq = (uint64_t)mid * mid;
		if (sq == (uint64_t)val) {
			return mid;
		} else if (sq < (uint64_t)val) {
			lo = mid + 1;
		} else {
			hi = mid - 1;
		}
	}
	return hi;
}


static int do_pdm_transfer(const struct device *dmic_dev,
			   struct dmic_cfg *cfg,
			   size_t block_count)
{
	int ret;

	LOG_INF("\n\n\nPCM output rate: %u, channels: %u \n",
		cfg->streams[0].pcm_rate, cfg->channel.req_num_chan);

	ret = dmic_configure(dmic_dev, cfg);
	if (ret < 0) {
		LOG_ERR("Failed to configure the driver: %d", ret);
		return ret;
	}

	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
	if (ret < 0) {
		LOG_ERR("START trigger failed: %d", ret);
		return ret;
	}

	for (int i = 0; i < block_count; ++i) {
		void *buffer;
		uint32_t size;

		ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
		if (ret < 0) {
			LOG_ERR("%d - read failed: %d", i, ret);
			return ret;
		}

		LOG_INF("%d - got buffer %p of %u bytes", i, buffer, size);

		// ANALYSIS CODE STARTS HERE

		{
			const int16_t *samples = (const int16_t *)buffer;
			uint32_t num_samples = size / BYTES_PER_SAMPLE;

			/* Pass 1: compute DC offset (mean) */
			int64_t sum = 0;
			for (uint32_t j = 0; j < num_samples; j++) {
				sum += samples[j];
			}
			int32_t dc_offset = (int32_t)(sum / num_samples);

			/* Pass 2: AC-coupled stats */
			int32_t ac_min = 0, ac_max = 0;
			int64_t ac_sum_sq = 0;
			uint32_t zero_crossings = 0;

			for (uint32_t j = 0; j < num_samples; j++) {
				int32_t ac = (int32_t)samples[j] - dc_offset;

				if (j == 0 || ac < ac_min) {
					ac_min = ac;
				}
				if (j == 0 || ac > ac_max) {
					ac_max = ac;
				}
				ac_sum_sq += (int64_t)ac * ac;

				if (j > 0) {
					int32_t prev_ac = (int32_t)samples[j - 1] - dc_offset;
					if ((prev_ac >= 0 && ac < 0) ||
					    (prev_ac < 0 && ac >= 0)) {
						zero_crossings++;
					}
				}
			}

			uint32_t ac_rms = (uint32_t)isqrt64(ac_sum_sq / num_samples);
			int32_t peak_to_peak = ac_max - ac_min;

			LOG_INF("Block %d (%u samples) | DC offset: %d",
				i, num_samples, dc_offset);
			LOG_INF("  AC Min: %d | AC Max: %d | P2P: %d",
				ac_min, ac_max, peak_to_peak);
			LOG_INF("  AC RMS: %u | Zero-crossings: %u",
				ac_rms, zero_crossings);
		}

		//ANALYSIS CODE ENDS HERE
		

		k_mem_slab_free(&mem_slab, buffer);
	}

	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
	if (ret < 0) {
		LOG_ERR("STOP trigger failed: %d", ret);
		return ret;
	}

	return ret;
}

int main(void)
{
	const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
	int ret;

	LOG_INF("DMIC sample");

	if (!device_is_ready(dmic_dev)) 
	{
		LOG_ERR("%s is not ready", dmic_dev->name);
		return 0;
	}

	struct pcm_stream_cfg stream = {
		.pcm_width = SAMPLE_BIT_WIDTH,
		.mem_slab  = &mem_slab,
	};
	struct dmic_cfg cfg = {
		.io = {
			/* These fields can be used to limit the PDM clock
			 * configurations that the driver is allowed to use
			 * to those supported by the microphone.
			 */
			.min_pdm_clk_freq = 1000000,
			.max_pdm_clk_freq = 3500000,
			.min_pdm_clk_dc   = 40,
			.max_pdm_clk_dc   = 60,
		},
		.streams = &stream,
		.channel = {
			.req_num_streams = 1,
		},
	};

	cfg.channel.req_num_chan = 1;
	cfg.channel.req_chan_map_lo =
		dmic_build_channel_map(0, 0, PDM_CHAN_LEFT);
	cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
	cfg.streams[0].block_size =
		BLOCK_SIZE(cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);

	ret = do_pdm_transfer(dmic_dev, &cfg, 2 * BLOCK_COUNT);
	if (ret < 0) {
		return 0;
	}

	cfg.channel.req_num_chan = 2;
	cfg.channel.req_chan_map_lo =
		dmic_build_channel_map(0, 0, PDM_CHAN_LEFT) |
		dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT);
	cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
	cfg.streams[0].block_size =
		BLOCK_SIZE(cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);

	ret = do_pdm_transfer(dmic_dev, &cfg, 2 * BLOCK_COUNT);
	if (ret < 0) {
		return 0;
	}

	LOG_INF("Exiting");
	return 0;
}



Output in Silent Environment:

*** Booting nRF Connect SDK v3.2.3-6f8485d2890d ***
*** Using Zephyr OS v4.2.99-c4c75f71e709 ***
[00:00:00.002,069] <inf> dmic_sample: DMIC sample
[00:00:00.002,076] <inf> dmic_sample: 


PCM output rate: 16000, channels: 1 

[00:00:00.101,045] <inf> dmic_sample: 0 - got buffer 0x20002360 of 3200 bytes
[00:00:00.101,469] <inf> dmic_sample: Block 0 (1600 samples) | DC offset: 6235
[00:00:00.101,475] <inf> dmic_sample:   AC Min: -8475 | AC Max: 14207 | P2P: 22682
[00:00:00.101,481] <inf> dmic_sample:   AC RMS: 8386 | Zero-crossings: 2
[00:00:00.199,889] <inf> dmic_sample: 1 - got buffer 0x20003c60 of 3200 bytes
[00:00:00.200,303] <inf> dmic_sample: Block 1 (1600 samples) | DC offset: -1047
[00:00:00.200,310] <inf> dmic_sample:   AC Min: -229 | AC Max: 219 | P2P: 448
[00:00:00.200,314] <inf> dmic_sample:   AC RMS: 144 | Zero-crossings: 7
[00:00:00.298,753] <inf> dmic_sample: 2 - got buffer 0x20005560 of 3200 bytes
[00:00:00.299,167] <inf> dmic_sample: Block 2 (1600 samples) | DC offset: -691
[00:00:00.299,178] <inf> dmic_sample:   AC Min: -144 | AC Max: 138 | P2P: 282
[00:00:00.299,183] <inf> dmic_sample:   AC RMS: 79 | Zero-crossings: 29
[00:00:00.397,601] <inf> dmic_sample: 3 - got buffer 0x20002360 of 3200 bytes
[00:00:00.398,021] <inf> dmic_sample: Block 3 (1600 samples) | DC offset: -426
[00:00:00.398,028] <inf> dmic_sample:   AC Min: -137 | AC Max: 116 | P2P: 253
[00:00:00.398,033] <inf> dmic_sample:   AC RMS: 72 | Zero-crossings: 13
[00:00:00.496,429] <inf> dmic_sample: 4 - got buffer 0x20003c60 of 3200 bytes
[00:00:00.496,860] <inf> dmic_sample: Block 4 (1600 samples) | DC offset: -311
[00:00:00.496,867] <inf> dmic_sample:   AC Min: -13 | AC Max: 14 | P2P: 27
[00:00:00.496,871] <inf> dmic_sample:   AC RMS: 4 | Zero-crossings: 540
[00:00:00.595,274] <inf> dmic_sample: 5 - got buffer 0x20005560 of 3200 bytes
[00:00:00.595,706] <inf> dmic_sample: Block 5 (1600 samples) | DC offset: -309
[00:00:00.595,716] <inf> dmic_sample:   AC Min: -13 | AC Max: 12 | P2P: 25
[00:00:00.595,721] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 593
[00:00:00.694,148] <inf> dmic_sample: 6 - got buffer 0x20002360 of 3200 bytes
[00:00:00.694,588] <inf> dmic_sample: Block 6 (1600 samples) | DC offset: -307
[00:00:00.694,595] <inf> dmic_sample:   AC Min: -13 | AC Max: 12 | P2P: 25
[00:00:00.694,600] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 616
[00:00:00.793,011] <inf> dmic_sample: 7 - got buffer 0x20003c60 of 3200 bytes
[00:00:00.793,443] <inf> dmic_sample: Block 7 (1600 samples) | DC offset: -306
[00:00:00.793,450] <inf> dmic_sample:   AC Min: -13 | AC Max: 10 | P2P: 23
[00:00:00.793,455] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 606
[00:00:00.793,467] <inf> dmic_sample: 


PCM output rate: 16000, channels: 2 

[00:00:00.892,414] <inf> dmic_sample: 0 - got buffer 0x20002360 of 6400 bytes
[00:00:00.893,237] <inf> dmic_sample: Block 0 (3200 samples) | DC offset: 6253
[00:00:00.893,244] <inf> dmic_sample:   AC Min: -7466 | AC Max: 14192 | P2P: 21658
[00:00:00.893,249] <inf> dmic_sample:   AC RMS: 8338 | Zero-crossings: 2
[00:00:00.991,274] <inf> dmic_sample: 1 - got buffer 0x20005560 of 6400 bytes
[00:00:00.992,096] <inf> dmic_sample: Block 1 (3200 samples) | DC offset: -1023
[00:00:00.992,103] <inf> dmic_sample:   AC Min: -202 | AC Max: 201 | P2P: 403
[00:00:00.992,108] <inf> dmic_sample:   AC RMS: 128 | Zero-crossings: 7
[00:00:01.090,099] <inf> dmic_sample: 2 - got buffer 0x20003c60 of 6400 bytes
[00:00:01.090,915] <inf> dmic_sample: Block 2 (3200 samples) | DC offset: -688
[00:00:01.090,922] <inf> dmic_sample:   AC Min: -145 | AC Max: 142 | P2P: 287
[00:00:01.090,926] <inf> dmic_sample:   AC RMS: 79 | Zero-crossings: 19
[00:00:01.188,946] <inf> dmic_sample: 3 - got buffer 0x20002360 of 6400 bytes
[00:00:01.189,762] <inf> dmic_sample: Block 3 (3200 samples) | DC offset: -427
[00:00:01.189,773] <inf> dmic_sample:   AC Min: -133 | AC Max: 119 | P2P: 252
[00:00:01.189,778] <inf> dmic_sample:   AC RMS: 72 | Zero-crossings: 15
[00:00:01.287,802] <inf> dmic_sample: 4 - got buffer 0x20005560 of 6400 bytes
[00:00:01.288,641] <inf> dmic_sample: Block 4 (3200 samples) | DC offset: -311
[00:00:01.288,648] <inf> dmic_sample:   AC Min: -13 | AC Max: 14 | P2P: 27
[00:00:01.288,653] <inf> dmic_sample:   AC RMS: 4 | Zero-crossings: 580
[00:00:01.386,638] <inf> dmic_sample: 5 - got buffer 0x20003c60 of 6400 bytes
[00:00:01.387,475] <inf> dmic_sample: Block 5 (3200 samples) | DC offset: -309
[00:00:01.387,482] <inf> dmic_sample:   AC Min: -15 | AC Max: 11 | P2P: 26
[00:00:01.387,486] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 670
[00:00:01.485,491] <inf> dmic_sample: 6 - got buffer 0x20002360 of 6400 bytes
[00:00:01.486,325] <inf> dmic_sample: Block 6 (3200 samples) | DC offset: -308
[00:00:01.486,335] <inf> dmic_sample:   AC Min: -16 | AC Max: 13 | P2P: 29
[00:00:01.486,340] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 619
[00:00:01.584,369] <inf> dmic_sample: 7 - got buffer 0x20005560 of 6400 bytes
[00:00:01.585,210] <inf> dmic_sample: Block 7 (3200 samples) | DC offset: -307
[00:00:01.585,216] <inf> dmic_sample:   AC Min: -13 | AC Max: 11 | P2P: 24
[00:00:01.585,222] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 636
[00:00:01.585,234] <inf> dmic_sample: Exiting


Output in Sufficiently Loud and Noisy Environment [Playing Loud Music on Speaker near Mic]:

*** Booting nRF Connect SDK v3.2.3-6f8485d2890d ***
*** Using Zephyr OS v4.2.99-c4c75f71e709 ***
[00:00:00.002,069] <inf> dmic_sample: DMIC sample
[00:00:00.002,076] <inf> dmic_sample: 


PCM output rate: 16000, channels: 1 

[00:00:00.101,050] <inf> dmic_sample: 0 - got buffer 0x20002360 of 3200 bytes
[00:00:00.101,473] <inf> dmic_sample: Block 0 (1600 samples) | DC offset: 6240
[00:00:00.101,479] <inf> dmic_sample:   AC Min: -8896 | AC Max: 14202 | P2P: 23098
[00:00:00.101,485] <inf> dmic_sample:   AC RMS: 8382 | Zero-crossings: 2
[00:00:00.199,920] <inf> dmic_sample: 1 - got buffer 0x20003c60 of 3200 bytes
[00:00:00.200,334] <inf> dmic_sample: Block 1 (1600 samples) | DC offset: -1035
[00:00:00.200,341] <inf> dmic_sample:   AC Min: -227 | AC Max: 217 | P2P: 444
[00:00:00.200,346] <inf> dmic_sample:   AC RMS: 141 | Zero-crossings: 7
[00:00:00.298,810] <inf> dmic_sample: 2 - got buffer 0x20005560 of 3200 bytes
[00:00:00.299,225] <inf> dmic_sample: Block 2 (1600 samples) | DC offset: -680
[00:00:00.299,235] <inf> dmic_sample:   AC Min: -154 | AC Max: 139 | P2P: 293
[00:00:00.299,240] <inf> dmic_sample:   AC RMS: 80 | Zero-crossings: 13
[00:00:00.397,703] <inf> dmic_sample: 3 - got buffer 0x20002360 of 3200 bytes
[00:00:00.398,124] <inf> dmic_sample: Block 3 (1600 samples) | DC offset: -417
[00:00:00.398,131] <inf> dmic_sample:   AC Min: -138 | AC Max: 112 | P2P: 250
[00:00:00.398,136] <inf> dmic_sample:   AC RMS: 71 | Zero-crossings: 23
[00:00:00.496,578] <inf> dmic_sample: 4 - got buffer 0x20003c60 of 3200 bytes
[00:00:00.497,007] <inf> dmic_sample: Block 4 (1600 samples) | DC offset: -310
[00:00:00.497,014] <inf> dmic_sample:   AC Min: -16 | AC Max: 13 | P2P: 29
[00:00:00.497,018] <inf> dmic_sample:   AC RMS: 4 | Zero-crossings: 499
[00:00:00.595,455] <inf> dmic_sample: 5 - got buffer 0x20005560 of 3200 bytes
[00:00:00.595,887] <inf> dmic_sample: Block 5 (1600 samples) | DC offset: -308
[00:00:00.595,897] <inf> dmic_sample:   AC Min: -14 | AC Max: 16 | P2P: 30
[00:00:00.595,902] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 572
[00:00:00.694,355] <inf> dmic_sample: 6 - got buffer 0x20002360 of 3200 bytes
[00:00:00.694,794] <inf> dmic_sample: Block 6 (1600 samples) | DC offset: -306
[00:00:00.694,801] <inf> dmic_sample:   AC Min: -15 | AC Max: 11 | P2P: 26
[00:00:00.694,806] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 603
[00:00:00.793,255] <inf> dmic_sample: 7 - got buffer 0x20003c60 of 3200 bytes
[00:00:00.793,687] <inf> dmic_sample: Block 7 (1600 samples) | DC offset: -306
[00:00:00.793,694] <inf> dmic_sample:   AC Min: -13 | AC Max: 14 | P2P: 27
[00:00:00.793,698] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 606
[00:00:00.793,711] <inf> dmic_sample: 


PCM output rate: 16000, channels: 2 

[00:00:00.892,702] <inf> dmic_sample: 0 - got buffer 0x20002360 of 6400 bytes
[00:00:00.893,525] <inf> dmic_sample: Block 0 (3200 samples) | DC offset: 6248
[00:00:00.893,531] <inf> dmic_sample:   AC Min: -7465 | AC Max: 14197 | P2P: 21662
[00:00:00.893,536] <inf> dmic_sample:   AC RMS: 8340 | Zero-crossings: 2
[00:00:00.991,625] <inf> dmic_sample: 1 - got buffer 0x20005560 of 6400 bytes
[00:00:00.992,448] <inf> dmic_sample: Block 1 (3200 samples) | DC offset: -1030
[00:00:00.992,455] <inf> dmic_sample:   AC Min: -198 | AC Max: 197 | P2P: 395
[00:00:00.992,460] <inf> dmic_sample:   AC RMS: 128 | Zero-crossings: 9
[00:00:01.090,513] <inf> dmic_sample: 2 - got buffer 0x20003c60 of 6400 bytes
[00:00:01.091,329] <inf> dmic_sample: Block 2 (3200 samples) | DC offset: -695
[00:00:01.091,336] <inf> dmic_sample:   AC Min: -146 | AC Max: 138 | P2P: 284
[00:00:01.091,341] <inf> dmic_sample:   AC RMS: 78 | Zero-crossings: 19
[00:00:01.189,403] <inf> dmic_sample: 3 - got buffer 0x20002360 of 6400 bytes
[00:00:01.190,220] <inf> dmic_sample: Block 3 (3200 samples) | DC offset: -433
[00:00:01.190,231] <inf> dmic_sample:   AC Min: -134 | AC Max: 123 | P2P: 257
[00:00:01.190,235] <inf> dmic_sample:   AC RMS: 72 | Zero-crossings: 31
[00:00:01.288,296] <inf> dmic_sample: 4 - got buffer 0x20005560 of 6400 bytes
[00:00:01.289,135] <inf> dmic_sample: Block 4 (3200 samples) | DC offset: -311
[00:00:01.289,142] <inf> dmic_sample:   AC Min: -14 | AC Max: 14 | P2P: 28
[00:00:01.289,147] <inf> dmic_sample:   AC RMS: 4 | Zero-crossings: 565
[00:00:01.387,201] <inf> dmic_sample: 5 - got buffer 0x20003c60 of 6400 bytes
[00:00:01.388,036] <inf> dmic_sample: Block 5 (3200 samples) | DC offset: -308
[00:00:01.388,043] <inf> dmic_sample:   AC Min: -13 | AC Max: 13 | P2P: 26
[00:00:01.388,048] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 598
[00:00:01.486,106] <inf> dmic_sample: 6 - got buffer 0x20002360 of 6400 bytes
[00:00:01.486,942] <inf> dmic_sample: Block 6 (3200 samples) | DC offset: -309
[00:00:01.486,953] <inf> dmic_sample:   AC Min: -13 | AC Max: 11 | P2P: 24
[00:00:01.486,958] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 613
[00:00:01.585,017] <inf> dmic_sample: 7 - got buffer 0x20005560 of 6400 bytes
[00:00:01.585,858] <inf> dmic_sample: Block 7 (3200 samples) | DC offset: -308
[00:00:01.585,865] <inf> dmic_sample:   AC Min: -12 | AC Max: 14 | P2P: 26
[00:00:01.585,870] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 629
[00:00:01.585,882] <inf> dmic_sample: Exiting

 

Parents Reply Children
  • Hi,

    I am currently using NRF Connect SDK 3.2.3

    I am using just the sample code, but added a small code before clearning the buffer to do a simple analysis on all the values of the buffer to understand whether the Mic is giving any real output data.

    The Problem remains exactly same what was described in the Main Question.
    Please help.


    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/audio/dmic.h>
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(dmic_sample);
    
    #define MAX_SAMPLE_RATE  16000
    #define SAMPLE_BIT_WIDTH 16
    #define BYTES_PER_SAMPLE sizeof(int16_t)
    /* Milliseconds to wait for a block to be read. */
    #define READ_TIMEOUT     1000
    
    /* Size of a block for 100 ms of audio data. */
    #define BLOCK_SIZE(_sample_rate, _number_of_channels) \
    	(BYTES_PER_SAMPLE * (_sample_rate / 10) * _number_of_channels)
    
    /* Driver will allocate blocks from this slab to receive audio data into them.
     * Application, after getting a given block from the driver and processing its
     * data, needs to free that block.
     */
    #define MAX_BLOCK_SIZE   BLOCK_SIZE(MAX_SAMPLE_RATE, 2)
    #define BLOCK_COUNT      4
    K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, 4);
    
    /* Simple integer square root (binary search). Used in Analysis */
    static uint32_t isqrt64(int64_t val)
    {
    	if (val <= 0) {
    		return 0;
    	}
    	uint32_t lo = 1;
    	uint32_t hi = 65535;
    	while (lo <= hi) {
    		uint32_t mid = lo + (hi - lo) / 2;
    		uint64_t sq = (uint64_t)mid * mid;
    		if (sq == (uint64_t)val) {
    			return mid;
    		} else if (sq < (uint64_t)val) {
    			lo = mid + 1;
    		} else {
    			hi = mid - 1;
    		}
    	}
    	return hi;
    }
    
    
    static int do_pdm_transfer(const struct device *dmic_dev,
    			   struct dmic_cfg *cfg,
    			   size_t block_count)
    {
    	int ret;
    
    	LOG_INF("\n\n\nPCM output rate: %u, channels: %u \n",
    		cfg->streams[0].pcm_rate, cfg->channel.req_num_chan);
    
    	ret = dmic_configure(dmic_dev, cfg);
    	if (ret < 0) {
    		LOG_ERR("Failed to configure the driver: %d", ret);
    		return ret;
    	}
    
    	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
    	if (ret < 0) {
    		LOG_ERR("START trigger failed: %d", ret);
    		return ret;
    	}
    
    	for (int i = 0; i < block_count; ++i) {
    		void *buffer;
    		uint32_t size;
    
    		ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
    		if (ret < 0) {
    			LOG_ERR("%d - read failed: %d", i, ret);
    			return ret;
    		}
    
    		LOG_INF("%d - got buffer %p of %u bytes", i, buffer, size);
    
    		// ANALYSIS CODE STARTS HERE
    
    		{
    			const int16_t *samples = (const int16_t *)buffer;
    			uint32_t num_samples = size / BYTES_PER_SAMPLE;
    
    			/* Pass 1: compute DC offset (mean) */
    			int64_t sum = 0;
    			for (uint32_t j = 0; j < num_samples; j++) {
    				sum += samples[j];
    			}
    			int32_t dc_offset = (int32_t)(sum / num_samples);
    
    			/* Pass 2: AC-coupled stats */
    			int32_t ac_min = 0, ac_max = 0;
    			int64_t ac_sum_sq = 0;
    			uint32_t zero_crossings = 0;
    
    			for (uint32_t j = 0; j < num_samples; j++) {
    				int32_t ac = (int32_t)samples[j] - dc_offset;
    
    				if (j == 0 || ac < ac_min) {
    					ac_min = ac;
    				}
    				if (j == 0 || ac > ac_max) {
    					ac_max = ac;
    				}
    				ac_sum_sq += (int64_t)ac * ac;
    
    				if (j > 0) {
    					int32_t prev_ac = (int32_t)samples[j - 1] - dc_offset;
    					if ((prev_ac >= 0 && ac < 0) ||
    					    (prev_ac < 0 && ac >= 0)) {
    						zero_crossings++;
    					}
    				}
    			}
    
    			uint32_t ac_rms = (uint32_t)isqrt64(ac_sum_sq / num_samples);
    			int32_t peak_to_peak = ac_max - ac_min;
    
    			LOG_INF("Block %d (%u samples) | DC offset: %d",
    				i, num_samples, dc_offset);
    			LOG_INF("  AC Min: %d | AC Max: %d | P2P: %d",
    				ac_min, ac_max, peak_to_peak);
    			LOG_INF("  AC RMS: %u | Zero-crossings: %u",
    				ac_rms, zero_crossings);
    		}
    
    		//ANALYSIS CODE ENDS HERE
    		
    
    		k_mem_slab_free(&mem_slab, buffer);
    	}
    
    	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_STOP);
    	if (ret < 0) {
    		LOG_ERR("STOP trigger failed: %d", ret);
    		return ret;
    	}
    
    	return ret;
    }
    
    int main(void)
    {
    	const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
    	int ret;
    
    	LOG_INF("DMIC sample");
    
    	if (!device_is_ready(dmic_dev)) 
    	{
    		LOG_ERR("%s is not ready", dmic_dev->name);
    		return 0;
    	}
    
    	struct pcm_stream_cfg stream = {
    		.pcm_width = SAMPLE_BIT_WIDTH,
    		.mem_slab  = &mem_slab,
    	};
    	struct dmic_cfg cfg = {
    		.io = {
    			/* These fields can be used to limit the PDM clock
    			 * configurations that the driver is allowed to use
    			 * to those supported by the microphone.
    			 */
    			.min_pdm_clk_freq = 1000000,
    			.max_pdm_clk_freq = 3500000,
    			.min_pdm_clk_dc   = 40,
    			.max_pdm_clk_dc   = 60,
    		},
    		.streams = &stream,
    		.channel = {
    			.req_num_streams = 1,
    		},
    	};
    
    	cfg.channel.req_num_chan = 1;
    	cfg.channel.req_chan_map_lo =
    		dmic_build_channel_map(0, 0, PDM_CHAN_LEFT);
    	cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
    	cfg.streams[0].block_size =
    		BLOCK_SIZE(cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);
    
    	ret = do_pdm_transfer(dmic_dev, &cfg, 2 * BLOCK_COUNT);
    	if (ret < 0) {
    		return 0;
    	}
    
    	cfg.channel.req_num_chan = 2;
    	cfg.channel.req_chan_map_lo =
    		dmic_build_channel_map(0, 0, PDM_CHAN_LEFT) |
    		dmic_build_channel_map(1, 0, PDM_CHAN_RIGHT);
    	cfg.streams[0].pcm_rate = MAX_SAMPLE_RATE;
    	cfg.streams[0].block_size =
    		BLOCK_SIZE(cfg.streams[0].pcm_rate, cfg.channel.req_num_chan);
    
    	ret = do_pdm_transfer(dmic_dev, &cfg, 2 * BLOCK_COUNT);
    	if (ret < 0) {
    		return 0;
    	}
    
    	LOG_INF("Exiting");
    	return 0;
    }
    



    Output in Silent Environment:
    *** Booting nRF Connect SDK v3.2.3-6f8485d2890d ***
    *** Using Zephyr OS v4.2.99-c4c75f71e709 ***
    [00:00:00.002,069] <inf> dmic_sample: DMIC sample
    [00:00:00.002,076] <inf> dmic_sample: 
    
    
    PCM output rate: 16000, channels: 1 
    
    [00:00:00.101,045] <inf> dmic_sample: 0 - got buffer 0x20002360 of 3200 bytes
    [00:00:00.101,469] <inf> dmic_sample: Block 0 (1600 samples) | DC offset: 6235
    [00:00:00.101,475] <inf> dmic_sample:   AC Min: -8475 | AC Max: 14207 | P2P: 22682
    [00:00:00.101,481] <inf> dmic_sample:   AC RMS: 8386 | Zero-crossings: 2
    [00:00:00.199,889] <inf> dmic_sample: 1 - got buffer 0x20003c60 of 3200 bytes
    [00:00:00.200,303] <inf> dmic_sample: Block 1 (1600 samples) | DC offset: -1047
    [00:00:00.200,310] <inf> dmic_sample:   AC Min: -229 | AC Max: 219 | P2P: 448
    [00:00:00.200,314] <inf> dmic_sample:   AC RMS: 144 | Zero-crossings: 7
    [00:00:00.298,753] <inf> dmic_sample: 2 - got buffer 0x20005560 of 3200 bytes
    [00:00:00.299,167] <inf> dmic_sample: Block 2 (1600 samples) | DC offset: -691
    [00:00:00.299,178] <inf> dmic_sample:   AC Min: -144 | AC Max: 138 | P2P: 282
    [00:00:00.299,183] <inf> dmic_sample:   AC RMS: 79 | Zero-crossings: 29
    [00:00:00.397,601] <inf> dmic_sample: 3 - got buffer 0x20002360 of 3200 bytes
    [00:00:00.398,021] <inf> dmic_sample: Block 3 (1600 samples) | DC offset: -426
    [00:00:00.398,028] <inf> dmic_sample:   AC Min: -137 | AC Max: 116 | P2P: 253
    [00:00:00.398,033] <inf> dmic_sample:   AC RMS: 72 | Zero-crossings: 13
    [00:00:00.496,429] <inf> dmic_sample: 4 - got buffer 0x20003c60 of 3200 bytes
    [00:00:00.496,860] <inf> dmic_sample: Block 4 (1600 samples) | DC offset: -311
    [00:00:00.496,867] <inf> dmic_sample:   AC Min: -13 | AC Max: 14 | P2P: 27
    [00:00:00.496,871] <inf> dmic_sample:   AC RMS: 4 | Zero-crossings: 540
    [00:00:00.595,274] <inf> dmic_sample: 5 - got buffer 0x20005560 of 3200 bytes
    [00:00:00.595,706] <inf> dmic_sample: Block 5 (1600 samples) | DC offset: -309
    [00:00:00.595,716] <inf> dmic_sample:   AC Min: -13 | AC Max: 12 | P2P: 25
    [00:00:00.595,721] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 593
    [00:00:00.694,148] <inf> dmic_sample: 6 - got buffer 0x20002360 of 3200 bytes
    [00:00:00.694,588] <inf> dmic_sample: Block 6 (1600 samples) | DC offset: -307
    [00:00:00.694,595] <inf> dmic_sample:   AC Min: -13 | AC Max: 12 | P2P: 25
    [00:00:00.694,600] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 616
    [00:00:00.793,011] <inf> dmic_sample: 7 - got buffer 0x20003c60 of 3200 bytes
    [00:00:00.793,443] <inf> dmic_sample: Block 7 (1600 samples) | DC offset: -306
    [00:00:00.793,450] <inf> dmic_sample:   AC Min: -13 | AC Max: 10 | P2P: 23
    [00:00:00.793,455] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 606
    [00:00:00.793,467] <inf> dmic_sample: 
    
    
    PCM output rate: 16000, channels: 2 
    
    [00:00:00.892,414] <inf> dmic_sample: 0 - got buffer 0x20002360 of 6400 bytes
    [00:00:00.893,237] <inf> dmic_sample: Block 0 (3200 samples) | DC offset: 6253
    [00:00:00.893,244] <inf> dmic_sample:   AC Min: -7466 | AC Max: 14192 | P2P: 21658
    [00:00:00.893,249] <inf> dmic_sample:   AC RMS: 8338 | Zero-crossings: 2
    [00:00:00.991,274] <inf> dmic_sample: 1 - got buffer 0x20005560 of 6400 bytes
    [00:00:00.992,096] <inf> dmic_sample: Block 1 (3200 samples) | DC offset: -1023
    [00:00:00.992,103] <inf> dmic_sample:   AC Min: -202 | AC Max: 201 | P2P: 403
    [00:00:00.992,108] <inf> dmic_sample:   AC RMS: 128 | Zero-crossings: 7
    [00:00:01.090,099] <inf> dmic_sample: 2 - got buffer 0x20003c60 of 6400 bytes
    [00:00:01.090,915] <inf> dmic_sample: Block 2 (3200 samples) | DC offset: -688
    [00:00:01.090,922] <inf> dmic_sample:   AC Min: -145 | AC Max: 142 | P2P: 287
    [00:00:01.090,926] <inf> dmic_sample:   AC RMS: 79 | Zero-crossings: 19
    [00:00:01.188,946] <inf> dmic_sample: 3 - got buffer 0x20002360 of 6400 bytes
    [00:00:01.189,762] <inf> dmic_sample: Block 3 (3200 samples) | DC offset: -427
    [00:00:01.189,773] <inf> dmic_sample:   AC Min: -133 | AC Max: 119 | P2P: 252
    [00:00:01.189,778] <inf> dmic_sample:   AC RMS: 72 | Zero-crossings: 15
    [00:00:01.287,802] <inf> dmic_sample: 4 - got buffer 0x20005560 of 6400 bytes
    [00:00:01.288,641] <inf> dmic_sample: Block 4 (3200 samples) | DC offset: -311
    [00:00:01.288,648] <inf> dmic_sample:   AC Min: -13 | AC Max: 14 | P2P: 27
    [00:00:01.288,653] <inf> dmic_sample:   AC RMS: 4 | Zero-crossings: 580
    [00:00:01.386,638] <inf> dmic_sample: 5 - got buffer 0x20003c60 of 6400 bytes
    [00:00:01.387,475] <inf> dmic_sample: Block 5 (3200 samples) | DC offset: -309
    [00:00:01.387,482] <inf> dmic_sample:   AC Min: -15 | AC Max: 11 | P2P: 26
    [00:00:01.387,486] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 670
    [00:00:01.485,491] <inf> dmic_sample: 6 - got buffer 0x20002360 of 6400 bytes
    [00:00:01.486,325] <inf> dmic_sample: Block 6 (3200 samples) | DC offset: -308
    [00:00:01.486,335] <inf> dmic_sample:   AC Min: -16 | AC Max: 13 | P2P: 29
    [00:00:01.486,340] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 619
    [00:00:01.584,369] <inf> dmic_sample: 7 - got buffer 0x20005560 of 6400 bytes
    [00:00:01.585,210] <inf> dmic_sample: Block 7 (3200 samples) | DC offset: -307
    [00:00:01.585,216] <inf> dmic_sample:   AC Min: -13 | AC Max: 11 | P2P: 24
    [00:00:01.585,222] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 636
    [00:00:01.585,234] <inf> dmic_sample: Exiting
    

    Output in Sufficiently Loud and Noisy Environment [Playing Loud Music on Speaker near Mic]:

    *** Booting nRF Connect SDK v3.2.3-6f8485d2890d ***
    *** Using Zephyr OS v4.2.99-c4c75f71e709 ***
    [00:00:00.002,069] <inf> dmic_sample: DMIC sample
    [00:00:00.002,076] <inf> dmic_sample: 
    
    
    PCM output rate: 16000, channels: 1 
    
    [00:00:00.101,050] <inf> dmic_sample: 0 - got buffer 0x20002360 of 3200 bytes
    [00:00:00.101,473] <inf> dmic_sample: Block 0 (1600 samples) | DC offset: 6240
    [00:00:00.101,479] <inf> dmic_sample:   AC Min: -8896 | AC Max: 14202 | P2P: 23098
    [00:00:00.101,485] <inf> dmic_sample:   AC RMS: 8382 | Zero-crossings: 2
    [00:00:00.199,920] <inf> dmic_sample: 1 - got buffer 0x20003c60 of 3200 bytes
    [00:00:00.200,334] <inf> dmic_sample: Block 1 (1600 samples) | DC offset: -1035
    [00:00:00.200,341] <inf> dmic_sample:   AC Min: -227 | AC Max: 217 | P2P: 444
    [00:00:00.200,346] <inf> dmic_sample:   AC RMS: 141 | Zero-crossings: 7
    [00:00:00.298,810] <inf> dmic_sample: 2 - got buffer 0x20005560 of 3200 bytes
    [00:00:00.299,225] <inf> dmic_sample: Block 2 (1600 samples) | DC offset: -680
    [00:00:00.299,235] <inf> dmic_sample:   AC Min: -154 | AC Max: 139 | P2P: 293
    [00:00:00.299,240] <inf> dmic_sample:   AC RMS: 80 | Zero-crossings: 13
    [00:00:00.397,703] <inf> dmic_sample: 3 - got buffer 0x20002360 of 3200 bytes
    [00:00:00.398,124] <inf> dmic_sample: Block 3 (1600 samples) | DC offset: -417
    [00:00:00.398,131] <inf> dmic_sample:   AC Min: -138 | AC Max: 112 | P2P: 250
    [00:00:00.398,136] <inf> dmic_sample:   AC RMS: 71 | Zero-crossings: 23
    [00:00:00.496,578] <inf> dmic_sample: 4 - got buffer 0x20003c60 of 3200 bytes
    [00:00:00.497,007] <inf> dmic_sample: Block 4 (1600 samples) | DC offset: -310
    [00:00:00.497,014] <inf> dmic_sample:   AC Min: -16 | AC Max: 13 | P2P: 29
    [00:00:00.497,018] <inf> dmic_sample:   AC RMS: 4 | Zero-crossings: 499
    [00:00:00.595,455] <inf> dmic_sample: 5 - got buffer 0x20005560 of 3200 bytes
    [00:00:00.595,887] <inf> dmic_sample: Block 5 (1600 samples) | DC offset: -308
    [00:00:00.595,897] <inf> dmic_sample:   AC Min: -14 | AC Max: 16 | P2P: 30
    [00:00:00.595,902] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 572
    [00:00:00.694,355] <inf> dmic_sample: 6 - got buffer 0x20002360 of 3200 bytes
    [00:00:00.694,794] <inf> dmic_sample: Block 6 (1600 samples) | DC offset: -306
    [00:00:00.694,801] <inf> dmic_sample:   AC Min: -15 | AC Max: 11 | P2P: 26
    [00:00:00.694,806] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 603
    [00:00:00.793,255] <inf> dmic_sample: 7 - got buffer 0x20003c60 of 3200 bytes
    [00:00:00.793,687] <inf> dmic_sample: Block 7 (1600 samples) | DC offset: -306
    [00:00:00.793,694] <inf> dmic_sample:   AC Min: -13 | AC Max: 14 | P2P: 27
    [00:00:00.793,698] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 606
    [00:00:00.793,711] <inf> dmic_sample: 
    
    
    PCM output rate: 16000, channels: 2 
    
    [00:00:00.892,702] <inf> dmic_sample: 0 - got buffer 0x20002360 of 6400 bytes
    [00:00:00.893,525] <inf> dmic_sample: Block 0 (3200 samples) | DC offset: 6248
    [00:00:00.893,531] <inf> dmic_sample:   AC Min: -7465 | AC Max: 14197 | P2P: 21662
    [00:00:00.893,536] <inf> dmic_sample:   AC RMS: 8340 | Zero-crossings: 2
    [00:00:00.991,625] <inf> dmic_sample: 1 - got buffer 0x20005560 of 6400 bytes
    [00:00:00.992,448] <inf> dmic_sample: Block 1 (3200 samples) | DC offset: -1030
    [00:00:00.992,455] <inf> dmic_sample:   AC Min: -198 | AC Max: 197 | P2P: 395
    [00:00:00.992,460] <inf> dmic_sample:   AC RMS: 128 | Zero-crossings: 9
    [00:00:01.090,513] <inf> dmic_sample: 2 - got buffer 0x20003c60 of 6400 bytes
    [00:00:01.091,329] <inf> dmic_sample: Block 2 (3200 samples) | DC offset: -695
    [00:00:01.091,336] <inf> dmic_sample:   AC Min: -146 | AC Max: 138 | P2P: 284
    [00:00:01.091,341] <inf> dmic_sample:   AC RMS: 78 | Zero-crossings: 19
    [00:00:01.189,403] <inf> dmic_sample: 3 - got buffer 0x20002360 of 6400 bytes
    [00:00:01.190,220] <inf> dmic_sample: Block 3 (3200 samples) | DC offset: -433
    [00:00:01.190,231] <inf> dmic_sample:   AC Min: -134 | AC Max: 123 | P2P: 257
    [00:00:01.190,235] <inf> dmic_sample:   AC RMS: 72 | Zero-crossings: 31
    [00:00:01.288,296] <inf> dmic_sample: 4 - got buffer 0x20005560 of 6400 bytes
    [00:00:01.289,135] <inf> dmic_sample: Block 4 (3200 samples) | DC offset: -311
    [00:00:01.289,142] <inf> dmic_sample:   AC Min: -14 | AC Max: 14 | P2P: 28
    [00:00:01.289,147] <inf> dmic_sample:   AC RMS: 4 | Zero-crossings: 565
    [00:00:01.387,201] <inf> dmic_sample: 5 - got buffer 0x20003c60 of 6400 bytes
    [00:00:01.388,036] <inf> dmic_sample: Block 5 (3200 samples) | DC offset: -308
    [00:00:01.388,043] <inf> dmic_sample:   AC Min: -13 | AC Max: 13 | P2P: 26
    [00:00:01.388,048] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 598
    [00:00:01.486,106] <inf> dmic_sample: 6 - got buffer 0x20002360 of 6400 bytes
    [00:00:01.486,942] <inf> dmic_sample: Block 6 (3200 samples) | DC offset: -309
    [00:00:01.486,953] <inf> dmic_sample:   AC Min: -13 | AC Max: 11 | P2P: 24
    [00:00:01.486,958] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 613
    [00:00:01.585,017] <inf> dmic_sample: 7 - got buffer 0x20005560 of 6400 bytes
    [00:00:01.585,858] <inf> dmic_sample: Block 7 (3200 samples) | DC offset: -308
    [00:00:01.585,865] <inf> dmic_sample:   AC Min: -12 | AC Max: 14 | P2P: 26
    [00:00:01.585,870] <inf> dmic_sample:   AC RMS: 3 | Zero-crossings: 629
    [00:00:01.585,882] <inf> dmic_sample: Exiting
    


    The same code execution is repeated on different Units of Hardware.
    NRF54L15DK
    Different PDM Mics

  • Hi,

    Are you building for the nrf54l15dk_nrf54l15_cpuapp target? If so, the pins you are using match what is used in the devicetree overlay file, so that should be good.

    What about the edge polarity for your microphone? Which exact microphone are you using? Is data output or rising or falling edge?  Also, could it be a mixup between left and right channel? What if you tie L/R to VDDIO instead?

    You can also try to swap the channel map in code by adjusting line 186-187 in your modified code so that it looks like this:

    cfg.channel.req_chan_map_lo =
        dmic_build_channel_map(0, 0, PDM_CHAN_RIGHT);

    Lastly, the sample shows both mono and stereo, an din the second pass (line 197-208 in your modified code), and this should be removed as you only use/have one channel.

Related