PDM data to speaker

Hi,I am working with pdm mic to read data .I can able to get the audio data using dmic_read api in pcm format.I got clock of 1280000 and sampling rate of 16000 of each 16 bit sample.Now I want to play that data in tas2562 speaker amplifier.I configured pins for i2s tx i.e,clock,fclk,data out pin and I got clock frequency of 507936 of sampling rate 15873.And I got following output.I am making buffer slab free for every loop.


00> [00:00:00.001,215] <inf> dmic_sample: DMIC sample
00> [00:00:00.001,229] <inf> dmic_nrfx_pdm: PDM clock frequency: 1280000, actual PCM rate: 16000
00> [00:00:00.001,268] <inf> i2s_nrfx: I2S MCK frequency: 507936, actual PCM rate: 15873
00> Configure Stream Successful.
00> Trigger Command Successful.
00> [00:00:10.911,696] <err> dmic_nrfx_pdm: No room in RX queue
00> [00:00:11.012,512] <err> i2s_nrfx: Next buffers not supplied on time
00> [00:00:11.952,097] <err> dmic_sample: read failed: -11

  • Error in line 42: Source code not found.

    Looks to me like you tried to start the I2S output before you have enough data from the DMIC in order to be able to feed it. Order of operation matters here a lot.

  • #include <zephyr/kernel.h>
    #include <zephyr/audio/dmic.h>
    #include <zephyr/drivers/i2s.h>
    #include <zephyr/drivers/i2c.h>
    #include <zephyr/logging/log.h>
    #include "tas.h"
    
    #define MAX_SAMPLE_RATE 16000
    #define SAMPLE_BIT_WIDTH 16
    #define BYTES_PER_SAMPLE sizeof(int16_t)
    #define READ_TIMEOUT 1000
    #define SAMPLES_PER_BLOCK 160 
    #define NUMBER_OF_CHANNELS 1
    #define I2S_TX_NODE DT_NODELABEL(i2s_tx)
    #define I2C1_NODE DT_NODELABEL(tas2563)
    
    #define BLOCK_SIZE (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
    #define MAX_BLOCK_SIZE BLOCK_SIZE
    #define BLOCK_COUNT 8
    
    LOG_MODULE_REGISTER(dmic_sample);
    K_MEM_SLAB_DEFINE_STATIC(mem_slab, MAX_BLOCK_SIZE, BLOCK_COUNT, 4);
    static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C1_NODE);
    
    static bool configure_streams(const struct device *i2s_dev_tx, const struct i2s_config *config)
    {
    	int ret = i2s_configure(i2s_dev_tx, I2S_DIR_TX, config);
    	if (ret == 0)
    	{
    		printk("Configure Stream Successful.\n");
    		return true;
    	}
    	printk("Failed to configure streams: %d\n", ret);
    	return false;
    }
    static bool prepare_transfer(const struct device *i2s_dev_tx)
    {
    	int ret;
    
    	for (int i = 0; i < 2; ++i)
    	{
    		void *mem_block;
    
    		ret = k_mem_slab_alloc(&mem_slab, &mem_block, K_NO_WAIT);
    		if (ret < 0)
    		{
    			printk("Failed to allocate TX block %d: %d\n", i, ret);
    			return false;
    		}
    		memset(mem_block, 0, BLOCK_SIZE);
    
    		ret = i2s_write(i2s_dev_tx, mem_block, BLOCK_SIZE);
    		if (ret < 0)
    		{
    			printk("Failed to write block %d: %d\n", i, ret);
    			return false;
    		}
    	}
    
    	return true;
    }
    static bool trigger_command(const struct device *i2s_dev_tx, enum i2s_trigger_cmd cmd)
    {
    	int ret = i2s_trigger(i2s_dev_tx, I2S_DIR_TX, cmd);
    	if (ret == 0)
    	{
    		printk("Trigger Command Successful.\n");
    		return true;
    	}
    	printk("Failed to trigger command %d: %d\n", cmd, ret);
    	return false;
    }
    int main(void)
    {
    	const struct device *const dmic_dev = DEVICE_DT_GET(DT_NODELABEL(dmic_dev));
    	const struct device *const i2s_dev_tx = DEVICE_DT_GET(I2S_TX_NODE);
    	struct i2s_config config;
    	int ret;
    
    	LOG_INF("DMIC sample");
    
    	if (!device_is_ready(dmic_dev))
    	{
    		LOG_ERR("%s is not ready", dmic_dev->name);
    		return 0;
    	}
    
    	if (!device_is_ready(i2s_dev_tx))
    	{
    		printk("%s is not ready\n", i2s_dev_tx->name);
    		return 0;
    	}
    
    
    	struct pcm_stream_cfg stream = {
    		.pcm_width = SAMPLE_BIT_WIDTH,
    		.mem_slab = &mem_slab,
    	};
    	struct dmic_cfg cfg = {
    		.io = {
    			.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;
    
    	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;
    	}
    	config.word_size = 16;
    	config.channels = NUMBER_OF_CHANNELS;
    	config.format = I2S_FMT_DATA_FORMAT_I2S;
    	config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
    	config.frame_clk_freq = MAX_SAMPLE_RATE;
    	config.mem_slab = &mem_slab;
    	config.block_size = BLOCK_SIZE;
    	config.timeout = READ_TIMEOUT;
    	if (!configure_streams(i2s_dev_tx, &config))
    	{
    		return 0;
    	}
    	if (!prepare_transfer(i2s_dev_tx))
    	{
    		return 0;
    	}
    	if (!trigger_command(i2s_dev_tx, I2S_TRIGGER_START))
    	{
    		return 0;
    	}
    
    	while (1)
    	{
    		void *buffer;
    		uint32_t size;
    		ret = dmic_read(dmic_dev, 0, &buffer, &size, READ_TIMEOUT);
    		if (ret < 0)
    		{
    			LOG_ERR("read failed: %d", ret);
    			return ret;
    		}
    
    		ret = i2s_buf_write(i2s_dev_tx, buffer, size);
    		if (ret)
    		{
    			printk("Failed to write I2S buffer: %d\n", ret);
    			return;
    		}
    
    		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;
    	}
    
    	LOG_INF("Exiting");
    	return 0;
    }

    Can you please check the implementation of reading data from pdm mic and sending to speaker.I have included source code for same.

  • Hello,

    It is difficult to say exact without seeing the entire application, and being able to reproduce, but you should try replacing i2s_buf_write() with i2s_write(). The issue is that i2s_buf_write() is blocking waiting for a free buffer, but this is not being provided. Also, you should only free the mem_slab if there is an error (because i2s_write() frees it upon success).

    ret = i2s_write(i2s_dev_tx, buffer, size);
    if (ret) {
        LOG_ERR("Failed to write I2S buffer: %d", ret);
        k_mem_slab_free(&mem_slab, buffer);  // only free on error
        return 0;
    }

    Try increasing the block count from 8 to e.g. 16.

    #define BLOCK_COUNT 16

    Try moving dmic_trigger() to after the I2S has been configured:

    	config.word_size = 16;
    	config.channels = NUMBER_OF_CHANNELS;
    	config.format = I2S_FMT_DATA_FORMAT_I2S;
    	config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER;
    	config.frame_clk_freq = MAX_SAMPLE_RATE;
    	config.mem_slab = &mem_slab;
    	config.block_size = BLOCK_SIZE;
    	config.timeout = READ_TIMEOUT;
    	if (!configure_streams(i2s_dev_tx, &config))
    	{
    		return 0;
    	}
    	
    	ret = dmic_trigger(dmic_dev, DMIC_TRIGGER_START);
    	if (ret < 0)
    	{
    		LOG_ERR("START trigger failed: %d", ret);
    		return ret;
    	}

    Let me know if that changes anything. If not, it would be helpful if you could zip and post the application that you are using to replicate this, so that I can try to do it on a DK.

    Best regards,

    Edvin

  • I tried with the changes you mentioned but the errors its throwing ,I am attaching logs and zip file .00> [00:00:00.001,184] <inf> dmic_sample: DMIC sample
    00> [00:00:00.001,197] <inf> dmic_nrfx_pdm: PDM clock frequency: 1280000, actual PCM rate: 16000
    00> [00:00:00.001,222] <inf> i2s_nrfx: I2S MCK frequency: 507936, actual PCM rate: 15873
    00> Configure Stream Successful.
    00> Trigger Command Successful.
    00> [00:00:10.911,696] <err> dmic_nrfx_pdm: No room in RX queue
    00> [00:00:11.012,482] <err> i2s_nrfx: Next buffers not supplied on time
    00> [00:00:11.952,065] <err> dmic_sample: read failed: -11


    8358.dmic.zip

  • You cut away parts of the log here, right?

    When I run your application, there are a lot of logs saying "got buffer of 320 bytes" for about 10 seconds before I see the error messages. Do you see the same?

    However, it ends with:

    ...
    00> [00:00:00.001,184] <inf> dmic_sample: DMIC sample
    00> [00:00:00.001,197] <inf> dmic_nrfx_pdm: PDM clock frequency: 1280000, actual PCM rate: 16000
    00> [00:00:00.001,222] <inf> i2s_nrfx: I2S MCK frequency: 507936, actual PCM rate: 15873
    00> Configure Stream Successful.
    00> Trigger Command Successful.
    00> [00:00:10.911,696] <err> dmic_nrfx_pdm: No room in RX queue
    00> [00:00:11.012,482] <err> i2s_nrfx: Next buffers not supplied on time
    00> [00:00:11.952,065] <err> dmic_sample: read failed: -11

    Assuming this is what you see as well:

    The main issue is that the mic and speaker are working at two slightly different speeds (16000 in via mic and 15873 out via speaker). What happens is that the DMIC's internal queue is slowly filling up, and after about 10 seconds, the buffers are full, and it is stuck trying to read more, while the buffer is full. However, the buffers will never be freed, because the I2S is being prevented from executing (it is still waiting for the DMIC trying to read).

    One way to work around this is to split this up into two threads. One constantly reading the mic, storing the data in buffers, and another one emptying it, outputting to the speaker. 

    Since there is a difference in sample rate, there will still be some data that can't be output to the speaker, but you will be able to detect it, and skip one block.

    Alternatively, you need to look at your sample rates, and try to find something that is a better match, if the resulting outcome is not sufficient.

    Attached is a simple application showing how it can be done.

    43325.dmic.zip

    Best regards,

    Edvin

Related